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 "util/helpers.h" 17#include "macros.h" 18#include "varbinder/ETSBinder.h" 19#include "parser/ETSparser.h" 20 21#include "checker/types/ets/etsTupleType.h" 22#include "checker/types/ets/etsReadonlyType.h" 23#include "checker/ets/typeRelationContext.h" 24#include "checker/ETSchecker.h" 25#include "checker/types/globalTypesHolder.h" 26#include "evaluate/scopedDebugInfoPlugin.h" 27 28#include "compiler/lowering/scopesInit/scopesInitPhase.h" 29#include "generated/signatures.h" 30 31namespace ark::es2panda::checker { 32varbinder::Variable *ETSChecker::FindVariableInFunctionScope(const util::StringView name, 33 const varbinder::ResolveBindingOptions options) 34{ 35 return Scope()->FindInFunctionScope(name, options).variable; 36} 37 38std::pair<varbinder::Variable *, const ETSObjectType *> ETSChecker::FindVariableInClassOrEnclosing( 39 const util::StringView name, const ETSObjectType *classType) 40{ 41 const auto searchFlags = PropertySearchFlags::SEARCH_ALL | PropertySearchFlags::SEARCH_IN_BASE | 42 PropertySearchFlags::SEARCH_IN_INTERFACES; 43 auto *resolved = classType->GetProperty(name, searchFlags); 44 while (classType->EnclosingType() != nullptr && resolved == nullptr) { 45 classType = classType->EnclosingType(); 46 resolved = classType->GetProperty(name, searchFlags); 47 } 48 49 return {resolved, classType}; 50} 51 52varbinder::Variable *ETSChecker::FindVariableInGlobal(const ir::Identifier *const identifier, 53 const varbinder::ResolveBindingOptions options) 54{ 55 return Scope()->FindInGlobal(identifier->Name(), options).variable; 56} 57 58bool ETSChecker::IsVariableStatic(const varbinder::Variable *var) 59{ 60 if (var->HasFlag(varbinder::VariableFlags::METHOD)) { 61 return var->TsType()->AsETSFunctionType()->CallSignatures()[0]->HasSignatureFlag(SignatureFlags::STATIC); 62 } 63 return var->HasFlag(varbinder::VariableFlags::STATIC); 64} 65 66bool ETSChecker::IsVariableGetterSetter(const varbinder::Variable *var) 67{ 68 return var->TsTypeOrError() != nullptr && var->TsTypeOrError()->HasTypeFlag(TypeFlag::GETTER_SETTER); 69} 70 71void ETSChecker::LogUnResolvedError(ir::Identifier *const ident) 72{ 73 LogTypeError({"Unresolved reference ", ident->Name()}, ident->Start()); 74} 75 76void ETSChecker::WrongContextErrorClassifyByType(ir::Identifier *ident, varbinder::Variable *const resolved) 77{ 78 std::string identCategoryName; 79 switch (static_cast<varbinder::VariableFlags>( 80 resolved->Flags() & 81 (varbinder::VariableFlags::CLASS_OR_INTERFACE_OR_ENUM | varbinder::VariableFlags::METHOD))) { 82 case varbinder::VariableFlags::CLASS: { 83 identCategoryName = "Class"; 84 break; 85 } 86 case varbinder::VariableFlags::METHOD: { 87 identCategoryName = "Function"; 88 break; 89 } 90 case varbinder::VariableFlags::INTERFACE: { 91 identCategoryName = "Interface"; 92 break; 93 } 94 case varbinder::VariableFlags::ENUM_LITERAL: { 95 identCategoryName = "Enum"; 96 break; 97 } 98 default: { 99 LogUnResolvedError(ident); 100 return; 101 } 102 } 103 LogTypeError({identCategoryName.c_str(), " name \"", ident->Name(), "\" used in the wrong context"}, 104 ident->Start()); 105} 106 107void ETSChecker::NotResolvedError(ir::Identifier *const ident, const varbinder::Variable *classVar, 108 const ETSObjectType *classType) 109{ 110 if (classVar == nullptr) { 111 LogUnResolvedError(ident); 112 return; 113 } 114 115 if (IsVariableStatic(classVar)) { 116 LogTypeError( 117 {"Static property '", ident->Name(), "' must be accessed through it's class '", classType->Name(), "'"}, 118 ident->Start()); 119 } else { 120 LogTypeError({"Property '", ident->Name(), "' must be accessed through 'this'"}, ident->Start()); 121 } 122} 123 124std::pair<const ir::Identifier *, ir::TypeNode *> ETSChecker::GetTargetIdentifierAndType(ir::Identifier *const ident) 125{ 126 if (ident->Parent()->IsClassProperty()) { 127 const auto *const classProp = ident->Parent()->AsClassProperty(); 128 ASSERT(classProp->Value() && classProp->Value() == ident); 129 return std::make_pair(classProp->Key()->AsIdentifier(), classProp->TypeAnnotation()); 130 } 131 const auto *const variableDecl = ident->Parent()->AsVariableDeclarator(); 132 ASSERT(variableDecl->Init() && variableDecl->Init() == ident); 133 return std::make_pair(variableDecl->Id()->AsIdentifier(), variableDecl->Id()->AsIdentifier()->TypeAnnotation()); 134} 135 136varbinder::Variable *ETSChecker::ExtraCheckForResolvedError(ir::Identifier *const ident) 137{ 138 const auto [class_var, class_type] = FindVariableInClassOrEnclosing(ident->Name(), Context().ContainingClass()); 139 auto *parentClass = FindAncestorGivenByType(ident, ir::AstNodeType::CLASS_DEFINITION); 140 if (parentClass != nullptr && parentClass->AsClassDefinition()->IsLocal()) { 141 if (parentClass != class_type->GetDeclNode()) { 142 LogTypeError({"Property '", ident->Name(), "' of enclosing class '", class_type->Name(), 143 "' is not allowed to be captured from the local class '", 144 parentClass->AsClassDefinition()->Ident()->Name(), "'"}, 145 ident->Start()); 146 } 147 } 148 NotResolvedError(ident, class_var, class_type); 149 return class_var; 150} 151 152bool ETSChecker::SaveCapturedVariableInLocalClass(varbinder::Variable *const var, ir::Identifier *ident) 153{ 154 const auto &pos = ident->Start(); 155 156 if (!HasStatus(CheckerStatus::IN_LOCAL_CLASS)) { 157 return false; 158 } 159 160 if (!var->HasFlag(varbinder::VariableFlags::LOCAL)) { 161 return false; 162 } 163 164 LOG(DEBUG, ES2PANDA) << "Checking variable (line:" << pos.line << "): " << var->Name(); 165 auto *scopeIter = Scope(); 166 bool inStaticMethod = false; 167 168 auto captureVariable = [this, var, ident, &scopeIter, &inStaticMethod, &pos]() { 169 if (inStaticMethod) { 170 LogTypeError({"Not allowed to capture variable '", var->Name(), "' in static method"}, pos); 171 return false; 172 } 173 if (scopeIter->Node()->AsClassDefinition()->CaptureVariable(var)) { 174 LOG(DEBUG, ES2PANDA) << " Captured in class:" << scopeIter->Node()->AsClassDefinition()->Ident()->Name(); 175 } 176 177 auto *parent = ident->Parent(); 178 179 if (parent->IsVariableDeclarator()) { 180 parent = parent->Parent()->Parent(); 181 } 182 183 if (!(parent->IsUpdateExpression() || 184 (parent->IsAssignmentExpression() && parent->AsAssignmentExpression()->Left() == ident)) || 185 var->Declaration() == nullptr) { 186 return false; 187 } 188 189 if (var->Declaration()->IsParameterDecl()) { 190 LOG(DEBUG, ES2PANDA) << " - Modified parameter "; 191 scopeIter->Node()->AsClassDefinition()->AddToLocalVariableIsNeeded(var); 192 } 193 return true; 194 }; 195 196 while (scopeIter != var->GetScope()) { 197 if (scopeIter->Node() != nullptr) { 198 if (scopeIter->Node()->IsScriptFunction() && scopeIter->Node()->AsScriptFunction()->IsStatic()) { 199 inStaticMethod = true; 200 } 201 202 if (scopeIter->Node()->IsClassDefinition()) { 203 captureVariable(); 204 return true; 205 } 206 } 207 scopeIter = scopeIter->Parent(); 208 } 209 210 return false; 211} 212 213void ETSChecker::SaveCapturedVariable(varbinder::Variable *const var, ir::Identifier *ident) 214{ 215 const auto &pos = ident->Start(); 216 217 if (!HasStatus(CheckerStatus::IN_LAMBDA)) { 218 return; 219 } 220 221 if (var->HasFlag(varbinder::VariableFlags::PROPERTY)) { 222 Context().AddCapturedVar(var, pos); 223 return; 224 } 225 226 if ((!var->HasFlag(varbinder::VariableFlags::LOCAL) && !var->HasFlag(varbinder::VariableFlags::METHOD)) || 227 Context().ContainingLambda()->IsVarFromSubscope(var)) { 228 return; 229 } 230 231 const auto *scopeIter = Scope(); 232 while (scopeIter != var->GetScope()) { 233 if (scopeIter->IsFunctionScope()) { 234 Context().AddCapturedVar(var, pos); 235 return; 236 } 237 scopeIter = scopeIter->Parent(); 238 } 239} 240 241Type *ETSChecker::ResolveIdentifier(ir::Identifier *ident) 242{ 243 if (ident->Variable() != nullptr) { 244 auto *const resolved = ident->Variable(); 245 SaveCapturedVariable(resolved, ident); 246 return GetTypeOfVariable(resolved); 247 } 248 249 auto options = ident->Parent()->IsTSTypeAliasDeclaration() ? varbinder::ResolveBindingOptions::TYPE_ALIASES 250 : varbinder::ResolveBindingOptions::ALL_NON_TYPE; 251 252 auto *resolved = FindVariableInFunctionScope(ident->Name(), options); 253 if (resolved == nullptr) { 254 // If the reference is not found already in the current class, then it is not bound to the class, so we have to 255 // find the reference in the global class first, then in the global scope 256 resolved = FindVariableInGlobal(ident, options); 257 if (UNLIKELY(resolved == nullptr && debugInfoPlugin_ != nullptr)) { 258 resolved = debugInfoPlugin_->FindIdentifier(ident); 259 } 260 } 261 262 if (resolved == nullptr) { 263 resolved = ExtraCheckForResolvedError(ident); 264 ident->SetVariable(resolved); 265 if (resolved == nullptr) { 266 return GlobalTypeError(); 267 } 268 return GetTypeOfVariable(resolved); 269 } 270 271 ident->SetVariable(resolved); 272 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) 273 ValidateResolvedIdentifier(ident, resolved); 274 275 ValidatePropertyAccess(resolved, Context().ContainingClass(), ident->Start()); 276 SaveCapturedVariable(resolved, ident); 277 278 return GetTypeOfVariable(resolved); 279} 280 281std::tuple<Type *, bool> ETSChecker::ApplyBinaryOperatorPromotion(Type *left, Type *right, TypeFlag test, 282 bool const doPromotion) 283{ 284 Type *const unboxedL = ETSBuiltinTypeAsPrimitiveType(left); 285 Type *const unboxedR = ETSBuiltinTypeAsPrimitiveType(right); 286 287 if (unboxedL == nullptr || unboxedR == nullptr) { 288 return {nullptr, false}; 289 } 290 291 if (!unboxedL->HasTypeFlag(test) || !unboxedR->HasTypeFlag(test)) { 292 return {nullptr, false}; 293 } 294 295 bool const bothConst = unboxedL->HasTypeFlag(TypeFlag::CONSTANT) && unboxedR->HasTypeFlag(TypeFlag::CONSTANT); 296 297 // extract just to reduce nested levels 298 auto const numericPromotion = [this, unboxedL, unboxedR, bothConst]() -> std::tuple<checker::Type *, bool> { 299 if (unboxedL->IsDoubleType() || unboxedR->IsDoubleType()) { 300 return {GlobalDoubleType(), bothConst}; 301 } 302 303 if (unboxedL->IsFloatType() || unboxedR->IsFloatType()) { 304 return {GlobalFloatType(), bothConst}; 305 } 306 307 if (unboxedL->IsLongType() || unboxedR->IsLongType()) { 308 return {GlobalLongType(), bothConst}; 309 } 310 311 if (unboxedL->IsCharType() && unboxedR->IsCharType()) { 312 return {GlobalCharType(), bothConst}; 313 } 314 315 return {GlobalIntType(), bothConst}; 316 }; 317 318 if (doPromotion) { 319 if (unboxedL->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) && 320 unboxedR->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC)) { 321 return numericPromotion(); 322 } 323 324 if (IsTypeIdenticalTo(unboxedL, unboxedR)) { 325 return {unboxedL, bothConst}; 326 } 327 } 328 329 return {unboxedR, bothConst}; 330} 331 332std::optional<checker::Type *> CheckLeftRightType(checker::ETSChecker *checker, checker::Type *unboxedL, 333 checker::Type *unboxedR) 334{ 335 if (unboxedL->IsDoubleType() || unboxedR->IsDoubleType()) { 336 return checker->GlobalDoubleType(); 337 } 338 if (unboxedL->IsFloatType() || unboxedR->IsFloatType()) { 339 return checker->GlobalFloatType(); 340 } 341 if (unboxedL->IsLongType() || unboxedR->IsLongType()) { 342 return checker->GlobalLongType(); 343 } 344 if (unboxedL->IsIntType() || unboxedR->IsIntType() || unboxedL->IsCharType() || unboxedR->IsCharType()) { 345 return checker->GlobalIntType(); 346 } 347 if (unboxedL->IsShortType() || unboxedR->IsShortType()) { 348 return checker->GlobalShortType(); 349 } 350 if (unboxedL->IsByteType() || unboxedR->IsByteType()) { 351 return checker->GlobalByteType(); 352 } 353 return std::nullopt; 354} 355checker::Type *ETSChecker::ApplyConditionalOperatorPromotion(checker::ETSChecker *checker, checker::Type *unboxedL, 356 checker::Type *unboxedR) 357{ 358 if ((unboxedL->HasTypeFlag(checker::TypeFlag::CONSTANT) && unboxedL->IsIntType()) || 359 (unboxedR->HasTypeFlag(checker::TypeFlag::CONSTANT) && unboxedR->IsIntType())) { 360 int value = unboxedL->IsIntType() ? unboxedL->AsIntType()->GetValue() : unboxedR->AsIntType()->GetValue(); 361 checker::Type *otherType = !unboxedL->IsIntType() ? unboxedL : unboxedR; 362 363 switch (checker::ETSChecker::ETSType(otherType)) { 364 case checker::TypeFlag::BYTE: 365 case checker::TypeFlag::CHAR: { 366 if (value <= static_cast<int>(std::numeric_limits<char>::max()) && 367 value >= static_cast<int>(std::numeric_limits<char>::min())) { 368 return checker->GetNonConstantType(otherType); 369 } 370 break; 371 } 372 case checker::TypeFlag::SHORT: { 373 if (value <= std::numeric_limits<int16_t>::max() && value >= std::numeric_limits<int16_t>::min()) { 374 return checker->GlobalShortType(); 375 } 376 break; 377 } 378 default: { 379 return otherType; 380 } 381 } 382 return checker->GlobalIntType(); 383 } 384 385 auto checkLeftRight = CheckLeftRightType(checker, unboxedL, unboxedR); 386 if (checkLeftRight.has_value()) { 387 return checkLeftRight.value(); 388 } 389 UNREACHABLE(); 390} 391 392Type *ETSChecker::ApplyUnaryOperatorPromotion(Type *type, const bool createConst, const bool doPromotion, 393 const bool isCondExpr) 394{ 395 Type *unboxedType = isCondExpr ? ETSBuiltinTypeAsConditionalType(type) : ETSBuiltinTypeAsPrimitiveType(type); 396 397 if (unboxedType == nullptr) { 398 return nullptr; 399 } 400 if (doPromotion) { 401 switch (ETSType(unboxedType)) { 402 case TypeFlag::BYTE: 403 case TypeFlag::SHORT: 404 case TypeFlag::CHAR: { 405 if (!createConst) { 406 return GlobalIntType(); 407 } 408 409 return CreateIntTypeFromType(unboxedType); 410 } 411 default: { 412 break; 413 } 414 } 415 } 416 return unboxedType; 417} 418 419bool ETSChecker::IsNullLikeOrVoidExpression(const ir::Expression *expr) const 420{ 421 return expr->TsType()->DefinitelyETSNullish() || expr->TsType()->IsETSVoidType(); 422} 423 424std::tuple<bool, bool> ETSChecker::IsResolvedAndValue(const ir::Expression *expr, Type *type) const 425{ 426 auto [isResolve, isValue] = 427 IsNullLikeOrVoidExpression(expr) ? std::make_tuple(true, false) : type->ResolveConditionExpr(); 428 429 const Type *tsType = expr->TsType(); 430 if (tsType->DefinitelyNotETSNullish() && !type->HasTypeFlag(TypeFlag::ETS_PRIMITIVE)) { 431 isResolve = true; 432 isValue = true; 433 } 434 return std::make_tuple(isResolve, isValue); 435} 436 437Type *ETSChecker::HandleBooleanLogicalOperatorsExtended(Type *leftType, Type *rightType, ir::BinaryExpression *expr) 438{ 439 ASSERT(leftType->IsConditionalExprType() && rightType->IsConditionalExprType()); 440 441 auto [resolveLeft, leftValue] = IsResolvedAndValue(expr->Left(), leftType); 442 auto [resolveRight, rightValue] = IsResolvedAndValue(expr->Right(), rightType); 443 444 if (!resolveLeft && !resolveRight && IsTypeIdenticalTo(leftType, rightType)) { 445 return leftType; 446 } 447 448 switch (expr->OperatorType()) { 449 case lexer::TokenType::PUNCTUATOR_LOGICAL_OR: { 450 if (leftValue) { 451 expr->SetResult(expr->Left()); 452 return leftType->IsETSBooleanType() ? CreateETSBooleanType(true) : leftType; 453 } 454 455 expr->SetResult(expr->Right()); 456 return rightType->IsETSBooleanType() && resolveRight ? CreateETSBooleanType(rightValue) : rightType; 457 } 458 case lexer::TokenType::PUNCTUATOR_LOGICAL_AND: { 459 if (leftValue) { 460 expr->SetResult(expr->Right()); 461 return rightType->IsETSBooleanType() && resolveRight ? CreateETSBooleanType(rightValue) : rightType; 462 } 463 464 expr->SetResult(expr->Left()); 465 return leftType->IsETSBooleanType() ? CreateETSBooleanType(false) : leftType; 466 } 467 default: { 468 break; 469 } 470 } 471 472 UNREACHABLE(); 473} 474 475Type *ETSChecker::HandleBooleanLogicalOperators(Type *leftType, Type *rightType, lexer::TokenType tokenType) 476{ 477 using UType = typename ETSBooleanType::UType; 478 ASSERT(leftType->IsETSBooleanType() && rightType->IsETSBooleanType()); 479 480 if (!leftType->HasTypeFlag(checker::TypeFlag::CONSTANT) || !rightType->HasTypeFlag(checker::TypeFlag::CONSTANT)) { 481 return GlobalETSBooleanType(); 482 } 483 484 UType leftValue = leftType->AsETSBooleanType()->GetValue(); 485 UType rightValue = rightType->AsETSBooleanType()->GetValue(); 486 487 switch (tokenType) { 488 case lexer::TokenType::PUNCTUATOR_BITWISE_XOR: { 489 return CreateETSBooleanType(leftValue ^ rightValue); 490 } 491 case lexer::TokenType::PUNCTUATOR_BITWISE_AND: { 492 return CreateETSBooleanType((static_cast<uint8_t>(leftValue) & static_cast<uint8_t>(rightValue)) != 0); 493 } 494 case lexer::TokenType::PUNCTUATOR_BITWISE_OR: { 495 return CreateETSBooleanType((static_cast<uint8_t>(leftValue) | static_cast<uint8_t>(rightValue)) != 0); 496 } 497 case lexer::TokenType::PUNCTUATOR_LOGICAL_OR: { 498 return CreateETSBooleanType(leftValue || rightValue); 499 } 500 case lexer::TokenType::PUNCTUATOR_LOGICAL_AND: { 501 return CreateETSBooleanType(leftValue && rightValue); 502 } 503 default: { 504 break; 505 } 506 } 507 508 UNREACHABLE(); 509 return nullptr; 510} 511 512void ETSChecker::ResolveReturnStatement(checker::Type *funcReturnType, checker::Type *argumentType, 513 ir::ScriptFunction *containingFunc, ir::ReturnStatement *st) 514{ 515 if (funcReturnType->IsETSReferenceType() || argumentType->IsETSReferenceType()) { 516 // function return type should be of reference (object) type 517 Relation()->SetFlags(checker::TypeRelationFlag::NONE); 518 519 if (!argumentType->IsETSReferenceType()) { 520 argumentType = PrimitiveTypeAsETSBuiltinType(argumentType); 521 if (argumentType == nullptr) { 522 LogTypeError("Invalid return statement expression", st->Argument()->Start()); 523 } else { 524 st->Argument()->AddBoxingUnboxingFlags(GetBoxingFlag(argumentType)); 525 } 526 } 527 528 if (!funcReturnType->IsETSReferenceType()) { 529 funcReturnType = PrimitiveTypeAsETSBuiltinType(funcReturnType); 530 if (funcReturnType == nullptr) { 531 LogTypeError("Invalid return function expression", st->Start()); 532 } 533 } 534 if (argumentType != nullptr && funcReturnType != nullptr) { 535 funcReturnType = CreateETSUnionType({funcReturnType, argumentType}); 536 containingFunc->Signature()->SetReturnType(funcReturnType); 537 containingFunc->Signature()->AddSignatureFlag(checker::SignatureFlags::INFERRED_RETURN_TYPE); 538 } 539 } else if (funcReturnType->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE_RETURN) && 540 argumentType->HasTypeFlag(checker::TypeFlag::ETS_PRIMITIVE_RETURN)) { 541 // function return type is of primitive type (including enums): 542 Relation()->SetFlags(checker::TypeRelationFlag::DIRECT_RETURN | 543 checker::TypeRelationFlag::IN_ASSIGNMENT_CONTEXT | 544 checker::TypeRelationFlag::ASSIGNMENT_CONTEXT); 545 if (Relation()->IsAssignableTo(funcReturnType, argumentType)) { 546 funcReturnType = argumentType; 547 containingFunc->Signature()->SetReturnType(funcReturnType); 548 containingFunc->Signature()->AddSignatureFlag(checker::SignatureFlags::INFERRED_RETURN_TYPE); 549 } else if (!Relation()->IsAssignableTo(argumentType, funcReturnType)) { 550 const Type *targetType = TryGettingFunctionTypeFromInvokeFunction(funcReturnType); 551 const Type *sourceType = TryGettingFunctionTypeFromInvokeFunction(argumentType); 552 553 LogTypeError({"Function cannot have different primitive return types, require '", targetType, "', found '", 554 sourceType, "'"}, 555 st->Argument()->Start()); 556 } 557 } else { 558 // Should never in this branch. 559 UNREACHABLE(); 560 return; 561 } 562} 563 564checker::Type *ETSChecker::CheckArrayElements(ir::ArrayExpression *init) 565{ 566 ArenaVector<checker::Type *> elementTypes(Allocator()->Adapter()); 567 for (auto e : init->AsArrayExpression()->Elements()) { 568 elementTypes.push_back(GetNonConstantType(e->Check(this))); 569 } 570 571 if (elementTypes.empty()) { 572 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) 573 return Allocator()->New<ETSArrayType>(GlobalETSObjectType()); 574 } 575 auto const isNumeric = [](checker::Type *ct) { return ct->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC); }; 576 auto const elementType = std::all_of(elementTypes.begin(), elementTypes.end(), isNumeric) 577 ? GlobalDoubleType() 578 : CreateETSUnionType(std::move(elementTypes)); 579 580 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) 581 return Allocator()->New<ETSArrayType>(elementType); 582} 583 584void ETSChecker::InferAliasLambdaType(ir::TypeNode *localTypeAnnotation, ir::ArrowFunctionExpression *init) 585{ 586 ASSERT(localTypeAnnotation != nullptr); 587 588 if (localTypeAnnotation->IsETSTypeReference()) { 589 bool isAnnotationTypeAlias = true; 590 while (localTypeAnnotation->IsETSTypeReference() && isAnnotationTypeAlias) { 591 auto *nodeVar = localTypeAnnotation->AsETSTypeReference()->Part()->Name()->AsIdentifier()->Variable(); 592 if (nodeVar == nullptr) { 593 break; 594 } 595 596 auto *node = nodeVar->Declaration()->Node(); 597 598 isAnnotationTypeAlias = node->IsTSTypeAliasDeclaration(); 599 if (isAnnotationTypeAlias) { 600 localTypeAnnotation = node->AsTSTypeAliasDeclaration()->TypeAnnotation(); 601 } 602 } 603 } 604 605 if (localTypeAnnotation->IsETSFunctionType()) { 606 auto *const arrowFuncExpr = init; 607 ir::ScriptFunction *const lambda = arrowFuncExpr->Function(); 608 if (lambda->Params().size() == localTypeAnnotation->AsETSFunctionType()->Params().size() && 609 NeedTypeInference(lambda)) { 610 InferTypesForLambda(lambda, localTypeAnnotation->AsETSFunctionType()); 611 } 612 } 613} 614 615checker::Type *ETSChecker::FixOptionalVariableType(varbinder::Variable *const bindingVar, ir::ModifierFlags flags, 616 ir::Expression *init) 617{ 618 if ((flags & ir::ModifierFlags::OPTIONAL) != 0) { 619 auto type = bindingVar->TsTypeOrError(); 620 if (init != nullptr && init->TsType()->IsETSEnumType()) { 621 init->SetBoxingUnboxingFlags(ir::BoxingUnboxingFlags::BOX_TO_ENUM); 622 } 623 if (type->IsETSEnumType()) { 624 type = type->AsETSEnumType()->GetDecl()->BoxedClass()->TsType(); 625 } 626 if (type->IsETSUnionType()) { 627 auto constituentTypes = type->AsETSUnionType()->ConstituentTypes(); 628 constituentTypes.push_back(GlobalETSUndefinedType()); 629 type = CreateETSUnionType(std::move(constituentTypes)); 630 } else { 631 type = CreateETSUnionType({GlobalETSUndefinedType(), type}); 632 } 633 bindingVar->SetTsType(type); 634 } 635 return bindingVar->TsTypeOrError(); 636} 637 638checker::Type *PreferredObjectTypeFromAnnotation(checker::Type *annotationType) 639{ 640 if (!annotationType->IsETSUnionType()) { 641 return annotationType; 642 } 643 644 checker::Type *resolvedType = nullptr; 645 for (auto constituentType : annotationType->AsETSUnionType()->ConstituentTypes()) { 646 if (constituentType->IsETSObjectType()) { 647 if (resolvedType != nullptr) { 648 return nullptr; 649 } 650 resolvedType = constituentType; 651 } 652 } 653 654 return resolvedType; 655} 656 657bool ETSChecker::CheckInit(ir::Identifier *ident, ir::TypeNode *typeAnnotation, ir::Expression *init, 658 checker::Type *annotationType, varbinder::Variable *const bindingVar) 659{ 660 if (typeAnnotation == nullptr) { 661 if (init->IsArrayExpression()) { 662 annotationType = CheckArrayElements(init->AsArrayExpression()); 663 bindingVar->SetTsType(annotationType); 664 } 665 666 if (init->IsObjectExpression()) { 667 LogTypeError( 668 {"Cannot infer type for ", ident->Name(), " because class composite needs an explicit target type"}, 669 ident->Start()); 670 return false; 671 } 672 } 673 674 if (init->IsMemberExpression() && init->AsMemberExpression()->Object()->IsObjectExpression()) { 675 LogTypeError({"Class composite must be constructed separately before referring their members."}, 676 ident->Start()); 677 } 678 679 if ((init->IsMemberExpression()) && (annotationType != nullptr)) { 680 SetArrayPreferredTypeForNestedMemberExpressions(init->AsMemberExpression(), annotationType); 681 } 682 683 if (init->IsArrayExpression() && (annotationType != nullptr) && annotationType->IsETSArrayType()) { 684 if (annotationType->IsETSTupleType()) { 685 if (!ValidateTupleMinElementSize(init->AsArrayExpression(), annotationType->AsETSTupleType())) { 686 return false; 687 } 688 } 689 690 init->AsArrayExpression()->SetPreferredType(annotationType); 691 } 692 693 if (init->IsObjectExpression()) { 694 init->AsObjectExpression()->SetPreferredType(PreferredObjectTypeFromAnnotation(annotationType)); 695 } 696 697 if (typeAnnotation != nullptr && init->IsArrowFunctionExpression()) { 698 InferAliasLambdaType(typeAnnotation, init->AsArrowFunctionExpression()); 699 } 700 return true; 701} 702void ETSChecker::CheckEnumType(ir::Expression *init, checker::Type *initType, const util::StringView &varName) 703{ 704 if (initType->IsETSObjectType() && initType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::ENUM) && 705 !init->IsMemberExpression()) { 706 LogTypeError({"Cannot assign type '", initType->AsETSObjectType()->Name(), "' for variable ", varName, "."}, 707 init->Start()); 708 } 709} 710 711static bool NeedWideningBasedOnInitializerHeuristics(ir::Expression *e) 712{ 713 // NOTE: need to be done by smart casts. Return true if we need to infer wider type. 714 if (e->IsUnaryExpression()) { 715 return NeedWideningBasedOnInitializerHeuristics(e->AsUnaryExpression()->Argument()); 716 } 717 const bool isConstInit = e->IsIdentifier() && e->Variable()->Declaration()->IsConstDecl(); 718 return e->IsConditionalExpression() || e->IsLiteral() || isConstInit; 719} 720 721checker::Type *ETSChecker::CheckVariableDeclaration(ir::Identifier *ident, ir::TypeNode *typeAnnotation, 722 ir::Expression *init, ir::ModifierFlags const flags) 723{ 724 const util::StringView &varName = ident->Name(); 725 ASSERT(ident->Variable()); 726 varbinder::Variable *const bindingVar = ident->Variable(); 727 checker::Type *annotationType = nullptr; 728 729 const bool isConst = (flags & ir::ModifierFlags::CONST) != 0; 730 const bool isReadonly = (flags & ir::ModifierFlags::READONLY) != 0; 731 const bool isStatic = (flags & ir::ModifierFlags::STATIC) != 0; 732 // Note(lujiahui): It should be checked if the readonly function parameter and readonly number[] parameters 733 // are assigned with CONSTANT, which would not be correct. (After feature supported) 734 const bool omitInitConstness = isConst || (isReadonly && isStatic); 735 736 if (typeAnnotation != nullptr) { 737 annotationType = typeAnnotation->GetType(this); 738 bindingVar->SetTsType(annotationType); 739 } 740 741 if (init == nullptr) { 742 return FixOptionalVariableType(bindingVar, flags, init); 743 } 744 745 if (!CheckInit(ident, typeAnnotation, init, annotationType, bindingVar)) { 746 return GlobalTypeError(); 747 } 748 749 checker::Type *initType = init->Check(this); 750 751 if (initType == nullptr) { 752 LogTypeError("Cannot get the expression type", init->Start()); 753 return GlobalTypeError(); 754 } 755 756 if (typeAnnotation == nullptr && initType->IsETSFunctionType()) { 757 if (!init->IsArrowFunctionExpression() && (initType->AsETSFunctionType()->CallSignatures().size() != 1)) { 758 ThrowTypeError("Ambiguous function initialization because of multiple overloads", init->Start()); 759 } 760 761 annotationType = 762 initType->AsETSFunctionType()->FunctionalInterface() == nullptr 763 ? FunctionTypeToFunctionalInterfaceType(initType->AsETSFunctionType()->CallSignatures().front()) 764 : initType->AsETSFunctionType()->FunctionalInterface(); 765 bindingVar->SetTsType(annotationType); 766 } 767 768 if (annotationType != nullptr) { 769 CheckAnnotationTypeForVariableDeclaration(annotationType, annotationType->IsETSUnionType(), init, initType); 770 771 if (omitInitConstness && 772 ((initType->HasTypeFlag(TypeFlag::ETS_PRIMITIVE) && annotationType->HasTypeFlag(TypeFlag::ETS_PRIMITIVE)) || 773 (initType->IsETSStringType() && annotationType->IsETSStringType()))) { 774 bindingVar->SetTsType(init->TsType()); 775 } 776 return FixOptionalVariableType(bindingVar, flags, init); 777 } 778 779 CheckEnumType(init, initType, varName); 780 781 // NOTE: need to be done by smart casts 782 const bool needWidening = 783 !omitInitConstness && typeAnnotation == nullptr && NeedWideningBasedOnInitializerHeuristics(init); 784 bindingVar->SetTsType(needWidening ? GetNonConstantType(initType) : initType); 785 786 return FixOptionalVariableType(bindingVar, flags, init); 787} 788 789void ETSChecker::CheckAnnotationTypeForVariableDeclaration(checker::Type *annotationType, bool isUnionFunction, 790 ir::Expression *init, checker::Type *initType) 791{ 792 Type *sourceType = TryGettingFunctionTypeFromInvokeFunction(initType); 793 794 if (!isUnionFunction && annotationType->IsETSUnionType()) { 795 for (auto it : annotationType->AsETSUnionType()->ConstituentTypes()) { 796 if (it->IsETSFunctionType() || 797 (it->IsETSObjectType() && it->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::FUNCTIONAL))) { 798 isUnionFunction = true; 799 break; 800 } 801 } 802 } 803 804 if (isUnionFunction) { 805 if (!AssignmentContext(Relation(), init, initType, annotationType, init->Start(), {}, 806 TypeRelationFlag::NO_THROW) 807 .IsAssignable()) { 808 LogTypeError({"Type '", sourceType, "' cannot be assigned to type '", annotationType, "'"}, init->Start()); 809 } 810 } else { 811 if (!AssignmentContext(Relation(), init, initType, annotationType, init->Start(), {}, 812 TypeRelationFlag::NO_THROW) 813 .IsAssignable()) { 814 LogTypeError({"Type '", sourceType, "' cannot be assigned to type '", 815 TryGettingFunctionTypeFromInvokeFunction(annotationType), "'"}, 816 init->Start()); 817 } 818 } 819} 820 821//==============================================================================// 822// Smart cast support 823//==============================================================================// 824 825checker::Type *ETSChecker::ResolveSmartType(checker::Type *sourceType, checker::Type *targetType) 826{ 827 // For left-hand variable of primitive type leave it as is. 828 if (targetType->HasTypeFlag(TypeFlag::ETS_PRIMITIVE_RETURN)) { 829 return targetType; 830 } 831 832 // For left-hand variable of tuple type leave it as is. 833 if (targetType->IsETSTupleType()) { 834 return targetType; 835 } 836 837 // For left-hand variable of builtin type leave it as is. 838 if (targetType->IsETSObjectType() && targetType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_TYPE)) { 839 return targetType; 840 } 841 842 // For the Function source or target types leave the target type as is 843 // until we will be able to create the functional interface type from the source. 844 if (targetType->HasTypeFlag(TypeFlag::FUNCTION) || sourceType->HasTypeFlag(TypeFlag::FUNCTION)) { 845 return targetType; 846 } 847 848 // Nothing to do with identical types: 849 auto *nonConstSourceType = GetNonConstantType(sourceType); 850 auto *nonConstTargetType = GetNonConstantType(targetType); 851 852 if (Relation()->IsIdenticalTo(nonConstSourceType, nonConstTargetType) || 853 Relation()->IsIdenticalTo(GlobalBuiltinJSValueType(), nonConstTargetType)) { 854 return targetType; 855 } 856 857 // For type parameter, null or undefined source type return it as is. 858 if (sourceType->IsETSTypeParameter() || sourceType->DefinitelyETSNullish()) { 859 return sourceType; 860 } 861 862 // In case of Union left-hand type we have to select the proper type from the Union 863 // NOTE: it always exists at this point! 864 if (targetType->IsETSUnionType()) { 865 sourceType = targetType->AsETSUnionType()->GetAssignableType(this, sourceType); 866 ASSERT(sourceType != nullptr); 867 return sourceType; 868 } 869 870 // If source is reference type, set it as the current and use it for identifier smart cast 871 if (sourceType->IsETSReferenceType()) { 872 return sourceType; 873 } 874 875 // For right-hand variable of primitive type apply boxing conversion (case: 'let x: Object = 5', then x => Int). 876 if (sourceType->HasTypeFlag(TypeFlag::ETS_PRIMITIVE) && !sourceType->IsETSVoidType() && 877 targetType->IsETSObjectType()) { 878 return PrimitiveTypeAsETSBuiltinType(sourceType); 879 } 880 881 // NOTE - it seems that all the other possible cases are assignments like: 882 // 'Object = ObjectLiteral' or smth similar ??? 883 // thus for such cases also leave the target type as is. 884 // Possible errors in tests should clarify this hypothesis sooner or later :) 885 return targetType; 886} 887 888// Auxiliary method to reduce the size of common 'CheckTestSmartCastConditions' function. 889std::pair<Type *, Type *> ETSChecker::CheckTestNullishCondition(Type *testedType, Type *actualType, bool const strict) 890{ 891 if (!strict) { 892 return RemoveNullishTypes(actualType); 893 } 894 895 if (testedType->IsETSNullType()) { 896 return {GlobalETSNullType(), RemoveNullType(actualType)}; 897 } 898 899 if (testedType->IsETSUndefinedType()) { 900 return {GlobalETSUndefinedType(), RemoveUndefinedType(actualType)}; 901 } 902 903 return {GlobalETSNullishType(), GetNonNullishType(actualType)}; 904} 905 906// Auxiliary method to reduce the size of common 'CheckTestSmartCastConditions' function. 907std::pair<Type *, Type *> ETSChecker::CheckTestObjectCondition(ETSArrayType *testedType, Type *actualType) 908{ 909 if (actualType->IsETSUnionType()) { 910 return actualType->AsETSUnionType()->GetComplimentaryType(this, testedType); 911 } 912 913 // Both testing and actual (smart) types are arrays. Set types according to their relation. 914 // NOTE: probably the rules of type extraction should be modified later on! 915 if (actualType->IsETSArrayType()) { 916 auto *const arrayType = actualType->AsETSArrayType(); 917 918 if (Relation()->IsIdenticalTo(arrayType, testedType) || 919 arrayType->AssemblerName() == testedType->AssemblerName()) { 920 return {testedType, GetGlobalTypesHolder()->GlobalNeverType()}; 921 } 922 923 if (Relation()->IsSupertypeOf(arrayType, testedType)) { 924 return {testedType, actualType}; 925 } 926 927 if (Relation()->IsSupertypeOf(testedType, arrayType)) { 928 return {testedType, actualType}; 929 } 930 } else if (actualType->IsETSObjectType() && actualType->AsETSObjectType()->IsGlobalETSObjectType()) { 931 return {testedType, actualType}; 932 } 933 934 return {GetGlobalTypesHolder()->GlobalNeverType(), actualType}; 935} 936 937// Auxiliary method to reduce the size of common 'CheckTestSmartCastConditions' function. 938std::pair<Type *, Type *> ETSChecker::CheckTestObjectCondition(ETSObjectType *testedType, Type *actualType, 939 bool const strict) 940{ 941 if (actualType->IsETSUnionType()) { 942 return actualType->AsETSUnionType()->GetComplimentaryType(this, testedType); 943 } 944 945 // Both testing and actual (smart) types are objects. Set types according to their relation. 946 // NOTE: probably the rules of type extraction should be modified later on! 947 if (actualType->IsETSObjectType()) { 948 auto *const objectType = actualType->AsETSObjectType(); 949 950 if (Relation()->IsIdenticalTo(objectType, testedType) || 951 objectType->AssemblerName() == testedType->AssemblerName()) { 952 return {testedType, strict ? GetGlobalTypesHolder()->GlobalNeverType() : actualType}; 953 } 954 955 if (Relation()->IsSupertypeOf(objectType, testedType)) { 956 return {testedType, actualType}; 957 } 958 959 if (Relation()->IsSupertypeOf(testedType, objectType)) { 960 return {testedType, actualType}; 961 } 962 963 return {GetGlobalTypesHolder()->GlobalNeverType(), actualType}; 964 } 965 966 // NOTE: other cases (for example with functional types) will be implemented later on 967 return {testedType, actualType}; 968} 969 970static constexpr std::size_t const VARIABLE_POSITION = 0UL; 971static constexpr std::size_t const CONSEQUENT_TYPE_POSITION = 1UL; 972static constexpr std::size_t const ALTERNATE_TYPE_POSITION = 2UL; 973 974void CheckerContext::CheckTestSmartCastCondition(lexer::TokenType operatorType) 975{ 976 if (operatorType != lexer::TokenType::EOS && operatorType != lexer::TokenType::PUNCTUATOR_LOGICAL_AND && 977 operatorType != lexer::TokenType::PUNCTUATOR_LOGICAL_OR) { 978 return; 979 } 980 981 auto types = ResolveSmartCastTypes(); 982 983 if (operatorType_ == lexer::TokenType::PUNCTUATOR_LOGICAL_AND) { 984 if (types.has_value()) { 985 auto const &variable = std::get<VARIABLE_POSITION>(*types); 986 // NOTE: now we support only cases like 'if (x != null && y == null)' but don't support different type 987 // checks for a single variable (like 'if (x != null && x instanceof string)'), because it seems that 988 // it doesn't make much sense. 989 // Can be implemented later on if the need arises. 990 if (auto [_, inserted] = 991 testSmartCasts_.emplace(variable, std::make_pair(std::get<CONSEQUENT_TYPE_POSITION>(*types), 992 std::get<ALTERNATE_TYPE_POSITION>(*types))); 993 !inserted) { 994 testSmartCasts_[variable] = {nullptr, nullptr}; 995 } 996 } 997 // Clear alternate types, because now they become indefinite 998 for (auto &smartCast : testSmartCasts_) { 999 smartCast.second.second = nullptr; 1000 } 1001 } else if (operatorType_ == lexer::TokenType::PUNCTUATOR_LOGICAL_OR) { 1002 if (bool const cleanConsequent = types.has_value() ? CheckTestOrSmartCastCondition(*types) : true; 1003 cleanConsequent) { 1004 // Clear consequent types, because now they become indefinite 1005 for (auto &smartCast : testSmartCasts_) { 1006 smartCast.second.first = nullptr; 1007 } 1008 } 1009 } else if (types.has_value()) { 1010 testSmartCasts_.emplace( 1011 std::get<VARIABLE_POSITION>(*types), 1012 std::make_pair(std::get<CONSEQUENT_TYPE_POSITION>(*types), std::get<ALTERNATE_TYPE_POSITION>(*types))); 1013 } 1014 1015 testCondition_ = {}; 1016 operatorType_ = operatorType; 1017} 1018 1019std::optional<SmartCastTuple> CheckerContext::ResolveSmartCastTypes() 1020{ 1021 if (testCondition_.variable == nullptr) { 1022 return std::nullopt; 1023 } 1024 1025 // Exclude processing of global variables and those captured in lambdas and modified there 1026 auto const *const variableScope = testCondition_.variable->GetScope(); 1027 auto const topLevelVariable = 1028 variableScope != nullptr ? variableScope->IsGlobalScope() || 1029 (variableScope->Parent() != nullptr && variableScope->Parent()->IsGlobalScope()) 1030 : false; 1031 if (topLevelVariable) { 1032 return std::nullopt; 1033 } 1034 1035 ASSERT(testCondition_.testedType != nullptr); 1036 // NOTE: functional types are not supported now 1037 if (!testCondition_.testedType->IsETSReferenceType() || 1038 testCondition_.testedType->HasTypeFlag(TypeFlag::FUNCTION)) { 1039 return std::nullopt; 1040 } 1041 1042 auto *smartType = GetSmartCast(testCondition_.variable); 1043 if (smartType == nullptr) { 1044 smartType = testCondition_.variable->TsType(); 1045 } 1046 1047 auto *const checker = parent_->AsETSChecker(); 1048 Type *consequentType = nullptr; 1049 Type *alternateType = nullptr; 1050 1051 if (testCondition_.testedType->DefinitelyETSNullish()) { 1052 // In case of testing for 'null' and/or 'undefined' remove corresponding null-like types. 1053 std::tie(consequentType, alternateType) = 1054 checker->CheckTestNullishCondition(testCondition_.testedType, smartType, testCondition_.strict); 1055 } else { 1056 if (testCondition_.testedType->IsETSObjectType()) { 1057 auto *const testedType = testCondition_.testedType->AsETSObjectType(); 1058 std::tie(consequentType, alternateType) = 1059 checker->CheckTestObjectCondition(testedType, smartType, testCondition_.strict); 1060 } else if (testCondition_.testedType->IsETSArrayType()) { 1061 auto *const testedType = testCondition_.testedType->AsETSArrayType(); 1062 std::tie(consequentType, alternateType) = checker->CheckTestObjectCondition(testedType, smartType); 1063 } else if (testCondition_.testedType->IsETSUnionType()) { 1064 // NOTE: now we don't support 'instanceof' operation for union types? 1065 UNREACHABLE(); 1066 } else { 1067 // NOTE: it seems that no more cases are possible here! :) 1068 UNREACHABLE(); 1069 } 1070 } 1071 1072 return !testCondition_.negate 1073 ? std::make_optional(std::make_tuple(testCondition_.variable, consequentType, alternateType)) 1074 : std::make_optional(std::make_tuple(testCondition_.variable, alternateType, consequentType)); 1075} 1076bool ETSChecker::CheckVoidAnnotation(const ir::ETSPrimitiveType *typeAnnotation) 1077{ 1078 // Void annotation is valid only when used as 'return type' , 'type parameter instantiation', 'default type'. 1079 if (typeAnnotation->GetPrimitiveType() != ir::PrimitiveType::VOID) { 1080 return true; 1081 } 1082 1083 auto parent = typeAnnotation->Parent(); 1084 if (parent->IsScriptFunction() && parent->AsScriptFunction()->ReturnTypeAnnotation() == typeAnnotation) { 1085 return true; 1086 } 1087 if (parent->IsETSFunctionType() && parent->AsETSFunctionType()->ReturnType() == typeAnnotation) { 1088 return true; 1089 } 1090 if (parent->IsTSTypeParameterInstantiation() || parent->IsTSTypeParameter()) { 1091 return true; 1092 } 1093 LogTypeError({"'void' used as type annotation."}, typeAnnotation->Start()); 1094 return false; 1095} 1096void ETSChecker::ApplySmartCast(varbinder::Variable const *const variable, checker::Type *const smartType) noexcept 1097{ 1098 ASSERT(variable != nullptr); 1099 if (smartType != nullptr) { 1100 auto *variableType = variable->TsType(); 1101 1102 if (Relation()->IsIdenticalTo(variableType, smartType)) { 1103 Context().RemoveSmartCast(variable); 1104 } else { 1105 Context().SetSmartCast(variable, smartType); 1106 } 1107 } 1108} 1109 1110bool CheckerContext::CheckTestOrSmartCastCondition(SmartCastTuple const &types) 1111{ 1112 auto *const &variable = std::get<VARIABLE_POSITION>(types); 1113 auto *const &consequentTypeNew = std::get<CONSEQUENT_TYPE_POSITION>(types); 1114 auto *const &alternateTypeNew = std::get<ALTERNATE_TYPE_POSITION>(types); 1115 1116 if (auto const it = testSmartCasts_.find(variable); it != testSmartCasts_.end()) { 1117 auto *const consequentTypeOld = it->second.first; 1118 if (consequentTypeOld == nullptr) { 1119 return true; 1120 } 1121 1122 if (consequentTypeNew != nullptr && !parent_->Relation()->IsIdenticalTo(consequentTypeOld, consequentTypeNew)) { 1123 it->second.first = parent_->AsETSChecker()->CreateETSUnionType({consequentTypeOld, consequentTypeNew}); 1124 } 1125 1126 if (auto *const alternateTypeOld = it->second.second; alternateTypeOld != nullptr) { 1127 if (alternateTypeNew != nullptr && 1128 !parent_->Relation()->IsIdenticalTo(alternateTypeOld, alternateTypeNew)) { 1129 it->second.second = parent_->AsETSChecker()->CreateETSUnionType({alternateTypeOld, alternateTypeNew}); 1130 } 1131 } else { 1132 it->second.second = alternateTypeNew; 1133 } 1134 1135 return false; 1136 } 1137 1138 // NOTE: now we support only cases like 'if (x != null || y != null)' or 'if (x instanceof A || x instanceof B)' 1139 // although it seems that the resulting variable type in the second case isn't used in subsequent code directly. 1140 // More complex conditions can be implemented later on if the need arises. 1141 testSmartCasts_.emplace(variable, std::make_pair(consequentTypeNew, alternateTypeNew)); 1142 return true; 1143} 1144 1145//==============================================================================// 1146 1147void ETSChecker::SetArrayPreferredTypeForNestedMemberExpressions(ir::MemberExpression *expr, Type *annotationType) 1148{ 1149 if ((expr == nullptr) || (annotationType == nullptr)) { 1150 return; 1151 } 1152 1153 if (expr->Kind() != ir::MemberExpressionKind::ELEMENT_ACCESS) { 1154 return; 1155 } 1156 1157 // Expand all member expressions 1158 Type *elementType = annotationType; 1159 ir::Expression *object = expr->Object(); 1160 while ((object != nullptr) && (object->IsMemberExpression())) { 1161 ir::MemberExpression *memberExpr = object->AsMemberExpression(); 1162 if (memberExpr->Kind() != ir::MemberExpressionKind::ELEMENT_ACCESS) { 1163 return; 1164 } 1165 1166 object = memberExpr->Object(); 1167 elementType = CreateETSArrayType(elementType); 1168 } 1169 1170 // Set explicit target type for array 1171 if ((object != nullptr) && (object->IsArrayExpression())) { 1172 ir::ArrayExpression *array = object->AsArrayExpression(); 1173 array->SetPreferredType(CreateETSArrayType(elementType)); 1174 } 1175} 1176 1177static void CheckExpandedType(Type *expandedAliasType, std::set<util::StringView> ¶metersNeedToBeBoxed, 1178 bool needToBeBoxed) 1179{ 1180 if (expandedAliasType->IsETSTypeParameter()) { 1181 auto paramName = expandedAliasType->AsETSTypeParameter()->GetDeclNode()->Name()->Name(); 1182 if (needToBeBoxed) { 1183 parametersNeedToBeBoxed.insert(paramName); 1184 } 1185 } else if (expandedAliasType->IsETSObjectType()) { 1186 auto objectType = expandedAliasType->AsETSObjectType(); 1187 needToBeBoxed = 1188 objectType->GetDeclNode()->IsClassDefinition() || objectType->GetDeclNode()->IsTSInterfaceDeclaration(); 1189 for (const auto typeArgument : objectType->TypeArguments()) { 1190 CheckExpandedType(typeArgument, parametersNeedToBeBoxed, needToBeBoxed); 1191 } 1192 } else if (expandedAliasType->IsETSTupleType()) { 1193 auto tupleType = expandedAliasType->AsETSTupleType(); 1194 needToBeBoxed = false; 1195 for (auto type : tupleType->GetTupleTypesList()) { 1196 CheckExpandedType(type, parametersNeedToBeBoxed, needToBeBoxed); 1197 } 1198 } else if (expandedAliasType->IsETSArrayType()) { 1199 auto arrayType = expandedAliasType->AsETSArrayType(); 1200 needToBeBoxed = false; 1201 auto elementType = arrayType->ElementType(); 1202 CheckExpandedType(elementType, parametersNeedToBeBoxed, needToBeBoxed); 1203 } else if (expandedAliasType->IsETSUnionType()) { 1204 auto unionType = expandedAliasType->AsETSUnionType(); 1205 needToBeBoxed = false; 1206 for (auto type : unionType->ConstituentTypes()) { 1207 CheckExpandedType(type, parametersNeedToBeBoxed, needToBeBoxed); 1208 } 1209 } 1210} 1211 1212Type *ETSChecker::HandleTypeAlias(ir::Expression *const name, const ir::TSTypeParameterInstantiation *const typeParams) 1213{ 1214 ASSERT(name->IsIdentifier() && name->AsIdentifier()->Variable() && 1215 name->AsIdentifier()->Variable()->Declaration()->IsTypeAliasDecl()); 1216 1217 auto *const typeAliasNode = 1218 name->AsIdentifier()->Variable()->Declaration()->AsTypeAliasDecl()->Node()->AsTSTypeAliasDeclaration(); 1219 1220 // NOTE (mmartin): modify for default params 1221 if ((typeParams == nullptr) != (typeAliasNode->TypeParams() == nullptr)) { 1222 if (typeParams == nullptr) { 1223 LogTypeError("Type alias declaration is generic, but no type parameters were provided", name->Start()); 1224 return GlobalTypeError(); 1225 } 1226 1227 LogTypeError("Type alias declaration is not generic, but type parameters were provided", typeParams->Start()); 1228 return GlobalTypeError(); 1229 } 1230 1231 if (typeParams == nullptr) { 1232 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) 1233 return GetReferencedTypeBase(name); 1234 } 1235 1236 for (auto *const origTypeParam : typeParams->Params()) { 1237 origTypeParam->Check(this); 1238 } 1239 1240 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) 1241 Type *const aliasType = GetReferencedTypeBase(name); 1242 auto *aliasSub = NewSubstitution(); 1243 if (typeAliasNode->TypeParams()->Params().size() != typeParams->Params().size()) { 1244 LogTypeError("Wrong number of type parameters for generic type alias", typeParams->Start()); 1245 return GlobalTypeError(); 1246 } 1247 1248 std::set<util::StringView> parametersNeedToBeBoxed; 1249 auto expandedAliasType = aliasType->Substitute(Relation(), aliasSub); 1250 CheckExpandedType(expandedAliasType, parametersNeedToBeBoxed, false); 1251 1252 for (std::size_t idx = 0; idx < typeAliasNode->TypeParams()->Params().size(); ++idx) { 1253 auto *typeAliasTypeName = typeAliasNode->TypeParams()->Params().at(idx)->Name(); 1254 auto *typeAliasType = typeAliasTypeName->Variable()->TsType(); 1255 if (!typeAliasType->IsETSTypeParameter()) { 1256 continue; 1257 } 1258 auto paramType = typeParams->Params().at(idx)->TsType(); 1259 if (parametersNeedToBeBoxed.find(typeAliasTypeName->Name()) != parametersNeedToBeBoxed.end()) { 1260 auto boxedType = PrimitiveTypeAsETSBuiltinType(typeParams->Params().at(idx)->GetType(this)); 1261 if (boxedType != nullptr) { 1262 paramType = boxedType; 1263 } 1264 } 1265 aliasSub->insert({typeAliasType->AsETSTypeParameter(), paramType}); 1266 } 1267 1268 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) 1269 ValidateGenericTypeAliasForClonedNode(typeAliasNode->AsTSTypeAliasDeclaration(), typeParams); 1270 1271 return aliasType->Substitute(Relation(), aliasSub); 1272} 1273 1274std::vector<util::StringView> ETSChecker::GetNameForSynteticObjectType(const util::StringView &source) 1275{ 1276 const std::string str = source.Mutf8(); 1277 std::istringstream ss {str}; 1278 const char delimiter = '.'; 1279 std::string token; 1280 1281 std::vector<util::StringView> syntheticName {}; 1282 1283 while (std::getline(ss, token, delimiter)) { 1284 if (!token.empty()) { 1285 util::UString sV(token, Allocator()); 1286 syntheticName.emplace_back(sV.View()); 1287 } 1288 } 1289 1290 return syntheticName; 1291} 1292 1293std::pair<bool, util::StringView> FindSpecifierForModuleObject(ir::ETSImportDeclaration *importDecl, 1294 util::StringView const &name) 1295{ 1296 if (importDecl == nullptr) { 1297 return std::make_pair(true, util::StringView()); 1298 } 1299 1300 for (auto item : importDecl->Specifiers()) { 1301 if (item->IsImportSpecifier() && item->AsImportSpecifier()->Imported()->Name().Is(name.Mutf8())) { 1302 if (!item->AsImportSpecifier()->Imported()->Name().Is(item->AsImportSpecifier()->Local()->Name().Mutf8())) { 1303 return std::make_pair(true, item->AsImportSpecifier()->Local()->Name()); 1304 } 1305 return std::make_pair(true, util::StringView()); 1306 } 1307 } 1308 return std::make_pair(false, util::StringView()); 1309} 1310 1311template <checker::PropertyType TYPE> 1312void ETSChecker::BindingsModuleObjectAddProperty(checker::ETSObjectType *moduleObjType, 1313 ir::ETSImportDeclaration *importDecl, 1314 const varbinder::Scope::VariableMap &bindings) 1315{ 1316 for (auto [_, var] : bindings) { 1317 (void)_; 1318 auto [found, aliasedName] = FindSpecifierForModuleObject(importDecl, var->AsLocalVariable()->Name()); 1319 if ((var->AsLocalVariable()->Declaration()->Node()->IsExported() || 1320 var->AsLocalVariable()->Declaration()->Node()->IsExportedType()) && 1321 found) { 1322 if (!aliasedName.Empty()) { 1323 moduleObjType->AddReExportAlias(var->Declaration()->Name(), aliasedName); 1324 } 1325 moduleObjType->AddProperty<TYPE>(var->AsLocalVariable(), 1326 FindPropNameForNamespaceImport(var->AsLocalVariable()->Name())); 1327 } 1328 } 1329} 1330 1331util::StringView ETSChecker::FindPropNameForNamespaceImport(const util::StringView &originalName) 1332{ 1333 if (auto relatedMapItem = 1334 VarBinder()->AsETSBinder()->GetSelectiveExportAliasMultimap().find(Program()->SourceFilePath()); 1335 relatedMapItem != VarBinder()->AsETSBinder()->GetSelectiveExportAliasMultimap().end()) { 1336 if (auto result = std::find_if(relatedMapItem->second.begin(), relatedMapItem->second.end(), 1337 [originalName](const auto &item) { return item.second == originalName; }); 1338 result != relatedMapItem->second.end()) { 1339 return result->first; 1340 } 1341 } 1342 1343 return originalName; 1344} 1345 1346// Helps to prevent searching for the imported file among external sources if it is the entry program 1347static parser::Program *SelectEntryOrExternalProgram(varbinder::ETSBinder *etsBinder, 1348 const util::StringView &importPath) 1349{ 1350 if (importPath.Is(etsBinder->GetGlobalRecordTable()->Program()->AbsoluteName().Mutf8())) { 1351 return etsBinder->GetGlobalRecordTable()->Program(); 1352 } 1353 1354 auto programList = etsBinder->GetProgramList(importPath); 1355 ASSERT(!programList.empty()); 1356 return programList.front(); 1357} 1358 1359void ETSChecker::SetPropertiesForModuleObject(checker::ETSObjectType *moduleObjType, const util::StringView &importPath, 1360 ir::ETSImportDeclaration *importDecl) 1361{ 1362 parser::Program *program = 1363 SelectEntryOrExternalProgram(static_cast<varbinder::ETSBinder *>(VarBinder()), importPath); 1364 // Check imported properties before assigning them to module object 1365 if (!program->IsASTChecked()) { 1366 // NOTE: helps to avoid endless loop in case of recursive imports that uses all bindings 1367 program->MarkASTAsChecked(); 1368 program->Ast()->Check(this); 1369 } 1370 1371 BindingsModuleObjectAddProperty<checker::PropertyType::STATIC_FIELD>( 1372 moduleObjType, importDecl, program->GlobalClassScope()->StaticFieldScope()->Bindings()); 1373 1374 BindingsModuleObjectAddProperty<checker::PropertyType::STATIC_METHOD>( 1375 moduleObjType, importDecl, program->GlobalClassScope()->StaticMethodScope()->Bindings()); 1376 1377 BindingsModuleObjectAddProperty<checker::PropertyType::STATIC_DECL>( 1378 moduleObjType, importDecl, program->GlobalClassScope()->StaticDeclScope()->Bindings()); 1379 1380 BindingsModuleObjectAddProperty<checker::PropertyType::STATIC_DECL>( 1381 moduleObjType, importDecl, program->GlobalClassScope()->InstanceDeclScope()->Bindings()); 1382 1383 BindingsModuleObjectAddProperty<checker::PropertyType::STATIC_DECL>( 1384 moduleObjType, importDecl, program->GlobalClassScope()->TypeAliasScope()->Bindings()); 1385} 1386 1387void ETSChecker::SetrModuleObjectTsType(ir::Identifier *local, checker::ETSObjectType *moduleObjType) 1388{ 1389 auto *etsBinder = static_cast<varbinder::ETSBinder *>(VarBinder()); 1390 1391 for (auto [bindingName, var] : etsBinder->TopScope()->Bindings()) { 1392 if (bindingName.Is(local->Name().Mutf8())) { 1393 var->SetTsType(moduleObjType); 1394 } 1395 } 1396} 1397 1398Type *ETSChecker::GetReferencedTypeFromBase([[maybe_unused]] Type *baseType, [[maybe_unused]] ir::Expression *name) 1399{ 1400 LogTypeError("Invalid type reference.", name->Start()); 1401 name->SetTsType(GlobalTypeError()); 1402 return name->TsTypeOrError(); 1403} 1404 1405Type *ETSChecker::GetReferencedTypeBase(ir::Expression *name) 1406{ 1407 if (name->IsTSQualifiedName()) { 1408 return name->Check(this); 1409 } 1410 1411 ASSERT(name->IsIdentifier() && name->AsIdentifier()->Variable() != nullptr); 1412 1413 // NOTE: kbaladurin. forbid usage imported entities as types without declarations 1414 auto *importData = VarBinder()->AsETSBinder()->DynamicImportDataForVar(name->AsIdentifier()->Variable()); 1415 if (importData != nullptr && importData->import->IsPureDynamic()) { 1416 name->SetTsType(GlobalBuiltinDynamicType(importData->import->Language())); 1417 return name->TsType(); 1418 } 1419 1420 auto *refVar = name->AsIdentifier()->Variable()->AsLocalVariable(); 1421 1422 checker::Type *tsType = nullptr; 1423 switch (refVar->Declaration()->Node()->Type()) { 1424 case ir::AstNodeType::TS_INTERFACE_DECLARATION: { 1425 tsType = GetTypeFromInterfaceReference(refVar); 1426 break; 1427 } 1428 case ir::AstNodeType::CLASS_DECLARATION: 1429 case ir::AstNodeType::STRUCT_DECLARATION: 1430 case ir::AstNodeType::CLASS_DEFINITION: { 1431 tsType = GetTypeFromClassReference(refVar); 1432 break; 1433 } 1434 case ir::AstNodeType::TS_ENUM_DECLARATION: { 1435 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) 1436 tsType = GetTypeFromEnumReference(refVar); 1437 break; 1438 } 1439 case ir::AstNodeType::TS_TYPE_PARAMETER: { 1440 tsType = GetTypeFromTypeParameterReference(refVar, name->Start()); 1441 break; 1442 } 1443 case ir::AstNodeType::TS_TYPE_ALIAS_DECLARATION: { 1444 tsType = GetTypeFromTypeAliasReference(refVar); 1445 break; 1446 } 1447 default: { 1448 UNREACHABLE(); 1449 } 1450 } 1451 name->SetTsType(tsType); 1452 return tsType; 1453} 1454 1455void ETSChecker::ConcatConstantString(util::UString &target, Type *type) 1456{ 1457 switch (ETSType(type)) { 1458 case TypeFlag::ETS_OBJECT: { 1459 ASSERT(type->IsETSStringType()); 1460 target.Append(type->AsETSStringType()->GetValue()); 1461 break; 1462 } 1463 case TypeFlag::ETS_BOOLEAN: { 1464 target.Append(type->AsETSBooleanType()->GetValue() ? "true" : "false"); 1465 break; 1466 } 1467 case TypeFlag::BYTE: { 1468 target.Append(std::to_string(type->AsByteType()->GetValue())); 1469 break; 1470 } 1471 case TypeFlag::CHAR: { 1472 std::string s(1, type->AsCharType()->GetValue()); 1473 target.Append(s); 1474 break; 1475 } 1476 case TypeFlag::SHORT: { 1477 target.Append(std::to_string(type->AsShortType()->GetValue())); 1478 break; 1479 } 1480 case TypeFlag::INT: { 1481 target.Append(std::to_string(type->AsIntType()->GetValue())); 1482 break; 1483 } 1484 case TypeFlag::LONG: { 1485 target.Append(std::to_string(type->AsLongType()->GetValue())); 1486 break; 1487 } 1488 case TypeFlag::FLOAT: { 1489 target.Append(std::to_string(type->AsFloatType()->GetValue())); 1490 break; 1491 } 1492 case TypeFlag::DOUBLE: { 1493 target.Append(std::to_string(type->AsDoubleType()->GetValue())); 1494 break; 1495 } 1496 default: { 1497 UNREACHABLE(); 1498 } 1499 } 1500} 1501 1502Type *ETSChecker::HandleStringConcatenation(Type *leftType, Type *rightType) 1503{ 1504 ASSERT(leftType->IsETSStringType() || rightType->IsETSStringType()); 1505 1506 if (!leftType->HasTypeFlag(checker::TypeFlag::CONSTANT) || !rightType->HasTypeFlag(checker::TypeFlag::CONSTANT) || 1507 leftType->IsETSBigIntType() || rightType->IsETSBigIntType()) { 1508 return GlobalETSStringLiteralType(); 1509 } 1510 1511 util::UString concatenated(Allocator()); 1512 ConcatConstantString(concatenated, leftType); 1513 ConcatConstantString(concatenated, rightType); 1514 1515 return CreateETSStringLiteralType(concatenated.View()); 1516} 1517 1518checker::ETSFunctionType *ETSChecker::FindFunctionInVectorGivenByName(util::StringView name, 1519 ArenaVector<checker::ETSFunctionType *> &list) 1520{ 1521 for (auto *it : list) { 1522 if (it->Name() == name) { 1523 return it; 1524 } 1525 } 1526 1527 return nullptr; 1528} 1529 1530bool ETSChecker::IsFunctionContainsSignature(checker::ETSFunctionType *funcType, Signature *signature) 1531{ 1532 for (auto *it : funcType->CallSignatures()) { 1533 Relation()->IsCompatibleTo(it, signature); 1534 if (Relation()->IsTrue()) { 1535 return true; 1536 } 1537 } 1538 1539 return false; 1540} 1541 1542bool ETSChecker::CheckFunctionContainsClashingSignature(const checker::ETSFunctionType *funcType, Signature *signature) 1543{ 1544 for (auto *it : funcType->CallSignatures()) { 1545 SavedTypeRelationFlagsContext strfCtx(Relation(), TypeRelationFlag::NONE); 1546 Relation()->IsCompatibleTo(it, signature); 1547 if (Relation()->IsTrue() && it->Function()->Id()->Name() == signature->Function()->Id()->Name()) { 1548 std::stringstream ss; 1549 it->ToString(ss, nullptr, true); 1550 auto sigStr1 = ss.str(); 1551 ss.str(std::string {}); // Clear buffer 1552 signature->ToString(ss, nullptr, true); 1553 auto sigStr2 = ss.str(); 1554 LogTypeError({"Function '", it->Function()->Id()->Name(), sigStr1.c_str(), 1555 "' is redeclared with different signature '", signature->Function()->Id()->Name(), 1556 sigStr2.c_str(), "'"}, 1557 signature->Function()->ReturnTypeAnnotation()->Start()); 1558 return false; 1559 } 1560 } 1561 return true; 1562} 1563 1564void ETSChecker::MergeSignatures(checker::ETSFunctionType *target, checker::ETSFunctionType *source) 1565{ 1566 for (auto *s : source->CallSignatures()) { 1567 if (IsFunctionContainsSignature(target, s)) { 1568 continue; 1569 } 1570 1571 if (!CheckFunctionContainsClashingSignature(target, s)) { 1572 continue; 1573 } 1574 target->AddCallSignature(s); 1575 } 1576} 1577 1578void ETSChecker::MergeComputedAbstracts(ArenaVector<checker::ETSFunctionType *> &merged, 1579 ArenaVector<checker::ETSFunctionType *> ¤t) 1580{ 1581 for (auto *curr : current) { 1582 auto name = curr->Name(); 1583 auto *found = FindFunctionInVectorGivenByName(name, merged); 1584 if (found != nullptr) { 1585 MergeSignatures(found, curr); 1586 continue; 1587 } 1588 1589 merged.push_back(curr); 1590 } 1591} 1592 1593ir::AstNode *ETSChecker::FindAncestorGivenByType(ir::AstNode *node, ir::AstNodeType type, const ir::AstNode *endNode) 1594{ 1595 auto *iter = node->Parent(); 1596 1597 while (iter != endNode) { 1598 if (iter->Type() == type) { 1599 return iter; 1600 } 1601 1602 iter = iter->Parent(); 1603 } 1604 1605 return nullptr; 1606} 1607 1608util::StringView ETSChecker::GetContainingObjectNameFromSignature(Signature *signature) 1609{ 1610 ASSERT(signature->Function()); 1611 auto *iter = signature->Function()->Parent(); 1612 1613 while (iter != nullptr) { 1614 if (iter->IsClassDefinition()) { 1615 return iter->AsClassDefinition()->Ident()->Name(); 1616 } 1617 1618 if (iter->IsTSInterfaceDeclaration()) { 1619 return iter->AsTSInterfaceDeclaration()->Id()->Name(); 1620 } 1621 1622 iter = iter->Parent(); 1623 } 1624 1625 UNREACHABLE(); 1626 return {""}; 1627} 1628 1629std::optional<const ir::AstNode *> ETSChecker::FindJumpTarget(ir::AstNode *node) 1630{ 1631 ASSERT(node->IsBreakStatement() || node->IsContinueStatement()); 1632 1633 bool const isContinue = node->IsContinueStatement(); 1634 1635 // Look for label 1636 auto label = isContinue ? node->AsContinueStatement()->Ident() : node->AsBreakStatement()->Ident(); 1637 if (label != nullptr) { 1638 auto var = label->Variable(); 1639 if (var != nullptr && var->Declaration()->IsLabelDecl()) { 1640 return var->Declaration()->Node(); 1641 } 1642 1643 // Failed to resolve variable for label 1644 LogUnResolvedError(label); 1645 return {}; 1646 } 1647 1648 // No label, find the nearest loop or switch statement 1649 const auto *iter = node->Parent(); 1650 while (iter != nullptr) { 1651 switch (iter->Type()) { 1652 case ir::AstNodeType::DO_WHILE_STATEMENT: 1653 case ir::AstNodeType::WHILE_STATEMENT: 1654 case ir::AstNodeType::FOR_UPDATE_STATEMENT: 1655 case ir::AstNodeType::FOR_OF_STATEMENT: 1656 case ir::AstNodeType::SWITCH_STATEMENT: { 1657 return iter; 1658 } 1659 default: { 1660 break; 1661 } 1662 } 1663 1664 iter = iter->Parent(); 1665 } 1666 1667 UNREACHABLE(); 1668} 1669 1670varbinder::VariableFlags ETSChecker::GetAccessFlagFromNode(const ir::AstNode *node) 1671{ 1672 if (node->IsPrivate()) { 1673 return varbinder::VariableFlags::PRIVATE; 1674 } 1675 1676 if (node->IsProtected()) { 1677 return varbinder::VariableFlags::PROTECTED; 1678 } 1679 1680 return varbinder::VariableFlags::PUBLIC; 1681} 1682 1683Type *ETSChecker::CheckSwitchDiscriminant(ir::Expression *discriminant) 1684{ 1685 discriminant->Check(this); 1686 auto *discriminantType = GetNonConstantType(MaybeUnboxExpression(discriminant)); 1687 if (!discriminantType->HasTypeFlag(TypeFlag::VALID_SWITCH_TYPE)) { 1688 if (!(discriminantType->IsETSObjectType() && 1689 discriminantType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_STRING | 1690 ETSObjectFlags::STRING | ETSObjectFlags::ENUM))) { 1691 LogTypeError({"Incompatible types. Found: ", discriminantType, 1692 ", required: char , byte , short , int, long , Char , Byte , Short , Int, Long , String " 1693 "or an enum type"}, 1694 discriminant->Start()); 1695 } 1696 } 1697 1698 return discriminantType; 1699} 1700 1701void ETSChecker::AddBoxingUnboxingFlagsToNode(ir::AstNode *node, Type *boxingUnboxingType) 1702{ 1703 if (boxingUnboxingType->IsETSObjectType()) { 1704 node->AddBoxingUnboxingFlags(GetBoxingFlag(boxingUnboxingType)); 1705 } else if (!boxingUnboxingType->IsETSUnionType()) { 1706 node->AddBoxingUnboxingFlags(GetUnboxingFlag(boxingUnboxingType)); 1707 } 1708} 1709 1710Type *ETSChecker::MaybeBoxExpression(ir::Expression *expr) 1711{ 1712 auto *promoted = MaybePromotedBuiltinType(expr->TsTypeOrError()); 1713 if (promoted != expr->TsTypeOrError()) { 1714 expr->AddBoxingUnboxingFlags(GetBoxingFlag(promoted)); 1715 } 1716 return promoted; 1717} 1718 1719Type *ETSChecker::MaybeUnboxExpression(ir::Expression *expr) 1720{ 1721 auto *primitive = MaybePrimitiveBuiltinType(expr->TsType()); 1722 if (primitive != expr->TsType()) { 1723 expr->AddBoxingUnboxingFlags(GetUnboxingFlag(primitive)); 1724 } 1725 return primitive; 1726} 1727 1728void ETSChecker::CheckForSameSwitchCases(ArenaVector<ir::SwitchCaseStatement *> const &cases) 1729{ 1730 CheckItemCasesConstant(cases); 1731 CheckItemCasesDuplicate(cases); 1732} 1733 1734std::string ETSChecker::GetStringFromIdentifierValue(checker::Type *caseType) const 1735{ 1736 const auto identifierTypeKind = ETSChecker::TypeKind(caseType); 1737 switch (identifierTypeKind) { 1738 case TypeFlag::BYTE: { 1739 return std::to_string(caseType->AsByteType()->GetValue()); 1740 } 1741 case TypeFlag::SHORT: { 1742 return std::to_string(caseType->AsShortType()->GetValue()); 1743 } 1744 case TypeFlag::CHAR: { 1745 return std::to_string(caseType->AsCharType()->GetValue()); 1746 } 1747 case TypeFlag::INT: { 1748 return std::to_string(caseType->AsIntType()->GetValue()); 1749 } 1750 case TypeFlag::LONG: { 1751 return std::to_string(caseType->AsLongType()->GetValue()); 1752 } 1753 case TypeFlag::ETS_OBJECT: { 1754 VarBinder()->ThrowError(caseType->AsETSObjectType()->Variable()->Declaration()->Node()->Start(), 1755 "not implemented"); 1756 } 1757 default: { 1758 UNREACHABLE(); 1759 } 1760 } 1761} 1762 1763bool IsConstantMemberOrIdentifierExpression(ir::Expression *expression) 1764{ 1765 if (expression->IsMemberExpression()) { 1766 auto *var = expression->AsMemberExpression()->PropVar(); 1767 return var->Declaration()->IsConstDecl() || 1768 (var->Declaration()->IsReadonlyDecl() && var->HasFlag(varbinder::VariableFlags::STATIC)); 1769 } 1770 1771 if (expression->IsIdentifier()) { 1772 auto *var = expression->AsIdentifier()->Variable(); 1773 return var->Declaration()->IsConstDecl() || 1774 (var->Declaration()->IsReadonlyDecl() && var->HasFlag(varbinder::VariableFlags::STATIC)); 1775 } 1776 1777 return false; 1778} 1779 1780void ETSChecker::CheckItemCasesConstant(ArenaVector<ir::SwitchCaseStatement *> const &cases) 1781{ 1782 for (auto &it : cases) { 1783 auto *caseTest = it->Test(); 1784 if (caseTest == nullptr) { 1785 continue; 1786 } 1787 auto *caseType = caseTest->TsType(); 1788 if (caseTest->TsType()->IsETSIntEnumType() || caseTest->TsType()->IsETSStringEnumType()) { 1789 continue; 1790 } 1791 1792 if (caseTest->IsIdentifier() || caseTest->IsMemberExpression()) { 1793 if (!IsConstantMemberOrIdentifierExpression(caseTest)) { 1794 LogTypeError("Constant expression required", it->Start()); 1795 continue; 1796 } 1797 1798 if (!caseType->HasTypeFlag(checker::TypeFlag::VALID_SWITCH_TYPE)) { 1799 LogTypeError("Unexpected type " + caseType->ToString(), it->Start()); 1800 continue; 1801 } 1802 continue; 1803 } 1804 } 1805} 1806 1807void CheckItemEnumType(ir::Expression const *const caseTest, ETSChecker *checker, ETSIntEnumType const *const type, 1808 bool &isDup) 1809{ 1810 if (caseTest->TsType()->AsETSIntEnumType()->IsSameEnumLiteralType(type)) { 1811 isDup = true; 1812 checker->LogTypeError("Case duplicate", caseTest->Start()); 1813 } 1814} 1815 1816void CheckItemStringEnumType(ir::Expression const *const caseTest, ETSChecker *checker, 1817 ETSStringEnumType const *const type, bool &isDup) 1818{ 1819 if (caseTest->TsType()->AsETSStringEnumType()->IsSameEnumLiteralType(type)) { 1820 isDup = true; 1821 checker->LogTypeError("Case duplicate", caseTest->Start()); 1822 } 1823} 1824 1825void ETSChecker::CheckItemCasesDuplicate(ArenaVector<ir::SwitchCaseStatement *> const &cases) 1826{ 1827 for (size_t caseNum = 0; caseNum < cases.size(); caseNum++) { 1828 bool isItemDuplicate = false; 1829 for (size_t compareCase = caseNum + 1; compareCase < cases.size(); compareCase++) { 1830 auto *caseTest = cases.at(caseNum)->Test(); 1831 auto *compareCaseTest = cases.at(compareCase)->Test(); 1832 1833 if (caseTest == nullptr || compareCaseTest == nullptr) { 1834 continue; 1835 } 1836 1837 if (caseTest->TsType()->IsETSIntEnumType()) { 1838 CheckItemEnumType(caseTest, this, compareCaseTest->TsType()->AsETSIntEnumType(), isItemDuplicate); 1839 continue; 1840 } 1841 1842 if (caseTest->TsType()->IsETSStringEnumType()) { 1843 CheckItemStringEnumType(caseTest, this, compareCaseTest->TsType()->AsETSStringEnumType(), 1844 isItemDuplicate); 1845 continue; 1846 } 1847 1848 if (caseTest->IsIdentifier() || caseTest->IsMemberExpression()) { 1849 CheckIdentifierSwitchCase(caseTest, compareCaseTest, cases.at(caseNum)->Start()); 1850 continue; 1851 } 1852 1853 if (compareCaseTest->IsIdentifier() || compareCaseTest->IsMemberExpression()) { 1854 CheckIdentifierSwitchCase(compareCaseTest, caseTest, cases.at(compareCase)->Start()); 1855 continue; 1856 } 1857 1858 if (caseTest->IsLiteral() && compareCaseTest->IsLiteral() && 1859 GetStringFromLiteral(caseTest) != GetStringFromLiteral(compareCaseTest)) { 1860 continue; 1861 } 1862 1863 if (!(IsConstantExpression(caseTest, caseTest->TsType()) || caseTest->IsLiteral()) || 1864 !(IsConstantExpression(compareCaseTest, compareCaseTest->TsType()) || compareCaseTest->IsLiteral())) { 1865 continue; 1866 } 1867 1868 if (!isItemDuplicate) { 1869 isItemDuplicate = true; 1870 LogTypeError("Case duplicate", cases.at(compareCase)->Start()); 1871 } 1872 } 1873 } 1874} 1875 1876bool ETSChecker::CompareIdentifiersValuesAreDifferent(ir::Expression *compareValue, const std::string &caseValue) 1877{ 1878 if (IsConstantMemberOrIdentifierExpression(compareValue)) { 1879 checker::Type *compareCaseType = compareValue->TsType(); 1880 1881 const auto compareCaseValue = GetStringFromIdentifierValue(compareCaseType); 1882 return caseValue != compareCaseValue; 1883 } 1884 1885 return caseValue != GetStringFromLiteral(compareValue); 1886} 1887 1888void ETSChecker::CheckIdentifierSwitchCase(ir::Expression *currentCase, ir::Expression *compareCase, 1889 const lexer::SourcePosition &pos) 1890{ 1891 currentCase->Check(this); 1892 1893 if (!IsConstantMemberOrIdentifierExpression(currentCase)) { 1894 return; 1895 } 1896 1897 checker::Type *caseType = currentCase->TsType(); 1898 1899 if (!caseType->HasTypeFlag(checker::TypeFlag::VALID_SWITCH_TYPE)) { 1900 return; 1901 } 1902 1903 if (!CompareIdentifiersValuesAreDifferent(compareCase, GetStringFromIdentifierValue(caseType))) { 1904 LogTypeError("Variable has same value with another switch case", pos); 1905 return; 1906 } 1907} 1908 1909std::string ETSChecker::GetStringFromLiteral(ir::Expression *caseTest) const 1910{ 1911 switch (caseTest->Type()) { 1912 case ir::AstNodeType::CHAR_LITERAL: { 1913 return std::to_string(caseTest->AsCharLiteral()->Char()); 1914 } 1915 case ir::AstNodeType::STRING_LITERAL: 1916 case ir::AstNodeType::NUMBER_LITERAL: { 1917 return util::Helpers::LiteralToPropName(caseTest).Mutf8(); 1918 } 1919 default: 1920 UNREACHABLE(); 1921 } 1922} 1923 1924bool ETSChecker::IsSameDeclarationType(varbinder::LocalVariable *target, varbinder::LocalVariable *compare) 1925{ 1926 return target->Declaration()->Type() == compare->Declaration()->Type(); 1927} 1928 1929bool ETSChecker::CheckRethrowingParams(const ir::AstNode *ancestorFunction, const ir::AstNode *node) 1930{ 1931 for (const auto param : ancestorFunction->AsScriptFunction()->Signature()->Function()->Params()) { 1932 if (node->AsCallExpression()->Callee()->AsIdentifier()->Name().Is( 1933 param->AsETSParameterExpression()->Ident()->Name().Mutf8())) { 1934 return true; 1935 } 1936 } 1937 return false; 1938} 1939 1940void ETSChecker::CheckThrowingStatements(ir::AstNode *node) 1941{ 1942 ir::AstNode *ancestorFunction = FindAncestorGivenByType(node, ir::AstNodeType::SCRIPT_FUNCTION); 1943 1944 if (ancestorFunction == nullptr) { 1945 LogTypeError( 1946 "This statement can cause an exception, therefore it must be enclosed in a try statement with a default " 1947 "catch clause", 1948 node->Start()); 1949 return; 1950 } 1951 1952 if (ancestorFunction->AsScriptFunction()->IsThrowing() || 1953 (ancestorFunction->AsScriptFunction()->IsRethrowing() && 1954 (!node->IsThrowStatement() && CheckRethrowingParams(ancestorFunction, node)))) { 1955 return; 1956 } 1957 1958 if (!CheckThrowingPlacement(node, ancestorFunction)) { 1959 if (ancestorFunction->AsScriptFunction()->IsRethrowing() && !node->IsThrowStatement()) { 1960 LogTypeError( 1961 "This statement can cause an exception, re-throwing functions can throw exception only by their " 1962 "parameters.", 1963 node->Start()); 1964 return; 1965 } 1966 1967 if (auto interfaces = 1968 ancestorFunction->AsScriptFunction()->Signature()->Owner()->AsETSObjectType()->Interfaces(); 1969 !(!interfaces.empty() && 1970 interfaces[0]->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::FUNCTIONAL_INTERFACE))) { 1971 LogTypeError( 1972 "This statement can cause an exception, therefore it must be enclosed in a try statement with a " 1973 "default " 1974 "catch clause", 1975 node->Start()); 1976 return; 1977 } 1978 } 1979} 1980 1981bool ETSChecker::CheckThrowingPlacement(ir::AstNode *node, const ir::AstNode *ancestorFunction) 1982{ 1983 ir::AstNode *startPoint = node; 1984 ir::AstNode *enclosingCatchClause = nullptr; 1985 ir::BlockStatement *enclosingFinallyBlock = nullptr; 1986 ir::AstNode *p = startPoint->Parent(); 1987 1988 bool isHandled = false; 1989 const auto predicateFunc = [&enclosingCatchClause](ir::CatchClause *clause) { 1990 return clause == enclosingCatchClause; 1991 }; 1992 1993 do { 1994 if (p->IsTryStatement() && p->AsTryStatement()->HasDefaultCatchClause()) { 1995 enclosingCatchClause = FindAncestorGivenByType(startPoint, ir::AstNodeType::CATCH_CLAUSE, p); 1996 enclosingFinallyBlock = FindFinalizerOfTryStatement(startPoint, p); 1997 const auto catches = p->AsTryStatement()->CatchClauses(); 1998 if (std::any_of(catches.begin(), catches.end(), predicateFunc)) { 1999 startPoint = enclosingCatchClause; 2000 } else if (enclosingFinallyBlock != nullptr && 2001 enclosingFinallyBlock == p->AsTryStatement()->FinallyBlock()) { 2002 startPoint = enclosingFinallyBlock; 2003 } else { 2004 isHandled = true; 2005 break; 2006 } 2007 } 2008 2009 p = p->Parent(); 2010 } while (p != ancestorFunction); 2011 2012 return isHandled; 2013} 2014 2015ir::BlockStatement *ETSChecker::FindFinalizerOfTryStatement(ir::AstNode *startFrom, const ir::AstNode *p) 2016{ 2017 auto *iter = startFrom->Parent(); 2018 2019 do { 2020 if (iter->IsBlockStatement()) { 2021 ir::BlockStatement *finallyBlock = iter->AsBlockStatement(); 2022 2023 if (finallyBlock == p->AsTryStatement()->FinallyBlock()) { 2024 return finallyBlock; 2025 } 2026 } 2027 2028 iter = iter->Parent(); 2029 } while (iter != p); 2030 2031 return nullptr; 2032} 2033 2034void ETSChecker::CheckRethrowingFunction(ir::ScriptFunction *func) 2035{ 2036 if (func->Signature()->Owner()->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::FUNCTIONAL_INTERFACE)) { 2037 return; 2038 } 2039 2040 bool foundThrowingParam = false; 2041 2042 // It doesn't support lambdas yet. 2043 for (auto item : func->Params()) { 2044 auto const *type = item->AsETSParameterExpression()->Ident()->TypeAnnotation(); 2045 2046 if (type->IsETSTypeReference()) { 2047 auto *typeDecl = type->AsETSTypeReference()->Part()->Name()->AsIdentifier()->Variable()->Declaration(); 2048 if (typeDecl->IsTypeAliasDecl()) { 2049 type = typeDecl->Node()->AsTSTypeAliasDeclaration()->TypeAnnotation(); 2050 } 2051 } 2052 2053 if (type->IsETSFunctionType() && type->AsETSFunctionType()->IsThrowing()) { 2054 foundThrowingParam = true; 2055 break; 2056 } 2057 } 2058 2059 if (!foundThrowingParam) { 2060 LogTypeError("A rethrowing function must have a throwing function parameter", func->Start()); 2061 } 2062} 2063 2064ETSObjectType *ETSChecker::GetRelevantArgumentedTypeFromChild(ETSObjectType *const child, ETSObjectType *const target) 2065{ 2066 if (child->GetDeclNode() == target->GetDeclNode()) { 2067 auto *relevantType = CreateNewETSObjectType(child->Name(), child->GetDeclNode(), child->ObjectFlags()); 2068 2069 ArenaVector<Type *> params = child->TypeArguments(); 2070 2071 relevantType->SetTypeArguments(std::move(params)); 2072 relevantType->SetEnclosingType(child->EnclosingType()); 2073 relevantType->SetSuperType(child->SuperType()); 2074 2075 return relevantType; 2076 } 2077 2078 ASSERT(child->SuperType() != nullptr); 2079 2080 return GetRelevantArgumentedTypeFromChild(child->SuperType(), target); 2081} 2082 2083void ETSChecker::EmplaceSubstituted(Substitution *substitution, ETSTypeParameter *tparam, Type *typeArg) 2084{ 2085 substitution->emplace(tparam, typeArg); 2086} 2087 2088util::StringView ETSChecker::GetHashFromTypeArguments(const ArenaVector<Type *> &typeArgTypes) 2089{ 2090 std::stringstream ss; 2091 2092 for (auto *it : typeArgTypes) { 2093 it->ToString(ss, true); 2094 ss << compiler::Signatures::MANGLE_SEPARATOR; 2095 2096 // In case of ETSTypeParameters storing the name might not be sufficient as there can 2097 // be multiple different type parameters with the same name. For those we test identity based 2098 // on their memory address equality, so we store them in the hash to keep it unique. 2099 // To make it consistent we store it for every type. 2100 // NOTE (mmartin): change bare address to something more appropriate unique representation 2101 ss << it << compiler::Signatures::MANGLE_SEPARATOR; 2102 } 2103 2104 return util::UString(ss.str(), Allocator()).View(); 2105} 2106 2107util::StringView ETSChecker::GetHashFromSubstitution(const Substitution *substitution) 2108{ 2109 std::vector<std::string> fields; 2110 for (auto [k, v] : *substitution) { 2111 std::stringstream ss; 2112 k->ToString(ss, true); 2113 ss << ":"; 2114 v->ToString(ss, true); 2115 // NOTE (mmartin): change bare address to something more appropriate unique representation 2116 ss << ":" << k << ":" << v; 2117 fields.push_back(ss.str()); 2118 } 2119 std::sort(fields.begin(), fields.end()); 2120 2121 std::stringstream ss; 2122 for (auto &fstr : fields) { 2123 ss << fstr; 2124 ss << ";"; 2125 } 2126 return util::UString(ss.str(), Allocator()).View(); 2127} 2128 2129util::StringView ETSChecker::GetHashFromFunctionType(ir::ETSFunctionType *type) 2130{ 2131 std::stringstream ss; 2132 for (auto *p : type->Params()) { 2133 auto *const param = p->AsETSParameterExpression(); 2134 param->TypeAnnotation()->GetType(this)->ToString(ss, true); 2135 ss << ";"; 2136 } 2137 2138 type->ReturnType()->GetType(this)->ToString(ss, true); 2139 ss << ";"; 2140 2141 if (type->IsThrowing()) { 2142 ss << "throws;"; 2143 } 2144 2145 if (type->IsRethrowing()) { 2146 ss << "rethrows;"; 2147 } 2148 2149 return util::UString(ss.str(), Allocator()).View(); 2150} 2151 2152ETSObjectType *ETSChecker::GetOriginalBaseType(Type *const object) 2153{ 2154 if (object == nullptr || !object->IsETSObjectType()) { 2155 return nullptr; 2156 } 2157 2158 return object->AsETSObjectType()->GetOriginalBaseType(); 2159} 2160 2161void ETSChecker::CheckValidGenericTypeParameter(Type *const argType, const lexer::SourcePosition &pos) 2162{ 2163 if (!argType->IsETSEnumType()) { 2164 return; 2165 } 2166 std::stringstream ss; 2167 argType->ToString(ss); 2168 LogTypeError("Type '" + ss.str() + "' is not valid for generic type arguments", pos); 2169} 2170 2171bool ETSChecker::CheckNumberOfTypeArguments(ETSObjectType *const type, ir::TSTypeParameterInstantiation *const typeArgs, 2172 const lexer::SourcePosition &pos) 2173{ 2174 auto const &typeParams = type->TypeArguments(); 2175 if (typeParams.empty()) { 2176 if (typeArgs != nullptr) { 2177 LogTypeError({"Type '", type, "' is not generic."}, pos); 2178 return false; 2179 } 2180 return true; 2181 } 2182 2183 size_t minimumTypeArgs = std::count_if(typeParams.begin(), typeParams.end(), [](Type *param) { 2184 return param->AsETSTypeParameter()->GetDefaultType() == nullptr; 2185 }); 2186 if (typeArgs == nullptr && minimumTypeArgs > 0) { 2187 LogTypeError({"Type '", type, "' is generic but type argument were not provided."}, pos); 2188 return false; 2189 } 2190 2191 if (typeArgs != nullptr && 2192 ((minimumTypeArgs > typeArgs->Params().size()) || (typeParams.size() < typeArgs->Params().size()))) { 2193 LogTypeError({"Type '", type, "' has ", minimumTypeArgs, " number of type parameters, but ", 2194 typeArgs->Params().size(), " type arguments were provided."}, 2195 pos); 2196 return false; 2197 } 2198 return true; 2199} 2200 2201bool ETSChecker::NeedTypeInference(const ir::ScriptFunction *lambda) 2202{ 2203 if (lambda->ReturnTypeAnnotation() == nullptr) { 2204 return true; 2205 } 2206 for (auto *const param : lambda->Params()) { 2207 const auto *const lambdaParam = param->AsETSParameterExpression()->Ident(); 2208 if (lambdaParam->TypeAnnotation() == nullptr) { 2209 return true; 2210 } 2211 } 2212 return false; 2213} 2214 2215std::vector<bool> ETSChecker::FindTypeInferenceArguments(const ArenaVector<ir::Expression *> &arguments) 2216{ 2217 std::vector<bool> argTypeInferenceRequired(arguments.size()); 2218 size_t index = 0; 2219 for (ir::Expression *arg : arguments) { 2220 if (arg->IsArrowFunctionExpression()) { 2221 ir::ScriptFunction *const lambda = arg->AsArrowFunctionExpression()->Function(); 2222 if (NeedTypeInference(lambda)) { 2223 argTypeInferenceRequired[index] = true; 2224 } 2225 } 2226 ++index; 2227 } 2228 return argTypeInferenceRequired; 2229} 2230 2231bool ETSChecker::CheckLambdaAssignableUnion(ir::AstNode *typeAnn, ir::ScriptFunction *lambda) 2232{ 2233 for (auto *type : typeAnn->AsETSUnionType()->Types()) { 2234 if (type->IsETSFunctionType()) { 2235 return lambda->Params().size() == type->AsETSFunctionType()->Params().size(); 2236 } 2237 } 2238 2239 return false; 2240} 2241 2242void ETSChecker::InferTypesForLambda(ir::ScriptFunction *lambda, ir::ETSFunctionType *calleeType, 2243 Signature *maybeSubstitutedFunctionSig) 2244{ 2245 for (size_t i = 0; i < calleeType->Params().size(); ++i) { 2246 const auto *const calleeParam = calleeType->Params()[i]->AsETSParameterExpression()->Ident(); 2247 auto *const lambdaParam = lambda->Params()[i]->AsETSParameterExpression()->Ident(); 2248 if (lambdaParam->TypeAnnotation() == nullptr) { 2249 auto *const typeAnnotation = calleeParam->TypeAnnotation()->Clone(Allocator(), lambdaParam); 2250 if (maybeSubstitutedFunctionSig != nullptr) { 2251 ASSERT(maybeSubstitutedFunctionSig->Params().size() == calleeType->Params().size()); 2252 typeAnnotation->SetTsType(maybeSubstitutedFunctionSig->Params()[i]->TsType()); 2253 } 2254 lambdaParam->SetTsTypeAnnotation(typeAnnotation); 2255 typeAnnotation->SetParent(lambdaParam); 2256 } 2257 } 2258 if (lambda->ReturnTypeAnnotation() == nullptr) { 2259 auto *const returnTypeAnnotation = calleeType->ReturnType()->Clone(Allocator(), lambda); 2260 if (maybeSubstitutedFunctionSig != nullptr) { 2261 returnTypeAnnotation->SetTsType(maybeSubstitutedFunctionSig->ReturnType()); 2262 } 2263 lambda->SetReturnTypeAnnotation(returnTypeAnnotation); 2264 } 2265} 2266 2267void ETSChecker::ModifyPreferredType(ir::ArrayExpression *const arrayExpr, Type *const newPreferredType) 2268{ 2269 // After modifying the preferred type of an array expression, it needs to be rechecked at the call site 2270 arrayExpr->SetPreferredType(newPreferredType); 2271 arrayExpr->SetTsType(nullptr); 2272 2273 for (auto *const element : arrayExpr->Elements()) { 2274 if (element->IsArrayExpression()) { 2275 ModifyPreferredType(element->AsArrayExpression(), nullptr); 2276 } 2277 } 2278} 2279 2280bool ETSChecker::IsInLocalClass(const ir::AstNode *node) const 2281{ 2282 while (node != nullptr) { 2283 if (node->Type() == ir::AstNodeType::CLASS_DEFINITION) { 2284 return node->AsClassDefinition()->IsLocal(); 2285 } 2286 node = node->Parent(); 2287 } 2288 2289 return false; 2290} 2291 2292ir::Expression *ETSChecker::GenerateImplicitInstantiateArg(varbinder::LocalVariable *instantiateMethod, 2293 const std::string &className) 2294{ 2295 auto callSignatures = instantiateMethod->TsType()->AsETSFunctionType()->CallSignatures(); 2296 ASSERT(!callSignatures.empty()); 2297 auto methodOwner = std::string(callSignatures[0]->Owner()->Name()); 2298 std::string implicitInstantiateArgument = "()=>{return new " + className + "()"; 2299 if (methodOwner != className) { 2300 implicitInstantiateArgument.append(" as " + methodOwner); 2301 } 2302 implicitInstantiateArgument.append("}"); 2303 2304 parser::Program program(Allocator(), VarBinder()); 2305 es2panda::CompilerOptions options; 2306 auto parser = parser::ETSParser(&program, options, parser::ParserStatus::NO_OPTS); 2307 auto *argExpr = parser.CreateExpression(implicitInstantiateArgument); 2308 compiler::InitScopesPhaseETS::RunExternalNode(argExpr, &program); 2309 2310 return argExpr; 2311} 2312 2313ir::ClassProperty *ETSChecker::ClassPropToImplementationProp(ir::ClassProperty *classProp, varbinder::ClassScope *scope) 2314{ 2315 classProp->Key()->AsIdentifier()->SetName( 2316 util::UString(std::string(compiler::Signatures::PROPERTY) + classProp->Key()->AsIdentifier()->Name().Mutf8(), 2317 Allocator()) 2318 .View()); 2319 classProp->AddModifier(ir::ModifierFlags::PRIVATE); 2320 2321 auto *fieldDecl = Allocator()->New<varbinder::LetDecl>(classProp->Key()->AsIdentifier()->Name()); 2322 fieldDecl->BindNode(classProp); 2323 2324 auto fieldVar = scope->InstanceFieldScope()->AddDecl(Allocator(), fieldDecl, ScriptExtension::ETS); 2325 fieldVar->AddFlag(varbinder::VariableFlags::PROPERTY); 2326 2327 classProp->Key()->SetVariable(fieldVar); 2328 classProp->Key()->AsIdentifier()->SetVariable(fieldVar); 2329 fieldVar->SetTsType(classProp->TsType()); 2330 2331 return classProp; 2332} 2333 2334void ETSChecker::GenerateGetterSetterBody(ArenaVector<ir::Statement *> &stmts, ArenaVector<ir::Expression *> ¶ms, 2335 ir::ClassProperty *const field, varbinder::FunctionParamScope *paramScope, 2336 bool isSetter) 2337{ 2338 auto *classDef = field->Parent()->AsClassDefinition(); 2339 2340 ir::Expression *baseExpression; 2341 if ((field->Modifiers() & ir::ModifierFlags::SUPER_OWNER) != 0U) { 2342 baseExpression = Allocator()->New<ir::SuperExpression>(); 2343 } else { 2344 baseExpression = Allocator()->New<ir::ThisExpression>(); 2345 } 2346 baseExpression->SetParent(classDef); 2347 baseExpression->SetTsType(classDef->TsType()); 2348 2349 auto *memberExpression = 2350 AllocNode<ir::MemberExpression>(baseExpression, field->Key()->AsIdentifier()->Clone(Allocator(), nullptr), 2351 ir::MemberExpressionKind::PROPERTY_ACCESS, false, false); 2352 memberExpression->SetTsType(field->TsType()); 2353 memberExpression->SetPropVar(field->Key()->Variable()->AsLocalVariable()); 2354 memberExpression->SetRange(classDef->Range()); 2355 if (memberExpression->ObjType() == nullptr && classDef->TsType() != nullptr) { 2356 memberExpression->SetObjectType(classDef->TsType()->AsETSObjectType()); 2357 } 2358 2359 if (!isSetter) { 2360 stmts.push_back(AllocNode<ir::ReturnStatement>(memberExpression)); 2361 return; 2362 } 2363 2364 auto *paramIdent = field->Key()->AsIdentifier()->Clone(Allocator(), nullptr); 2365 if (field->TypeAnnotation() != nullptr) { 2366 auto *const typeAnnotation = field->TypeAnnotation()->Clone(Allocator(), paramIdent); 2367 paramIdent->SetTsTypeAnnotation(typeAnnotation); 2368 } else { 2369 paramIdent->SetTsType(field->TsType()); 2370 } 2371 2372 auto *paramExpression = AllocNode<ir::ETSParameterExpression>(paramIdent, nullptr); 2373 paramExpression->SetRange(paramIdent->Range()); 2374 auto *const paramVar = std::get<2>(paramScope->AddParamDecl(Allocator(), paramExpression)); 2375 paramExpression->SetVariable(paramVar); 2376 2377 params.push_back(paramExpression); 2378 2379 auto *assignmentExpression = AllocNode<ir::AssignmentExpression>( 2380 memberExpression, paramExpression->Clone(Allocator(), nullptr), lexer::TokenType::PUNCTUATOR_SUBSTITUTION); 2381 assignmentExpression->SetTsType(paramVar->TsType()); 2382 2383 assignmentExpression->SetRange({field->Start(), field->End()}); 2384 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) 2385 stmts.push_back(AllocNode<ir::ExpressionStatement>(assignmentExpression)); 2386 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) 2387 stmts.push_back(Allocator()->New<ir::ReturnStatement>(nullptr)); 2388} 2389 2390ir::MethodDefinition *ETSChecker::GenerateDefaultGetterSetter(ir::ClassProperty *const property, 2391 ir::ClassProperty *const field, 2392 varbinder::ClassScope *classScope, bool isSetter, 2393 ETSChecker *checker) 2394{ 2395 auto *paramScope = checker->Allocator()->New<varbinder::FunctionParamScope>(checker->Allocator(), classScope); 2396 auto *functionScope = checker->Allocator()->New<varbinder::FunctionScope>(checker->Allocator(), paramScope); 2397 2398 functionScope->BindParamScope(paramScope); 2399 paramScope->BindFunctionScope(functionScope); 2400 2401 ArenaVector<ir::Expression *> params(checker->Allocator()->Adapter()); 2402 ArenaVector<ir::Statement *> stmts(checker->Allocator()->Adapter()); 2403 checker->GenerateGetterSetterBody(stmts, params, field, paramScope, isSetter); 2404 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) 2405 auto *body = checker->AllocNode<ir::BlockStatement>(checker->Allocator(), std::move(stmts)); 2406 auto funcFlags = isSetter ? ir::ScriptFunctionFlags::SETTER : ir::ScriptFunctionFlags::GETTER; 2407 auto *const returnTypeAnn = isSetter || field->TypeAnnotation() == nullptr 2408 ? nullptr 2409 : field->TypeAnnotation()->Clone(checker->Allocator(), nullptr); 2410 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) 2411 auto *func = checker->AllocNode<ir::ScriptFunction>( 2412 checker->Allocator(), 2413 ir::ScriptFunction::ScriptFunctionData {body, ir::FunctionSignature(nullptr, std::move(params), returnTypeAnn), 2414 funcFlags, ir::ModifierFlags::PUBLIC, true}); 2415 2416 if (!isSetter) { 2417 func->AddFlag(ir::ScriptFunctionFlags::HAS_RETURN); 2418 } 2419 func->SetRange(field->Range()); 2420 func->SetScope(functionScope); 2421 body->SetScope(functionScope); 2422 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) 2423 auto *methodIdent = property->Key()->AsIdentifier()->Clone(checker->Allocator(), nullptr); 2424 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) 2425 auto *funcExpr = checker->AllocNode<ir::FunctionExpression>(func); 2426 funcExpr->SetRange(func->Range()); 2427 func->AddFlag(ir::ScriptFunctionFlags::METHOD); 2428 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) 2429 auto *method = checker->AllocNode<ir::MethodDefinition>(ir::MethodDefinitionKind::METHOD, methodIdent, funcExpr, 2430 ir::ModifierFlags::PUBLIC, checker->Allocator(), false); 2431 2432 auto *decl = checker->Allocator()->New<varbinder::FunctionDecl>(checker->Allocator(), 2433 property->Key()->AsIdentifier()->Name(), method); 2434 auto *var = checker->Allocator()->New<varbinder::LocalVariable>(decl, varbinder::VariableFlags::VAR); 2435 var->AddFlag(varbinder::VariableFlags::METHOD); 2436 2437 methodIdent->SetVariable(var); 2438 2439 method->Id()->SetMutator(); 2440 method->SetRange(field->Range()); 2441 method->Function()->SetIdent(method->Id()->Clone(checker->Allocator(), nullptr)); 2442 method->Function()->AddModifier(method->Modifiers()); 2443 method->SetVariable(var); 2444 method->SetParent(field->Parent()); 2445 2446 paramScope->BindNode(func); 2447 functionScope->BindNode(func); 2448 2449 auto classCtx = varbinder::LexicalScope<varbinder::ClassScope>::Enter(checker->VarBinder(), classScope); 2450 checker->VarBinder()->AsETSBinder()->ResolveMethodDefinition(method); 2451 2452 functionScope->BindName(classScope->Node()->AsClassDefinition()->InternalName()); 2453 method->Check(checker); 2454 2455 return method; 2456} 2457 2458ir::ClassProperty *GetImplementationClassProp(ETSChecker *checker, ir::ClassProperty *interfaceProp, 2459 ir::ClassProperty *originalProp, ETSObjectType *classType) 2460{ 2461 bool isSuperOwner = ((originalProp->Modifiers() & ir::ModifierFlags::SUPER_OWNER) != 0U); 2462 if (!isSuperOwner) { 2463 auto *const classDef = classType->GetDeclNode()->AsClassDefinition(); 2464 auto *const scope = checker->Scope()->AsClassScope(); 2465 auto *const classProp = checker->ClassPropToImplementationProp( 2466 interfaceProp->Clone(checker->Allocator(), originalProp->Parent()), scope); 2467 classType->AddProperty<PropertyType::INSTANCE_FIELD>(classProp->Key()->Variable()->AsLocalVariable()); 2468 classDef->Body().push_back(classProp); 2469 return classProp; 2470 } 2471 2472 auto *const classProp = classType 2473 ->GetProperty(interfaceProp->Key()->AsIdentifier()->Name(), 2474 PropertySearchFlags::SEARCH_ALL | PropertySearchFlags::SEARCH_IN_BASE) 2475 ->Declaration() 2476 ->Node() 2477 ->AsClassProperty(); 2478 classProp->AddModifier(ir::ModifierFlags::SUPER_OWNER); 2479 return classProp; 2480} 2481 2482static void SetupGetterSetterFlags(ir::ClassProperty *originalProp, ETSObjectType *classType, 2483 ir::MethodDefinition *getter, ir::MethodDefinition *setter, const bool inExternal) 2484{ 2485 auto *const classDef = classType->GetDeclNode()->AsClassDefinition(); 2486 for (auto &method : {getter, setter}) { 2487 if (method == nullptr) { 2488 continue; 2489 } 2490 2491 const auto mflag = method == getter ? ir::ModifierFlags::GETTER : ir::ModifierFlags::SETTER; 2492 const auto tflag = method == getter ? TypeFlag::GETTER : TypeFlag::SETTER; 2493 2494 method->TsType()->AddTypeFlag(tflag); 2495 method->Variable()->SetTsType(method->TsType()); 2496 if (((originalProp->Modifiers() & mflag) != 0U)) { 2497 method->Function()->AddModifier(ir::ModifierFlags::OVERRIDE); 2498 } 2499 2500 if (inExternal) { 2501 method->Function()->AddFlag(ir::ScriptFunctionFlags::EXTERNAL); 2502 } 2503 method->SetParent(classDef); 2504 classType->AddProperty<checker::PropertyType::INSTANCE_METHOD>(method->Variable()->AsLocalVariable()); 2505 } 2506} 2507 2508void ETSChecker::GenerateGetterSetterPropertyAndMethod(ir::ClassProperty *originalProp, ETSObjectType *classType) 2509{ 2510 auto *const classDef = classType->GetDeclNode()->AsClassDefinition(); 2511 auto *interfaceProp = originalProp->Clone(Allocator(), originalProp->Parent()); 2512 interfaceProp->ClearModifier(ir::ModifierFlags::GETTER_SETTER); 2513 2514 auto *const scope = Scope()->AsClassScope(); 2515 scope->InstanceFieldScope()->EraseBinding(interfaceProp->Key()->AsIdentifier()->Name()); 2516 interfaceProp->SetRange(originalProp->Range()); 2517 2518 auto *const classProp = GetImplementationClassProp(this, interfaceProp, originalProp, classType); 2519 2520 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) 2521 ir::MethodDefinition *getter = GenerateDefaultGetterSetter(interfaceProp, classProp, scope, false, this); 2522 classDef->Body().push_back(getter); 2523 2524 const auto &name = getter->Key()->AsIdentifier()->Name(); 2525 2526 ir::MethodDefinition *setter = 2527 !classProp->IsConst() 2528 ? GenerateDefaultGetterSetter(interfaceProp, classProp, Scope()->AsClassScope(), true, this) 2529 : nullptr; 2530 2531 auto *const methodScope = scope->InstanceMethodScope(); 2532 auto *const decl = Allocator()->New<varbinder::FunctionDecl>(Allocator(), name, getter); 2533 auto *var = methodScope->AddDecl(Allocator(), decl, ScriptExtension::ETS); 2534 2535 if (var == nullptr) { 2536 auto *const prevDecl = methodScope->FindDecl(name); 2537 for (const auto &method : {getter, setter}) { 2538 if (method != nullptr) { 2539 prevDecl->Node()->AsMethodDefinition()->AddOverload(method); 2540 } 2541 } 2542 var = methodScope->FindLocal(name, varbinder::ResolveBindingOptions::BINDINGS); 2543 } 2544 var->AddFlag(varbinder::VariableFlags::METHOD); 2545 2546 SetupGetterSetterFlags(originalProp, classType, getter, setter, HasStatus(CheckerStatus::IN_EXTERNAL)); 2547 2548 if (setter != nullptr) { 2549 getter->Variable()->TsType()->AsETSFunctionType()->AddCallSignature( 2550 setter->TsType()->AsETSFunctionType()->CallSignatures()[0]); 2551 } 2552 2553 getter->Function()->Id()->SetVariable(var); 2554 if (setter != nullptr) { 2555 getter->AddOverload(setter); 2556 } 2557} 2558 2559Type *ETSChecker::TryGettingFunctionTypeFromInvokeFunction(Type *type) 2560{ 2561 if (type->IsETSObjectType() && type->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::FUNCTIONAL)) { 2562 auto const propInvoke = type->AsETSObjectType()->GetProperty(FUNCTIONAL_INTERFACE_INVOKE_METHOD_NAME, 2563 PropertySearchFlags::SEARCH_INSTANCE_METHOD); 2564 ASSERT(propInvoke != nullptr); 2565 2566 return propInvoke->TsType(); 2567 } 2568 2569 return type; 2570} 2571 2572bool ETSChecker::TryTransformingToStaticInvoke(ir::Identifier *const ident, const Type *resolvedType) 2573{ 2574 ASSERT(ident->Parent()->IsCallExpression()); 2575 ASSERT(ident->Parent()->AsCallExpression()->Callee() == ident); 2576 2577 if (!resolvedType->IsETSObjectType()) { 2578 return false; 2579 } 2580 2581 auto className = ident->Name(); 2582 std::string_view propertyName; 2583 2584 PropertySearchFlags searchFlag = PropertySearchFlags::SEARCH_IN_INTERFACES | PropertySearchFlags::SEARCH_IN_BASE | 2585 PropertySearchFlags::SEARCH_STATIC_METHOD; 2586 auto *instantiateMethod = 2587 resolvedType->AsETSObjectType()->GetProperty(compiler::Signatures::STATIC_INSTANTIATE_METHOD, searchFlag); 2588 auto *invokeMethod = 2589 resolvedType->AsETSObjectType()->GetProperty(compiler::Signatures::STATIC_INVOKE_METHOD, searchFlag); 2590 if (instantiateMethod != nullptr) { 2591 propertyName = compiler::Signatures::STATIC_INSTANTIATE_METHOD; 2592 } else if (invokeMethod != nullptr) { 2593 propertyName = compiler::Signatures::STATIC_INVOKE_METHOD; 2594 } else { 2595 LogTypeError({"No static ", compiler::Signatures::STATIC_INVOKE_METHOD, " method and static ", 2596 compiler::Signatures::STATIC_INSTANTIATE_METHOD, " method in ", className, ". ", className, 2597 "() is not allowed."}, 2598 ident->Start()); 2599 return true; 2600 } 2601 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) 2602 auto *classId = AllocNode<ir::Identifier>(className, Allocator()); 2603 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) 2604 auto *methodId = AllocNode<ir::Identifier>(propertyName, Allocator()); 2605 if (propertyName == compiler::Signatures::STATIC_INSTANTIATE_METHOD) { 2606 methodId->SetVariable(instantiateMethod); 2607 } else if (propertyName == compiler::Signatures::STATIC_INVOKE_METHOD) { 2608 methodId->SetVariable(invokeMethod); 2609 } 2610 2611 auto *transformedCallee = 2612 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) 2613 AllocNode<ir::MemberExpression>(classId, methodId, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false); 2614 2615 classId->SetRange(ident->Range()); 2616 methodId->SetRange(ident->Range()); 2617 transformedCallee->SetRange(ident->Range()); 2618 2619 auto *callExpr = ident->Parent()->AsCallExpression(); 2620 transformedCallee->SetParent(callExpr); 2621 callExpr->SetCallee(transformedCallee); 2622 2623 if (instantiateMethod != nullptr) { 2624 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint) 2625 auto *argExpr = GenerateImplicitInstantiateArg(instantiateMethod, std::string(className)); 2626 2627 argExpr->SetParent(callExpr); 2628 argExpr->SetRange(ident->Range()); 2629 2630 VarBinder()->AsETSBinder()->HandleCustomNodes(argExpr); 2631 2632 auto &arguments = callExpr->Arguments(); 2633 arguments.insert(arguments.begin(), argExpr); 2634 } 2635 2636 return true; 2637} 2638 2639checker::ETSObjectType *ETSChecker::CreateSyntheticType(util::StringView const &syntheticName, 2640 checker::ETSObjectType *lastObjectType, ir::Identifier *id) 2641{ 2642 auto *syntheticObjType = Allocator()->New<checker::ETSObjectType>(Allocator(), syntheticName, syntheticName, id, 2643 checker::ETSObjectFlags::NO_OPTS); 2644 2645 auto *classDecl = Allocator()->New<varbinder::ClassDecl>(syntheticName); 2646 varbinder::LocalVariable *var = 2647 Allocator()->New<varbinder::LocalVariable>(classDecl, varbinder::VariableFlags::CLASS); 2648 var->SetTsType(syntheticObjType); 2649 lastObjectType->AddProperty<checker::PropertyType::STATIC_FIELD>(var); 2650 syntheticObjType->SetEnclosingType(lastObjectType); 2651 return syntheticObjType; 2652} 2653 2654void ETSChecker::ImportNamespaceObjectTypeAddReExportType(ir::ETSImportDeclaration *importDecl, 2655 checker::ETSObjectType *lastObjectType, ir::Identifier *ident) 2656{ 2657 for (auto item : VarBinder()->AsETSBinder()->ReExportImports()) { 2658 if (!importDecl->ResolvedSource()->Str().Is(item->GetProgramPath().Mutf8())) { 2659 continue; 2660 } 2661 auto *reExportType = GetImportSpecifierObjectType(item->GetETSImportDeclarations(), ident); 2662 lastObjectType->AddReExports(reExportType); 2663 for (auto node : importDecl->Specifiers()) { 2664 if (node->IsImportSpecifier()) { 2665 auto specifier = node->AsImportSpecifier(); 2666 lastObjectType->AddReExportAlias(specifier->Imported()->Name(), specifier->Local()->Name()); 2667 } 2668 } 2669 } 2670} 2671 2672ETSObjectType *ETSChecker::GetImportSpecifierObjectType(ir::ETSImportDeclaration *importDecl, ir::Identifier *ident) 2673{ 2674 auto importPath = importDecl->ResolvedSource()->Str(); 2675 parser::Program *program = 2676 SelectEntryOrExternalProgram(static_cast<varbinder::ETSBinder *>(VarBinder()), importPath); 2677 std::vector<util::StringView> syntheticNames = GetNameForSynteticObjectType(program->ModuleName()); 2678 ASSERT(!syntheticNames.empty()); 2679 auto assemblerName = syntheticNames[0]; 2680 2681 if (!program->OmitModuleName()) { 2682 assemblerName = util::UString(assemblerName.Mutf8() 2683 .append(compiler::Signatures::METHOD_SEPARATOR) 2684 .append(compiler::Signatures::ETS_GLOBAL), 2685 Allocator()) 2686 .View(); 2687 } 2688 2689 auto *moduleObjectType = 2690 Allocator()->New<checker::ETSObjectType>(Allocator(), syntheticNames[0], assemblerName, 2691 std::make_tuple(ident, checker::ETSObjectFlags::CLASS, Relation())); 2692 2693 auto *rootDecl = Allocator()->New<varbinder::ClassDecl>(syntheticNames[0]); 2694 varbinder::LocalVariable *rootVar = 2695 Allocator()->New<varbinder::LocalVariable>(rootDecl, varbinder::VariableFlags::NONE); 2696 rootVar->SetTsType(moduleObjectType); 2697 2698 syntheticNames.erase(syntheticNames.begin()); 2699 checker::ETSObjectType *lastObjectType(moduleObjectType); 2700 2701 for (const auto &syntheticName : syntheticNames) { 2702 lastObjectType = CreateSyntheticType(syntheticName, lastObjectType, ident); 2703 } 2704 2705 ImportNamespaceObjectTypeAddReExportType(importDecl, lastObjectType, ident); 2706 SetPropertiesForModuleObject(lastObjectType, importPath, 2707 importDecl->Specifiers()[0]->IsImportNamespaceSpecifier() ? nullptr : importDecl); 2708 SetrModuleObjectTsType(ident, lastObjectType); 2709 2710 return moduleObjectType; 2711} 2712} // namespace ark::es2panda::checker 2713