1/**
2 * Copyright (c) 2021 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "memberExpression.h"
17
18#include <compiler/core/pandagen.h>
19#include <typescript/checker.h>
20#include <ir/astDump.h>
21#include <ir/base/classDefinition.h>
22#include <ir/expressions/identifier.h>
23#include <ir/expressions/privateIdentifier.h>
24#include <ir/expressions/literals/numberLiteral.h>
25#include <ir/expressions/literals/stringLiteral.h>
26
27namespace panda::es2panda::ir {
28
29void MemberExpression::Iterate(const NodeTraverser &cb) const
30{
31    cb(object_);
32    cb(property_);
33}
34
35void MemberExpression::Dump(ir::AstDumper *dumper) const
36{
37    dumper->Add({{"type", "MemberExpression"},
38                 {"object", object_},
39                 {"property", property_},
40                 {"computed", computed_},
41                 {"optional", optional_}});
42}
43
44void MemberExpression::CompileObject(compiler::PandaGen *pg, compiler::VReg dest) const
45{
46    object_->Compile(pg);
47    pg->StoreAccumulator(this, dest);
48    pg->GetOptionalChain()->CheckNullish(optional_, dest);
49}
50
51compiler::Operand MemberExpression::CompileKey(compiler::PandaGen *pg) const
52{
53    return pg->ToPropertyKey(property_, computed_);
54}
55
56void MemberExpression::Compile(compiler::PandaGen *pg) const
57{
58    compiler::RegScope rs(pg);
59    compiler::VReg objReg = pg->AllocReg();
60    Compile(pg, objReg);
61}
62
63void MemberExpression::Compile(compiler::PandaGen *pg, compiler::VReg objReg) const
64{
65    CompileObject(pg, objReg);
66    if (AccessPrivateProperty()) {
67        auto name = property_->AsPrivateIdentifier()->Name();
68        auto result = pg->Scope()->FindPrivateName(name);
69        if (!result.result.isMethod) {
70            pg->LoadAccumulator(this, objReg);
71            pg->LoadPrivateProperty(this, result.lexLevel, result.result.slot);
72            return;
73        }
74        if (result.result.isSetter) {
75            pg->ThrowTypeError(this, "Property is not defined with Getter");
76        }
77        if (result.result.isStatic) {
78            pg->LoadLexicalVar(this, result.lexLevel, result.result.validateMethodSlot);
79            pg->Equal(this, objReg);
80            pg->ThrowTypeErrorIfFalse(this, "Object does not have private property");
81        } else {
82            pg->LoadAccumulator(this, objReg);
83            pg->LoadPrivateProperty(this, result.lexLevel, result.result.validateMethodSlot);
84        }
85
86        if (result.result.isGetter) {
87            pg->LoadAccumulator(this, objReg);
88            pg->LoadPrivateProperty(this, result.lexLevel, result.result.slot);
89            return;
90        }
91        pg->LoadLexicalVar(this, result.lexLevel, result.result.slot);
92        return;
93    }
94    compiler::Operand prop = CompileKey(pg);
95
96    if (object_->IsSuperExpression()) {
97        pg->LoadSuperProperty(this, objReg, prop);
98    } else {
99        pg->LoadObjProperty(this, objReg, prop);
100    }
101}
102
103checker::Type *MemberExpression::Check(checker::Checker *checker) const
104{
105    checker::Type *baseType = checker->CheckNonNullType(object_->Check(checker), object_->Start());
106
107    if (computed_) {
108        checker::Type *indexType = property_->Check(checker);
109        checker::Type *indexedAccessType = checker->GetPropertyTypeForIndexType(baseType, indexType);
110
111        if (indexedAccessType) {
112            return indexedAccessType;
113        }
114
115        if (!indexType->HasTypeFlag(checker::TypeFlag::STRING_LIKE | checker::TypeFlag::NUMBER_LIKE)) {
116            checker->ThrowTypeError({"Type ", indexType, " cannot be used as index type"}, property_->Start());
117        }
118
119        if (indexType->IsNumberType()) {
120            checker->ThrowTypeError("No index signature with a parameter of type 'string' was found on type this type",
121                                    Start());
122        }
123
124        if (indexType->IsStringType()) {
125            checker->ThrowTypeError("No index signature with a parameter of type 'number' was found on type this type",
126                                    Start());
127        }
128
129        switch (property_->Type()) {
130            case ir::AstNodeType::IDENTIFIER: {
131                checker->ThrowTypeError(
132                    {"Property ", property_->AsIdentifier()->Name(), " does not exist on this type."},
133                    property_->Start());
134            }
135            case ir::AstNodeType::NUMBER_LITERAL: {
136                checker->ThrowTypeError(
137                    {"Property ", property_->AsNumberLiteral()->Str(), " does not exist on this type."},
138                    property_->Start());
139            }
140            case ir::AstNodeType::STRING_LITERAL: {
141                checker->ThrowTypeError(
142                    {"Property ", property_->AsStringLiteral()->Str(), " does not exist on this type."},
143                    property_->Start());
144            }
145            default: {
146                UNREACHABLE();
147            }
148        }
149    }
150
151    binder::Variable *prop = checker->GetPropertyOfType(baseType, property_->AsIdentifier()->Name());
152
153    if (prop) {
154        checker::Type *propType = checker->GetTypeOfVariable(prop);
155        if (prop->HasFlag(binder::VariableFlags::READONLY)) {
156            propType->AddTypeFlag(checker::TypeFlag::READONLY);
157        }
158
159        return propType;
160    }
161
162    if (baseType->IsObjectType()) {
163        checker::ObjectType *objType = baseType->AsObjectType();
164
165        if (objType->StringIndexInfo()) {
166            checker::Type *indexType = objType->StringIndexInfo()->GetType();
167            if (objType->StringIndexInfo()->Readonly()) {
168                indexType->AddTypeFlag(checker::TypeFlag::READONLY);
169            }
170
171            return indexType;
172        }
173    }
174
175    checker->ThrowTypeError({"Property ", property_->AsIdentifier()->Name(), " does not exist on this type."},
176                            property_->Start());
177    return nullptr;
178}
179
180void MemberExpression::UpdateSelf(const NodeUpdater &cb, [[maybe_unused]] binder::Binder *binder)
181{
182    object_ = std::get<ir::AstNode *>(cb(object_))->AsExpression();
183    property_ = std::get<ir::AstNode *>(cb(property_))->AsExpression();
184}
185
186}  // namespace panda::es2panda::ir
187