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 26namespace ark::es2panda::ir { 27MemberExpression::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 35bool MemberExpression::IsPrivateReference() const noexcept 36{ 37 return property_->IsIdentifier() && property_->AsIdentifier()->IsPrivateIdent(); 38} 39 40void 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 53void MemberExpression::Iterate(const NodeTraverser &cb) const 54{ 55 cb(object_); 56 cb(property_); 57} 58 59void 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 68void 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 94void 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 114void 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 130void MemberExpression::Compile(compiler::PandaGen *pg) const 131{ 132 pg->GetAstCompiler()->Compile(this); 133} 134 135void 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 143void MemberExpression::Compile(compiler::ETSGen *etsg) const 144{ 145 etsg->GetAstCompiler()->Compile(this); 146} 147 148checker::Type *MemberExpression::Check(checker::TSChecker *checker) 149{ 150 return checker->GetAnalyzer()->Check(this); 151} 152 153std::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 174std::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 208checker::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 233checker::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 241checker::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 253checker::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 265bool 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 298checker::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 355checker::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 378checker::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 427checker::Type *MemberExpression::Check(checker::ETSChecker *checker) 428{ 429 return checker->GetAnalyzer()->Check(this); 430} 431 432MemberExpression *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