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([[maybe_unused]] 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 
35 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 
ResolveEnumMember(checker::ETSChecker *checker, checker::Type *type) const153 std::pair<checker::Type *, varbinder::LocalVariable *> MemberExpression::ResolveEnumMember(checker::ETSChecker *checker,
154                                                                                            checker::Type *type) const
155 {
156     auto const *const enumInterface = [type]() -> checker::ETSEnumType const * {
157         if (type->IsETSIntEnumType()) {
158             return type->AsETSIntEnumType();
159         }
160         return type->AsETSStringEnumType();
161     }();
162 
163     if (parent_->Type() == ir::AstNodeType::CALL_EXPRESSION && parent_->AsCallExpression()->Callee() == this) {
164         return {enumInterface->LookupMethod(checker, object_, property_->AsIdentifier()), nullptr};
165     }
166 
167     auto *const literalType = enumInterface->LookupConstant(checker, object_, property_->AsIdentifier());
168     if (literalType == nullptr) {
169         return {nullptr, nullptr};
170     }
171     return {literalType, literalType->GetMemberVar()};
172 }
173 
ResolveObjectMember( checker::ETSChecker *checker) const174 std::pair<checker::Type *, varbinder::LocalVariable *> MemberExpression::ResolveObjectMember(
175     checker::ETSChecker *checker) const
176 {
177     auto resolveRes = checker->ResolveMemberReference(this, objType_);
178     switch (resolveRes.size()) {
179         case 0U: {
180             /* resolution failed, error already reported */
181             return {nullptr, nullptr};
182         }
183         case 1U: {
184             if (resolveRes[0]->Kind() == checker::ResolvedKind::PROPERTY) {
185                 auto var = resolveRes[0]->Variable()->AsLocalVariable();
186                 checker->ValidatePropertyAccess(var, objType_, property_->Start());
187                 return {checker->GetTypeOfVariable(var), var};
188             }
189             return {checker->GetTypeOfVariable(resolveRes[0]->Variable()), nullptr};
190         }
191         case 2U: {
192             auto classMethodType = checker->GetTypeOfVariable(resolveRes[1]->Variable());
193             auto extensionMethodType = checker->GetTypeOfVariable(resolveRes[0]->Variable());
194             auto *resolvedType = extensionMethodType;
195             if (classMethodType->IsETSFunctionType()) {
196                 ASSERT(extensionMethodType->IsETSFunctionType());
197                 resolvedType = checker->CreateETSExtensionFuncHelperType(classMethodType->AsETSFunctionType(),
198                                                                          extensionMethodType->AsETSFunctionType());
199             }
200             return {resolvedType, nullptr};
201         }
202         default: {
203             UNREACHABLE();
204         }
205     }
206 }
207 
TraverseUnionMember(checker::ETSChecker *checker, checker::ETSUnionType *unionType, checker::Type *commonPropType)208 checker::Type *MemberExpression::TraverseUnionMember(checker::ETSChecker *checker, checker::ETSUnionType *unionType,
209                                                      checker::Type *commonPropType)
210 
211 {
212     auto const addPropType = [this, checker, &commonPropType](checker::Type *memberType) {
213         if (commonPropType != nullptr && commonPropType != memberType) {
214             checker->LogTypeError("Member type must be the same for all union objects.", Start());
215         } else {
216             commonPropType = memberType;
217         }
218     };
219     for (auto *const type : unionType->ConstituentTypes()) {
220         auto *const apparent = checker->GetApparentType(type);
221         if (apparent->IsETSObjectType()) {
222             SetObjectType(apparent->AsETSObjectType());
223             addPropType(ResolveObjectMember(checker).first);
224         } else if (apparent->IsETSEnumType()) {
225             addPropType(ResolveEnumMember(checker, apparent).first);
226         } else {
227             checker->LogTypeError({"Type ", unionType, " is illegal in union member expression."}, Start());
228         }
229     }
230     return commonPropType;
231 }
232 
CheckUnionMember(checker::ETSChecker *checker, checker::Type *baseType)233 checker::Type *MemberExpression::CheckUnionMember(checker::ETSChecker *checker, checker::Type *baseType)
234 {
235     auto *const unionType = baseType->AsETSUnionType();
236     auto *const commonPropType = TraverseUnionMember(checker, unionType, nullptr);
237     SetObjectType(checker->GlobalETSObjectType());
238     return commonPropType;
239 }
240 
AdjustType(checker::ETSChecker *checker, checker::Type *type)241 checker::Type *MemberExpression::AdjustType(checker::ETSChecker *checker, checker::Type *type)
242 {
243     auto *const objType = checker->GetApparentType(Object()->TsType());
244     if (PropVar() != nullptr) {  // access erased property type
245         uncheckedType_ = checker->GuaranteedTypeForUncheckedPropertyAccess(PropVar());
246     } else if (IsComputed() && objType->IsETSArrayType()) {  // access erased array or tuple type
247         uncheckedType_ = checker->GuaranteedTypeForUncheckedCast(objType->AsETSArrayType()->ElementType(), type);
248     }
249     SetTsType(type == nullptr ? checker->GlobalTypeError() : type);
250     return TsTypeOrError();
251 }
252 
SetAndAdjustType(checker::ETSChecker *checker, checker::ETSObjectType *objectType)253 checker::Type *MemberExpression::SetAndAdjustType(checker::ETSChecker *checker, checker::ETSObjectType *objectType)
254 {
255     SetObjectType(objectType);
256     auto [resType, resVar] = ResolveObjectMember(checker);
257     if (resType == nullptr) {
258         SetTsType(checker->GlobalTypeError());
259         return checker->GlobalTypeError();
260     }
261     SetPropVar(resVar);
262     return AdjustType(checker, resType);
263 }
264 
CheckArrayIndexValue(checker::ETSChecker *checker) const265 bool MemberExpression::CheckArrayIndexValue(checker::ETSChecker *checker) const
266 {
267     std::size_t index;
268 
269     auto const &number = property_->AsNumberLiteral()->Number();
270 
271     if (number.IsInteger()) {
272         auto const value = number.GetLong();
273         if (value < 0) {
274             checker->LogTypeError("Index value cannot be less than zero.", property_->Start());
275             return false;
276         }
277         index = static_cast<std::size_t>(value);
278     } else if (number.IsReal()) {
279         double value = number.GetDouble();
280         double fraction = std::modf(value, &value);
281         if (value < 0.0 || fraction >= std::numeric_limits<double>::epsilon()) {
282             checker->LogTypeError("Index value cannot be less than zero or fractional.", property_->Start());
283             return false;
284         }
285         index = static_cast<std::size_t>(value);
286     } else {
287         UNREACHABLE();
288     }
289 
290     if (object_->IsArrayExpression() && object_->AsArrayExpression()->Elements().size() <= index) {
291         checker->LogTypeError("Index value cannot be greater than or equal to the array size.", property_->Start());
292         return false;
293     }
294 
295     return true;
296 }
297 
CheckIndexAccessMethod(checker::ETSChecker *checker)298 checker::Type *MemberExpression::CheckIndexAccessMethod(checker::ETSChecker *checker)
299 {
300     checker::PropertySearchFlags searchFlag =
301         checker::PropertySearchFlags::SEARCH_METHOD | checker::PropertySearchFlags::IS_FUNCTIONAL;
302     searchFlag |= checker::PropertySearchFlags::SEARCH_IN_BASE | checker::PropertySearchFlags::SEARCH_IN_INTERFACES;
303     // NOTE(DZ) maybe we need to exclude static methods: search_flag &= ~(checker::PropertySearchFlags::SEARCH_STATIC);
304 
305     if (objType_->HasTypeFlag(checker::TypeFlag::GENERIC)) {
306         searchFlag |= checker::PropertySearchFlags::SEARCH_ALL;
307     }
308 
309     bool const isSetter = Parent()->IsAssignmentExpression() && Parent()->AsAssignmentExpression()->Left() == this;
310     std::string_view const methodName =
311         isSetter ? compiler::Signatures::SET_INDEX_METHOD : compiler::Signatures::GET_INDEX_METHOD;
312 
313     auto *const method = objType_->GetProperty(methodName, searchFlag);
314     if (method == nullptr || !method->HasFlag(varbinder::VariableFlags::METHOD)) {
315         checker->LogTypeError("Object type doesn't have proper index access method.", Start());
316         return nullptr;
317     }
318 
319     ArenaVector<Expression *> arguments {checker->Allocator()->Adapter()};
320     arguments.emplace_back(property_);
321     if (isSetter) {
322         //  Temporary change the parent of right assignment node to check if correct "$_set" function presents.
323         //  later on in lowering the entire assignment expression will be replace top the call to that method.
324         auto *value = Parent()->AsAssignmentExpression()->Right();
325         value->SetParent(this);
326         arguments.emplace_back(value);
327     }
328 
329     auto &signatures = checker->GetTypeOfVariable(method)->AsETSFunctionType()->CallSignatures();
330 
331     checker::Signature *signature = checker->ValidateSignatures(signatures, nullptr, arguments, Start(), "indexing",
332                                                                 checker::TypeRelationFlag::NO_THROW);
333     if (signature == nullptr) {
334         checker->LogTypeError("Cannot find index access method with the required signature.", Property()->Start());
335         return nullptr;
336     }
337     checker->ValidateSignatureAccessibility(objType_, nullptr, signature, Start(),
338                                             "Index access method is not visible here.");
339 
340     ASSERT(signature->Function() != nullptr);
341 
342     if (signature->Function()->IsThrowing() || signature->Function()->IsRethrowing()) {
343         checker->CheckThrowingStatements(this);
344     }
345 
346     if (isSetter) {
347         //  Restore the right assignment node's parent to keep AST invariant valid.
348         Parent()->AsAssignmentExpression()->Right()->SetParent(Parent());
349         return signature->Params()[1]->TsType();
350     }
351 
352     return signature->ReturnType();
353 }
354 
CheckTupleAccessMethod(checker::ETSChecker *checker, checker::Type *baseType)355 checker::Type *MemberExpression::CheckTupleAccessMethod(checker::ETSChecker *checker, checker::Type *baseType)
356 {
357     ASSERT(baseType->IsETSTupleType());
358 
359     auto idxIfAny = checker->GetTupleElementAccessValue(Property()->TsType(), Property()->Start());
360     if (!idxIfAny.has_value()) {
361         return nullptr;
362     }
363     auto *const tupleTypeAtIdx = baseType->AsETSTupleType()->GetTypeAtIndex(*idxIfAny);
364 
365     if ((!Parent()->IsAssignmentExpression() || Parent()->AsAssignmentExpression()->Left() != this) &&
366         (!Parent()->IsUpdateExpression())) {
367         // Error never should be thrown by this call, because LUB of types can be converted to any type which
368         // LUB was calculated by casting
369         const checker::CastingContext cast(
370             checker->Relation(), {"Tuple type couldn't be converted "},
371             checker::CastingContext::ConstructorData {this, baseType->AsETSArrayType()->ElementType(), tupleTypeAtIdx,
372                                                       Start()});
373     }
374 
375     return tupleTypeAtIdx;
376 }
377 
CheckComputed(checker::ETSChecker *checker, checker::Type *baseType)378 checker::Type *MemberExpression::CheckComputed(checker::ETSChecker *checker, checker::Type *baseType)
379 {
380     if (baseType->IsETSDynamicType()) {
381         checker->ValidateArrayIndex(property_);
382         return checker->GlobalBuiltinDynamicType(baseType->AsETSDynamicType()->Language());
383     }
384 
385     if (baseType->IsETSArrayType()) {
386         auto *dflt = baseType->AsETSArrayType()->ElementType();
387         if (!baseType->IsETSTupleType() && !checker->ValidateArrayIndex(property_)) {
388             // error already reported to log
389             return dflt;
390         }
391 
392         if (baseType->IsETSTupleType() && !checker->ValidateTupleIndex(baseType->AsETSTupleType(), this)) {
393             // error reported to log
394             return dflt;
395         }
396 
397         // Check if the index value is inside array bounds if it is defined explicitly
398         if (property_->IsNumberLiteral() && !CheckArrayIndexValue(checker)) {
399             // error reported to log
400             return dflt;
401         }
402 
403         // NOTE: apply capture conversion on this type
404         if (baseType->IsETSTupleType()) {
405             auto *res = CheckTupleAccessMethod(checker, baseType);
406             return (res == nullptr) ? dflt : res;
407         }
408 
409         return dflt;
410     }
411 
412     if (baseType->IsETSObjectType()) {
413         SetObjectType(baseType->AsETSObjectType());
414         return CheckIndexAccessMethod(checker);
415     }
416     if ((baseType->IsETSEnumType()) && (kind_ == MemberExpressionKind::ELEMENT_ACCESS)) {
417         property_->Check(checker);
418         if (property_->TsType()->IsETSEnumType()) {
419             AddAstNodeFlags(ir::AstNodeFlags::GENERATE_GET_NAME);
420             return checker->GlobalBuiltinETSStringType();
421         }
422     }
423     checker->LogTypeError("Indexed access is not supported for such expression type.", Object()->Start());
424     return nullptr;
425 }
426 
Check(checker::ETSChecker *checker)427 checker::Type *MemberExpression::Check(checker::ETSChecker *checker)
428 {
429     return checker->GetAnalyzer()->Check(this);
430 }
431 
Clone(ArenaAllocator *const allocator, AstNode *const parent)432 MemberExpression *MemberExpression::Clone(ArenaAllocator *const allocator, AstNode *const parent)
433 {
434     if (auto *const clone = allocator->New<MemberExpression>(Tag {}, *this, allocator); clone != nullptr) {
435         if (parent != nullptr) {
436             clone->SetParent(parent);
437         }
438 
439         clone->SetRange(Range());
440         return clone;
441     }
442 
443     throw Error(ErrorType::GENERIC, "", CLONE_ALLOCATION_ERROR);
444 }
445 
446 }  // namespace ark::es2panda::ir
447