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