• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2025 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 "util/diagnostic.h"
24 
25 namespace ark::es2panda::ir {
MemberExpression(Tag const tag,MemberExpression const & other,ArenaAllocator * allocator)26 MemberExpression::MemberExpression([[maybe_unused]] Tag const tag, MemberExpression const &other,
27                                    ArenaAllocator *allocator)
28     : MemberExpression(other)
29 {
30     object_ = other.object_ != nullptr ? other.object_->Clone(allocator, this)->AsExpression() : nullptr;
31     property_ = other.property_ != nullptr ? other.property_->Clone(allocator, this)->AsExpression() : nullptr;
32 }
33 
IsPrivateReference() const34 bool MemberExpression::IsPrivateReference() const noexcept
35 {
36     return property_->IsIdentifier() && property_->AsIdentifier()->IsPrivateIdent();
37 }
38 
TransformChildren(const NodeTransformer & cb,std::string_view const transformationName)39 void MemberExpression::TransformChildren(const NodeTransformer &cb, std::string_view const transformationName)
40 {
41     if (auto *transformedNode = cb(object_); object_ != transformedNode) {
42         object_->SetTransformedNode(transformationName, transformedNode);
43         object_ = transformedNode->AsExpression();
44     }
45 
46     if (auto *transformedNode = cb(property_); property_ != transformedNode) {
47         property_->SetTransformedNode(transformationName, transformedNode);
48         property_ = transformedNode->AsExpression();
49     }
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     ES2PANDA_ASSERT(object_ != nullptr);
70     ES2PANDA_ASSERT(property_ != nullptr);
71 
72     object_->Dump(dumper);
73     if (IsOptional()) {
74         dumper->Add("?");
75         if ((MemberExpressionKind::ELEMENT_ACCESS & kind_) != 0U) {
76             dumper->Add(".");
77         }
78     }
79     if ((MemberExpressionKind::ELEMENT_ACCESS & kind_) != 0U) {
80         dumper->Add("[");
81         property_->Dump(dumper);
82         dumper->Add("]");
83     } else {
84         dumper->Add(".");
85         property_->Dump(dumper);
86     }
87     if ((parent_ != nullptr) && (parent_->IsBlockStatement() || parent_->IsBlockExpression())) {
88         dumper->Add(";");
89         dumper->Endl();
90     }
91 }
92 
LoadRhs(compiler::PandaGen * pg) const93 void MemberExpression::LoadRhs(compiler::PandaGen *pg) const
94 {
95     compiler::RegScope rs(pg);
96     bool isSuper = object_->IsSuperExpression();
97     compiler::Operand prop = pg->ToPropertyKey(property_, computed_, isSuper);
98 
99     if (isSuper) {
100         pg->LoadSuperProperty(this, prop);
101     } else if (IsPrivateReference()) {
102         const auto &name = property_->AsIdentifier()->Name();
103         compiler::VReg objReg = pg->AllocReg();
104         pg->StoreAccumulator(this, objReg);
105         compiler::VReg ctor = pg->AllocReg();
106         compiler::Function::LoadClassContexts(this, pg, ctor, name);
107         pg->ClassPrivateFieldGet(this, ctor, objReg, name);
108     } else {
109         pg->LoadObjProperty(this, prop);
110     }
111 }
112 
CompileToRegs(compiler::PandaGen * pg,compiler::VReg object,compiler::VReg property) const113 void MemberExpression::CompileToRegs(compiler::PandaGen *pg, compiler::VReg object, compiler::VReg property) const
114 {
115     object_->Compile(pg);
116     pg->StoreAccumulator(this, object);
117 
118     pg->OptionalChainCheck(IsOptional(), object);
119 
120     if (!computed_) {
121         pg->LoadAccumulatorString(this, property_->AsIdentifier()->Name());
122     } else {
123         property_->Compile(pg);
124     }
125 
126     pg->StoreAccumulator(this, property);
127 }
128 
Compile(compiler::PandaGen * pg) const129 void MemberExpression::Compile(compiler::PandaGen *pg) const
130 {
131     pg->GetAstCompiler()->Compile(this);
132 }
133 
CompileToReg(compiler::PandaGen * pg,compiler::VReg objReg) const134 void MemberExpression::CompileToReg(compiler::PandaGen *pg, compiler::VReg objReg) const
135 {
136     object_->Compile(pg);
137     pg->StoreAccumulator(this, objReg);
138     pg->OptionalChainCheck(IsOptional(), objReg);
139     LoadRhs(pg);
140 }
141 
Compile(compiler::ETSGen * etsg) const142 void MemberExpression::Compile(compiler::ETSGen *etsg) const
143 {
144     etsg->GetAstCompiler()->Compile(this);
145 }
146 
Check(checker::TSChecker * checker)147 checker::Type *MemberExpression::Check(checker::TSChecker *checker)
148 {
149     return checker->GetAnalyzer()->Check(this);
150 }
151 
ResolveObjectMember(checker::ETSChecker * checker) const152 std::pair<checker::Type *, varbinder::LocalVariable *> MemberExpression::ResolveObjectMember(
153     checker::ETSChecker *checker) const
154 {
155     auto resolveRes = checker->ResolveMemberReference(this, objType_);
156     switch (resolveRes.size()) {
157         case 0U: {
158             /* resolution failed, error already reported */
159             return {nullptr, nullptr};
160         }
161         case 1U: {
162             if (resolveRes[0]->Kind() == checker::ResolvedKind::PROPERTY) {
163                 auto var = resolveRes[0]->Variable()->AsLocalVariable();
164                 checker->ValidatePropertyAccess(var, objType_, property_->Start());
165                 return {checker->GetTypeOfVariable(var), var};
166             }
167 
168             if (resolveRes[0]->Kind() == checker::ResolvedKind::EXTENSION_ACCESSOR) {
169                 auto *callee = const_cast<ir::Expression *>(this->AsExpression());
170                 callee->AsMemberExpression()->AddMemberKind(ir::MemberExpressionKind::EXTENSION_ACCESSOR);
171                 return {resolveRes[0]->Variable()->TsType(), nullptr};
172             }
173 
174             return {checker->GetTypeOfVariable(resolveRes[0]->Variable()), nullptr};
175         }
176         case 2U: {
177             auto classMethodType = checker->GetTypeOfVariable(resolveRes[1]->Variable());
178             auto extensionMethodType = checker->GetTypeOfVariable(resolveRes[0]->Variable());
179             auto *resolvedType = extensionMethodType;
180             if (classMethodType->IsETSFunctionType()) {
181                 ES2PANDA_ASSERT(extensionMethodType->IsETSFunctionType());
182                 resolvedType = checker->CreateETSExtensionFuncHelperType(classMethodType->AsETSFunctionType(),
183                                                                          extensionMethodType->AsETSFunctionType());
184             }
185             return {resolvedType, nullptr};
186         }
187         default: {
188             ES2PANDA_UNREACHABLE();
189         }
190     }
191 }
192 
TraverseUnionMember(checker::ETSChecker * checker,checker::ETSUnionType * unionType)193 checker::Type *MemberExpression::TraverseUnionMember(checker::ETSChecker *checker, checker::ETSUnionType *unionType)
194 
195 {
196     checker::Type *commonPropType = nullptr;
197 
198     auto const addPropType = [this, checker, &commonPropType](checker::Type *memberType) {
199         if (commonPropType == nullptr) {
200             commonPropType = memberType;
201             return;
202         }
203 
204         if (memberType == nullptr) {
205             checker->LogError(diagnostic::MEMBER_TYPE_MISMATCH_ACROSS_UNION, {}, Start());
206             return;
207         }
208 
209         if (!commonPropType->IsETSMethodType() && !memberType->IsETSMethodType()) {
210             if (!checker->IsTypeIdenticalTo(commonPropType, memberType)) {
211                 checker->LogError(diagnostic::MEMBER_TYPE_MISMATCH_ACROSS_UNION, {}, Start());
212             }
213             return;
214         }
215 
216         auto newType =
217             checker->IntersectSignatureSets(commonPropType->AsETSFunctionType(), memberType->AsETSFunctionType());
218         if (newType->AsETSFunctionType()->CallSignatures().empty()) {
219             checker->LogError(diagnostic::MEMBER_TYPE_MISMATCH_ACROSS_UNION, {}, Start());
220         }
221 
222         commonPropType = newType;
223     };
224 
225     for (auto *const type : unionType->ConstituentTypes()) {
226         auto *const apparent = checker->GetApparentType(type);
227         ES2PANDA_ASSERT(apparent != nullptr);
228         if (apparent->IsETSObjectType()) {
229             SetObjectType(apparent->AsETSObjectType());
230             auto *memberType = ResolveObjectMember(checker).first;
231             if (memberType != nullptr && memberType->IsTypeError()) {
232                 return checker->GlobalTypeError();
233             }
234 
235             addPropType(memberType);
236         } else {
237             checker->LogError(diagnostic::UNION_MEMBER_ILLEGAL_TYPE, {unionType}, Start());
238         }
239     }
240     return commonPropType;
241 }
242 
CheckUnionMember(checker::ETSChecker * checker,checker::Type * baseType)243 checker::Type *MemberExpression::CheckUnionMember(checker::ETSChecker *checker, checker::Type *baseType)
244 {
245     auto *const unionType = baseType->AsETSUnionType();
246     if (object_->Variable() != nullptr && object_->Variable()->Declaration() != nullptr &&
247         object_->Variable()->Declaration()->IsTypeAliasDecl()) {
248         checker->LogError(diagnostic::STATIC_UNION, {}, Start());
249         return checker->GlobalTypeError();
250     }
251     auto *const commonPropType = TraverseUnionMember(checker, unionType);
252     SetObjectType(checker->GlobalETSObjectType());
253     return commonPropType;
254 }
255 
AdjustRecordReturnType(checker::Type * type,checker::Type * objType)256 static checker::Type *AdjustRecordReturnType(checker::Type *type, checker::Type *objType)
257 {
258     auto *recordKeyType = objType->AsETSObjectType()->TypeArguments()[0];
259     auto *recordValueType = objType->AsETSObjectType()->TypeArguments()[1];
260 
261     auto const isStringLiteralOrConstantUnion = [](checker::Type *recordKey) {
262         if (recordKey->IsETSStringType() && recordKey->IsConstantType()) {
263             return true;
264         }
265         if (!recordKey->IsETSUnionType()) {
266             return false;
267         }
268         auto constituentTypes = recordKey->AsETSUnionType()->ConstituentTypes();
269         return std::all_of(constituentTypes.begin(), constituentTypes.end(),
270                            [](auto *it) { return it->IsETSStringType() && it->IsConstantType(); });
271     };
272     if (isStringLiteralOrConstantUnion(recordKeyType)) {
273         if (type->IsETSUnionType()) {
274             return recordValueType;
275         }
276 
277         if (type->IsETSFunctionType() && type->AsETSFunctionType()->Name().Is(compiler::Signatures::GET_INDEX_METHOD)) {
278             type->AsETSFunctionType()->CallSignatures()[0]->SetReturnType(recordValueType);
279         }
280     }
281 
282     return type;
283 }
284 
AdjustType(checker::ETSChecker * checker,checker::Type * type)285 checker::Type *MemberExpression::AdjustType(checker::ETSChecker *checker, checker::Type *type)
286 {
287     auto *const objType = checker->GetApparentType(Object()->TsType());
288     ES2PANDA_ASSERT(objType != nullptr);
289     if (type != nullptr && objType->IsETSObjectType() &&
290         objType->ToAssemblerName().str() == compiler::Signatures::BUILTIN_RECORD) {
291         type = AdjustRecordReturnType(type, objType);
292     }
293 
294     if (PropVar() != nullptr) {  // access erased property type
295         uncheckedType_ = checker->GuaranteedTypeForUncheckedPropertyAccess(PropVar());
296     } else if (IsComputed() && objType->IsETSArrayType()) {  // access erased array or tuple type
297         uncheckedType_ = checker->GuaranteedTypeForUncheckedCast(objType->AsETSArrayType()->ElementType(), type);
298     } else if (IsComputed() && objType->IsETSTupleType()) {
299         if (!checker->ValidateTupleIndex(objType->AsETSTupleType(), this, false)) {
300             // error recovery
301             return checker->InvalidateType(this);
302         }
303         uncheckedType_ = checker->GetApparentType(checker->MaybeBoxType(GetTypeOfTupleElement(checker, objType)));
304     } else if (objType->IsETSUnionType()) {
305         uncheckedType_ = checker->GuaranteedTypeForUnionFieldAccess(this, objType->AsETSUnionType());
306     } else if (checker->IsExtensionAccessorFunctionType(type)) {
307         SetTsType(type);
308         checker::Type *accessorReturnType = checker->GetExtensionAccessorReturnType(this);
309         SetTsType(accessorReturnType == nullptr ? checker->GlobalTypeError() : accessorReturnType);
310         return TsType();
311     }
312     SetTsType(type == nullptr ? checker->GlobalTypeError() : type);
313     return TsType();
314 }
315 
SetAndAdjustType(checker::ETSChecker * checker,checker::ETSObjectType * objectType)316 checker::Type *MemberExpression::SetAndAdjustType(checker::ETSChecker *checker, checker::ETSObjectType *objectType)
317 {
318     SetObjectType(objectType);
319     auto [resType, resVar] = ResolveObjectMember(checker);
320     if (resType == nullptr) {
321         return checker->InvalidateType(this);
322     }
323     SetPropVar(resVar);
324     return AdjustType(checker, resType);
325 }
326 
GetTupleIndexValue() const327 std::optional<std::size_t> MemberExpression::GetTupleIndexValue() const
328 {
329     auto *propType = property_->TsType();
330     if (object_->TsType() == nullptr || !object_->TsType()->IsETSTupleType() ||
331         !propType->HasTypeFlag(checker::TypeFlag::CONSTANT | checker::TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)) {
332         return std::nullopt;
333     }
334 
335     if (propType->IsByteType()) {
336         return propType->AsByteType()->GetValue();
337     }
338 
339     if (propType->IsShortType()) {
340         return propType->AsShortType()->GetValue();
341     }
342 
343     if (propType->IsIntType()) {
344         return propType->AsIntType()->GetValue();
345     }
346 
347     if (propType->IsLongType()) {
348         if (auto val = propType->AsLongType()->GetValue();
349             val <= std::numeric_limits<int32_t>::max() && val >= std::numeric_limits<int32_t>::min()) {
350             return static_cast<std::size_t>(val);
351         }
352         return std::nullopt;
353     }
354 
355     ES2PANDA_UNREACHABLE();
356 }
357 
CheckArrayIndexValue(checker::ETSChecker * checker) const358 bool MemberExpression::CheckArrayIndexValue(checker::ETSChecker *checker) const
359 {
360     std::size_t index;
361 
362     auto const &number = property_->AsNumberLiteral()->Number();
363 
364     if (number.IsInteger()) {
365         auto const value = number.GetLong();
366         if (value < 0) {
367             checker->LogError(diagnostic::NEGATIVE_INDEX, {}, property_->Start());
368             return false;
369         }
370         index = static_cast<std::size_t>(value);
371     } else {
372         ES2PANDA_ASSERT(number.IsReal());
373 
374         double value = number.GetDouble();
375         double fraction = std::modf(value, &value);
376         if (value < 0.0 || fraction >= std::numeric_limits<double>::epsilon()) {
377             checker->LogError(diagnostic::INDEX_NEGATIVE_OR_FRACTIONAL, {}, property_->Start());
378             return false;
379         }
380         index = static_cast<std::size_t>(value);
381     }
382 
383     if (object_->IsArrayExpression() && object_->AsArrayExpression()->Elements().size() <= index) {
384         checker->LogError(diagnostic::INDEX_OOB, {}, property_->Start());
385         return false;
386     }
387 
388     return true;
389 }
390 
ResolveReturnTypeFromSignature(checker::ETSChecker * checker,bool isSetter,ArenaVector<ir::Expression * > & arguments,ArenaVector<checker::Signature * > & signatures,std::string_view const methodName)391 checker::Type *MemberExpression::ResolveReturnTypeFromSignature(checker::ETSChecker *checker, bool isSetter,
392                                                                 ArenaVector<ir::Expression *> &arguments,
393                                                                 ArenaVector<checker::Signature *> &signatures,
394                                                                 std::string_view const methodName)
395 {
396     auto flags = checker::TypeRelationFlag::NONE;
397     checker::Signature *signature =
398         checker->ValidateSignatures(signatures, nullptr, arguments, Start(), "indexing", flags);
399     if (signature == nullptr) {
400         if (isSetter) {
401             Parent()->AsAssignmentExpression()->Right()->SetParent(Parent());
402         }
403         checker->LogError(diagnostic::MISSING_INDEX_ACCESSOR_WITH_SIG, {}, Property()->Start());
404         return nullptr;
405     }
406     checker->ValidateSignatureAccessibility(objType_, signature, Start());
407 
408     ES2PANDA_ASSERT(signature->Function() != nullptr);
409 
410     if (isSetter) {
411         if (checker->IsClassStaticMethod(objType_, signature)) {
412             checker->LogError(diagnostic::PROP_IS_STATIC, {methodName, objType_->Name()}, Property()->Start());
413         }
414         // Restore the right assignment node's parent to keep AST invariant valid.
415         Parent()->AsAssignmentExpression()->Right()->SetParent(Parent());
416         return signature->Params()[1]->TsType();
417     }
418 
419     // #24301: requires enum refactoring
420     if (!signature->Owner()->IsETSEnumType() && checker->IsClassStaticMethod(objType_, signature)) {
421         checker->LogError(diagnostic::PROP_IS_STATIC, {methodName, objType_->Name()}, Property()->Start());
422         return nullptr;
423     }
424     return signature->ReturnType();
425 }
426 
CheckIndexAccessMethod(checker::ETSChecker * checker)427 checker::Type *MemberExpression::CheckIndexAccessMethod(checker::ETSChecker *checker)
428 {
429     checker::PropertySearchFlags searchFlag = checker::PropertySearchFlags::SEARCH_METHOD;
430     searchFlag |= checker::PropertySearchFlags::SEARCH_IN_BASE | checker::PropertySearchFlags::SEARCH_IN_INTERFACES;
431     // NOTE(DZ) maybe we need to exclude static methods: search_flag &= ~(checker::PropertySearchFlags::SEARCH_STATIC);
432 
433     if (objType_->HasTypeFlag(checker::TypeFlag::GENERIC)) {
434         searchFlag |= checker::PropertySearchFlags::SEARCH_ALL;
435     }
436 
437     bool const isSetter = Parent()->IsAssignmentExpression() && Parent()->AsAssignmentExpression()->Left() == this;
438     std::string_view const methodName =
439         isSetter ? compiler::Signatures::SET_INDEX_METHOD : compiler::Signatures::GET_INDEX_METHOD;
440     auto *const method = objType_->GetProperty(methodName, searchFlag);
441     if (method == nullptr || !method->HasFlag(varbinder::VariableFlags::METHOD)) {
442         checker->LogError(diagnostic::ERROR_ARKTS_NO_PROPERTIES_BY_INDEX, {}, Start());
443         return nullptr;
444     }
445 
446     ArenaVector<Expression *> arguments {checker->ProgramAllocator()->Adapter()};
447     arguments.emplace_back(property_);
448     if (isSetter) {
449         //  Temporary change the parent of right assignment node to check if correct "$_set" function presents.
450         //  later on in lowering the entire assignment expression will be replace top the call to that method.
451         auto *value = Parent()->AsAssignmentExpression()->Right();
452         value->SetParent(this);
453         arguments.emplace_back(value);
454     }
455     auto &signatures = checker->GetTypeOfVariable(method)->AsETSFunctionType()->CallSignatures();
456 
457     return ResolveReturnTypeFromSignature(checker, isSetter, arguments, signatures, methodName);
458 }
459 
GetTypeOfTupleElement(checker::ETSChecker * checker,checker::Type * baseType)460 checker::Type *MemberExpression::GetTypeOfTupleElement(checker::ETSChecker *checker, checker::Type *baseType)
461 {
462     ES2PANDA_ASSERT(baseType->IsETSTupleType());
463     checker::Type *type = nullptr;
464     if (Property()->HasBoxingUnboxingFlags(ir::BoxingUnboxingFlags::UNBOXING_FLAG)) {
465         ES2PANDA_ASSERT(Property()->Variable()->Declaration()->Node()->AsClassElement()->Value());
466         type = Property()->Variable()->Declaration()->Node()->AsClassElement()->Value()->TsType();
467     } else {
468         type = Property()->TsType();
469     }
470 
471     auto idxIfAny = checker->GetTupleElementAccessValue(type);
472     if (!idxIfAny.has_value()) {
473         return nullptr;
474     }
475 
476     return baseType->AsETSTupleType()->GetTypeAtIndex(*idxIfAny);
477 }
478 
CastTupleElementFromClassMemberType(checker::ETSChecker * checker,ir::MemberExpression * tupleElementAccessor,checker::Type * baseType)479 static void CastTupleElementFromClassMemberType(checker::ETSChecker *checker,
480                                                 ir::MemberExpression *tupleElementAccessor, checker::Type *baseType)
481 {
482     auto *typeOfTuple = tupleElementAccessor->GetTypeOfTupleElement(checker, baseType);
483     auto *storedTupleType = checker->MaybeBoxType(typeOfTuple);
484 
485     const checker::CastingContext tupleCast(
486         checker->Relation(), diagnostic::CAST_FAIL_UNREACHABLE, {},
487         checker::CastingContext::ConstructorData {tupleElementAccessor, storedTupleType, typeOfTuple,
488                                                   tupleElementAccessor->Start(), checker::TypeRelationFlag::NO_THROW});
489 }
490 
CheckComputed(checker::ETSChecker * checker,checker::Type * baseType)491 checker::Type *MemberExpression::CheckComputed(checker::ETSChecker *checker, checker::Type *baseType)
492 {
493     if (baseType->IsETSDynamicType()) {
494         if (!property_->Check(checker)->IsETSStringType()) {
495             checker->ValidateArrayIndex(property_);
496         }
497         return checker->GlobalBuiltinDynamicType(baseType->AsETSDynamicType()->Language());
498     }
499 
500     if (baseType->IsETSArrayType()) {
501         auto *dflt = baseType->AsETSArrayType()->ElementType();
502         if (!checker->ValidateArrayIndex(property_)) {
503             // error already reported to log
504             return dflt;
505         }
506 
507         // Check if the index value is inside array bounds if it is defined explicitly
508         if (property_->IsNumberLiteral() && !CheckArrayIndexValue(checker)) {
509             // error reported to log
510             return dflt;
511         }
512 
513         return dflt;
514     }
515 
516     if (baseType->IsETSTupleType()) {
517         if (!checker->ValidateTupleIndex(baseType->AsETSTupleType(), this)) {
518             // error reported to log
519             return baseType;
520         }
521 
522         if (Parent()->IsAssignmentExpression() && Parent()->AsAssignmentExpression()->Left() == this) {
523             // NOTE (smartin): When an assignment to a tuple type happens, see it as the boxed (stored) type of the
524             // element. This can be removed, when generics without type erasure is possible
525             auto *typeOfTuple = GetTypeOfTupleElement(checker, baseType);
526             auto *storedTupleType = checker->MaybeBoxType(typeOfTuple);
527             return storedTupleType;
528         }
529 
530         CastTupleElementFromClassMemberType(checker, this, baseType);
531 
532         // NOTE: apply capture conversion on this type
533         auto *res = GetTypeOfTupleElement(checker, baseType);
534         return (res == nullptr) ? baseType : res;
535     }
536 
537     if (baseType->IsETSObjectType()) {
538         SetObjectType(baseType->AsETSObjectType());
539         return CheckIndexAccessMethod(checker);
540     }
541     checker->LogError(diagnostic::INDEX_ON_INVALID_TYPE, {}, Object()->Start());
542     return nullptr;
543 }
544 
Check(checker::ETSChecker * checker)545 checker::VerifiedType MemberExpression::Check(checker::ETSChecker *checker)
546 {
547     return {this, checker->GetAnalyzer()->Check(this)};
548 }
549 
Clone(ArenaAllocator * const allocator,AstNode * const parent)550 MemberExpression *MemberExpression::Clone(ArenaAllocator *const allocator, AstNode *const parent)
551 {
552     auto *const clone = allocator->New<MemberExpression>(Tag {}, *this, allocator);
553     ES2PANDA_ASSERT(clone != nullptr);
554     if (parent != nullptr) {
555         clone->SetParent(parent);
556     }
557 
558     clone->SetRange(Range());
559     return clone;
560 }
561 
ToString() const562 std::string MemberExpression::ToString() const
563 {
564     auto str1 = object_ != nullptr ? object_->ToString() : std::string {INVALID_EXPRESSION};
565     if (str1 == INVALID_EXPRESSION) {
566         return str1;
567     }
568 
569     auto str2 = property_ != nullptr ? property_->ToString() : std::string {INVALID_EXPRESSION};
570 
571     if (kind_ == MemberExpressionKind::ELEMENT_ACCESS) {
572         return str1 + '[' + str2 + ']';
573     }
574 
575     if (str2 == INVALID_EXPRESSION) {
576         return str1 + str2;
577     }
578 
579     return str1 + '.' + str2;
580 }
581 }  // namespace ark::es2panda::ir
582