• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2024 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 "checker/TSchecker.h"
19 #include "checker/ets/castingContext.h"
20 #include "compiler/core/ETSGen.h"
21 #include "compiler/core/pandagen.h"
22 #include "ir/astDump.h"
23 #include "ir/srcDump.h"
24 
25 namespace panda::es2panda::ir {
MemberExpression(Tag const tag,MemberExpression const & other,Expression * const object,Expression * const property)26 MemberExpression::MemberExpression([[maybe_unused]] Tag const tag, MemberExpression const &other,
27                                    Expression *const object, Expression *const property)
28     : MemberExpression(other)
29 {
30     object_ = object;
31     if (object_ != nullptr) {
32         object_->SetParent(this);
33     }
34 
35     property_ = property;
36     if (property_ != nullptr) {
37         property_->SetParent(this);
38     }
39 }
40 
IsPrivateReference() const41 bool MemberExpression::IsPrivateReference() const noexcept
42 {
43     return property_->IsIdentifier() && property_->AsIdentifier()->IsPrivateIdent();
44 }
45 
TransformChildren(const NodeTransformer & cb)46 void MemberExpression::TransformChildren(const NodeTransformer &cb)
47 {
48     object_ = cb(object_)->AsExpression();
49     property_ = cb(property_)->AsExpression();
50 }
51 
Iterate(const NodeTraverser & cb) const52 void MemberExpression::Iterate(const NodeTraverser &cb) const
53 {
54     cb(object_);
55     cb(property_);
56 }
57 
Dump(ir::AstDumper * dumper) const58 void MemberExpression::Dump(ir::AstDumper *dumper) const
59 {
60     dumper->Add({{"type", "MemberExpression"},
61                  {"object", object_},
62                  {"property", property_},
63                  {"computed", computed_},
64                  {"optional", IsOptional()}});
65 }
66 
Dump(ir::SrcDumper * dumper) const67 void MemberExpression::Dump(ir::SrcDumper *dumper) const
68 {
69     ASSERT(object_ != nullptr);
70     ASSERT(property_ != nullptr);
71 
72     object_->Dump(dumper);
73     if (IsOptional()) {
74         dumper->Add("?");
75     }
76     if ((MemberExpressionKind::ELEMENT_ACCESS & kind_) != 0U) {
77         dumper->Add("[");
78         property_->Dump(dumper);
79         dumper->Add("]");
80     } else {
81         dumper->Add(".");
82         property_->Dump(dumper);
83     }
84     if ((parent_ != nullptr) && (parent_->IsBlockStatement() || parent_->IsBlockExpression())) {
85         dumper->Add(";");
86         dumper->Endl();
87     }
88 }
89 
LoadRhs(compiler::PandaGen * pg) const90 void MemberExpression::LoadRhs(compiler::PandaGen *pg) const
91 {
92     compiler::RegScope rs(pg);
93     bool isSuper = object_->IsSuperExpression();
94     compiler::Operand prop = pg->ToPropertyKey(property_, computed_, isSuper);
95 
96     if (isSuper) {
97         pg->LoadSuperProperty(this, prop);
98     } else if (IsPrivateReference()) {
99         const auto &name = property_->AsIdentifier()->Name();
100         compiler::VReg objReg = pg->AllocReg();
101         pg->StoreAccumulator(this, objReg);
102         compiler::VReg ctor = pg->AllocReg();
103         compiler::Function::LoadClassContexts(this, pg, ctor, name);
104         pg->ClassPrivateFieldGet(this, ctor, objReg, name);
105     } else {
106         pg->LoadObjProperty(this, prop);
107     }
108 }
109 
CompileToRegs(compiler::PandaGen * pg,compiler::VReg object,compiler::VReg property) const110 void MemberExpression::CompileToRegs(compiler::PandaGen *pg, compiler::VReg object, compiler::VReg property) const
111 {
112     object_->Compile(pg);
113     pg->StoreAccumulator(this, object);
114 
115     pg->OptionalChainCheck(IsOptional(), object);
116 
117     if (!computed_) {
118         pg->LoadAccumulatorString(this, property_->AsIdentifier()->Name());
119     } else {
120         property_->Compile(pg);
121     }
122 
123     pg->StoreAccumulator(this, property);
124 }
125 
Compile(compiler::PandaGen * pg) const126 void MemberExpression::Compile(compiler::PandaGen *pg) const
127 {
128     pg->GetAstCompiler()->Compile(this);
129 }
130 
CompileToReg(compiler::PandaGen * pg,compiler::VReg objReg) const131 void MemberExpression::CompileToReg(compiler::PandaGen *pg, compiler::VReg objReg) const
132 {
133     object_->Compile(pg);
134     pg->StoreAccumulator(this, objReg);
135     pg->OptionalChainCheck(IsOptional(), objReg);
136     LoadRhs(pg);
137 }
138 
Compile(compiler::ETSGen * etsg) const139 void MemberExpression::Compile(compiler::ETSGen *etsg) const
140 {
141     etsg->GetAstCompiler()->Compile(this);
142 }
143 
Check(checker::TSChecker * checker)144 checker::Type *MemberExpression::Check(checker::TSChecker *checker)
145 {
146     return checker->GetAnalyzer()->Check(this);
147 }
148 
ResolveEnumMember(checker::ETSChecker * checker,checker::Type * type) const149 std::pair<checker::Type *, varbinder::LocalVariable *> MemberExpression::ResolveEnumMember(checker::ETSChecker *checker,
150                                                                                            checker::Type *type) const
151 {
152     auto const *const enumInterface = [type]() -> checker::ETSEnumInterface const * {
153         if (type->IsETSEnumType()) {
154             return type->AsETSEnumType();
155         }
156         return type->AsETSStringEnumType();
157     }();
158 
159     if (parent_->Type() == ir::AstNodeType::CALL_EXPRESSION && parent_->AsCallExpression()->Callee() == this) {
160         return {enumInterface->LookupMethod(checker, object_, property_->AsIdentifier()), nullptr};
161     }
162 
163     auto *const literalType = enumInterface->LookupConstant(checker, object_, property_->AsIdentifier());
164     return {literalType, literalType->GetMemberVar()};
165 }
166 
ResolveObjectMember(checker::ETSChecker * checker) const167 std::pair<checker::Type *, varbinder::LocalVariable *> MemberExpression::ResolveObjectMember(
168     checker::ETSChecker *checker) const
169 {
170     auto resolveRes = checker->ResolveMemberReference(this, objType_);
171     switch (resolveRes.size()) {
172         case 1U: {
173             if (resolveRes[0]->Kind() == checker::ResolvedKind::PROPERTY) {
174                 auto var = resolveRes[0]->Variable()->AsLocalVariable();
175                 checker->ValidatePropertyAccess(var, objType_, property_->Start());
176                 return {checker->GetTypeOfVariable(var), var};
177             }
178             return {checker->GetTypeOfVariable(resolveRes[0]->Variable()), nullptr};
179         }
180         case 2U: {
181             // ETSExtensionFuncHelperType(class_method_type, extension_method_type)
182             auto *resolvedType = checker->CreateETSExtensionFuncHelperType(
183                 checker->GetTypeOfVariable(resolveRes[1]->Variable())->AsETSFunctionType(),
184                 checker->GetTypeOfVariable(resolveRes[0]->Variable())->AsETSFunctionType());
185             return {resolvedType, nullptr};
186         }
187         default: {
188             UNREACHABLE();
189         }
190     }
191 }
192 
CheckUnionMember(checker::ETSChecker * checker,checker::Type * baseType)193 checker::Type *MemberExpression::CheckUnionMember(checker::ETSChecker *checker, checker::Type *baseType)
194 {
195     auto *const unionType = baseType->AsETSUnionType();
196     checker::Type *commonPropType = nullptr;
197     auto const addPropType = [this, checker, &commonPropType](checker::Type *memberType) {
198         if (commonPropType != nullptr && commonPropType != memberType) {
199             checker->ThrowTypeError("Member type must be the same for all union objects.", Start());
200         }
201         commonPropType = memberType;
202     };
203     for (auto *const type : unionType->ConstituentTypes()) {
204         auto *const apparent = checker->GetApparentType(type);
205         if (apparent->IsETSObjectType()) {
206             SetObjectType(apparent->AsETSObjectType());
207             addPropType(ResolveObjectMember(checker).first);
208         } else if (apparent->IsETSEnumType() || baseType->IsETSStringEnumType()) {
209             addPropType(ResolveEnumMember(checker, apparent).first);
210         } else {
211             UNREACHABLE();
212         }
213     }
214     SetObjectType(unionType->GetLeastUpperBoundType()->AsETSObjectType());
215     return commonPropType;
216 }
217 
AdjustType(checker::ETSChecker * checker,checker::Type * type)218 checker::Type *MemberExpression::AdjustType(checker::ETSChecker *checker, checker::Type *type)
219 {
220     SetOptionalType(type);
221     if (PropVar() != nullptr) {
222         uncheckedType_ = checker->GuaranteedTypeForUncheckedPropertyAccess(PropVar());
223     }
224     if (IsOptional() && checker->MayHaveNulllikeValue(Object()->TsType())) {
225         checker->Relation()->SetNode(this);
226         type = checker->CreateOptionalResultType(type);
227         checker->Relation()->SetNode(nullptr);
228     }
229     SetTsType(type);
230     return TsType();
231 }
232 
CheckArrayIndexValue(checker::ETSChecker * checker) const233 void MemberExpression::CheckArrayIndexValue(checker::ETSChecker *checker) const
234 {
235     std::size_t index;
236 
237     auto const &number = property_->AsNumberLiteral()->Number();
238 
239     if (number.IsInteger()) {
240         auto const value = number.GetLong();
241         if (value < 0) {
242             checker->ThrowTypeError("Index value cannot be less than zero.", property_->Start());
243         }
244         index = static_cast<std::size_t>(value);
245     } else if (number.IsReal()) {
246         double value = number.GetDouble();
247         double fraction = std::modf(value, &value);
248         if (value < 0.0 || fraction >= std::numeric_limits<double>::epsilon()) {
249             checker->ThrowTypeError("Index value cannot be less than zero or fractional.", property_->Start());
250         }
251         index = static_cast<std::size_t>(value);
252     } else {
253         UNREACHABLE();
254     }
255 
256     if (object_->IsArrayExpression() && object_->AsArrayExpression()->Elements().size() <= index) {
257         checker->ThrowTypeError("Index value cannot be greater than or equal to the array size.", property_->Start());
258     }
259 
260     if (object_->IsIdentifier() &&
261         object_->AsIdentifier()->Variable()->Declaration()->Node()->Parent()->IsVariableDeclarator()) {
262         auto const varDecl =
263             object_->AsIdentifier()->Variable()->Declaration()->Node()->Parent()->AsVariableDeclarator();
264         if (varDecl->Init() != nullptr && varDecl->Init()->IsArrayExpression() &&
265             varDecl->Init()->AsArrayExpression()->Elements().size() <= index) {
266             checker->ThrowTypeError("Index value cannot be greater than or equal to the array size.",
267                                     property_->Start());
268         }
269     }
270 }
271 
CheckIndexAccessMethod(checker::ETSChecker * checker)272 checker::Type *MemberExpression::CheckIndexAccessMethod(checker::ETSChecker *checker)
273 {
274     checker::PropertySearchFlags searchFlag =
275         checker::PropertySearchFlags::SEARCH_METHOD | checker::PropertySearchFlags::IS_FUNCTIONAL;
276     searchFlag |= checker::PropertySearchFlags::SEARCH_IN_BASE | checker::PropertySearchFlags::SEARCH_IN_INTERFACES;
277     // NOTE(DZ) maybe we need to exclude static methods: search_flag &= ~(checker::PropertySearchFlags::SEARCH_STATIC);
278 
279     if (objType_->HasTypeFlag(checker::TypeFlag::GENERIC)) {
280         searchFlag |= checker::PropertySearchFlags::SEARCH_ALL;
281     }
282 
283     bool const isSetter = Parent()->IsAssignmentExpression() && Parent()->AsAssignmentExpression()->Left() == this;
284     std::string_view const methodName =
285         isSetter ? compiler::Signatures::SET_INDEX_METHOD : compiler::Signatures::GET_INDEX_METHOD;
286 
287     auto *const method = objType_->GetProperty(methodName, searchFlag);
288     if (method == nullptr || !method->HasFlag(varbinder::VariableFlags::METHOD)) {
289         checker->ThrowTypeError("Object type doesn't have proper index access method.", Start());
290     }
291 
292     ArenaVector<Expression *> arguments {checker->Allocator()->Adapter()};
293     arguments.emplace_back(property_);
294     if (isSetter) {
295         arguments.emplace_back(Parent()->AsAssignmentExpression()->Right());
296     }
297 
298     auto &signatures = checker->GetTypeOfVariable(method)->AsETSFunctionType()->CallSignatures();
299 
300     checker::Signature *signature = checker->ValidateSignatures(signatures, nullptr, arguments, Start(), "indexing",
301                                                                 checker::TypeRelationFlag::NO_THROW);
302     if (signature == nullptr) {
303         checker->ThrowTypeError("Cannot find index access method with the required signature.", Property()->Start());
304     }
305     checker->ValidateSignatureAccessibility(objType_, nullptr, signature, Start(),
306                                             "Index access method is not visible here.");
307 
308     ASSERT(signature->Function() != nullptr);
309 
310     if (signature->Function()->IsThrowing() || signature->Function()->IsRethrowing()) {
311         checker->CheckThrowingStatements(this);
312     }
313 
314     return isSetter ? signature->Params()[1]->TsType() : signature->ReturnType();
315 }
316 
CheckTupleAccessMethod(checker::ETSChecker * checker,checker::Type * baseType)317 checker::Type *MemberExpression::CheckTupleAccessMethod(checker::ETSChecker *checker, checker::Type *baseType)
318 {
319     ASSERT(baseType->IsETSTupleType());
320 
321     auto *const tupleTypeAtIdx =
322         baseType->AsETSTupleType()->GetTypeAtIndex(checker->GetTupleElementAccessValue(Property()->TsType()));
323 
324     if ((!Parent()->IsAssignmentExpression() || Parent()->AsAssignmentExpression()->Left() != this) &&
325         (!Parent()->IsUpdateExpression())) {
326         // Error never should be thrown by this call, because LUB of types can be converted to any type which
327         // LUB was calculated by casting
328         const checker::CastingContext cast(checker->Relation(), this, baseType->AsETSArrayType()->ElementType(),
329                                            tupleTypeAtIdx, Start(), {"Tuple type couldn't be converted "});
330 
331         // NOTE(mmartin): this can be replaced with the general type mapper, once implemented
332         if ((GetBoxingUnboxingFlags() & ir::BoxingUnboxingFlags::UNBOXING_FLAG) != 0U) {
333             auto *const savedNode = checker->Relation()->GetNode();
334             if (savedNode == nullptr) {
335                 checker->Relation()->SetNode(this);
336             }
337 
338             SetTupleConvertedType(checker->PrimitiveTypeAsETSBuiltinType(tupleTypeAtIdx));
339 
340             checker->Relation()->SetNode(savedNode);
341         }
342 
343         if (tupleTypeAtIdx->IsETSObjectType() && baseType->AsETSArrayType()->ElementType()->IsETSObjectType()) {
344             SetTupleConvertedType(tupleTypeAtIdx);
345         }
346     }
347 
348     return tupleTypeAtIdx;
349 }
350 
CheckComputed(checker::ETSChecker * checker,checker::Type * baseType)351 checker::Type *MemberExpression::CheckComputed(checker::ETSChecker *checker, checker::Type *baseType)
352 {
353     if (baseType->IsETSArrayType() || baseType->IsETSDynamicType()) {
354         checker->ValidateArrayIndex(property_);
355 
356         if (baseType->IsETSTupleType()) {
357             checker->ValidateTupleIndex(baseType->AsETSTupleType(), this);
358         } else if (baseType->IsETSArrayType() && property_->IsNumberLiteral()) {
359             // Check if the index value is inside array bounds if it is defined explicitly
360             CheckArrayIndexValue(checker);
361         }
362 
363         // NOTE: apply capture conversion on this type
364         if (baseType->IsETSArrayType()) {
365             if (baseType->IsETSTupleType()) {
366                 return CheckTupleAccessMethod(checker, baseType);
367             }
368 
369             if (object_->IsArrayExpression() && property_->IsNumberLiteral()) {
370                 auto const number = property_->AsNumberLiteral()->Number().GetLong();
371                 return object_->AsArrayExpression()->Elements()[number]->Check(checker);
372             }
373 
374             return baseType->AsETSArrayType()->ElementType();
375         }
376 
377         // Dynamic
378         return checker->GlobalBuiltinDynamicType(baseType->AsETSDynamicType()->Language());
379     }
380 
381     if (baseType->IsETSObjectType()) {
382         SetObjectType(baseType->AsETSObjectType());
383         return CheckIndexAccessMethod(checker);
384     }
385 
386     checker->ThrowTypeError("Indexed access is not supported for such expression type.", Object()->Start());
387 }
388 
Check(checker::ETSChecker * checker)389 checker::Type *MemberExpression::Check(checker::ETSChecker *checker)
390 {
391     return checker->GetAnalyzer()->Check(this);
392 }
393 
394 // NOLINTNEXTLINE(google-default-arguments)
Clone(ArenaAllocator * const allocator,AstNode * const parent)395 MemberExpression *MemberExpression::Clone(ArenaAllocator *const allocator, AstNode *const parent)
396 {
397     auto *const object = object_ != nullptr ? object_->Clone(allocator)->AsExpression() : nullptr;
398     auto *const property = property_ != nullptr ? property_->Clone(allocator)->AsExpression() : nullptr;
399 
400     if (auto *const clone =
401             allocator->New<MemberExpression>(object, property, kind_, computed_, MaybeOptionalExpression::IsOptional());
402         clone != nullptr) {
403         if (object != nullptr) {
404             object->SetParent(clone);
405         }
406         if (property != nullptr) {
407             property->SetParent(clone);
408         }
409         if (parent != nullptr) {
410             clone->SetParent(parent);
411         }
412         return clone;
413     }
414 
415     throw Error(ErrorType::GENERIC, "", CLONE_ALLOCATION_ERROR);
416 }
417 
418 }  // namespace panda::es2panda::ir
419