• 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         if ((MemberExpressionKind::ELEMENT_ACCESS & kind_) != 0U) {
77             dumper->Add(".");
78         }
79     }
80     if ((MemberExpressionKind::ELEMENT_ACCESS & kind_) != 0U) {
81         dumper->Add("[");
82         property_->Dump(dumper);
83         dumper->Add("]");
84     } else {
85         dumper->Add(".");
86         property_->Dump(dumper);
87     }
88     if ((parent_ != nullptr) && (parent_->IsBlockStatement() || parent_->IsBlockExpression())) {
89         dumper->Add(";");
90         dumper->Endl();
91     }
92 }
93 
LoadRhs(compiler::PandaGen * pg) const94 void MemberExpression::LoadRhs(compiler::PandaGen *pg) const
95 {
96     compiler::RegScope rs(pg);
97     bool isSuper = object_->IsSuperExpression();
98     compiler::Operand prop = pg->ToPropertyKey(property_, computed_, isSuper);
99 
100     if (isSuper) {
101         pg->LoadSuperProperty(this, prop);
102     } else if (IsPrivateReference()) {
103         const auto &name = property_->AsIdentifier()->Name();
104         compiler::VReg objReg = pg->AllocReg();
105         pg->StoreAccumulator(this, objReg);
106         compiler::VReg ctor = pg->AllocReg();
107         compiler::Function::LoadClassContexts(this, pg, ctor, name);
108         pg->ClassPrivateFieldGet(this, ctor, objReg, name);
109     } else {
110         pg->LoadObjProperty(this, prop);
111     }
112 }
113 
CompileToRegs(compiler::PandaGen * pg,compiler::VReg object,compiler::VReg property) const114 void MemberExpression::CompileToRegs(compiler::PandaGen *pg, compiler::VReg object, compiler::VReg property) const
115 {
116     object_->Compile(pg);
117     pg->StoreAccumulator(this, object);
118 
119     pg->OptionalChainCheck(IsOptional(), object);
120 
121     if (!computed_) {
122         pg->LoadAccumulatorString(this, property_->AsIdentifier()->Name());
123     } else {
124         property_->Compile(pg);
125     }
126 
127     pg->StoreAccumulator(this, property);
128 }
129 
Compile(compiler::PandaGen * pg) const130 void MemberExpression::Compile(compiler::PandaGen *pg) const
131 {
132     pg->GetAstCompiler()->Compile(this);
133 }
134 
CompileToReg(compiler::PandaGen * pg,compiler::VReg objReg) const135 void MemberExpression::CompileToReg(compiler::PandaGen *pg, compiler::VReg objReg) const
136 {
137     object_->Compile(pg);
138     pg->StoreAccumulator(this, objReg);
139     pg->OptionalChainCheck(IsOptional(), objReg);
140     LoadRhs(pg);
141 }
142 
Compile(compiler::ETSGen * etsg) const143 void MemberExpression::Compile(compiler::ETSGen *etsg) const
144 {
145     etsg->GetAstCompiler()->Compile(this);
146 }
147 
Check(checker::TSChecker * checker)148 checker::Type *MemberExpression::Check(checker::TSChecker *checker)
149 {
150     return checker->GetAnalyzer()->Check(this);
151 }
152 
GetEnumMethodVariable(checker::ETSEnumType const * const enumInterface,const util::StringView propName)153 static varbinder::LocalVariable *GetEnumMethodVariable(checker::ETSEnumType const *const enumInterface,
154                                                        const util::StringView propName)
155 {
156     varbinder::LocalVariable *methodVar = nullptr;
157 
158     const auto *const boxedClass = enumInterface->GetDecl()->BoxedClass();
159     ASSERT(boxedClass->TsType()->IsETSObjectType());
160     const auto *const obj = boxedClass->TsType()->AsETSObjectType();
161 
162     std::string_view methodName = propName.Utf8();
163     if (enumInterface->IsETSStringEnumType() && (propName == checker::ETSEnumType::VALUE_OF_METHOD_NAME)) {
164         // For string enums valueOf method calls toString method
165         methodName = checker::ETSEnumType::TO_STRING_METHOD_NAME;
166     }
167 
168     const auto searchFlags =
169         checker::PropertySearchFlags::SEARCH_METHOD | checker::PropertySearchFlags::DISALLOW_SYNTHETIC_METHOD_CREATION;
170     methodVar = obj->GetProperty(methodName, searchFlags);
171 
172     return methodVar;
173 }
174 
ResolveEnumMember(checker::ETSChecker * checker,checker::ETSEnumType * type) const175 std::pair<checker::Type *, varbinder::LocalVariable *> MemberExpression::ResolveEnumMember(
176     checker::ETSChecker *checker, checker::ETSEnumType *type) const
177 {
178     if (parent_->Type() == ir::AstNodeType::CALL_EXPRESSION && parent_->AsCallExpression()->Callee() == this) {
179         auto *const memberType = type->LookupMethod(checker, object_, property_->AsIdentifier());
180         varbinder::LocalVariable *const memberVar = GetEnumMethodVariable(type, property_->AsIdentifier()->Name());
181         return {memberType, memberVar};
182     }
183 
184     auto *const literalType = type->LookupConstant(checker, object_, property_->AsIdentifier());
185     if (literalType == nullptr) {
186         return {nullptr, nullptr};
187     }
188     return {literalType, literalType->GetMemberVar()};
189 }
190 
ResolveObjectMember(checker::ETSChecker * checker) const191 std::pair<checker::Type *, varbinder::LocalVariable *> MemberExpression::ResolveObjectMember(
192     checker::ETSChecker *checker) const
193 {
194     auto resolveRes = checker->ResolveMemberReference(this, objType_);
195     switch (resolveRes.size()) {
196         case 0U: {
197             /* resolution failed, error already reported */
198             return {nullptr, nullptr};
199         }
200         case 1U: {
201             if (resolveRes[0]->Kind() == checker::ResolvedKind::PROPERTY) {
202                 auto var = resolveRes[0]->Variable()->AsLocalVariable();
203                 checker->ValidatePropertyAccess(var, objType_, property_->Start());
204                 return {checker->GetTypeOfVariable(var), var};
205             }
206             return {checker->GetTypeOfVariable(resolveRes[0]->Variable()), nullptr};
207         }
208         case 2U: {
209             auto classMethodType = checker->GetTypeOfVariable(resolveRes[1]->Variable());
210             auto extensionMethodType = checker->GetTypeOfVariable(resolveRes[0]->Variable());
211             auto *resolvedType = extensionMethodType;
212             if (classMethodType->IsETSFunctionType()) {
213                 ASSERT(extensionMethodType->IsETSFunctionType());
214                 resolvedType = checker->CreateETSExtensionFuncHelperType(classMethodType->AsETSFunctionType(),
215                                                                          extensionMethodType->AsETSFunctionType());
216             }
217             return {resolvedType, nullptr};
218         }
219         default: {
220             UNREACHABLE();
221         }
222     }
223 }
224 
TraverseUnionMember(checker::ETSChecker * checker,checker::ETSUnionType * unionType,checker::Type * commonPropType)225 checker::Type *MemberExpression::TraverseUnionMember(checker::ETSChecker *checker, checker::ETSUnionType *unionType,
226                                                      checker::Type *commonPropType)
227 
228 {
229     auto const addPropType = [this, checker, &commonPropType](checker::Type *memberType) {
230         if (commonPropType != nullptr && commonPropType != memberType) {
231             checker->LogTypeError("Member type must be the same for all union objects.", Start());
232         } else {
233             commonPropType = memberType;
234         }
235     };
236     for (auto *const type : unionType->ConstituentTypes()) {
237         auto *const apparent = checker->GetApparentType(type);
238         if (apparent->IsETSObjectType()) {
239             SetObjectType(apparent->AsETSObjectType());
240             addPropType(ResolveObjectMember(checker).first);
241         } else {
242             checker->LogTypeError({"Type ", unionType, " is illegal in union member expression."}, Start());
243         }
244     }
245     return commonPropType;
246 }
247 
CheckUnionMember(checker::ETSChecker * checker,checker::Type * baseType)248 checker::Type *MemberExpression::CheckUnionMember(checker::ETSChecker *checker, checker::Type *baseType)
249 {
250     auto *const unionType = baseType->AsETSUnionType();
251     auto *const commonPropType = TraverseUnionMember(checker, unionType, nullptr);
252     SetObjectType(checker->GlobalETSObjectType());
253     return commonPropType;
254 }
255 
AdjustType(checker::ETSChecker * checker,checker::Type * type)256 checker::Type *MemberExpression::AdjustType(checker::ETSChecker *checker, checker::Type *type)
257 {
258     auto *const objType = checker->GetApparentType(Object()->TsType());
259     if (PropVar() != nullptr) {  // access erased property type
260         uncheckedType_ = checker->GuaranteedTypeForUncheckedPropertyAccess(PropVar());
261     } else if (IsComputed() && objType->IsETSArrayType()) {  // access erased array or tuple type
262         uncheckedType_ = checker->GuaranteedTypeForUncheckedCast(objType->AsETSArrayType()->ElementType(), type);
263     }
264     SetTsType(type == nullptr ? checker->GlobalTypeError() : type);
265     return TsType();
266 }
267 
SetAndAdjustType(checker::ETSChecker * checker,checker::ETSObjectType * objectType)268 checker::Type *MemberExpression::SetAndAdjustType(checker::ETSChecker *checker, checker::ETSObjectType *objectType)
269 {
270     SetObjectType(objectType);
271     auto [resType, resVar] = ResolveObjectMember(checker);
272     if (resType == nullptr) {
273         SetTsType(checker->GlobalTypeError());
274         return checker->GlobalTypeError();
275     }
276     SetPropVar(resVar);
277     return AdjustType(checker, resType);
278 }
279 
CheckArrayIndexValue(checker::ETSChecker * checker) const280 bool MemberExpression::CheckArrayIndexValue(checker::ETSChecker *checker) const
281 {
282     std::size_t index;
283 
284     auto const &number = property_->AsNumberLiteral()->Number();
285 
286     if (number.IsInteger()) {
287         auto const value = number.GetLong();
288         if (value < 0) {
289             checker->LogTypeError("Index value cannot be less than zero.", property_->Start());
290             return false;
291         }
292         index = static_cast<std::size_t>(value);
293     } else if (number.IsReal()) {
294         double value = number.GetDouble();
295         double fraction = std::modf(value, &value);
296         if (value < 0.0 || fraction >= std::numeric_limits<double>::epsilon()) {
297             checker->LogTypeError("Index value cannot be less than zero or fractional.", property_->Start());
298             return false;
299         }
300         index = static_cast<std::size_t>(value);
301     } else {
302         UNREACHABLE();
303     }
304 
305     if (object_->IsArrayExpression() && object_->AsArrayExpression()->Elements().size() <= index) {
306         checker->LogTypeError("Index value cannot be greater than or equal to the array size.", property_->Start());
307         return false;
308     }
309 
310     return true;
311 }
312 
CheckIndexAccessMethod(checker::ETSChecker * checker)313 checker::Type *MemberExpression::CheckIndexAccessMethod(checker::ETSChecker *checker)
314 {
315     checker::PropertySearchFlags searchFlag =
316         checker::PropertySearchFlags::SEARCH_METHOD | checker::PropertySearchFlags::IS_FUNCTIONAL;
317     searchFlag |= checker::PropertySearchFlags::SEARCH_IN_BASE | checker::PropertySearchFlags::SEARCH_IN_INTERFACES;
318     // NOTE(DZ) maybe we need to exclude static methods: search_flag &= ~(checker::PropertySearchFlags::SEARCH_STATIC);
319 
320     if (objType_->HasTypeFlag(checker::TypeFlag::GENERIC)) {
321         searchFlag |= checker::PropertySearchFlags::SEARCH_ALL;
322     }
323 
324     bool const isSetter = Parent()->IsAssignmentExpression() && Parent()->AsAssignmentExpression()->Left() == this;
325     std::string_view const methodName =
326         isSetter ? compiler::Signatures::SET_INDEX_METHOD : compiler::Signatures::GET_INDEX_METHOD;
327 
328     auto *const method = objType_->GetProperty(methodName, searchFlag);
329     if (method == nullptr || !method->HasFlag(varbinder::VariableFlags::METHOD)) {
330         checker->LogTypeError("Object type doesn't have proper index access method.", Start());
331         return nullptr;
332     }
333 
334     ArenaVector<Expression *> arguments {checker->Allocator()->Adapter()};
335     arguments.emplace_back(property_);
336     if (isSetter) {
337         //  Temporary change the parent of right assignment node to check if correct "$_set" function presents.
338         //  later on in lowering the entire assignment expression will be replace top the call to that method.
339         auto *value = Parent()->AsAssignmentExpression()->Right();
340         value->SetParent(this);
341         arguments.emplace_back(value);
342     }
343 
344     auto &signatures = checker->GetTypeOfVariable(method)->AsETSFunctionType()->CallSignatures();
345 
346     checker::Signature *signature = checker->ValidateSignatures(signatures, nullptr, arguments, Start(), "indexing",
347                                                                 checker::TypeRelationFlag::NO_THROW);
348     if (signature == nullptr) {
349         checker->LogTypeError("Cannot find index access method with the required signature.", Property()->Start());
350         return nullptr;
351     }
352     checker->ValidateSignatureAccessibility(objType_, nullptr, signature, Start(),
353                                             "Index access method is not visible here.");
354 
355     ASSERT(signature->Function() != nullptr);
356 
357     if (signature->Function()->IsThrowing() || signature->Function()->IsRethrowing()) {
358         checker->CheckThrowingStatements(this);
359     }
360 
361     if (isSetter) {
362         //  Restore the right assignment node's parent to keep AST invariant valid.
363         Parent()->AsAssignmentExpression()->Right()->SetParent(Parent());
364         return signature->Params()[1]->TsType();
365     }
366 
367     return signature->ReturnType();
368 }
369 
CheckTupleAccessMethod(checker::ETSChecker * checker,checker::Type * baseType)370 checker::Type *MemberExpression::CheckTupleAccessMethod(checker::ETSChecker *checker, checker::Type *baseType)
371 {
372     ASSERT(baseType->IsETSTupleType());
373 
374     auto idxIfAny = checker->GetTupleElementAccessValue(Property()->TsType(), Property()->Start());
375     if (!idxIfAny.has_value()) {
376         return nullptr;
377     }
378     auto *const tupleTypeAtIdx = baseType->AsETSTupleType()->GetTypeAtIndex(*idxIfAny);
379 
380     if ((!Parent()->IsAssignmentExpression() || Parent()->AsAssignmentExpression()->Left() != this) &&
381         (!Parent()->IsUpdateExpression())) {
382         // Error never should be thrown by this call, because LUB of types can be converted to any type which
383         // LUB was calculated by casting
384         const checker::CastingContext cast(
385             checker->Relation(), {"Tuple type couldn't be converted "},
386             checker::CastingContext::ConstructorData {this, baseType->AsETSArrayType()->ElementType(), tupleTypeAtIdx,
387                                                       Start()});
388     }
389 
390     return tupleTypeAtIdx;
391 }
392 
CheckComputed(checker::ETSChecker * checker,checker::Type * baseType)393 checker::Type *MemberExpression::CheckComputed(checker::ETSChecker *checker, checker::Type *baseType)
394 {
395     if (baseType->IsETSDynamicType()) {
396         checker->ValidateArrayIndex(property_);
397         return checker->GlobalBuiltinDynamicType(baseType->AsETSDynamicType()->Language());
398     }
399 
400     if (baseType->IsETSArrayType()) {
401         auto *dflt = baseType->AsETSArrayType()->ElementType();
402         if (!baseType->IsETSTupleType() && !checker->ValidateArrayIndex(property_)) {
403             // error already reported to log
404             return dflt;
405         }
406 
407         if (baseType->IsETSTupleType() && !checker->ValidateTupleIndex(baseType->AsETSTupleType(), this)) {
408             // error reported to log
409             return dflt;
410         }
411 
412         // Check if the index value is inside array bounds if it is defined explicitly
413         if (property_->IsNumberLiteral() && !CheckArrayIndexValue(checker)) {
414             // error reported to log
415             return dflt;
416         }
417 
418         // NOTE: apply capture conversion on this type
419         if (baseType->IsETSTupleType()) {
420             auto *res = CheckTupleAccessMethod(checker, baseType);
421             return (res == nullptr) ? dflt : res;
422         }
423 
424         return dflt;
425     }
426 
427     if (baseType->IsETSObjectType()) {
428         SetObjectType(baseType->AsETSObjectType());
429         return CheckIndexAccessMethod(checker);
430     }
431     // NOTE(vpukhov): #20510 lowering
432     if (baseType->IsETSEnumType()) {
433         property_->Check(checker);
434         if (property_->TsType()->IsETSEnumType()) {
435             AddAstNodeFlags(ir::AstNodeFlags::GENERATE_GET_NAME);
436             return checker->GlobalBuiltinETSStringType();
437         }
438     }
439     checker->LogTypeError("Indexed access is not supported for such expression type.", Object()->Start());
440     return nullptr;
441 }
442 
Check(checker::ETSChecker * checker)443 checker::Type *MemberExpression::Check(checker::ETSChecker *checker)
444 {
445     return checker->GetAnalyzer()->Check(this);
446 }
447 
Clone(ArenaAllocator * const allocator,AstNode * const parent)448 MemberExpression *MemberExpression::Clone(ArenaAllocator *const allocator, AstNode *const parent)
449 {
450     if (auto *const clone = allocator->New<MemberExpression>(Tag {}, *this, allocator); clone != nullptr) {
451         if (parent != nullptr) {
452             clone->SetParent(parent);
453         }
454 
455         clone->SetRange(Range());
456         return clone;
457     }
458 
459     throw Error(ErrorType::GENERIC, "", CLONE_ALLOCATION_ERROR);
460 }
461 
462 }  // namespace ark::es2panda::ir
463