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