1 /**
2 * Copyright (c) 2021-2025 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "checker/ETSchecker.h"
17
18 #include "checker/types/globalTypesHolder.h"
19 #include "checker/types/ets/etsTupleType.h"
20 #include "checker/ets/typeRelationContext.h"
21 #include "checker/ets/typeConverter.h"
22 #include "evaluate/scopedDebugInfoPlugin.h"
23 #include "compiler/lowering/scopesInit/scopesInitPhase.h"
24 #include "compiler/lowering/util.h"
25 #include "util/helpers.h"
26
27 namespace ark::es2panda::checker {
28
FindVariableInFunctionScope(const util::StringView name)29 varbinder::Variable *ETSChecker::FindVariableInFunctionScope(const util::StringView name)
30 {
31 return Scope() != nullptr ? Scope()->FindInFunctionScope(name, varbinder::ResolveBindingOptions::ALL).variable
32 : nullptr;
33 }
34
FindVariableInClassOrEnclosing(const util::StringView name,const ETSObjectType * classType)35 std::pair<varbinder::Variable *, const ETSObjectType *> ETSChecker::FindVariableInClassOrEnclosing(
36 const util::StringView name, const ETSObjectType *classType)
37 {
38 // For Annotation, it doesnot have containing class, so classType will be nullptr.
39 if (classType == nullptr) {
40 return {nullptr, nullptr};
41 }
42 const auto searchFlags = PropertySearchFlags::SEARCH_ALL | PropertySearchFlags::SEARCH_IN_BASE |
43 PropertySearchFlags::SEARCH_IN_INTERFACES |
44 PropertySearchFlags::DISALLOW_SYNTHETIC_METHOD_CREATION;
45 auto *resolved = classType->GetProperty(name, searchFlags);
46 while (classType->EnclosingType() != nullptr && resolved == nullptr) {
47 classType = classType->EnclosingType();
48 resolved = classType->GetProperty(name, searchFlags);
49 }
50
51 return {resolved, classType};
52 }
53
FindVariableInGlobal(const ir::Identifier * const identifier)54 varbinder::Variable *ETSChecker::FindVariableInGlobal(const ir::Identifier *const identifier)
55 {
56 return Scope() != nullptr
57 ? Scope()->FindInGlobal(identifier->Name(), varbinder::ResolveBindingOptions::ALL).variable
58 : nullptr;
59 }
60
IsVariableStatic(const varbinder::Variable * var)61 bool ETSChecker::IsVariableStatic(const varbinder::Variable *var)
62 {
63 CHECK_NOT_NULL(var);
64 if (var->HasFlag(varbinder::VariableFlags::METHOD)) {
65 return var->TsType()->IsETSFunctionType() &&
66 var->TsType()->AsETSFunctionType()->CallSignatures()[0]->HasSignatureFlag(SignatureFlags::STATIC);
67 }
68 return var->HasFlag(varbinder::VariableFlags::STATIC);
69 }
70
IsVariableGetterSetter(const varbinder::Variable * var)71 bool ETSChecker::IsVariableGetterSetter(const varbinder::Variable *var)
72 {
73 return var != nullptr && var->TsType() != nullptr && var->TsType()->HasTypeFlag(TypeFlag::GETTER_SETTER);
74 }
75
IsVariableExtensionAccessor(const varbinder::Variable * var)76 bool ETSChecker::IsVariableExtensionAccessor(const varbinder::Variable *var)
77 {
78 return var != nullptr && var->TsType() != nullptr && var->TsType()->IsETSFunctionType() &&
79 var->TsType()->AsETSFunctionType()->IsExtensionAccessorType();
80 }
81
LogUnresolvedReferenceError(ir::Identifier * const ident)82 void ETSChecker::LogUnresolvedReferenceError(ir::Identifier *const ident)
83 {
84 if (!ident->IsErrorPlaceHolder()) {
85 LogError(diagnostic::UNRESOLVED_REF, {ident->Name()}, ident->Start());
86 }
87 }
88
WrongContextErrorClassifyByType(ir::Identifier * ident)89 void ETSChecker::WrongContextErrorClassifyByType(ir::Identifier *ident)
90 {
91 if (ident->IsErrorPlaceHolder()) {
92 return;
93 }
94
95 std::string identCategoryName {};
96 switch (static_cast<varbinder::VariableFlags>(
97 ident->Variable()->Flags() &
98 (varbinder::VariableFlags::CLASS_OR_INTERFACE_OR_ENUM | varbinder::VariableFlags::METHOD |
99 varbinder::VariableFlags::NAMESPACE | varbinder::VariableFlags::ANNOTATIONDECL |
100 varbinder::VariableFlags::ANNOTATIONUSAGE | varbinder::VariableFlags::TYPE_ALIAS |
101 varbinder::VariableFlags::TYPE))) {
102 case varbinder::VariableFlags::CLASS:
103 identCategoryName = "Class";
104 break;
105
106 case varbinder::VariableFlags::NAMESPACE:
107 identCategoryName = "Namespace";
108 break;
109
110 case varbinder::VariableFlags::METHOD:
111 identCategoryName = "Function";
112 break;
113
114 case varbinder::VariableFlags::INTERFACE:
115 identCategoryName = "Interface";
116 break;
117
118 case varbinder::VariableFlags::ENUM_LITERAL:
119 identCategoryName = "Enum";
120 break;
121
122 case varbinder::VariableFlags::ANNOTATIONDECL:
123 [[fallthrough]];
124 case varbinder::VariableFlags::ANNOTATIONUSAGE:
125 identCategoryName = "Annotation";
126 break;
127
128 case varbinder::VariableFlags::TYPE:
129 [[fallthrough]];
130 case varbinder::VariableFlags::TYPE_ALIAS:
131 identCategoryName = "Type";
132 break;
133
134 default:
135 LogError(diagnostic::ID_WRONG_CTX, {ident->Name()}, ident->Start());
136 return;
137 }
138 LogError(diagnostic::ID_IN_WRONG_CTX, {identCategoryName.c_str(), ident->Name()}, ident->Start());
139 }
140
NotResolvedError(ir::Identifier * const ident,const varbinder::Variable * classVar,const ETSObjectType * classType)141 void ETSChecker::NotResolvedError(ir::Identifier *const ident, const varbinder::Variable *classVar,
142 const ETSObjectType *classType)
143 {
144 if (classVar == nullptr) {
145 LogUnresolvedReferenceError(ident);
146 return;
147 }
148
149 if (IsVariableStatic(classVar)) {
150 LogError(diagnostic::STATIC_PROP_INVALID_CTX, {ident->Name(), classType}, ident->Start());
151 } else {
152 LogError(diagnostic::PROP_ACCESS_WITHOUT_THIS, {ident->Name()}, ident->Start());
153 }
154 }
155
GetTargetIdentifierAndType(ir::Identifier * const ident)156 std::pair<const ir::Identifier *, ir::TypeNode *> ETSChecker::GetTargetIdentifierAndType(ir::Identifier *const ident)
157 {
158 if (ident->Parent()->IsClassProperty()) {
159 const auto *const classProp = ident->Parent()->AsClassProperty();
160 ES2PANDA_ASSERT(classProp->Value() && classProp->Value() == ident);
161 return std::make_pair(classProp->Key()->AsIdentifier(), classProp->TypeAnnotation());
162 }
163 const auto *const variableDecl = ident->Parent()->AsVariableDeclarator();
164 ES2PANDA_ASSERT(variableDecl->Init() && variableDecl->Init() == ident);
165 return std::make_pair(variableDecl->Id()->AsIdentifier(), variableDecl->Id()->AsIdentifier()->TypeAnnotation());
166 }
167
ExtraCheckForResolvedError(ir::Identifier * const ident)168 varbinder::Variable *ETSChecker::ExtraCheckForResolvedError(ir::Identifier *const ident)
169 {
170 const auto [class_var, class_type] = FindVariableInClassOrEnclosing(ident->Name(), Context().ContainingClass());
171 auto *parentClass = FindAncestorGivenByType(ident, ir::AstNodeType::CLASS_DEFINITION);
172 if (parentClass != nullptr && parentClass->AsClassDefinition()->IsLocal()) {
173 if (parentClass != class_type->GetDeclNode()) {
174 LogError(diagnostic::PROPERTY_CAPTURE,
175 {ident->Name(), class_type->Name(), parentClass->AsClassDefinition()->Ident()->Name()},
176 ident->Start());
177 }
178 }
179 NotResolvedError(ident, class_var, class_type);
180 return class_var;
181 }
182
SaveCapturedVariableInLocalClass(varbinder::Variable * const var,ir::Identifier * ident)183 bool ETSChecker::SaveCapturedVariableInLocalClass(varbinder::Variable *const var, ir::Identifier *ident)
184 {
185 const auto &pos = ident->Start();
186
187 if (!HasStatus(CheckerStatus::IN_LOCAL_CLASS)) {
188 return false;
189 }
190
191 if (!var->HasFlag(varbinder::VariableFlags::LOCAL)) {
192 return false;
193 }
194
195 LOG(DEBUG, ES2PANDA) << "Checking variable (line:" << pos.line << "): " << var->Name();
196 auto *scopeIter = Scope();
197 bool inStaticMethod = false;
198
199 auto captureVariable = [this, var, ident, &scopeIter, &inStaticMethod, &pos]() {
200 if (inStaticMethod) {
201 LogError(diagnostic::PROPERTY_CAPTURE_IN_STATIC, {var->Name()}, pos);
202 return false;
203 }
204 if (scopeIter->Node()->AsClassDefinition()->CaptureVariable(var)) {
205 LOG(DEBUG, ES2PANDA) << " Captured in class:" << scopeIter->Node()->AsClassDefinition()->Ident()->Name();
206 }
207
208 auto *parent = ident->Parent();
209
210 if (parent->IsVariableDeclarator()) {
211 parent = parent->Parent()->Parent();
212 }
213
214 if (!(parent->IsUpdateExpression() ||
215 (parent->IsAssignmentExpression() && parent->AsAssignmentExpression()->Left() == ident)) ||
216 var->Declaration() == nullptr) {
217 return false;
218 }
219
220 if (var->Declaration()->IsParameterDecl()) {
221 LOG(DEBUG, ES2PANDA) << " - Modified parameter ";
222 scopeIter->Node()->AsClassDefinition()->AddToLocalVariableIsNeeded(var);
223 }
224 return true;
225 };
226
227 while (scopeIter != var->GetScope()) {
228 if (scopeIter->Node() != nullptr) {
229 if (scopeIter->Node()->IsScriptFunction() && scopeIter->Node()->AsScriptFunction()->IsStatic()) {
230 inStaticMethod = true;
231 }
232
233 if (scopeIter->Node()->IsClassDefinition()) {
234 captureVariable();
235 return true;
236 }
237 }
238 scopeIter = scopeIter->Parent();
239 }
240
241 return false;
242 }
243
SaveCapturedVariable(varbinder::Variable * const var,ir::Identifier * ident)244 void ETSChecker::SaveCapturedVariable(varbinder::Variable *const var, ir::Identifier *ident)
245 {
246 const auto &pos = ident->Start();
247
248 if (!HasStatus(CheckerStatus::IN_LAMBDA)) {
249 return;
250 }
251
252 if (var->HasFlag(varbinder::VariableFlags::PROPERTY)) {
253 Context().AddCapturedVar(var, pos);
254 return;
255 }
256
257 if ((!var->HasFlag(varbinder::VariableFlags::LOCAL) && !var->HasFlag(varbinder::VariableFlags::METHOD)) ||
258 Context().ContainingLambda()->IsVarFromSubscope(var)) {
259 return;
260 }
261
262 const auto *scopeIter = Scope();
263 while (scopeIter != var->GetScope()) {
264 if (scopeIter->IsFunctionScope()) {
265 Context().AddCapturedVar(var, pos);
266 return;
267 }
268 scopeIter = scopeIter->Parent();
269 }
270 }
271
ResolveIdentifier(ir::Identifier * ident)272 Type *ETSChecker::ResolveIdentifier(ir::Identifier *ident)
273 {
274 if (ident->Variable() != nullptr) {
275 auto *const resolved = ident->Variable();
276 SaveCapturedVariable(resolved, ident);
277 return GetTypeOfVariable(resolved);
278 }
279
280 auto *resolved = FindVariableInFunctionScope(ident->Name());
281 if (resolved == nullptr) {
282 // If the reference is not found already in the current class, then it is not bound to the class, so we have to
283 // find the reference in the global class first, then in the global scope
284 resolved = FindVariableInGlobal(ident);
285 if (UNLIKELY(resolved == nullptr && debugInfoPlugin_ != nullptr)) {
286 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
287 resolved = debugInfoPlugin_->FindIdentifier(ident);
288 }
289 }
290
291 if (resolved == nullptr) {
292 resolved = ExtraCheckForResolvedError(ident);
293 if (resolved == nullptr) {
294 if (VarBinder()->GetScope()->IsClassScope() && this->IsAnyError()) {
295 return ident->SetTsType(GlobalTypeError());
296 }
297 auto [decl, var] = VarBinder()->NewVarDecl<varbinder::LetDecl>(
298 ident->Start(),
299 !ident->IsErrorPlaceHolder() ? ident->Name() : compiler::GenName(ProgramAllocator()).View());
300 var->SetScope(VarBinder()->GetScope());
301 ident->SetVariable(var);
302 decl->BindNode(ident);
303 return ident->SetTsType(var->SetTsType(GlobalTypeError()));
304 }
305 ident->SetVariable(resolved);
306 return GetTypeOfVariable(resolved);
307 }
308
309 ident->SetVariable(resolved);
310 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
311 ValidateResolvedIdentifier(ident);
312
313 ValidatePropertyAccess(resolved, Context().ContainingClass(), ident->Start());
314 SaveCapturedVariable(resolved, ident);
315
316 return GetTypeOfVariable(resolved);
317 }
318
CheckLeftRightType(checker::ETSChecker * checker,checker::Type * unboxedL,checker::Type * unboxedR)319 std::optional<checker::Type *> CheckLeftRightType(checker::ETSChecker *checker, checker::Type *unboxedL,
320 checker::Type *unboxedR)
321 {
322 if (unboxedL->IsDoubleType() || unboxedR->IsDoubleType()) {
323 return checker->GlobalDoubleType();
324 }
325 if (unboxedL->IsFloatType() || unboxedR->IsFloatType()) {
326 return checker->GlobalFloatType();
327 }
328 if (unboxedL->IsLongType() || unboxedR->IsLongType()) {
329 return checker->GlobalLongType();
330 }
331 if (unboxedL->IsIntType() || unboxedR->IsIntType() || unboxedL->IsCharType() || unboxedR->IsCharType()) {
332 return checker->GlobalIntType();
333 }
334 if (unboxedL->IsShortType() || unboxedR->IsShortType()) {
335 return checker->GlobalShortType();
336 }
337 if (unboxedL->IsByteType() || unboxedR->IsByteType()) {
338 return checker->GlobalByteType();
339 }
340 return std::nullopt;
341 }
ApplyConditionalOperatorPromotion(checker::ETSChecker * checker,checker::Type * unboxedL,checker::Type * unboxedR)342 checker::Type *ETSChecker::ApplyConditionalOperatorPromotion(checker::ETSChecker *checker, checker::Type *unboxedL,
343 checker::Type *unboxedR)
344 {
345 if ((unboxedL->HasTypeFlag(checker::TypeFlag::CONSTANT) && unboxedL->IsIntType()) ||
346 (unboxedR->HasTypeFlag(checker::TypeFlag::CONSTANT) && unboxedR->IsIntType())) {
347 int value = unboxedL->IsIntType() ? unboxedL->AsIntType()->GetValue() : unboxedR->AsIntType()->GetValue();
348 checker::Type *otherType = !unboxedL->IsIntType() ? unboxedL : unboxedR;
349
350 switch (checker::ETSChecker::ETSType(otherType)) {
351 case checker::TypeFlag::BYTE:
352 case checker::TypeFlag::CHAR: {
353 if (value <= static_cast<int>(std::numeric_limits<char>::max()) &&
354 value >= static_cast<int>(std::numeric_limits<char>::min())) {
355 return checker->GetNonConstantType(otherType);
356 }
357 break;
358 }
359 case checker::TypeFlag::SHORT: {
360 if (value <= std::numeric_limits<int16_t>::max() && value >= std::numeric_limits<int16_t>::min()) {
361 return checker->GlobalShortType();
362 }
363 break;
364 }
365 default: {
366 return otherType;
367 }
368 }
369 return checker->GlobalIntType();
370 }
371
372 auto checkLeftRight = CheckLeftRightType(checker, unboxedL, unboxedR);
373 if (checkLeftRight.has_value()) {
374 return checkLeftRight.value();
375 }
376 ES2PANDA_UNREACHABLE();
377 }
378
ApplyUnaryOperatorPromotion(Type * type,const bool createConst,const bool doPromotion,const bool isCondExpr)379 Type *ETSChecker::ApplyUnaryOperatorPromotion(Type *type, const bool createConst, const bool doPromotion,
380 const bool isCondExpr)
381 {
382 Type *unboxedType = isCondExpr ? MaybeUnboxConditionalInRelation(type) : MaybeUnboxInRelation(type);
383
384 if (unboxedType == nullptr) {
385 return nullptr;
386 }
387 if (doPromotion) {
388 switch (ETSType(unboxedType)) {
389 case TypeFlag::BYTE:
390 case TypeFlag::SHORT:
391 case TypeFlag::CHAR: {
392 if (!createConst) {
393 return GlobalIntType();
394 }
395
396 return CreateIntTypeFromType(unboxedType);
397 }
398 default: {
399 break;
400 }
401 }
402 }
403 return unboxedType;
404 }
405
IsNullLikeOrVoidExpression(const ir::Expression * expr) const406 bool ETSChecker::IsNullLikeOrVoidExpression(const ir::Expression *expr) const
407 {
408 // NOTE(vpukhov): #19701 void refactoring
409 return expr->TsType()->DefinitelyETSNullish() || expr->TsType()->IsETSVoidType();
410 }
411
IsResolvedAndValue(const ir::Expression * expr,Type * type) const412 std::tuple<bool, bool> ETSChecker::IsResolvedAndValue(const ir::Expression *expr, Type *type) const
413 {
414 auto [isResolve, isValue] =
415 IsNullLikeOrVoidExpression(expr) ? std::make_tuple(true, false) : type->ResolveConditionExpr();
416
417 const Type *tsType = expr->TsType();
418 if (tsType->DefinitelyNotETSNullish() && !type->IsETSPrimitiveOrEnumType()) {
419 isResolve = true;
420 isValue = true;
421 }
422 return std::make_tuple(isResolve, isValue);
423 }
424
HandleBooleanLogicalOperators(Type * leftType,Type * rightType,lexer::TokenType tokenType)425 Type *ETSChecker::HandleBooleanLogicalOperators(Type *leftType, Type *rightType, lexer::TokenType tokenType)
426 {
427 using UType = typename ETSBooleanType::UType;
428 ES2PANDA_ASSERT(leftType->IsETSBooleanType() && rightType->IsETSBooleanType());
429
430 if (!leftType->HasTypeFlag(checker::TypeFlag::CONSTANT) || !rightType->HasTypeFlag(checker::TypeFlag::CONSTANT)) {
431 return GlobalETSBooleanType();
432 }
433
434 UType leftValue = leftType->AsETSBooleanType()->GetValue();
435 UType rightValue = rightType->AsETSBooleanType()->GetValue();
436
437 switch (tokenType) {
438 case lexer::TokenType::PUNCTUATOR_BITWISE_XOR: {
439 return CreateETSBooleanType(leftValue ^ rightValue);
440 }
441 case lexer::TokenType::PUNCTUATOR_BITWISE_AND: {
442 return CreateETSBooleanType((static_cast<uint8_t>(leftValue) & static_cast<uint8_t>(rightValue)) != 0);
443 }
444 case lexer::TokenType::PUNCTUATOR_BITWISE_OR: {
445 return CreateETSBooleanType((static_cast<uint8_t>(leftValue) | static_cast<uint8_t>(rightValue)) != 0);
446 }
447 case lexer::TokenType::PUNCTUATOR_LOGICAL_OR: {
448 return CreateETSBooleanType(leftValue || rightValue);
449 }
450 case lexer::TokenType::PUNCTUATOR_LOGICAL_AND: {
451 return CreateETSBooleanType(leftValue && rightValue);
452 }
453 default: {
454 break;
455 }
456 }
457
458 ES2PANDA_UNREACHABLE();
459 return nullptr;
460 }
461
HandleLogicalPotentialResult(ir::Expression * left,ir::Expression * right,ir::BinaryExpression * expr,checker::Type * leftType)462 bool ETSChecker::HandleLogicalPotentialResult(ir::Expression *left, ir::Expression *right, ir::BinaryExpression *expr,
463 checker::Type *leftType)
464 {
465 if (leftType->IsConstantType() && leftType->IsETSBooleanType()) {
466 if (expr->OperatorType() == lexer::TokenType::PUNCTUATOR_LOGICAL_AND) {
467 expr->SetResult(leftType->AsETSBooleanType()->GetValue() ? right : left);
468 return true;
469 }
470 expr->SetResult(leftType->AsETSBooleanType()->GetValue() ? left : right);
471 return true;
472 }
473
474 if (!leftType->IsETSPrimitiveType() && !leftType->PossiblyETSValueTyped()) {
475 expr->SetResult(expr->OperatorType() == lexer::TokenType::PUNCTUATOR_LOGICAL_AND ? right : left);
476 return true;
477 }
478 if (leftType->IsETSNullType() || leftType->IsETSUndefinedType()) {
479 expr->SetResult(expr->OperatorType() == lexer::TokenType::PUNCTUATOR_LOGICAL_AND ? left : right);
480 return true;
481 }
482
483 return false;
484 }
485
ResolveReturnStatement(checker::Type * funcReturnType,checker::Type * argumentType,ir::ScriptFunction * containingFunc,ir::ReturnStatement * st)486 void ETSChecker::ResolveReturnStatement(checker::Type *funcReturnType, checker::Type *argumentType,
487 ir::ScriptFunction *containingFunc, ir::ReturnStatement *st)
488 {
489 if (funcReturnType->IsETSPrimitiveOrEnumType() && argumentType->IsETSPrimitiveOrEnumType()) {
490 // function return type is of primitive type (including enums):
491 Relation()->SetFlags(checker::TypeRelationFlag::DIRECT_RETURN |
492 checker::TypeRelationFlag::IN_ASSIGNMENT_CONTEXT |
493 checker::TypeRelationFlag::ASSIGNMENT_CONTEXT);
494 if (Relation()->IsAssignableTo(funcReturnType, argumentType)) {
495 funcReturnType = argumentType;
496 containingFunc->Signature()->SetReturnType(funcReturnType);
497 containingFunc->Signature()->AddSignatureFlag(checker::SignatureFlags::INFERRED_RETURN_TYPE);
498 st->Argument()->SetTsType(funcReturnType);
499 } else if (!Relation()->IsAssignableTo(argumentType, funcReturnType)) {
500 LogError(diagnostic::RETURN_DIFFERENT_PRIM, {funcReturnType, argumentType}, st->Argument()->Start());
501 }
502 } else if (funcReturnType->IsETSReferenceType() || argumentType->IsETSReferenceType()) {
503 // function return type should be of reference (object) type
504 Relation()->SetFlags(checker::TypeRelationFlag::NONE);
505
506 if (!argumentType->IsETSReferenceType()) {
507 argumentType = MaybeBoxInRelation(argumentType);
508 if (argumentType == nullptr) {
509 LogError(diagnostic::INVALID_EXPR_IN_RETURN, {}, st->Argument()->Start());
510 } else {
511 st->Argument()->AddBoxingUnboxingFlags(GetBoxingFlag(argumentType));
512 }
513 }
514
515 if (!funcReturnType->IsETSReferenceType()) {
516 funcReturnType = MaybeBoxInRelation(funcReturnType);
517 if (funcReturnType == nullptr) {
518 LogError(diagnostic::INVALID_RETURN_FUNC_EXPR, {}, st->Start());
519 }
520 }
521 if (argumentType != nullptr && funcReturnType != nullptr) {
522 funcReturnType = CreateETSUnionType({funcReturnType, argumentType});
523 containingFunc->Signature()->SetReturnType(funcReturnType);
524 containingFunc->Signature()->AddSignatureFlag(checker::SignatureFlags::INFERRED_RETURN_TYPE);
525 }
526 } else {
527 // Should never in this branch.
528 ES2PANDA_UNREACHABLE();
529 return;
530 }
531 }
532
CheckArrayElements(ir::ArrayExpression * init)533 checker::Type *ETSChecker::CheckArrayElements(ir::ArrayExpression *init)
534 {
535 ArenaVector<checker::Type *> elementTypes(ProgramAllocator()->Adapter());
536 for (auto *elementNode : init->AsArrayExpression()->Elements()) {
537 Type *elementType = elementNode->Check(this);
538 if (elementType->IsTypeError()) {
539 return elementType;
540 }
541
542 if (elementNode->IsSpreadElement() && elementType->IsETSTupleType()) {
543 for (auto *typeFromTuple : elementType->AsETSTupleType()->GetTupleTypesList()) {
544 elementTypes.emplace_back(typeFromTuple);
545 }
546
547 continue;
548 }
549
550 if (elementNode->IsSpreadElement() && elementType->IsETSArrayType()) {
551 elementType = elementType->AsETSArrayType()->ElementType();
552 }
553
554 elementTypes.push_back(GetNonConstantType(elementType));
555 }
556
557 if (elementTypes.empty()) {
558 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
559 return ProgramAllocator()->New<ETSArrayType>(GlobalETSObjectType());
560 }
561 auto const isNumeric = [](checker::Type *ct) { return ct->HasTypeFlag(TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC); };
562 auto const isChar = [](checker::Type *ct) { return ct->HasTypeFlag(TypeFlag::CHAR); };
563 auto *const arrayElementType =
564 std::all_of(elementTypes.begin(), elementTypes.end(), isNumeric)
565 ? std::all_of(elementTypes.begin(), elementTypes.end(), isChar) ? GlobalCharType() : GlobalDoubleType()
566 : CreateETSUnionType(std::move(elementTypes));
567
568 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
569 return CreateETSResizableArrayType(arrayElementType);
570 }
571
InferAliasLambdaType(ir::TypeNode * localTypeAnnotation,ir::ArrowFunctionExpression * init)572 void ETSChecker::InferAliasLambdaType(ir::TypeNode *localTypeAnnotation, ir::ArrowFunctionExpression *init)
573 {
574 ES2PANDA_ASSERT(localTypeAnnotation != nullptr);
575
576 if (localTypeAnnotation->IsETSTypeReference()) {
577 bool isAnnotationTypeAlias = true;
578 while (localTypeAnnotation->IsETSTypeReference() && isAnnotationTypeAlias) {
579 auto *nodeVar = localTypeAnnotation->AsETSTypeReference()->Part()->GetIdent()->Variable();
580 if (nodeVar == nullptr) {
581 break;
582 }
583
584 auto *node = nodeVar->Declaration()->Node();
585
586 isAnnotationTypeAlias = node->IsTSTypeAliasDeclaration();
587 if (isAnnotationTypeAlias) {
588 localTypeAnnotation = node->AsTSTypeAliasDeclaration()->TypeAnnotation();
589 }
590 }
591 }
592
593 if (localTypeAnnotation->IsETSFunctionType()) {
594 auto *const arrowFuncExpr = init;
595 ir::ScriptFunction *const lambda = arrowFuncExpr->Function();
596 if (lambda->Params().size() == localTypeAnnotation->AsETSFunctionType()->Params().size() &&
597 NeedTypeInference(lambda)) {
598 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
599 InferTypesForLambda(lambda, localTypeAnnotation->AsETSFunctionType());
600 }
601 }
602 }
603
FixOptionalVariableType(varbinder::Variable * const bindingVar,ir::ModifierFlags flags,ir::Expression * init)604 checker::Type *ETSChecker::FixOptionalVariableType(varbinder::Variable *const bindingVar, ir::ModifierFlags flags,
605 ir::Expression *init)
606 {
607 ES2PANDA_ASSERT(bindingVar != nullptr);
608 if ((flags & ir::ModifierFlags::OPTIONAL) != 0) {
609 if (init != nullptr) {
610 auto *type = bindingVar->TsType();
611 ES2PANDA_ASSERT(type != nullptr);
612 if (type->IsETSPrimitiveType()) {
613 init->SetBoxingUnboxingFlags(GetBoxingFlag(bindingVar->TsType()));
614 }
615 }
616 auto *variableType = bindingVar->TsType() != nullptr ? bindingVar->TsType() : GlobalTypeError();
617 bindingVar->SetTsType(CreateETSUnionType({GlobalETSUndefinedType(), variableType}));
618 }
619 return bindingVar->TsType();
620 }
621
PreferredObjectTypeFromAnnotation(checker::Type * annotationType)622 checker::Type *PreferredObjectTypeFromAnnotation(checker::Type *annotationType)
623 {
624 if (!annotationType->IsETSUnionType()) {
625 return annotationType;
626 }
627
628 checker::Type *resolvedType = nullptr;
629 int objectTypeCount = 0;
630 for (auto constituentType : annotationType->AsETSUnionType()->ConstituentTypes()) {
631 if (constituentType->IsETSObjectType()) {
632 objectTypeCount++;
633 if (resolvedType == nullptr) {
634 resolvedType = constituentType;
635 }
636 }
637 }
638
639 // If there's exactly one object type, return it
640 if (objectTypeCount == 1) {
641 return resolvedType;
642 }
643
644 // If there are multiple object types, return the union type itself
645 // so that our union resolution logic can handle it
646 if (objectTypeCount > 1) {
647 return annotationType;
648 }
649
650 // If there are no object types, return nullptr
651 return nullptr;
652 }
653
SetPreferredTypeForExpression(ETSChecker * checker,ir::Identifier * ident,ir::TypeNode * typeAnnotation,ir::Expression * init,checker::Type * annotationType)654 bool SetPreferredTypeForExpression(ETSChecker *checker, ir::Identifier *ident, ir::TypeNode *typeAnnotation,
655 ir::Expression *init, checker::Type *annotationType)
656 {
657 if (init->IsMemberExpression() && init->AsMemberExpression()->Object()->IsObjectExpression()) {
658 checker->LogError(diagnostic::MEMBER_OF_OBJECT_LIT, {}, ident->Start());
659 }
660
661 if (annotationType != nullptr && annotationType->HasTypeFlag(TypeFlag::TYPE_ERROR)) {
662 return false;
663 }
664
665 if ((init->IsMemberExpression()) && (annotationType != nullptr)) {
666 checker->SetArrayPreferredTypeForNestedMemberExpressions(init->AsMemberExpression(), annotationType);
667 }
668
669 if (init->IsArrayExpression() && (annotationType != nullptr) && !annotationType->IsETSDynamicType()) {
670 if (annotationType->IsETSTupleType() &&
671 !checker->IsArrayExprSizeValidForTuple(init->AsArrayExpression(), annotationType->AsETSTupleType())) {
672 return false;
673 }
674
675 init->AsArrayExpression()->SetPreferredType(annotationType);
676 }
677
678 if (init->IsObjectExpression() && annotationType != nullptr) {
679 init->AsObjectExpression()->SetPreferredType(PreferredObjectTypeFromAnnotation(annotationType));
680 }
681
682 if (init->IsETSNewArrayInstanceExpression() && annotationType != nullptr) {
683 init->AsETSNewArrayInstanceExpression()->SetPreferredType(annotationType);
684 }
685 if (init->IsETSNewMultiDimArrayInstanceExpression() && annotationType != nullptr) {
686 init->AsETSNewMultiDimArrayInstanceExpression()->SetPreferredType(annotationType);
687 }
688
689 if (typeAnnotation != nullptr && init->IsArrowFunctionExpression()) {
690 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
691 checker->InferAliasLambdaType(typeAnnotation, init->AsArrowFunctionExpression());
692 }
693
694 return true;
695 }
696
CheckInit(ir::Identifier * ident,ir::TypeNode * typeAnnotation,ir::Expression * init,checker::Type * annotationType)697 bool ETSChecker::CheckInit(ir::Identifier *ident, ir::TypeNode *typeAnnotation, ir::Expression *init,
698 checker::Type *annotationType)
699 {
700 if (typeAnnotation == nullptr) {
701 if (init->IsArrayExpression()) {
702 annotationType = CheckArrayElements(init->AsArrayExpression());
703 } else if (init->IsETSNewArrayInstanceExpression()) {
704 annotationType = init->AsETSNewArrayInstanceExpression()->TypeReference()->GetType(this);
705 annotationType = CreateETSResizableArrayType(annotationType);
706 } else if (init->IsETSNewMultiDimArrayInstanceExpression()) {
707 auto multiArray = init->AsETSNewMultiDimArrayInstanceExpression();
708 annotationType = multiArray->TypeReference()->GetType(this);
709 annotationType = CreateETSMultiDimResizableArrayType(annotationType, multiArray->Dimensions().size());
710 } else if (init->IsObjectExpression()) {
711 LogError(diagnostic::CANNOT_INFER_OBJ_LIT, {ident->Name()}, ident->Start());
712 return false;
713 }
714 }
715 return SetPreferredTypeForExpression(this, ident, typeAnnotation, init, annotationType);
716 }
717
CheckEnumType(ir::Expression * init,checker::Type * initType,const util::StringView & varName)718 void ETSChecker::CheckEnumType(ir::Expression *init, checker::Type *initType, const util::StringView &varName)
719 {
720 if (initType->IsETSObjectType() && initType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::ENUM) &&
721 !init->IsMemberExpression()) {
722 LogError(diagnostic::TYPE_MISMATCH_ENUM, {initType->AsETSObjectType()->Name(), varName}, init->Start());
723 }
724 }
725
IsOmitConstInit(ir::ModifierFlags const flags)726 static bool IsOmitConstInit(ir::ModifierFlags const flags)
727 {
728 return ((flags & ir::ModifierFlags::CONST) != 0) ||
729 (((flags & ir::ModifierFlags::READONLY) != 0) && ((flags & ir::ModifierFlags::STATIC) != 0));
730 }
731
NeedWidening(ir::Expression * e)732 static bool NeedWidening(ir::Expression *e)
733 {
734 // NOTE: need to be done by smart casts. Return true if we need to infer wider type.
735 if (e->IsUnaryExpression()) {
736 return NeedWidening(e->AsUnaryExpression()->Argument());
737 }
738 const bool isConstInit = e->IsIdentifier() && e->Variable()->Declaration()->IsConstDecl();
739
740 return e->IsConditionalExpression() || e->IsLiteral() || isConstInit || e->IsTemplateLiteral();
741 }
742
743 // Isolated until 'constant' types are tracked in some cases
ShouldPreserveConstantTypeInVariableDeclaration(Type * annotation,Type * init)744 static bool ShouldPreserveConstantTypeInVariableDeclaration(Type *annotation, Type *init)
745 {
746 auto const isNumericWithConstTracking = [](Type *type) {
747 return type->HasTypeFlag(TypeFlag::ETS_NUMERIC) || type->IsCharType();
748 };
749
750 return ((isNumericWithConstTracking(init) && isNumericWithConstTracking(annotation)) ||
751 (init->IsETSStringType() && annotation->IsETSStringType()));
752 }
753
CheckAssignForDeclare(ir::Identifier * ident,ir::TypeNode * typeAnnotation,ir::Expression * init,ir::ModifierFlags const flags,ETSChecker * check)754 static void CheckAssignForDeclare(ir::Identifier *ident, ir::TypeNode *typeAnnotation, ir::Expression *init,
755 ir::ModifierFlags const flags, ETSChecker *check)
756 {
757 const bool isDeclare = (flags & ir::ModifierFlags::DECLARE) != 0;
758 const bool isAbstract = (flags & ir::ModifierFlags::ABSTRACT) != 0;
759 if (!isDeclare || isAbstract) {
760 return;
761 }
762 if (typeAnnotation != nullptr && init != nullptr && !init->IsUndefinedLiteral()) {
763 check->LogError(diagnostic::INIT_IN_AMBIENT, {ident->Name()}, init->Start());
764 return;
765 }
766 const bool isConst = (flags & ir::ModifierFlags::CONST) != 0;
767 ES2PANDA_ASSERT(init != nullptr);
768 bool numberLiteralButNotBigInt = init->IsNumberLiteral() && !init->IsBigIntLiteral();
769 bool multilineLiteralWithNoEmbedding =
770 init->IsTemplateLiteral() && init->AsTemplateLiteral()->Expressions().empty();
771 if (isConst && !numberLiteralButNotBigInt && !init->IsStringLiteral() && !multilineLiteralWithNoEmbedding) {
772 check->LogError(diagnostic::AMBIENT_CONST_INVALID_LIT, {ident->Name()}, init->Start());
773 }
774 }
775
CheckRecordType(ir::Expression * init,checker::Type * annotationType,ETSChecker * checker)776 static void CheckRecordType(ir::Expression *init, checker::Type *annotationType, ETSChecker *checker)
777 {
778 if (!annotationType->IsETSObjectType() || !init->IsObjectExpression()) {
779 return;
780 }
781
782 std::stringstream ss;
783 init->TsType()->ToAssemblerType(ss);
784 if (ss.str() != "escompat.Record" && ss.str() != "escompat.Map") {
785 return;
786 }
787
788 auto objectExpr = init->AsObjectExpression();
789 auto typeArguments = annotationType->AsETSObjectType()->TypeArguments();
790 auto properties = objectExpr->Properties();
791
792 for (const auto &property : properties) {
793 if (!property->IsProperty()) {
794 checker->LogError(diagnostic::IMPROPER_NESTING_INTERFACE, {}, property->Start());
795 continue;
796 }
797 ES2PANDA_ASSERT(property->IsProperty());
798 auto p = property->AsProperty();
799
800 ETSChecker::SetPreferredTypeIfPossible(p->Key(), typeArguments[0]);
801 ETSChecker::SetPreferredTypeIfPossible(p->Value(), typeArguments[1]);
802
803 Type *keyType = p->Key()->Check(checker);
804 Type *valueType = p->Value()->Check(checker);
805
806 checker::AssignmentContext(
807 checker->Relation(), p->Key(), keyType, typeArguments[0], p->Key()->Start(),
808 util::DiagnosticWithParams {diagnostic::TYPE_MISMATCH_AT_IDX, {keyType, typeArguments[0], size_t(1)}});
809 checker::AssignmentContext(
810 checker->Relation(), p->Value(), valueType, typeArguments[1], p->Value()->Start(),
811 util::DiagnosticWithParams {diagnostic::TYPE_MISMATCH_AT_IDX, {valueType, typeArguments[1], size_t(2)}});
812 }
813 }
814
815 // CC-OFFNXT(huge_method,huge_cca_cyclomatic_complexity,huge_cyclomatic_complexity,G.FUN.01-CPP) solid logic
CheckVariableDeclaration(ir::Identifier * ident,ir::TypeNode * typeAnnotation,ir::Expression * init,ir::ModifierFlags const flags)816 checker::Type *ETSChecker::CheckVariableDeclaration(ir::Identifier *ident, ir::TypeNode *typeAnnotation,
817 ir::Expression *init, ir::ModifierFlags const flags)
818 {
819 ES2PANDA_ASSERT(ident != nullptr);
820 varbinder::Variable *const bindingVar = ident->Variable();
821 checker::Type *annotationType = nullptr;
822
823 // We have to process possible parser errors when variable was not created and bind:
824 if (bindingVar != nullptr) {
825 if (typeAnnotation != nullptr) {
826 annotationType = typeAnnotation->GetType(this);
827 bindingVar->SetTsType(annotationType);
828 }
829
830 if (init == nullptr) {
831 return FixOptionalVariableType(bindingVar, flags, init);
832 }
833 CheckAssignForDeclare(ident, typeAnnotation, init, flags, this);
834 } else {
835 ES2PANDA_ASSERT(IsAnyError());
836 }
837
838 checker::Type *initType = nullptr;
839 if (init != nullptr) {
840 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
841 if (!CheckInit(ident, typeAnnotation, init, annotationType)) {
842 init->SetTsType(GlobalTypeError());
843 } else {
844 initType = init->Check(this);
845 }
846 } else {
847 ES2PANDA_ASSERT(IsAnyError());
848 }
849
850 if (bindingVar == nullptr) {
851 return annotationType != nullptr ? annotationType : GlobalTypeError();
852 }
853
854 // initType should not be nullptr. If an error occurs during check, set it to GlobalTypeError().
855 if (initType == nullptr || initType->IsTypeError()) {
856 return bindingVar->SetTsType(annotationType != nullptr ? annotationType : GlobalTypeError());
857 }
858
859 if (typeAnnotation == nullptr && initType->IsETSFunctionType()) {
860 annotationType = initType->AsETSFunctionType();
861 bindingVar->SetTsType(annotationType);
862 }
863
864 if (annotationType != nullptr) {
865 if (typeAnnotation != nullptr) {
866 CheckRecordType(init, annotationType, this);
867 AssignmentContext(Relation(), init, initType, annotationType, init->Start(),
868 {{diagnostic::INVALID_ASSIGNMNENT, {initType, annotationType}}});
869 if (!Relation()->IsTrue()) {
870 return annotationType;
871 }
872 }
873
874 if (IsOmitConstInit(flags) && ShouldPreserveConstantTypeInVariableDeclaration(annotationType, initType)) {
875 VariableTypeFromInitializer(bindingVar, MaybeUnboxType(annotationType), initType);
876 }
877 } else {
878 CheckEnumType(init, initType, ident->Name());
879
880 // NOTE: need to be done by smart casts
881 auto needWidening = !IsOmitConstInit(flags) && typeAnnotation == nullptr && NeedWidening(init);
882 bindingVar->SetTsType(needWidening ? GetNonConstantType(initType) : initType);
883 }
884
885 return FixOptionalVariableType(bindingVar, flags, init);
886 }
887
VariableTypeFromInitializer(varbinder::Variable * variable,Type * annotationType,Type * initType)888 void ETSChecker::VariableTypeFromInitializer(varbinder::Variable *variable, Type *annotationType, Type *initType)
889 {
890 if (!initType->IsConstantType()) {
891 return;
892 }
893
894 if (!initType->IsETSPrimitiveType()) {
895 variable->SetTsType(initType);
896 return;
897 }
898
899 ES2PANDA_ASSERT(annotationType->IsETSPrimitiveType());
900 // We suppose here that all required checks were passed and correct conversion is possible without accuracy loss
901 variable->SetTsType(TypeConverter::ConvertConstantTypes(initType, annotationType, ProgramAllocator()));
902 }
903
ResolveGetter(checker::ETSChecker * checker,ir::MemberExpression * const expr,ETSFunctionType * funcType)904 static checker::Type *ResolveGetter(checker::ETSChecker *checker, ir::MemberExpression *const expr,
905 ETSFunctionType *funcType)
906 {
907 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
908 auto signature = checker->FindRelativeExtensionGetter(expr, funcType);
909 if (signature != nullptr) {
910 return signature->ReturnType();
911 }
912
913 PropertySearchFlags flags = PropertySearchFlags::SEARCH_INSTANCE_METHOD | PropertySearchFlags::SEARCH_IN_BASE |
914 PropertySearchFlags::SEARCH_IN_INTERFACES | PropertySearchFlags::IS_GETTER;
915 auto objType = expr->ObjType();
916 auto searchName = objType->GetReExportAliasValue(expr->Property()->AsIdentifier()->Name());
917 auto *prop = objType->GetProperty(searchName, flags);
918 if (prop == nullptr || prop->TsType() == nullptr ||
919 !prop->TsType()->HasTypeFlag(checker::TypeFlag::GETTER_SETTER)) {
920 return nullptr;
921 }
922 auto *propType = prop->TsType()->AsETSFunctionType();
923 auto originalGetter = propType->FindGetter();
924 if (originalGetter == nullptr) {
925 return nullptr;
926 }
927
928 checker->ValidateSignatureAccessibility(objType, originalGetter, expr->Start());
929 return originalGetter->ReturnType();
930 }
931
FindRelativeExtensionGetter(ir::MemberExpression * const expr,ETSFunctionType * funcType)932 Signature *ETSChecker::FindRelativeExtensionGetter(ir::MemberExpression *const expr, ETSFunctionType *funcType)
933 {
934 ES2PANDA_ASSERT(expr->ObjType() != nullptr);
935 ES2PANDA_ASSERT(funcType != nullptr && funcType->IsExtensionAccessorType());
936 ES2PANDA_ASSERT(expr->Property()->AsIdentifier()->Name() == funcType->Name());
937 if (auto sig = FindExtensionGetterInMap(funcType->Name(), expr->ObjType()); sig != nullptr) {
938 return sig;
939 }
940
941 ArenaVector<ir::Expression *> arguments(ProgramAllocator()->Adapter());
942 arguments.insert(arguments.begin(), expr->Object());
943 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
944 Signature *signature = ValidateSignatures(funcType->GetExtensionAccessorSigs(), nullptr, arguments, expr->Start(),
945 "call", TypeRelationFlag::NO_THROW);
946 if (signature != nullptr) {
947 InsertExtensionGetterToMap(funcType->Name(), expr->ObjType(), signature);
948 }
949 return signature;
950 }
951
FindRelativeExtensionSetter(ir::MemberExpression * expr,ETSFunctionType * funcType)952 Signature *ETSChecker::FindRelativeExtensionSetter(ir::MemberExpression *expr, ETSFunctionType *funcType)
953 {
954 ES2PANDA_ASSERT(expr->ObjType() != nullptr);
955 ES2PANDA_ASSERT(funcType != nullptr && funcType->IsExtensionAccessorType());
956 ES2PANDA_ASSERT(expr->Property()->AsIdentifier()->Name() == funcType->Name());
957 ES2PANDA_ASSERT(expr->Parent()->IsAssignmentExpression() || expr->Parent()->IsUpdateExpression());
958 if (auto const sig = FindExtensionSetterInMap(funcType->Name(), expr->ObjType()); sig != nullptr) {
959 return sig;
960 }
961
962 Signature *signature = nullptr;
963 ArenaVector<ir::Expression *> arguments(ProgramAllocator()->Adapter());
964 arguments.insert(arguments.begin(), expr->Object());
965 if (expr->Parent()->IsAssignmentExpression()) {
966 arguments.emplace_back(expr->Parent()->AsAssignmentExpression()->Right());
967 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
968 signature = ValidateSignatures(funcType->GetExtensionAccessorSigs(), nullptr, arguments, expr->Start(), "call",
969 TypeRelationFlag::NO_THROW);
970 } else {
971 // When handle ++a.m, a.m++, is mean to check whether a.m(xx, 1) existed.
972 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
973 Type *getterReturnType = ResolveGetter(this, expr, funcType);
974 expr->SetTsType(getterReturnType);
975 arguments.emplace_back(expr);
976 signature = ValidateSignatures(funcType->GetExtensionAccessorSigs(), nullptr, arguments, expr->Start(), "call",
977 TypeRelationFlag::NO_THROW);
978 }
979
980 if (signature == nullptr) {
981 return nullptr;
982 }
983 InsertExtensionSetterToMap(funcType->Name(), expr->ObjType(), signature);
984 return signature;
985 }
986
987 // Note: extension accessor looks same as member expression in checker phase, but its return type is FunctionType,
988 // we need to get type of extension accessor in checker. For getter, its type is the return type of the function.
989 // and for setter, it was a member expression set as left child of an assignment expression, we temporarily set its
990 // type the same as the right child type. Further work will be done in lowering.
GetExtensionAccessorReturnType(ir::MemberExpression * expr)991 checker::Type *ETSChecker::GetExtensionAccessorReturnType(ir::MemberExpression *expr)
992 {
993 ES2PANDA_ASSERT(IsExtensionETSFunctionType(expr->TsType()));
994
995 auto eAccType = expr->TsType()->AsETSFunctionType();
996 auto assignExpr = expr->Parent();
997 bool isExtensionSetterLike =
998 assignExpr->IsAssignmentExpression() && (assignExpr->AsAssignmentExpression()->Left() == expr);
999 if (isExtensionSetterLike) {
1000 if (!assignExpr->Parent()->IsExpressionStatement()) {
1001 LogError(diagnostic::EXTENSION_GETTER_INVALID_CTX, {}, assignExpr->Start());
1002 return GlobalTypeError();
1003 }
1004 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
1005 auto signature = FindRelativeExtensionSetter(expr, eAccType);
1006 if (signature != nullptr && !expr->Parent()->IsUpdateExpression()) {
1007 // for a.m += otherExpr, we need to validateSignature again.
1008 ArenaVector<ir::Expression *> arguments(ProgramAllocator()->Adapter());
1009 ArenaVector<Signature *> candidateSig(ProgramAllocator()->Adapter());
1010 candidateSig.emplace_back(signature);
1011 arguments.emplace_back(expr->Object());
1012 arguments.emplace_back(expr->Parent()->AsAssignmentExpression()->Right());
1013 signature =
1014 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
1015 ValidateSignatures(candidateSig, nullptr, arguments, expr->Start(), "call", TypeRelationFlag::NO_THROW);
1016 }
1017
1018 if (signature == nullptr) {
1019 LogError(diagnostic::MISSING_EXTENSION_ACCESSOR, {}, expr->Start());
1020 return GlobalTypeError();
1021 }
1022 }
1023 expr->SetExtensionAccessorType(eAccType);
1024 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
1025 return ResolveGetter(this, expr, eAccType);
1026 }
1027
1028 //==============================================================================//
1029 // Smart cast support
1030 //==============================================================================//
1031
ResolveSmartType(checker::Type * sourceType,checker::Type * targetType)1032 checker::Type *ETSChecker::ResolveSmartType(checker::Type *sourceType, checker::Type *targetType)
1033 {
1034 // For left-hand variable of primitive type leave it as is.
1035 if (targetType->IsETSPrimitiveType()) {
1036 return targetType;
1037 }
1038
1039 // For left-hand variable of tuple type leave it as is.
1040 if (targetType->IsETSTupleType()) {
1041 return targetType;
1042 }
1043
1044 // For left-hand invalid variable set smart type to right-hand type.
1045 if (targetType->IsTypeError()) {
1046 return sourceType;
1047 }
1048
1049 // For left-hand variable of builtin type leave it as is.
1050 if (targetType->IsETSObjectType() && targetType->AsETSObjectType()->HasObjectFlag(ETSObjectFlags::BUILTIN_TYPE)) {
1051 return targetType;
1052 }
1053
1054 // Nothing to do with identical types:
1055 auto *nonConstSourceType = GetNonConstantType(sourceType);
1056 auto *nonConstTargetType = GetNonConstantType(targetType);
1057
1058 if (Relation()->IsIdenticalTo(nonConstSourceType, nonConstTargetType) ||
1059 Relation()->IsIdenticalTo(GlobalBuiltinJSValueType(), nonConstTargetType)) {
1060 return targetType;
1061 }
1062
1063 // For type parameter, null or undefined source type return it as is.
1064 if (sourceType->IsETSTypeParameter() || sourceType->DefinitelyETSNullish()) {
1065 return sourceType;
1066 }
1067
1068 // In case of Union left-hand type we have to select the proper type from the Union
1069 // Because now we have logging of errors we have to continue analyze incorrect program, for
1070 // this case we change typeError to source type.
1071 if (targetType->IsETSUnionType()) {
1072 auto component = targetType->AsETSUnionType()->GetAssignableType(this, sourceType);
1073 return component->IsTypeError() ? MaybeBoxType(sourceType) : component;
1074 }
1075
1076 // If source is reference type, set it as the current and use it for identifier smart cast
1077 if (sourceType->IsETSReferenceType()) {
1078 return sourceType;
1079 }
1080
1081 // For right-hand variable of primitive type apply boxing conversion (case: 'let x: Object = 5', then x => Int).
1082 if (sourceType->IsETSPrimitiveType() && !sourceType->IsETSVoidType() && targetType->IsETSObjectType()) {
1083 return MaybeBoxInRelation(sourceType);
1084 }
1085
1086 // NOTE - it seems that all the other possible cases are assignments like:
1087 // 'Object = ObjectLiteral' or smth similar ???
1088 // thus for such cases also leave the target type as is.
1089 // Possible errors in tests should clarify this hypothesis sooner or later :)
1090 return targetType;
1091 }
1092
1093 // Auxiliary method to reduce the size of common 'CheckTestSmartCastConditions' function.
CheckTestNullishCondition(Type * testedType,Type * actualType,bool const strict)1094 std::pair<Type *, Type *> ETSChecker::CheckTestNullishCondition(Type *testedType, Type *actualType, bool const strict)
1095 {
1096 if (!strict) {
1097 return RemoveNullishTypes(actualType);
1098 }
1099
1100 if (testedType->IsETSNullType()) {
1101 return {GlobalETSNullType(), RemoveNullType(actualType)};
1102 }
1103
1104 if (testedType->IsETSUndefinedType()) {
1105 return {GlobalETSUndefinedType(), RemoveUndefinedType(actualType)};
1106 }
1107
1108 return {GlobalETSUnionUndefinedNull(), GetNonNullishType(actualType)};
1109 }
1110
1111 // Auxiliary method to reduce the size of common 'CheckTestSmartCastConditions' function.
CheckTestObjectCondition(ETSArrayType * testedType,Type * actualType)1112 std::pair<Type *, Type *> ETSChecker::CheckTestObjectCondition(ETSArrayType *testedType, Type *actualType)
1113 {
1114 if (actualType->IsETSUnionType()) {
1115 return actualType->AsETSUnionType()->GetComplimentaryType(this, testedType);
1116 }
1117
1118 // Both testing and actual (smart) types are arrays. Set types according to their relation.
1119 // NOTE: probably the rules of type extraction should be modified later on!
1120 if (actualType->IsETSArrayType()) {
1121 auto *const arrayType = actualType->AsETSArrayType();
1122
1123 if (Relation()->IsIdenticalTo(arrayType, testedType)) {
1124 return {testedType, GetGlobalTypesHolder()->GlobalETSNeverType()};
1125 }
1126
1127 if (Relation()->IsSupertypeOf(arrayType, testedType)) {
1128 return {testedType, actualType};
1129 }
1130
1131 if (Relation()->IsSupertypeOf(testedType, arrayType)) {
1132 return {testedType, actualType};
1133 }
1134 } else if (actualType->IsETSObjectType() && actualType->AsETSObjectType()->IsGlobalETSObjectType()) {
1135 return {testedType, actualType};
1136 }
1137
1138 return {GetGlobalTypesHolder()->GlobalETSNeverType(), actualType};
1139 }
1140
1141 // Auxiliary method to reduce the size of common 'CheckTestSmartCastConditions' function.
CheckTestObjectCondition(ETSObjectType * testedType,Type * actualType,bool const strict)1142 std::pair<Type *, Type *> ETSChecker::CheckTestObjectCondition(ETSObjectType *testedType, Type *actualType,
1143 bool const strict)
1144 {
1145 if (actualType->IsETSUnionType()) {
1146 return actualType->AsETSUnionType()->GetComplimentaryType(this, testedType);
1147 }
1148
1149 // Both testing and actual (smart) types are objects. Set types according to their relation.
1150 // NOTE: probably the rules of type extraction should be modified later on!
1151 if (actualType->IsETSObjectType()) {
1152 auto *const objectType = actualType->AsETSObjectType();
1153
1154 if (Relation()->IsIdenticalTo(objectType, testedType) ||
1155 objectType->AssemblerName() == testedType->AssemblerName()) {
1156 return {testedType, strict ? GetGlobalTypesHolder()->GlobalETSNeverType() : actualType};
1157 }
1158
1159 if (Relation()->IsSupertypeOf(objectType, testedType)) {
1160 return {testedType, actualType};
1161 }
1162
1163 if (Relation()->IsSupertypeOf(testedType, objectType)) {
1164 return {testedType, actualType};
1165 }
1166
1167 return {GetGlobalTypesHolder()->GlobalETSNeverType(), actualType};
1168 }
1169
1170 return {testedType, actualType};
1171 }
1172
1173 // Simple and conservative implementation unless smartcasts are not rewritten completely
ComputeConditionalSubtypes(TypeRelation * relation,Type * condition,Type * actual)1174 static std::pair<Type *, Type *> ComputeConditionalSubtypes(TypeRelation *relation, Type *condition, Type *actual)
1175 {
1176 if (relation->IsSupertypeOf(condition, actual)) {
1177 if (relation->IsIdenticalTo(condition, actual)) {
1178 return {condition, relation->GetChecker()->GetGlobalTypesHolder()->GlobalETSNeverType()};
1179 }
1180 return {actual, actual};
1181 }
1182 return {condition, actual};
1183 }
1184
GetUnionOfTypes(checker::Type * const type1,checker::Type * const type2) const1185 checker::Type *CheckerContext::GetUnionOfTypes(checker::Type *const type1, checker::Type *const type2) const noexcept
1186 {
1187 if (type1 == nullptr || type2 == nullptr) {
1188 return type1 == nullptr ? type2 : type1;
1189 }
1190
1191 const auto never = parent_->AsETSChecker()->GlobalETSNeverType();
1192 if (type1 == never || type2 == never) {
1193 return type1 == never ? type2 : type1;
1194 }
1195
1196 return parent_->AsETSChecker()->CreateETSUnionType({type1, type2});
1197 }
1198
GetIntersectionOfTypeAndTypeSetIfExist(ETSChecker * checker,checker::Type * type,const ArenaVector<Type * > & typeSet)1199 static std::optional<checker::Type *> GetIntersectionOfTypeAndTypeSetIfExist(ETSChecker *checker, checker::Type *type,
1200 const ArenaVector<Type *> &typeSet)
1201 {
1202 for (auto *const typeOfTypeSet : typeSet) {
1203 if (checker->Relation()->IsSupertypeOf(type, typeOfTypeSet)) {
1204 return std::make_optional(typeOfTypeSet);
1205 }
1206
1207 if (checker->Relation()->IsSupertypeOf(typeOfTypeSet, type)) {
1208 return std::make_optional(type);
1209 }
1210 }
1211
1212 return std::nullopt;
1213 }
1214
1215 // The method name 'intersection' may be misleading. The function does not create a theoretically correct
1216 // intersection type from 2 arbitrary types, instead does an intersection operation on the type set of 2 (possibly)
1217 // union types. Currently this is good enough, and the correct implementation with type algebra will be in the complete
1218 // smart type rework with CFG.
GetIntersectionOfTypes(checker::Type * const type1,checker::Type * const type2) const1219 checker::Type *CheckerContext::GetIntersectionOfTypes(checker::Type *const type1,
1220 checker::Type *const type2) const noexcept
1221 {
1222 if (type1 == nullptr || type2 == nullptr) {
1223 return type1 == nullptr ? type2 : type1;
1224 }
1225
1226 auto *const checker = parent_->AsETSChecker();
1227
1228 if (checker->Relation()->IsIdenticalTo(type1, type2)) {
1229 return type1;
1230 }
1231
1232 ArenaVector<Type *> typeSet1(checker->ProgramAllocator()->Adapter());
1233 ArenaVector<Type *> typeSet2(checker->ProgramAllocator()->Adapter());
1234 ArenaVector<Type *> intersection(checker->ProgramAllocator()->Adapter());
1235
1236 if (type1->IsETSUnionType()) {
1237 typeSet1 = type1->AsETSUnionType()->ConstituentTypes();
1238 } else {
1239 typeSet1.push_back(type1);
1240 }
1241
1242 if (type2->IsETSUnionType()) {
1243 typeSet2 = type2->AsETSUnionType()->ConstituentTypes();
1244 } else {
1245 typeSet2.push_back(type2);
1246 }
1247
1248 for (auto *const typeOfTypeSet1 : typeSet1) {
1249 auto possibleIntersectionType = GetIntersectionOfTypeAndTypeSetIfExist(checker, typeOfTypeSet1, typeSet2);
1250 if (possibleIntersectionType.has_value()) {
1251 intersection.emplace_back(*possibleIntersectionType);
1252 }
1253 }
1254
1255 return checker->CreateETSUnionType(std::move(intersection));
1256 }
1257
1258 static constexpr std::size_t const VARIABLE_POSITION = 0UL;
1259 static constexpr std::size_t const CONSEQUENT_TYPE_POSITION = 1UL;
1260 static constexpr std::size_t const ALTERNATE_TYPE_POSITION = 2UL;
1261
MergeSmartTypesForLogicalAnd(SmartCastTuple & newSmartCastTypes)1262 void CheckerContext::MergeSmartTypesForLogicalAnd(SmartCastTuple &newSmartCastTypes)
1263 {
1264 auto const &variable = std::get<VARIABLE_POSITION>(newSmartCastTypes);
1265
1266 if (testSmartCasts_.find(variable) == testSmartCasts_.end()) {
1267 testSmartCasts_.emplace(
1268 variable, std::make_pair(std::get<CONSEQUENT_TYPE_POSITION>(newSmartCastTypes), variable->TsType()));
1269 return;
1270 }
1271
1272 auto *newConsequentType = std::get<CONSEQUENT_TYPE_POSITION>(newSmartCastTypes);
1273 auto *newAlternateType = std::get<ALTERNATE_TYPE_POSITION>(newSmartCastTypes);
1274 auto [currentConsequentType, currentAlternateType] = testSmartCasts_.at(variable);
1275 auto *const mergedConsequentType = GetIntersectionOfTypes(currentConsequentType, newConsequentType);
1276 auto *const mergedAlternateType = GetUnionOfTypes(currentAlternateType, newAlternateType);
1277 testSmartCasts_[variable] = {mergedConsequentType, mergedAlternateType};
1278 }
1279
InvalidateNecessarySmartCastsInLogicalAnd(std::optional<SmartCastTuple> & newSmartCastTypes)1280 void CheckerContext::InvalidateNecessarySmartCastsInLogicalAnd(std::optional<SmartCastTuple> &newSmartCastTypes)
1281 {
1282 // If there is no new smart casts in the checked expression, then invalidate all smart casts for the alternate
1283 // branch.
1284 // Eg. if(x == null && false) -> we didn't get a new smart cast from 'false', so in the alternate branch 'x'
1285 // can also be 'null'
1286
1287 if (!newSmartCastTypes.has_value()) {
1288 for (auto &smartCast : testSmartCasts_) {
1289 smartCast.second.second = nullptr;
1290 }
1291
1292 return;
1293 }
1294
1295 // If there is already exist a smart cast for a variable, that isn't checked in the current expression for the
1296 // logical and, it means that in the alternate branch it's smart type will not be valid.
1297 //
1298 // Example 1:
1299 // cond: x != null && y != null
1300 // Here the new smart cast will only contain smart type for 'y', but it's unrelated to the type of 'x', so both
1301 // smart type needs to by invalidated in the alternate branch.
1302 //
1303 // Example 2:
1304 // let x: null|undefined|A <- A is an arbitrary type
1305 // cond: x != null && x != undefined
1306 // Here the new smart type will contain further check for 'x', so it's still checking the same variable. In this
1307 // case the alternate type will contain the already computed union type 'null|undefined' , and not needed to be
1308 // restored to 'null|undefined|A'.
1309
1310 auto const &variable = std::get<VARIABLE_POSITION>(*newSmartCastTypes);
1311 bool anyOtherSmartCastCleared = false;
1312
1313 for (auto existingSmartCast : testSmartCasts_) {
1314 if (existingSmartCast.first != variable) {
1315 // If there is a smart cast that doesn't include the current variable, then we need to invalidate the
1316 // consequent types, because from now on these are unrelated restrictions
1317 testSmartCasts_[existingSmartCast.first].second = nullptr;
1318 anyOtherSmartCastCleared = true;
1319 }
1320 }
1321
1322 if (anyOtherSmartCastCleared) {
1323 testSmartCasts_[variable].second = nullptr;
1324 }
1325 }
1326
GetReassignedVariablesInNode(const ir::AstNode * const node) const1327 ReassignedVariableMap CheckerContext::GetReassignedVariablesInNode(const ir::AstNode *const node) const
1328 {
1329 ReassignedVariableMap changedVariables {};
1330 node->Iterate([this, &changedVariables](ir::AstNode *childNode) { CheckAssignments(childNode, changedVariables); });
1331 return changedVariables;
1332 }
1333
CheckTestSmartCastCondition(lexer::TokenType operatorType)1334 void CheckerContext::CheckTestSmartCastCondition(lexer::TokenType operatorType)
1335 {
1336 if (operatorType != lexer::TokenType::EOS && operatorType != lexer::TokenType::PUNCTUATOR_LOGICAL_AND &&
1337 operatorType != lexer::TokenType::PUNCTUATOR_LOGICAL_OR) {
1338 return;
1339 }
1340
1341 auto types = ResolveSmartCastTypes();
1342
1343 if (operatorType_ == lexer::TokenType::PUNCTUATOR_LOGICAL_AND) {
1344 if (types.has_value()) {
1345 MergeSmartTypesForLogicalAnd(*types);
1346 }
1347 InvalidateNecessarySmartCastsInLogicalAnd(types);
1348 } else if (operatorType_ == lexer::TokenType::PUNCTUATOR_LOGICAL_OR) {
1349 if (!types.has_value() || CheckTestOrSmartCastCondition(*types)) {
1350 // Clear consequent types, because now they become indefinite
1351 for (auto &smartCast : testSmartCasts_) {
1352 smartCast.second.first = nullptr;
1353 }
1354 }
1355 } else if (types.has_value()) {
1356 testSmartCasts_.emplace(
1357 std::get<VARIABLE_POSITION>(*types),
1358 std::make_pair(std::get<CONSEQUENT_TYPE_POSITION>(*types), std::get<ALTERNATE_TYPE_POSITION>(*types)));
1359 }
1360
1361 testCondition_ = {};
1362 operatorType_ = operatorType;
1363 }
1364
ResolveSmartCastTypes()1365 std::optional<SmartCastTuple> CheckerContext::ResolveSmartCastTypes()
1366 {
1367 if (testCondition_.variable == nullptr) {
1368 return std::nullopt;
1369 }
1370
1371 // Exclude processing of global variables and those captured in lambdas and modified there
1372 auto const *const variableScope = testCondition_.variable->GetScope();
1373 auto const topLevelVariable =
1374 variableScope != nullptr ? variableScope->IsGlobalScope() ||
1375 (variableScope->Parent() != nullptr && variableScope->Parent()->IsGlobalScope())
1376 : false;
1377 if (topLevelVariable) {
1378 return std::nullopt;
1379 }
1380
1381 ES2PANDA_ASSERT(testCondition_.testedType != nullptr);
1382 if (!testCondition_.testedType->IsETSReferenceType()) {
1383 return std::nullopt;
1384 }
1385
1386 auto *smartType = GetSmartCast(testCondition_.variable);
1387 if (smartType == nullptr) {
1388 smartType = testCondition_.variable->TsType();
1389 if (smartType == nullptr) {
1390 return std::nullopt;
1391 }
1392 }
1393
1394 auto *const checker = parent_->AsETSChecker();
1395 Type *consequentType = nullptr;
1396 Type *alternateType = nullptr;
1397
1398 if (testCondition_.testedType->DefinitelyETSNullish()) {
1399 // In case of testing for 'null' and/or 'undefined' remove corresponding null-like types.
1400 std::tie(consequentType, alternateType) =
1401 checker->CheckTestNullishCondition(testCondition_.testedType, smartType, testCondition_.strict);
1402 } else if (testCondition_.testedType->IsETSObjectType()) {
1403 auto *const testedType = testCondition_.testedType->AsETSObjectType();
1404 std::tie(consequentType, alternateType) =
1405 checker->CheckTestObjectCondition(testedType, smartType, testCondition_.strict);
1406 } else if (testCondition_.testedType->IsETSArrayType()) {
1407 auto *const testedType = testCondition_.testedType->AsETSArrayType();
1408 std::tie(consequentType, alternateType) = checker->CheckTestObjectCondition(testedType, smartType);
1409 } else if (testCondition_.strict) {
1410 std::tie(consequentType, alternateType) =
1411 ComputeConditionalSubtypes(checker->Relation(), testCondition_.testedType, smartType);
1412 }
1413
1414 return !testCondition_.negate
1415 ? std::make_optional(std::make_tuple(testCondition_.variable, consequentType, alternateType))
1416 : std::make_optional(std::make_tuple(testCondition_.variable, alternateType, consequentType));
1417 }
CheckVoidAnnotation(const ir::ETSPrimitiveType * typeAnnotation)1418 bool ETSChecker::CheckVoidAnnotation(const ir::ETSPrimitiveType *typeAnnotation)
1419 {
1420 // Void annotation is valid only when used as 'return type' , 'type parameter instantiation', 'default type'.
1421 if (typeAnnotation->GetPrimitiveType() != ir::PrimitiveType::VOID) {
1422 return true;
1423 }
1424
1425 auto parent = typeAnnotation->Parent();
1426 if (parent->IsScriptFunction() && parent->AsScriptFunction()->ReturnTypeAnnotation() == typeAnnotation) {
1427 return true;
1428 }
1429 if (parent->IsETSFunctionType() && parent->AsETSFunctionType()->ReturnType() == typeAnnotation) {
1430 return true;
1431 }
1432 if (parent->IsTSTypeParameterInstantiation() || parent->IsTSTypeParameter()) {
1433 return true;
1434 }
1435 LogError(diagnostic::ANNOT_IS_VOID, {}, typeAnnotation->Start());
1436 return false;
1437 }
ApplySmartCast(varbinder::Variable const * const variable,checker::Type * const smartType)1438 void ETSChecker::ApplySmartCast(varbinder::Variable const *const variable, checker::Type *const smartType) noexcept
1439 {
1440 ES2PANDA_ASSERT(variable != nullptr);
1441 if (smartType != nullptr) {
1442 auto *variableType = variable->TsType();
1443
1444 if (Relation()->IsIdenticalTo(variableType, smartType)) {
1445 Context().RemoveSmartCast(variable);
1446 } else {
1447 Context().SetSmartCast(variable, smartType);
1448 }
1449 }
1450 }
1451
CheckTestOrSmartCastCondition(SmartCastTuple const & types)1452 bool CheckerContext::CheckTestOrSmartCastCondition(SmartCastTuple const &types)
1453 {
1454 auto *const &variable = std::get<VARIABLE_POSITION>(types);
1455 auto *const &consequentTypeNew = std::get<CONSEQUENT_TYPE_POSITION>(types);
1456 auto *const &alternateTypeNew = std::get<ALTERNATE_TYPE_POSITION>(types);
1457
1458 if (auto const it = testSmartCasts_.find(variable); it != testSmartCasts_.end()) {
1459 auto *const consequentTypeOld = it->second.first;
1460 if (consequentTypeOld == nullptr) {
1461 return true;
1462 }
1463
1464 if (consequentTypeNew != nullptr && !parent_->Relation()->IsIdenticalTo(consequentTypeOld, consequentTypeNew)) {
1465 it->second.first = parent_->AsETSChecker()->CreateETSUnionType({consequentTypeOld, consequentTypeNew});
1466 }
1467
1468 if (auto *const alternateTypeOld = it->second.second; alternateTypeOld != nullptr) {
1469 if (alternateTypeNew != nullptr &&
1470 !parent_->Relation()->IsIdenticalTo(alternateTypeOld, alternateTypeNew)) {
1471 it->second.second = parent_->AsETSChecker()->CreateETSUnionType({alternateTypeOld, alternateTypeNew});
1472 }
1473 } else {
1474 it->second.second = alternateTypeNew;
1475 }
1476
1477 return false;
1478 }
1479
1480 // NOTE: now we support only cases like 'if (x != null || y != null)' or 'if (x instanceof A || x instanceof B)'
1481 // although it seems that the resulting variable type in the second case isn't used in subsequent code directly.
1482 // More complex conditions can be implemented later on if the need arises.
1483 testSmartCasts_.emplace(variable, std::make_pair(consequentTypeNew, alternateTypeNew));
1484 return true;
1485 }
1486
1487 //==============================================================================//
1488
SetArrayPreferredTypeForNestedMemberExpressions(ir::MemberExpression * expr,Type * annotationType)1489 void ETSChecker::SetArrayPreferredTypeForNestedMemberExpressions(ir::MemberExpression *expr, Type *annotationType)
1490 {
1491 if ((expr == nullptr) || (annotationType == nullptr)) {
1492 return;
1493 }
1494
1495 if (expr->Kind() != ir::MemberExpressionKind::ELEMENT_ACCESS) {
1496 return;
1497 }
1498
1499 // Expand all member expressions
1500 Type *elementType = annotationType;
1501 ir::Expression *object = expr->Object();
1502 while ((object != nullptr) && (object->IsMemberExpression())) {
1503 ir::MemberExpression *memberExpr = object->AsMemberExpression();
1504 if (memberExpr->Kind() != ir::MemberExpressionKind::ELEMENT_ACCESS) {
1505 return;
1506 }
1507
1508 object = memberExpr->Object();
1509 elementType = CreateETSArrayType(elementType);
1510 }
1511
1512 // Set explicit target type for array
1513 if ((object != nullptr) && (object->IsArrayExpression())) {
1514 ir::ArrayExpression *array = object->AsArrayExpression();
1515 array->SetPreferredType(CreateETSArrayType(elementType));
1516 }
1517 }
1518
1519 // 22955: type alias should be instantiated with Substitute
CollectAliasParametersForBoxing(Type * expandedAliasType,std::set<Type * > & parametersNeedToBeBoxed,bool needToBeBoxed)1520 static void CollectAliasParametersForBoxing(Type *expandedAliasType, std::set<Type *> ¶metersNeedToBeBoxed,
1521 bool needToBeBoxed)
1522 {
1523 if (expandedAliasType->IsETSTypeParameter() && needToBeBoxed) {
1524 parametersNeedToBeBoxed.insert(expandedAliasType);
1525 } else if (expandedAliasType->IsETSObjectType()) {
1526 auto objectType = expandedAliasType->AsETSObjectType();
1527 needToBeBoxed = objectType->GetDeclNode() != nullptr && (objectType->GetDeclNode()->IsClassDefinition() ||
1528 objectType->GetDeclNode()->IsTSInterfaceDeclaration());
1529 for (const auto typeArgument : objectType->TypeArguments()) {
1530 CollectAliasParametersForBoxing(typeArgument, parametersNeedToBeBoxed, needToBeBoxed);
1531 }
1532 } else if (expandedAliasType->IsETSTupleType()) {
1533 auto tupleType = expandedAliasType->AsETSTupleType();
1534 needToBeBoxed = false;
1535 for (auto type : tupleType->GetTupleTypesList()) {
1536 CollectAliasParametersForBoxing(type, parametersNeedToBeBoxed, needToBeBoxed);
1537 }
1538 } else if (expandedAliasType->IsETSArrayType()) {
1539 auto arrayType = expandedAliasType->AsETSArrayType();
1540 needToBeBoxed = false;
1541 auto elementType = arrayType->ElementType();
1542 CollectAliasParametersForBoxing(elementType, parametersNeedToBeBoxed, needToBeBoxed);
1543 } else if (expandedAliasType->IsETSUnionType()) {
1544 auto unionType = expandedAliasType->AsETSUnionType();
1545 needToBeBoxed = false;
1546 for (auto type : unionType->ConstituentTypes()) {
1547 CollectAliasParametersForBoxing(type, parametersNeedToBeBoxed, needToBeBoxed);
1548 }
1549 } else if (expandedAliasType->IsETSFunctionType()) {
1550 auto functionType = expandedAliasType->AsETSFunctionType();
1551 needToBeBoxed = true;
1552 for (auto param : functionType->ArrowSignature()->Params()) {
1553 CollectAliasParametersForBoxing(param->TsType(), parametersNeedToBeBoxed, needToBeBoxed);
1554 }
1555 CollectAliasParametersForBoxing(functionType->ArrowSignature()->ReturnType(), parametersNeedToBeBoxed,
1556 needToBeBoxed);
1557 }
1558 }
1559
CheckMinimumTypeArgsPresent(const ir::TSTypeAliasDeclaration * typeAliasNode,const ir::TSTypeParameterInstantiation * typeParams)1560 bool ETSChecker::CheckMinimumTypeArgsPresent(const ir::TSTypeAliasDeclaration *typeAliasNode,
1561 const ir::TSTypeParameterInstantiation *typeParams)
1562 {
1563 size_t minNumberOfTypeParams =
1564 std::count_if(typeAliasNode->TypeParams()->Params().begin(), typeAliasNode->TypeParams()->Params().end(),
1565 [](const ir::TSTypeParameter *param) { return param->DefaultType() == nullptr; });
1566 if (minNumberOfTypeParams > typeParams->Params().size() ||
1567 typeParams->Params().size() > typeAliasNode->TypeParams()->Params().size()) {
1568 LogError(diagnostic::EXPECTED_TYPE_ARGUMENTS, {minNumberOfTypeParams, typeParams->Params().size()},
1569 typeParams->Start());
1570 return true;
1571 }
1572
1573 return false;
1574 }
1575
ResolveTypeNodeForTypeArg(const ir::TSTypeAliasDeclaration * typeAliasNode,const ir::TSTypeParameterInstantiation * typeParams,size_t idx)1576 ir::TypeNode *ETSChecker::ResolveTypeNodeForTypeArg(const ir::TSTypeAliasDeclaration *typeAliasNode,
1577 const ir::TSTypeParameterInstantiation *typeParams, size_t idx)
1578 {
1579 if (typeParams != nullptr && typeParams->Params().size() > idx) {
1580 return typeParams->Params().at(idx);
1581 }
1582
1583 return typeAliasNode->TypeParams()->Params().at(idx)->DefaultType();
1584 }
1585
HandleTypeAlias(ir::Expression * const name,const ir::TSTypeParameterInstantiation * const typeParams,ir::TSTypeAliasDeclaration * const typeAliasNode)1586 Type *ETSChecker::HandleTypeAlias(ir::Expression *const name, const ir::TSTypeParameterInstantiation *const typeParams,
1587 ir::TSTypeAliasDeclaration *const typeAliasNode)
1588 {
1589 if (typeParams == nullptr && typeAliasNode->TypeParams() != nullptr) {
1590 auto declTypeParams = typeAliasNode->TypeParams()->Params();
1591 auto isAllTypeParamsHasDefaultType =
1592 std::find_if(declTypeParams.begin(), declTypeParams.end(), [](ir::TSTypeParameter *param) {
1593 return param->DefaultType() == nullptr;
1594 }) == declTypeParams.end();
1595 if (!isAllTypeParamsHasDefaultType) {
1596 LogError(diagnostic::GENERIC_ALIAS_WITHOUT_PARAMS, {}, name->Start());
1597 return GlobalTypeError();
1598 }
1599 }
1600
1601 if (typeParams != nullptr && typeAliasNode->TypeParams() == nullptr) {
1602 LogError(diagnostic::NON_GENERIC_ALIAS_WITH_PARAMS, {}, typeParams->Start());
1603 return GlobalTypeError();
1604 }
1605
1606 if (typeParams == nullptr && typeAliasNode->TypeParams() == nullptr) {
1607 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
1608 return GetReferencedTypeBase(name);
1609 }
1610
1611 if (typeParams != nullptr) {
1612 for (auto *const origTypeParam : typeParams->Params()) {
1613 origTypeParam->Check(this);
1614 }
1615 if (CheckMinimumTypeArgsPresent(typeAliasNode, typeParams)) {
1616 return GlobalTypeError();
1617 }
1618 }
1619
1620 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
1621 Type *const aliasType = GetReferencedTypeBase(name);
1622 auto *substitution = NewSubstitution();
1623
1624 std::set<Type *> parametersNeedToBeBoxed;
1625 auto expandedAliasType = aliasType->Substitute(Relation(), substitution);
1626 CollectAliasParametersForBoxing(expandedAliasType, parametersNeedToBeBoxed, false);
1627
1628 ES2PANDA_ASSERT(substitution != nullptr);
1629 for (std::size_t idx = 0; idx < typeAliasNode->TypeParams()->Params().size(); ++idx) {
1630 auto *typeAliasTypeName = typeAliasNode->TypeParams()->Params().at(idx)->Name();
1631 auto *typeAliasType = typeAliasTypeName->Variable()->TsType();
1632 if (!typeAliasType->IsETSTypeParameter()) {
1633 continue;
1634 }
1635
1636 ir::TypeNode *typeNode = ResolveTypeNodeForTypeArg(typeAliasNode, typeParams, idx);
1637 auto paramType = typeNode->GetType(this);
1638
1639 if (parametersNeedToBeBoxed.find(typeAliasType) != parametersNeedToBeBoxed.end()) {
1640 if (const auto boxedType = MaybeBoxInRelation(typeNode->GetType(this)); boxedType != nullptr) {
1641 paramType = boxedType;
1642 }
1643 }
1644 substitution->insert({typeAliasType->AsETSTypeParameter(), paramType}); // #21835: type argument is not boxed
1645 }
1646
1647 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
1648 ValidateGenericTypeAliasForClonedNode(typeAliasNode->AsTSTypeAliasDeclaration(), typeParams);
1649
1650 return aliasType->Substitute(Relation(), substitution);
1651 }
1652
GetNameForSynteticObjectType(const util::StringView & source)1653 std::vector<util::StringView> ETSChecker::GetNameForSynteticObjectType(const util::StringView &source)
1654 {
1655 const std::string str = source.Mutf8();
1656 std::istringstream ss {str};
1657 const char delimiter = '.';
1658 std::string token;
1659
1660 std::vector<util::StringView> syntheticName {};
1661
1662 while (std::getline(ss, token, delimiter)) {
1663 if (!token.empty()) {
1664 util::UString sV(token, ProgramAllocator());
1665 syntheticName.emplace_back(sV.View());
1666 }
1667 }
1668
1669 return syntheticName;
1670 }
1671
FindSpecifierForModuleObject(ir::ETSImportDeclaration * importDecl,util::StringView const & name)1672 std::pair<bool, util::StringView> FindSpecifierForModuleObject(ir::ETSImportDeclaration *importDecl,
1673 util::StringView const &name)
1674 {
1675 if (importDecl == nullptr) {
1676 return std::make_pair(true, util::StringView());
1677 }
1678
1679 for (auto item : importDecl->Specifiers()) {
1680 if (item->IsImportSpecifier() && item->AsImportSpecifier()->Imported()->Name().Is(name.Mutf8())) {
1681 if (!item->AsImportSpecifier()->Imported()->Name().Is(item->AsImportSpecifier()->Local()->Name().Mutf8())) {
1682 return std::make_pair(true, item->AsImportSpecifier()->Local()->Name());
1683 }
1684 return std::make_pair(true, util::StringView());
1685 }
1686 }
1687 return std::make_pair(false, util::StringView());
1688 }
1689
1690 template <checker::PropertyType TYPE>
BindingsModuleObjectAddProperty(checker::ETSObjectType * moduleObjType,ir::ETSImportDeclaration * importDecl,const varbinder::Scope::VariableMap & bindings,const util::StringView & importPath)1691 void ETSChecker::BindingsModuleObjectAddProperty(checker::ETSObjectType *moduleObjType,
1692 ir::ETSImportDeclaration *importDecl,
1693 const varbinder::Scope::VariableMap &bindings,
1694 const util::StringView &importPath)
1695 {
1696 for (auto [_, var] : bindings) {
1697 (void)_;
1698 auto [found, aliasedName] = FindSpecifierForModuleObject(importDecl, var->AsLocalVariable()->Name());
1699 if ((var->AsLocalVariable()->Declaration()->Node()->IsExported() ||
1700 var->AsLocalVariable()->Declaration()->Node()->HasExportAlias()) &&
1701 found) {
1702 if (!aliasedName.Empty()) {
1703 moduleObjType->AddReExportAlias(var->Declaration()->Name(), aliasedName);
1704 }
1705 moduleObjType->AddProperty<TYPE>(
1706 var->AsLocalVariable(), FindPropNameForNamespaceImport(var->AsLocalVariable()->Name(), importPath));
1707 }
1708 }
1709 }
1710
FindPropNameForNamespaceImport(const util::StringView & originalName,const util::StringView & importPath)1711 util::StringView ETSChecker::FindPropNameForNamespaceImport(const util::StringView &originalName,
1712 const util::StringView &importPath)
1713 {
1714 auto exportAliases = VarBinder()->AsETSBinder()->GetSelectiveExportAliasMultimap();
1715 auto relatedMapItem = exportAliases.find(importPath);
1716 if (relatedMapItem != exportAliases.end()) {
1717 if (auto result = std::find_if(relatedMapItem->second.begin(), relatedMapItem->second.end(),
1718 [originalName](const auto &item) { return item.second.first == originalName; });
1719 result != relatedMapItem->second.end()) {
1720 return result->first;
1721 }
1722 }
1723
1724 return originalName;
1725 }
1726
1727 // Helps to prevent searching for the imported file among external sources if it is the entry program
SelectEntryOrExternalProgram(varbinder::ETSBinder * etsBinder,const util::StringView & importPath)1728 static parser::Program *SelectEntryOrExternalProgram(varbinder::ETSBinder *etsBinder,
1729 const util::StringView &importPath)
1730 {
1731 if (importPath.Is(etsBinder->GetGlobalRecordTable()->Program()->AbsoluteName().Mutf8())) {
1732 return etsBinder->GetGlobalRecordTable()->Program();
1733 }
1734
1735 auto programList = etsBinder->GetProgramList(importPath);
1736 return !programList.empty() ? programList.front() : nullptr;
1737 }
1738
SetPropertiesForModuleObject(checker::ETSObjectType * moduleObjType,const util::StringView & importPath,ir::ETSImportDeclaration * importDecl)1739 void ETSChecker::SetPropertiesForModuleObject(checker::ETSObjectType *moduleObjType, const util::StringView &importPath,
1740 ir::ETSImportDeclaration *importDecl)
1741 {
1742 parser::Program *program =
1743 SelectEntryOrExternalProgram(static_cast<varbinder::ETSBinder *>(VarBinder()), importPath);
1744 // Check imported properties before assigning them to module object
1745 ES2PANDA_ASSERT(program != nullptr);
1746 if (!program->IsASTChecked()) {
1747 // NOTE: helps to avoid endless loop in case of recursive imports that uses all bindings
1748 program->SetASTChecked();
1749 program->Ast()->Check(this);
1750 }
1751
1752 BindingsModuleObjectAddProperty<checker::PropertyType::STATIC_FIELD>(
1753 moduleObjType, importDecl, program->GlobalClassScope()->StaticFieldScope()->Bindings(), importPath);
1754
1755 BindingsModuleObjectAddProperty<checker::PropertyType::STATIC_METHOD>(
1756 moduleObjType, importDecl, program->GlobalClassScope()->StaticMethodScope()->Bindings(), importPath);
1757
1758 BindingsModuleObjectAddProperty<checker::PropertyType::STATIC_DECL>(
1759 moduleObjType, importDecl, program->GlobalClassScope()->StaticDeclScope()->Bindings(), importPath);
1760
1761 BindingsModuleObjectAddProperty<checker::PropertyType::STATIC_DECL>(
1762 moduleObjType, importDecl, program->GlobalClassScope()->InstanceDeclScope()->Bindings(), importPath);
1763
1764 BindingsModuleObjectAddProperty<checker::PropertyType::STATIC_DECL>(
1765 moduleObjType, importDecl, program->GlobalClassScope()->TypeAliasScope()->Bindings(), importPath);
1766 }
1767
SetrModuleObjectTsType(ir::Identifier * local,checker::ETSObjectType * moduleObjType)1768 void ETSChecker::SetrModuleObjectTsType(ir::Identifier *local, checker::ETSObjectType *moduleObjType)
1769 {
1770 auto *etsBinder = static_cast<varbinder::ETSBinder *>(VarBinder());
1771
1772 for (auto [bindingName, var] : etsBinder->TopScope()->Bindings()) {
1773 if (bindingName.Is(local->Name().Mutf8())) {
1774 var->SetTsType(moduleObjType);
1775 }
1776 }
1777 }
1778
GetReferencedTypeFromBase(Type * baseType,ir::Expression * name)1779 Type *ETSChecker::GetReferencedTypeFromBase([[maybe_unused]] Type *baseType, [[maybe_unused]] ir::Expression *name)
1780 {
1781 return TypeError(name, diagnostic::INVALID_TYPE_REF, name->Start());
1782 }
1783
GetReferencedTypeBase(ir::Expression * name)1784 Type *ETSChecker::GetReferencedTypeBase(ir::Expression *name)
1785 {
1786 if (name->IsTSQualifiedName()) {
1787 return name->Check(this);
1788 }
1789
1790 ES2PANDA_ASSERT(name->IsIdentifier());
1791 if (name->AsIdentifier()->Variable() == nullptr) {
1792 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
1793 VarBinder()->AsETSBinder()->LookupTypeReference(name->AsIdentifier(), false);
1794 }
1795
1796 auto *const var = name->AsIdentifier()->Variable();
1797 ES2PANDA_ASSERT(var != nullptr);
1798
1799 if (var->TsType() != nullptr && var->TsType()->IsTypeError()) {
1800 return name->SetTsType(GlobalTypeError());
1801 }
1802
1803 auto *importData = VarBinder()->AsETSBinder()->DynamicImportDataForVar(var);
1804 if (importData != nullptr && importData->import->IsPureDynamic()) {
1805 return name->SetTsType(GlobalBuiltinDynamicType(importData->import->Language()));
1806 }
1807
1808 return name->SetTsType(ResolveReferencedType(var->AsLocalVariable(), name));
1809 }
1810
ResolveReferencedType(varbinder::LocalVariable * refVar,const ir::Expression * name)1811 Type *ETSChecker::ResolveReferencedType(varbinder::LocalVariable *refVar, const ir::Expression *name)
1812 {
1813 switch (refVar->Declaration()->Node()->Type()) {
1814 case ir::AstNodeType::TS_INTERFACE_DECLARATION:
1815 return GetTypeFromInterfaceReference(refVar);
1816 case ir::AstNodeType::TS_ENUM_DECLARATION:
1817 return GlobalTypeError();
1818 case ir::AstNodeType::CLASS_DECLARATION:
1819 case ir::AstNodeType::STRUCT_DECLARATION:
1820 case ir::AstNodeType::CLASS_DEFINITION:
1821 if (refVar->Declaration()->Node()->AsClassDefinition()->IsNamespaceTransformed()) {
1822 LogError(diagnostic::NAMESPACE_AS_TYPE, {refVar->Name()}, name->Start());
1823 return GlobalTypeError();
1824 }
1825 return GetTypeFromClassReference(refVar);
1826 case ir::AstNodeType::TS_TYPE_PARAMETER:
1827 return GetTypeFromTypeParameterReference(refVar, name->Start());
1828 case ir::AstNodeType::TS_TYPE_ALIAS_DECLARATION:
1829 return GetTypeFromTypeAliasReference(refVar);
1830 case ir::AstNodeType::ANNOTATION_DECLARATION:
1831 LogError(diagnostic::ANNOTATION_AS_TYPE, {}, name->Start());
1832 return GlobalTypeError();
1833
1834 default:
1835 ES2PANDA_UNREACHABLE();
1836 }
1837 }
1838
GetElementTypeOfArray(checker::Type * type)1839 checker::Type *ETSChecker::GetElementTypeOfArray(checker::Type *type)
1840 {
1841 if (type->IsTypeError()) {
1842 return GlobalTypeError();
1843 }
1844 if (type->IsETSArrayType()) {
1845 return type->AsETSArrayType()->ElementType();
1846 }
1847
1848 ES2PANDA_ASSERT(type->IsETSResizableArrayType());
1849 return type->AsETSResizableArrayType()->ElementType();
1850 }
1851
GetElementTypeOfArray(const checker::Type * type) const1852 const checker::Type *ETSChecker::GetElementTypeOfArray(const checker::Type *type) const
1853 {
1854 if (type->IsETSArrayType()) {
1855 return type->AsETSArrayType()->ElementType();
1856 }
1857
1858 ES2PANDA_ASSERT(type->IsETSResizableArrayType());
1859 return type->AsETSResizableArrayType()->ElementType();
1860 }
1861
ConcatConstantString(util::UString & target,Type * type)1862 void ETSChecker::ConcatConstantString(util::UString &target, Type *type)
1863 {
1864 switch (ETSType(type)) {
1865 case TypeFlag::ETS_OBJECT: {
1866 ES2PANDA_ASSERT(type->IsETSStringType());
1867 target.Append(type->AsETSStringType()->GetValue());
1868 break;
1869 }
1870 case TypeFlag::ETS_BOOLEAN: {
1871 target.Append(type->AsETSBooleanType()->GetValue() ? "true" : "false");
1872 break;
1873 }
1874 case TypeFlag::BYTE: {
1875 target.Append(std::to_string(type->AsByteType()->GetValue()));
1876 break;
1877 }
1878 case TypeFlag::CHAR: {
1879 std::string s(1, type->AsCharType()->GetValue());
1880 target.Append(s);
1881 break;
1882 }
1883 case TypeFlag::SHORT: {
1884 target.Append(std::to_string(type->AsShortType()->GetValue()));
1885 break;
1886 }
1887 case TypeFlag::INT: {
1888 target.Append(std::to_string(type->AsIntType()->GetValue()));
1889 break;
1890 }
1891 case TypeFlag::LONG: {
1892 target.Append(std::to_string(type->AsLongType()->GetValue()));
1893 break;
1894 }
1895 case TypeFlag::FLOAT: {
1896 target.Append(std::to_string(type->AsFloatType()->GetValue()));
1897 break;
1898 }
1899 case TypeFlag::DOUBLE: {
1900 target.Append(std::to_string(type->AsDoubleType()->GetValue()));
1901 break;
1902 }
1903 default: {
1904 ES2PANDA_UNREACHABLE();
1905 }
1906 }
1907 }
1908
HandleStringConcatenation(Type * leftType,Type * rightType)1909 Type *ETSChecker::HandleStringConcatenation(Type *leftType, Type *rightType)
1910 {
1911 ES2PANDA_ASSERT(leftType->IsETSStringType() || rightType->IsETSStringType());
1912
1913 if (!leftType->HasTypeFlag(checker::TypeFlag::CONSTANT) || !rightType->HasTypeFlag(checker::TypeFlag::CONSTANT) ||
1914 leftType->IsETSBigIntType() || rightType->IsETSBigIntType()) {
1915 return GlobalETSStringLiteralType();
1916 }
1917
1918 util::UString concatenated(ProgramAllocator());
1919 ConcatConstantString(concatenated, leftType);
1920 ConcatConstantString(concatenated, rightType);
1921
1922 return CreateETSStringLiteralType(concatenated.View());
1923 }
1924
FindFunctionInVectorGivenByName(util::StringView name,ArenaVector<checker::ETSFunctionType * > & list)1925 checker::ETSFunctionType *ETSChecker::FindFunctionInVectorGivenByName(util::StringView name,
1926 ArenaVector<checker::ETSFunctionType *> &list)
1927 {
1928 for (auto *it : list) {
1929 if (it->Name() == name) {
1930 return it;
1931 }
1932 }
1933
1934 return nullptr;
1935 }
1936
IsFunctionContainsSignature(checker::ETSFunctionType * funcType,Signature * signature)1937 bool ETSChecker::IsFunctionContainsSignature(checker::ETSFunctionType *funcType, Signature *signature)
1938 {
1939 for (auto *it : funcType->CallSignatures()) {
1940 Relation()->SignatureIsSupertypeOf(it, signature);
1941 if (Relation()->IsTrue()) {
1942 return true;
1943 }
1944 }
1945
1946 return false;
1947 }
1948
CheckFunctionContainsClashingSignature(const checker::ETSFunctionType * funcType,Signature * signature)1949 bool ETSChecker::CheckFunctionContainsClashingSignature(const checker::ETSFunctionType *funcType, Signature *signature)
1950 {
1951 for (auto *it : funcType->CallSignatures()) {
1952 SavedTypeRelationFlagsContext strfCtx(Relation(), TypeRelationFlag::NONE);
1953 Relation()->SignatureIsSupertypeOf(it, signature);
1954 if (Relation()->IsTrue() && it->Function()->Id()->Name() == signature->Function()->Id()->Name()) {
1955 std::stringstream ss;
1956 it->ToString(ss, nullptr, true);
1957 auto sigStr1 = ss.str();
1958 ss.str(std::string {}); // Clear buffer
1959 signature->ToString(ss, nullptr, true);
1960 auto sigStr2 = ss.str();
1961 LogError(
1962 diagnostic::FUNCTION_REDECLERATION,
1963 {it->Function()->Id()->Name(), sigStr1.c_str(), signature->Function()->Id()->Name(), sigStr2.c_str()},
1964 signature->Function()->ReturnTypeAnnotation()->Start());
1965 return false;
1966 }
1967 }
1968 return true;
1969 }
1970
MergeSignatures(checker::ETSFunctionType * target,checker::ETSFunctionType * source)1971 void ETSChecker::MergeSignatures(checker::ETSFunctionType *target, checker::ETSFunctionType *source)
1972 {
1973 for (auto *s : source->CallSignatures()) {
1974 if (IsFunctionContainsSignature(target, s)) {
1975 continue;
1976 }
1977
1978 if (!CheckFunctionContainsClashingSignature(target, s)) {
1979 continue;
1980 }
1981 target->AddCallSignature(s);
1982 }
1983 }
1984
MergeComputedAbstracts(ArenaVector<checker::ETSFunctionType * > & merged,ArenaVector<checker::ETSFunctionType * > & current)1985 void ETSChecker::MergeComputedAbstracts(ArenaVector<checker::ETSFunctionType *> &merged,
1986 ArenaVector<checker::ETSFunctionType *> ¤t)
1987 {
1988 for (auto *curr : current) {
1989 auto name = curr->Name();
1990 auto *found = FindFunctionInVectorGivenByName(name, merged);
1991 if (found != nullptr) {
1992 MergeSignatures(found, curr);
1993 continue;
1994 }
1995
1996 merged.push_back(curr);
1997 }
1998 }
1999
FindAncestorGivenByType(ir::AstNode * node,ir::AstNodeType type,const ir::AstNode * endNode)2000 ir::AstNode *ETSChecker::FindAncestorGivenByType(ir::AstNode *node, ir::AstNodeType type, const ir::AstNode *endNode)
2001 {
2002 auto *iter = node->Parent();
2003
2004 while (iter != endNode) {
2005 if (iter->Type() == type) {
2006 return iter;
2007 }
2008
2009 iter = iter->Parent();
2010 }
2011
2012 return nullptr;
2013 }
2014
GetContainingObjectNameFromSignature(Signature * signature)2015 util::StringView ETSChecker::GetContainingObjectNameFromSignature(Signature *signature)
2016 {
2017 ES2PANDA_ASSERT(signature->Function());
2018 auto *iter = signature->Function()->Parent();
2019
2020 while (iter != nullptr) {
2021 if (iter->IsClassDefinition()) {
2022 return iter->AsClassDefinition()->Ident()->Name();
2023 }
2024
2025 if (iter->IsTSInterfaceDeclaration()) {
2026 return iter->AsTSInterfaceDeclaration()->Id()->Name();
2027 }
2028
2029 iter = iter->Parent();
2030 }
2031
2032 ES2PANDA_UNREACHABLE();
2033 return {""};
2034 }
2035
GetAccessFlagFromNode(const ir::AstNode * node)2036 varbinder::VariableFlags ETSChecker::GetAccessFlagFromNode(const ir::AstNode *node)
2037 {
2038 if (node->IsPrivate()) {
2039 return varbinder::VariableFlags::PRIVATE;
2040 }
2041
2042 if (node->IsProtected()) {
2043 return varbinder::VariableFlags::PROTECTED;
2044 }
2045
2046 return varbinder::VariableFlags::PUBLIC;
2047 }
2048
CheckSwitchDiscriminant(ir::Expression * discriminant)2049 Type *ETSChecker::CheckSwitchDiscriminant(ir::Expression *discriminant)
2050 {
2051 discriminant->Check(this);
2052 auto *discriminantType = GetNonConstantType(MaybeUnboxExpression(discriminant));
2053 ES2PANDA_ASSERT(discriminantType != nullptr);
2054 if (!discriminantType->HasTypeFlag(TypeFlag::VALID_SWITCH_TYPE)) {
2055 if (!(discriminantType->IsETSObjectType() &&
2056 discriminantType->AsETSObjectType()->HasObjectFlag(
2057 ETSObjectFlags::BUILTIN_STRING | ETSObjectFlags::STRING | ETSObjectFlags::ENUM_OBJECT))) {
2058 LogError(diagnostic::ENUM_INVALID_DISCRIMINANT, {discriminantType}, discriminant->Start());
2059 }
2060 }
2061
2062 return discriminantType;
2063 }
2064
AddBoxingUnboxingFlagsToNode(ir::AstNode * node,Type * boxingUnboxingType)2065 void ETSChecker::AddBoxingUnboxingFlagsToNode(ir::AstNode *node, Type *boxingUnboxingType)
2066 {
2067 if (boxingUnboxingType->IsETSObjectType()) {
2068 node->AddBoxingUnboxingFlags(GetBoxingFlag(boxingUnboxingType));
2069 } else if (!boxingUnboxingType->IsETSUnionType()) {
2070 node->AddBoxingUnboxingFlags(GetUnboxingFlag(boxingUnboxingType));
2071 }
2072 }
2073
MaybeBoxExpression(ir::Expression * expr)2074 Type *ETSChecker::MaybeBoxExpression(ir::Expression *expr)
2075 {
2076 auto *promoted = MaybeBoxType(expr->TsType());
2077 if (promoted != expr->TsType()) {
2078 expr->AddBoxingUnboxingFlags(GetBoxingFlag(promoted));
2079 }
2080 return promoted;
2081 }
2082
MaybeUnboxExpression(ir::Expression * expr)2083 Type *ETSChecker::MaybeUnboxExpression(ir::Expression *expr)
2084 {
2085 auto *primitive = MaybeUnboxType(expr->TsType());
2086 if (primitive != expr->TsType()) {
2087 expr->AddBoxingUnboxingFlags(GetUnboxingFlag(primitive));
2088 }
2089 return primitive;
2090 }
2091
CheckForSameSwitchCases(ArenaVector<ir::SwitchCaseStatement * > const & cases)2092 void ETSChecker::CheckForSameSwitchCases(ArenaVector<ir::SwitchCaseStatement *> const &cases)
2093 {
2094 CheckItemCasesConstant(cases);
2095 CheckItemCasesDuplicate(cases);
2096 }
2097
GetStringFromIdentifierValue(checker::Type * caseType) const2098 std::string ETSChecker::GetStringFromIdentifierValue(checker::Type *caseType) const
2099 {
2100 if (caseType->IsETSStringType()) {
2101 return std::string(caseType->AsETSStringType()->GetValue());
2102 }
2103 const auto identifierTypeKind = ETSChecker::TypeKind(caseType);
2104 switch (identifierTypeKind) {
2105 case TypeFlag::BYTE: {
2106 return std::to_string(caseType->AsByteType()->GetValue());
2107 }
2108 case TypeFlag::SHORT: {
2109 return std::to_string(caseType->AsShortType()->GetValue());
2110 }
2111 case TypeFlag::CHAR: {
2112 return std::to_string(caseType->AsCharType()->GetValue());
2113 }
2114 case TypeFlag::INT: {
2115 return std::to_string(caseType->AsIntType()->GetValue());
2116 }
2117 case TypeFlag::LONG: {
2118 return std::to_string(caseType->AsLongType()->GetValue());
2119 }
2120 case TypeFlag::ETS_OBJECT: {
2121 VarBinder()->ThrowError(caseType->AsETSObjectType()->Variable()->Declaration()->Node()->Start(),
2122 diagnostic::NOT_IMPLEMENTED);
2123 return "error";
2124 }
2125 default: {
2126 ES2PANDA_UNREACHABLE();
2127 }
2128 }
2129 }
2130
IsConstantMemberOrIdentifierExpression(ir::Expression * expression,bool checkForConst=false)2131 bool IsConstantMemberOrIdentifierExpression(ir::Expression *expression, bool checkForConst = false)
2132 {
2133 varbinder::Variable *var = nullptr;
2134 if (expression->IsIdentifier()) {
2135 var = expression->AsIdentifier()->Variable();
2136 } else if (expression->IsMemberExpression()) {
2137 var = expression->AsMemberExpression()->PropVar();
2138 }
2139
2140 if (var == nullptr) {
2141 return false;
2142 }
2143 bool isConst = checkForConst ? (var->TsType()->HasTypeFlag(checker::TypeFlag::CONSTANT)) : true;
2144 return ((var->Declaration()->IsConstDecl() && isConst) ||
2145 (var->Declaration()->IsReadonlyDecl() && var->HasFlag(varbinder::VariableFlags::STATIC)));
2146 }
2147
IsValidSwitchType(checker::Type * caseType)2148 static bool IsValidSwitchType(checker::Type *caseType)
2149 {
2150 return caseType->HasTypeFlag(checker::TypeFlag::VALID_SWITCH_TYPE) || caseType->IsETSStringType() ||
2151 caseType->IsETSEnumType();
2152 }
2153
CheckEnumCaseUnqualified(ETSChecker * checker,ir::Expression const * const caseTest)2154 void CheckEnumCaseUnqualified(ETSChecker *checker, ir::Expression const *const caseTest)
2155 {
2156 if (!caseTest->IsMemberExpression()) {
2157 checker->LogError(diagnostic::SWITCH_ENUM_NOT_UNQUALIFIED_ENUM_CONST, {}, caseTest->Start());
2158 return;
2159 }
2160
2161 auto caseMember = caseTest->AsMemberExpression();
2162 auto baseObject = caseMember->Object();
2163
2164 util::StringView enumName;
2165 if (baseObject->IsIdentifier()) {
2166 enumName = baseObject->AsIdentifier()->Name();
2167 } else if (baseObject->IsMemberExpression()) {
2168 enumName = baseObject->AsMemberExpression()->Property()->AsIdentifier()->Name();
2169 } else {
2170 checker->LogError(diagnostic::SWITCH_ENUM_NOT_UNQUALIFIED_ENUM_CONST, {}, caseTest->Start());
2171 }
2172
2173 auto enumType = caseTest->TsType()->AsETSObjectType();
2174 if (enumName != enumType->Name()) {
2175 checker->LogError(diagnostic::SWITCH_ENUM_NOT_UNQUALIFIED_ENUM_CONST, {}, caseTest->Start());
2176 }
2177 }
2178
CheckItemCasesConstant(ArenaVector<ir::SwitchCaseStatement * > const & cases)2179 void ETSChecker::CheckItemCasesConstant(ArenaVector<ir::SwitchCaseStatement *> const &cases)
2180 {
2181 for (auto &it : cases) {
2182 auto *caseTest = it->Test();
2183 if (caseTest == nullptr) {
2184 continue;
2185 }
2186 auto *caseType = caseTest->TsType();
2187 if (caseType->HasTypeFlag(TypeFlag::TYPE_ERROR)) {
2188 continue;
2189 }
2190
2191 if (caseTest->TsType()->IsETSEnumType()) {
2192 CheckEnumCaseUnqualified(this, caseTest);
2193 continue;
2194 }
2195
2196 if (caseTest->IsIdentifier() || caseTest->IsMemberExpression()) {
2197 if (!IsConstantMemberOrIdentifierExpression(caseTest)) {
2198 LogError(diagnostic::NOT_CONSTANT, {}, it->Start());
2199 continue;
2200 }
2201
2202 if (!IsValidSwitchType(caseType)) {
2203 LogError(diagnostic::SWITCH_CASE_INVALID_TYPE, {caseType}, it->Start());
2204 }
2205 }
2206 }
2207 }
2208
CheckItemEnumType(ir::Expression const * const caseTest,ir::Expression const * const compareCaseTest,ETSChecker * checker,bool & isDup)2209 void CheckItemEnumType(ir::Expression const *const caseTest, ir::Expression const *const compareCaseTest,
2210 ETSChecker *checker, bool &isDup)
2211 {
2212 // These case has logged error before, no need log error.
2213 if (!caseTest->IsMemberExpression() || !compareCaseTest->IsMemberExpression()) {
2214 return;
2215 }
2216 if (!caseTest->AsMemberExpression()->Object()->IsIdentifier() ||
2217 !compareCaseTest->AsMemberExpression()->Object()->IsIdentifier()) {
2218 return;
2219 }
2220 if (caseTest->AsMemberExpression()->Object()->AsIdentifier()->Name() !=
2221 compareCaseTest->AsMemberExpression()->Object()->AsIdentifier()->Name()) {
2222 return;
2223 }
2224
2225 if (!caseTest->AsMemberExpression()->Property()->IsIdentifier() ||
2226 !compareCaseTest->AsMemberExpression()->Property()->IsIdentifier()) {
2227 return;
2228 }
2229 if (caseTest->AsMemberExpression()->Property()->AsIdentifier()->Name() ==
2230 compareCaseTest->AsMemberExpression()->Property()->AsIdentifier()->Name()) {
2231 isDup = true;
2232 checker->LogError(diagnostic::SWITCH_CASE_DUPLICATE, {}, caseTest->Start());
2233 }
2234 }
2235
CheckItemCasesDuplicate(ArenaVector<ir::SwitchCaseStatement * > const & cases)2236 void ETSChecker::CheckItemCasesDuplicate(ArenaVector<ir::SwitchCaseStatement *> const &cases)
2237 {
2238 for (size_t caseNum = 0; caseNum < cases.size(); caseNum++) {
2239 bool isItemDuplicate = false;
2240 for (size_t compareCase = caseNum + 1; compareCase < cases.size(); compareCase++) {
2241 auto *caseTest = cases.at(caseNum)->Test();
2242 auto *compareCaseTest = cases.at(compareCase)->Test();
2243
2244 if (caseTest == nullptr || compareCaseTest == nullptr) {
2245 continue;
2246 }
2247
2248 if (caseTest->TsType()->IsETSEnumType() && compareCaseTest->TsType()->IsETSEnumType()) {
2249 CheckItemEnumType(caseTest, compareCaseTest, this, isItemDuplicate);
2250 continue;
2251 }
2252
2253 if (caseTest->IsIdentifier() || caseTest->IsMemberExpression()) {
2254 CheckIdentifierSwitchCase(caseTest, compareCaseTest, cases.at(caseNum)->Start());
2255 continue;
2256 }
2257
2258 if (compareCaseTest->IsIdentifier() || compareCaseTest->IsMemberExpression()) {
2259 CheckIdentifierSwitchCase(compareCaseTest, caseTest, cases.at(compareCase)->Start());
2260 continue;
2261 }
2262
2263 if (caseTest->IsLiteral() && compareCaseTest->IsLiteral() &&
2264 GetStringFromLiteral(caseTest) != GetStringFromLiteral(compareCaseTest)) {
2265 continue;
2266 }
2267
2268 if (!(IsConstantExpression(caseTest, caseTest->TsType()) || caseTest->IsLiteral()) ||
2269 !(IsConstantExpression(compareCaseTest, compareCaseTest->TsType()) || compareCaseTest->IsLiteral())) {
2270 continue;
2271 }
2272
2273 if (!isItemDuplicate) {
2274 isItemDuplicate = true;
2275 LogError(diagnostic::SWITCH_CASE_DUPLICATE, {}, cases.at(compareCase)->Start());
2276 }
2277 }
2278 }
2279 }
2280
CompareIdentifiersValuesAreDifferent(ir::Expression * compareValue,const std::string & caseValue)2281 bool ETSChecker::CompareIdentifiersValuesAreDifferent(ir::Expression *compareValue, const std::string &caseValue)
2282 {
2283 checker::Type *compareCaseType = compareValue->TsType();
2284
2285 if (compareCaseType->HasTypeFlag(TypeFlag::TYPE_ERROR)) {
2286 return true;
2287 }
2288
2289 if (IsConstantMemberOrIdentifierExpression(compareValue)) {
2290 const auto compareCaseValue = GetStringFromIdentifierValue(compareCaseType);
2291 return caseValue != compareCaseValue;
2292 }
2293
2294 return caseValue != GetStringFromLiteral(compareValue);
2295 }
2296
CheckIdentifierSwitchCase(ir::Expression * currentCase,ir::Expression * compareCase,const lexer::SourcePosition & pos)2297 void ETSChecker::CheckIdentifierSwitchCase(ir::Expression *currentCase, ir::Expression *compareCase,
2298 const lexer::SourcePosition &pos)
2299 {
2300 currentCase->Check(this);
2301
2302 if (!IsConstantMemberOrIdentifierExpression(currentCase, true)) {
2303 return;
2304 }
2305
2306 checker::Type *caseType = currentCase->TsType();
2307
2308 if (!IsValidSwitchType(caseType)) {
2309 return;
2310 }
2311
2312 if (!CompareIdentifiersValuesAreDifferent(compareCase, GetStringFromIdentifierValue(caseType))) {
2313 LogError(diagnostic::SWITCH_CASE_VAR_DUPLICATE_VAL, {}, pos);
2314 return;
2315 }
2316 }
2317
GetStringFromLiteral(ir::Expression * caseTest) const2318 std::string ETSChecker::GetStringFromLiteral(ir::Expression *caseTest) const
2319 {
2320 switch (caseTest->Type()) {
2321 case ir::AstNodeType::CHAR_LITERAL: {
2322 return std::to_string(caseTest->AsCharLiteral()->Char());
2323 }
2324 case ir::AstNodeType::STRING_LITERAL:
2325 case ir::AstNodeType::NULL_LITERAL:
2326 case ir::AstNodeType::UNDEFINED_LITERAL:
2327 case ir::AstNodeType::NUMBER_LITERAL: {
2328 return util::Helpers::LiteralToPropName(caseTest).Mutf8();
2329 }
2330 default:
2331 ES2PANDA_UNREACHABLE();
2332 }
2333 }
2334
IsSameDeclarationType(varbinder::LocalVariable * target,varbinder::LocalVariable * compare)2335 bool ETSChecker::IsSameDeclarationType(varbinder::LocalVariable *target, varbinder::LocalVariable *compare)
2336 {
2337 return target->Declaration()->Type() == compare->Declaration()->Type();
2338 }
2339
FindFinalizerOfTryStatement(ir::AstNode * startFrom,const ir::AstNode * p)2340 ir::BlockStatement *ETSChecker::FindFinalizerOfTryStatement(ir::AstNode *startFrom, const ir::AstNode *p)
2341 {
2342 auto *iter = startFrom->Parent();
2343
2344 do {
2345 if (iter->IsBlockStatement()) {
2346 ir::BlockStatement *finallyBlock = iter->AsBlockStatement();
2347
2348 if (finallyBlock == p->AsTryStatement()->FinallyBlock()) {
2349 return finallyBlock;
2350 }
2351 }
2352
2353 iter = iter->Parent();
2354 } while (iter != p);
2355
2356 return nullptr;
2357 }
2358
GetRelevantArgumentedTypeFromChild(ETSObjectType * const child,ETSObjectType * const target)2359 ETSObjectType *ETSChecker::GetRelevantArgumentedTypeFromChild(ETSObjectType *const child, ETSObjectType *const target)
2360 {
2361 if (child->GetDeclNode() == target->GetDeclNode()) {
2362 auto *relevantType = CreateETSObjectType(child->GetDeclNode(), child->ObjectFlags());
2363
2364 ArenaVector<Type *> params = child->TypeArguments();
2365 ES2PANDA_ASSERT(relevantType != nullptr);
2366 relevantType->SetTypeArguments(std::move(params));
2367 relevantType->SetEnclosingType(child->EnclosingType());
2368 relevantType->SetSuperType(child->SuperType());
2369
2370 return relevantType;
2371 }
2372
2373 ES2PANDA_ASSERT(child->SuperType() != nullptr);
2374
2375 return GetRelevantArgumentedTypeFromChild(child->SuperType(), target);
2376 }
2377
EmplaceSubstituted(Substitution * substitution,ETSTypeParameter * tparam,Type * typeArg)2378 void ETSChecker::EmplaceSubstituted(Substitution *substitution, ETSTypeParameter *tparam, Type *typeArg)
2379 {
2380 // *only* reference type may be substituted, no exceptions
2381 ES2PANDA_ASSERT(typeArg->IsETSReferenceType());
2382 ES2PANDA_ASSERT(substitution != nullptr);
2383 substitution->emplace(tparam, typeArg);
2384 }
2385
GetHashFromTypeArguments(const ArenaVector<Type * > & typeArgTypes)2386 util::StringView ETSChecker::GetHashFromTypeArguments(const ArenaVector<Type *> &typeArgTypes)
2387 {
2388 std::stringstream ss;
2389
2390 for (auto *it : typeArgTypes) {
2391 it->ToString(ss, true);
2392 ss << compiler::Signatures::MANGLE_SEPARATOR;
2393
2394 // In case of ETSTypeParameters storing the name might not be sufficient as there can
2395 // be multiple different type parameters with the same name. For those we test identity based
2396 // on their memory address equality, so we store them in the hash to keep it unique.
2397 // To make it consistent we store it for every type.
2398 // NOTE (mmartin): change bare address to something more appropriate unique representation
2399 ss << it << compiler::Signatures::MANGLE_SEPARATOR;
2400 }
2401
2402 return util::UString(ss.str(), ProgramAllocator()).View();
2403 }
2404
GetHashFromSubstitution(const Substitution * substitution,const bool extensionFuncFlag)2405 util::StringView ETSChecker::GetHashFromSubstitution(const Substitution *substitution, const bool extensionFuncFlag)
2406 {
2407 std::vector<std::string> fields;
2408 for (auto [k, v] : *substitution) {
2409 std::stringstream ss;
2410 k->ToString(ss, true);
2411 ss << ":";
2412 v->ToString(ss, true);
2413 // NOTE (mmartin): change bare address to something more appropriate unique representation
2414 ss << ":" << k << ":" << v;
2415 fields.push_back(ss.str());
2416 }
2417 std::sort(fields.begin(), fields.end());
2418
2419 std::stringstream ss;
2420 for (auto &fstr : fields) {
2421 ss << fstr;
2422 ss << ";";
2423 }
2424
2425 if (extensionFuncFlag) {
2426 ss << "extensionFunctionType;";
2427 }
2428 return util::UString(ss.str(), ProgramAllocator()).View();
2429 }
2430
GetHashFromFunctionType(ir::ETSFunctionType * type)2431 util::StringView ETSChecker::GetHashFromFunctionType(ir::ETSFunctionType *type)
2432 {
2433 std::stringstream ss;
2434 for (auto *p : type->Params()) {
2435 auto *const param = p->AsETSParameterExpression();
2436 auto *paramType = param->TypeAnnotation()->GetType(this);
2437 ES2PANDA_ASSERT(paramType != nullptr);
2438 paramType->ToString(ss, true);
2439 ss << ";";
2440 }
2441
2442 if (type->IsExtensionFunction()) {
2443 if (type->ReturnType()->IsTSThisType()) {
2444 type->Params()[0]->AsETSParameterExpression()->TypeAnnotation()->TsType()->ToString(ss, true);
2445 } else {
2446 auto *returnType = type->ReturnType()->GetType(this);
2447 ES2PANDA_ASSERT(returnType != nullptr);
2448 returnType->ToString(ss, true);
2449 }
2450 ss << "extensionFunction;";
2451 } else {
2452 auto *returnType = type->ReturnType()->GetType(this);
2453 ES2PANDA_ASSERT(returnType != nullptr);
2454 returnType->ToString(ss, true);
2455 }
2456
2457 ss << ";";
2458
2459 if (type->IsThrowing()) {
2460 ss << "throws;";
2461 }
2462
2463 if (type->IsRethrowing()) {
2464 ss << "rethrows;";
2465 }
2466
2467 return util::UString(ss.str(), ProgramAllocator()).View();
2468 }
2469
GetOriginalBaseType(Type * const object)2470 ETSObjectType *ETSChecker::GetOriginalBaseType(Type *const object)
2471 {
2472 if (object == nullptr || !object->IsETSObjectType()) {
2473 return nullptr;
2474 }
2475
2476 return object->AsETSObjectType()->GetOriginalBaseType();
2477 }
2478
CheckValidGenericTypeParameter(Type * const argType,const lexer::SourcePosition & pos)2479 void ETSChecker::CheckValidGenericTypeParameter(Type *const argType, const lexer::SourcePosition &pos)
2480 {
2481 std::stringstream ss;
2482 argType->ToString(ss);
2483 LogError(diagnostic::INVALID_TYPE_PARAM, {ss.str()}, pos);
2484 }
2485
CheckNumberOfTypeArguments(ETSObjectType * const type,ir::TSTypeParameterInstantiation * const typeArgs,const lexer::SourcePosition & pos)2486 bool ETSChecker::CheckNumberOfTypeArguments(ETSObjectType *const type, ir::TSTypeParameterInstantiation *const typeArgs,
2487 const lexer::SourcePosition &pos)
2488 {
2489 auto const &typeParams = type->TypeArguments();
2490 if (typeParams.empty()) {
2491 if (typeArgs != nullptr) {
2492 LogError(diagnostic::NOT_GENERIC, {type}, pos);
2493 return false;
2494 }
2495 return true;
2496 }
2497
2498 size_t minimumTypeArgs = std::count_if(typeParams.begin(), typeParams.end(), [](Type *param) {
2499 return param->IsETSTypeParameter() && param->AsETSTypeParameter()->GetDefaultType() == nullptr;
2500 });
2501 if (typeArgs == nullptr && minimumTypeArgs > 0) {
2502 LogError(diagnostic::GENERIC_WITHOUT_TYPE_PARAMS, {type}, pos);
2503 return false;
2504 }
2505
2506 if (typeArgs != nullptr &&
2507 ((minimumTypeArgs > typeArgs->Params().size()) || (typeParams.size() < typeArgs->Params().size()))) {
2508 LogError(diagnostic::GENERIC_TYPE_PARAM_COUNT_MISMATCH, {type, minimumTypeArgs, typeArgs->Params().size()},
2509 pos);
2510 return false;
2511 }
2512 return true;
2513 }
2514
NeedTypeInference(const ir::ScriptFunction * lambda)2515 bool ETSChecker::NeedTypeInference(const ir::ScriptFunction *lambda)
2516 {
2517 if (lambda->ReturnTypeAnnotation() == nullptr) {
2518 return true;
2519 }
2520 for (auto *const param : lambda->Params()) {
2521 if (param->AsETSParameterExpression()->TypeAnnotation() == nullptr) {
2522 return true;
2523 }
2524 }
2525 return false;
2526 }
2527
FindTypeInferenceArguments(const ArenaVector<ir::Expression * > & arguments)2528 std::vector<bool> ETSChecker::FindTypeInferenceArguments(const ArenaVector<ir::Expression *> &arguments)
2529 {
2530 std::vector<bool> argTypeInferenceRequired(arguments.size());
2531 size_t index = 0;
2532 for (ir::Expression *arg : arguments) {
2533 if (arg->IsArrowFunctionExpression()) {
2534 ir::ScriptFunction *const lambda = arg->AsArrowFunctionExpression()->Function();
2535 if (NeedTypeInference(lambda)) {
2536 argTypeInferenceRequired[index] = true;
2537 }
2538 }
2539 ++index;
2540 }
2541 return argTypeInferenceRequired;
2542 }
2543
2544 // #22952: optional arrow leftovers
CheckLambdaAssignableUnion(ir::AstNode * typeAnn,ir::ScriptFunction * lambda)2545 bool ETSChecker::CheckLambdaAssignableUnion(ir::AstNode *typeAnn, ir::ScriptFunction *lambda)
2546 {
2547 bool assignable = false;
2548 for (auto *type : typeAnn->AsETSUnionType()->Types()) {
2549 if (type->IsETSFunctionType()) {
2550 assignable |= lambda->Params().size() == type->AsETSFunctionType()->Params().size();
2551 continue;
2552 }
2553
2554 if (type->IsETSTypeReference()) {
2555 auto aliasType = util::Helpers::DerefETSTypeReference(type);
2556 assignable |= aliasType->IsETSFunctionType() &&
2557 lambda->Params().size() == aliasType->AsETSFunctionType()->Params().size();
2558 }
2559 }
2560
2561 return assignable;
2562 }
2563
InferTypesForLambda(ir::ScriptFunction * lambda,ir::ETSFunctionType * calleeType,Signature * maybeSubstitutedFunctionSig)2564 void ETSChecker::InferTypesForLambda(ir::ScriptFunction *lambda, ir::ETSFunctionType *calleeType,
2565 Signature *maybeSubstitutedFunctionSig)
2566 {
2567 for (size_t i = 0; i < lambda->Params().size(); ++i) {
2568 if (!lambda->Params().at(i)->IsETSParameterExpression()) {
2569 LogError(diagnostic::INVALID_LAMBDA_PARAMETER, lambda->Params().at(i)->Start());
2570 continue;
2571 }
2572 auto *const lambdaParam = lambda->Params().at(i)->AsETSParameterExpression()->Ident();
2573 if (lambdaParam->TypeAnnotation() == nullptr) {
2574 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2575 Type *inferredType = calleeType->Params()[i]->AsETSParameterExpression()->TypeAnnotation()->Check(this);
2576 bool isPrimitive = inferredType != nullptr && inferredType->IsETSPrimitiveType();
2577 if (!isPrimitive && maybeSubstitutedFunctionSig != nullptr) {
2578 ES2PANDA_ASSERT(maybeSubstitutedFunctionSig->Params().size() == calleeType->Params().size());
2579 inferredType = maybeSubstitutedFunctionSig->Params()[i]->TsType();
2580 }
2581 lambdaParam->Variable()->SetTsType(inferredType);
2582 lambdaParam->SetTsType(inferredType);
2583 }
2584 }
2585
2586 if (lambda->ReturnTypeAnnotation() == nullptr) {
2587 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2588 Type *inferredReturnType = calleeType->ReturnType()->GetType(this);
2589 bool isPrimitive = inferredReturnType != nullptr && inferredReturnType->IsETSPrimitiveType();
2590 if (!isPrimitive && maybeSubstitutedFunctionSig != nullptr) {
2591 inferredReturnType = maybeSubstitutedFunctionSig->ReturnType();
2592 }
2593 lambda->SetPreferredReturnType(inferredReturnType);
2594 }
2595 }
2596
InferTypesForLambda(ir::ScriptFunction * lambda,Signature * signature)2597 void ETSChecker::InferTypesForLambda(ir::ScriptFunction *lambda, Signature *signature)
2598 {
2599 ES2PANDA_ASSERT(signature->Params().size() >= lambda->Params().size());
2600 for (size_t i = 0; i < lambda->Params().size(); ++i) {
2601 if (!lambda->Params().at(i)->IsETSParameterExpression()) {
2602 LogError(diagnostic::INVALID_LAMBDA_PARAMETER, lambda->Params().at(i)->Start());
2603 continue;
2604 }
2605 auto *const lambdaParam = lambda->Params().at(i)->AsETSParameterExpression()->Ident();
2606 if (lambdaParam->TypeAnnotation() == nullptr) {
2607 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2608 lambdaParam->Variable()->SetTsType(signature->Params().at(i)->TsType());
2609 lambdaParam->SetTsType(signature->Params().at(i)->TsType());
2610 }
2611 }
2612
2613 if (lambda->ReturnTypeAnnotation() == nullptr) {
2614 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2615 lambda->SetPreferredReturnType(signature->ReturnType());
2616 }
2617 }
2618
ModifyPreferredType(ir::ArrayExpression * const arrayExpr,Type * const newPreferredType)2619 void ETSChecker::ModifyPreferredType(ir::ArrayExpression *const arrayExpr, Type *const newPreferredType)
2620 {
2621 // After modifying the preferred type of an array expression, it needs to be rechecked at the call site
2622 arrayExpr->SetPreferredType(newPreferredType);
2623 arrayExpr->SetTsType(nullptr);
2624
2625 for (auto *const element : arrayExpr->Elements()) {
2626 if (element->IsArrayExpression()) {
2627 ModifyPreferredType(element->AsArrayExpression(), nullptr);
2628 }
2629 }
2630 }
2631
TryInferTypeForLambdaTypeAlias(ir::ArrowFunctionExpression * expr,ETSFunctionType * calleeType)2632 void ETSChecker::TryInferTypeForLambdaTypeAlias(ir::ArrowFunctionExpression *expr, ETSFunctionType *calleeType)
2633 {
2634 ES2PANDA_ASSERT(expr->IsArrowFunctionExpression());
2635 ES2PANDA_ASSERT(calleeType->IsETSArrowType());
2636
2637 ir::ScriptFunction *const lambda = expr->AsArrowFunctionExpression()->Function();
2638
2639 auto *signature = calleeType->CallSignaturesOfMethodOrArrow()[0];
2640 if (signature->Params().size() >= lambda->Params().size() && NeedTypeInference(lambda)) {
2641 InferTypesForLambda(lambda, signature);
2642 }
2643 }
2644
IsInLocalClass(const ir::AstNode * node) const2645 bool ETSChecker::IsInLocalClass(const ir::AstNode *node) const
2646 {
2647 while (node != nullptr) {
2648 if (node->Type() == ir::AstNodeType::CLASS_DEFINITION) {
2649 return node->AsClassDefinition()->IsLocal();
2650 }
2651 node = node->Parent();
2652 }
2653
2654 return false;
2655 }
2656
GenerateImplicitInstantiateArg(const std::string & className)2657 ir::Expression *ETSChecker::GenerateImplicitInstantiateArg(const std::string &className)
2658 {
2659 std::string implicitInstantiateArgument = "()=>{return new " + className + "()}";
2660 parser::Program program(ProgramAllocator(), VarBinder());
2661 auto parser = parser::ETSParser(&program, nullptr, DiagnosticEngine());
2662 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2663 auto *argExpr = parser.CreateExpression(implicitInstantiateArgument);
2664 // NOTE(kaskov): #23399 We temporary delete SourceRange of all artificially created nodes (not from original
2665 // Lexer()), because all errors, which created by this code, will got a segfault. That caused because Program exist
2666 // till the end this function, and not avaible in diagnosticEngine. Now errors printed, but whitout position
2667 // because it doesn't actually exist). PS.Previously written competely wrong positons and file, so situation
2668 // isn't changed.
2669 compiler::SetSourceRangesRecursively(argExpr, lexer::SourceRange());
2670 argExpr->IterateRecursively([](ir::AstNode *node) -> void { node->SetRange(lexer::SourceRange()); });
2671 compiler::InitScopesPhaseETS::RunExternalNode(argExpr, &program);
2672
2673 return argExpr;
2674 }
2675
ReInitScopesForTypeAnnotation(ETSChecker * checker,ir::TypeNode * typeAnno,varbinder::Scope * paramScope)2676 static void ReInitScopesForTypeAnnotation(ETSChecker *checker, ir::TypeNode *typeAnno, varbinder::Scope *paramScope)
2677 {
2678 if (typeAnno != nullptr) {
2679 compiler::ClearTypesVariablesAndScopes(typeAnno);
2680 auto classCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(checker->VarBinder(), paramScope);
2681 compiler::InitScopesPhaseETS::RunExternalNode(typeAnno, checker->VarBinder());
2682 }
2683 }
2684
ClassPropToImplementationProp(ir::ClassProperty * classProp,varbinder::ClassScope * scope)2685 ir::ClassProperty *ETSChecker::ClassPropToImplementationProp(ir::ClassProperty *classProp, varbinder::ClassScope *scope)
2686 {
2687 classProp->Key()->AsIdentifier()->SetName(
2688 util::UString(std::string(compiler::Signatures::PROPERTY) + classProp->Key()->AsIdentifier()->Name().Mutf8(),
2689 ProgramAllocator())
2690 .View());
2691 classProp->AddModifier(ir::ModifierFlags::PRIVATE);
2692
2693 auto *fieldDecl = ProgramAllocator()->New<varbinder::LetDecl>(classProp->Key()->AsIdentifier()->Name());
2694 ES2PANDA_ASSERT(fieldDecl != nullptr);
2695 fieldDecl->BindNode(classProp);
2696
2697 auto fieldVar = scope->InstanceFieldScope()->AddDecl(ProgramAllocator(), fieldDecl, ScriptExtension::ETS);
2698 fieldVar->AddFlag(varbinder::VariableFlags::PROPERTY);
2699 fieldVar->SetScope(scope->InstanceFieldScope());
2700
2701 classProp->Key()->SetVariable(fieldVar);
2702 fieldVar->SetTsType(classProp->TsType());
2703
2704 auto classCtx = varbinder::LexicalScope<varbinder::ClassScope>::Enter(VarBinder(), scope);
2705 ReInitScopesForTypeAnnotation(this, classProp->TypeAnnotation(), scope);
2706 compiler::InitScopesPhaseETS::RunExternalNode(classProp->Value(), VarBinder());
2707
2708 return classProp;
2709 }
2710
2711 // CC-OFFNXT(huge_method[C++], G.FUN.01-CPP) solid logic
GenerateGetterSetterBody(ArenaVector<ir::Statement * > & stmts,ArenaVector<ir::Expression * > & params,ir::ClassProperty * const field,varbinder::FunctionParamScope * paramScope,bool isSetter)2712 void ETSChecker::GenerateGetterSetterBody(ArenaVector<ir::Statement *> &stmts, ArenaVector<ir::Expression *> ¶ms,
2713 ir::ClassProperty *const field, varbinder::FunctionParamScope *paramScope,
2714 bool isSetter)
2715 {
2716 auto *classDef = field->Parent()->AsClassDefinition();
2717
2718 ir::Expression *baseExpression;
2719 if ((field->Modifiers() & ir::ModifierFlags::SUPER_OWNER) != 0U) {
2720 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2721 baseExpression = ProgramAllocator()->New<ir::SuperExpression>();
2722 } else {
2723 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2724 baseExpression = ProgramAllocator()->New<ir::ThisExpression>();
2725 }
2726 baseExpression->SetParent(classDef);
2727 baseExpression->SetTsType(classDef->TsType());
2728
2729 auto *memberExpression =
2730 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2731 ProgramAllocNode<ir::MemberExpression>(baseExpression,
2732 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2733 field->Key()->AsIdentifier()->Clone(ProgramAllocator(), nullptr),
2734 ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
2735 memberExpression->SetTsType(field->TsType());
2736 memberExpression->SetPropVar(field->Key()->Variable()->AsLocalVariable());
2737 memberExpression->SetRange(classDef->Range());
2738 if (memberExpression->ObjType() == nullptr && classDef->TsType() != nullptr) {
2739 memberExpression->SetObjectType(classDef->TsType()->AsETSObjectType());
2740 }
2741
2742 if (!isSetter) {
2743 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2744 stmts.push_back(ProgramAllocNode<ir::ReturnStatement>(memberExpression));
2745 return;
2746 }
2747
2748 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2749 auto *paramIdent = field->Key()->AsIdentifier()->Clone(ProgramAllocator(), nullptr);
2750 if (field->TypeAnnotation() != nullptr) {
2751 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2752 auto *const typeAnnotation = field->TypeAnnotation()->Clone(ProgramAllocator(), paramIdent);
2753 paramIdent->SetTsTypeAnnotation(typeAnnotation);
2754 ReInitScopesForTypeAnnotation(this, typeAnnotation, paramScope);
2755 } else {
2756 paramIdent->SetTsType(field->TsType());
2757 }
2758
2759 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2760 auto *paramExpression = ProgramAllocNode<ir::ETSParameterExpression>(paramIdent, false, ProgramAllocator());
2761 paramExpression->SetRange(paramIdent->Range());
2762
2763 auto [paramVar, node] = paramScope->AddParamDecl(ProgramAllocator(), VarBinder(), paramExpression);
2764 if (node != nullptr) {
2765 VarBinder()->ThrowRedeclaration(node->Start(), paramVar->Name(), paramVar->Declaration()->Type());
2766 }
2767
2768 paramIdent->SetVariable(paramVar);
2769 paramExpression->SetVariable(paramVar);
2770 params.push_back(paramExpression);
2771
2772 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2773 auto ident = ProgramAllocNode<ir::Identifier>(paramExpression->Ident()->Name(), ProgramAllocator());
2774 ident->SetVariable(paramExpression->Variable());
2775 auto *assignmentExpression =
2776 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2777 ProgramAllocNode<ir::AssignmentExpression>(memberExpression, ident, lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
2778 ident->SetParent(assignmentExpression);
2779 assignmentExpression->SetTsType(paramVar->TsType());
2780
2781 assignmentExpression->SetRange({field->Start(), field->End()});
2782 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2783 stmts.push_back(ProgramAllocNode<ir::ExpressionStatement>(assignmentExpression));
2784 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2785 stmts.push_back(ProgramAllocator()->New<ir::ReturnStatement>(nullptr));
2786 }
2787
GenGetterSetterBodyHelper(ETSChecker * checker,ArenaVector<ir::Statement * > & stmts,ir::ClassProperty * const property,varbinder::FunctionScope * functionScope)2788 static ir::BlockStatement *GenGetterSetterBodyHelper(ETSChecker *checker, ArenaVector<ir::Statement *> &stmts,
2789 ir::ClassProperty *const property,
2790 varbinder::FunctionScope *functionScope)
2791 {
2792 if (property->IsDeclare()) {
2793 return nullptr;
2794 }
2795 auto *body = checker->ProgramAllocNode<ir::BlockStatement>(checker->ProgramAllocator(), std::move(stmts));
2796 ES2PANDA_ASSERT(body != nullptr);
2797 body->SetScope(functionScope);
2798 return body;
2799 }
2800
2801 // Need to avoid codecheck
GenGetterSetterScriptFunc(ir::ClassProperty * const property,ir::ClassProperty * const field,varbinder::ClassScope * classScope,bool isSetter,ETSChecker * checker)2802 static std::tuple<ir::ScriptFunction *, varbinder::FunctionScope *, ir::ModifierFlags> GenGetterSetterScriptFunc(
2803 ir::ClassProperty *const property, ir::ClassProperty *const field, varbinder::ClassScope *classScope, bool isSetter,
2804 ETSChecker *checker)
2805 {
2806 auto *paramScope =
2807 checker->ProgramAllocator()->New<varbinder::FunctionParamScope>(checker->ProgramAllocator(), classScope);
2808 auto *functionScope =
2809 checker->ProgramAllocator()->New<varbinder::FunctionScope>(checker->ProgramAllocator(), paramScope);
2810
2811 functionScope->BindParamScope(paramScope);
2812 paramScope->BindFunctionScope(functionScope);
2813
2814 ArenaVector<ir::Expression *> params(checker->ProgramAllocator()->Adapter());
2815 ArenaVector<ir::Statement *> stmts(checker->ProgramAllocator()->Adapter());
2816 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2817 checker->GenerateGetterSetterBody(stmts, params, field, paramScope, isSetter);
2818 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2819 auto funcFlags = isSetter ? ir::ScriptFunctionFlags::SETTER
2820 : ir::ScriptFunctionFlags::GETTER | ir::ScriptFunctionFlags::HAS_RETURN;
2821 auto *returnTypeAnn = isSetter || field->TypeAnnotation() == nullptr
2822 ? nullptr
2823 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2824 : field->TypeAnnotation()->Clone(checker->ProgramAllocator(), nullptr);
2825 ReInitScopesForTypeAnnotation(checker, returnTypeAnn, paramScope);
2826 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2827 ir::ModifierFlags modifierFlag =
2828 (ir::ModifierFlags::PUBLIC |
2829 static_cast<ir::ModifierFlags>(property->Modifiers() & ir::ModifierFlags::DECLARE) |
2830 (isSetter ? ir::ModifierFlags::SETTER : ir::ModifierFlags::GETTER));
2831 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2832 auto *func = checker->ProgramAllocNode<ir::ScriptFunction>(
2833 checker->ProgramAllocator(),
2834 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2835 ir::ScriptFunction::ScriptFunctionData {GenGetterSetterBodyHelper(checker, stmts, property, functionScope),
2836 ir::FunctionSignature(nullptr, std::move(params), returnTypeAnn),
2837 funcFlags, modifierFlag});
2838 paramScope->BindNode(func);
2839 return {func, functionScope, modifierFlag};
2840 }
2841
GenerateDefaultGetterSetter(ir::ClassProperty * const property,ir::ClassProperty * const field,varbinder::ClassScope * classScope,bool isSetter,ETSChecker * checker)2842 ir::MethodDefinition *ETSChecker::GenerateDefaultGetterSetter(ir::ClassProperty *const property,
2843 ir::ClassProperty *const field,
2844 varbinder::ClassScope *classScope, bool isSetter,
2845 ETSChecker *checker)
2846 {
2847 auto [func, functionScope, modifierFlag] =
2848 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2849 GenGetterSetterScriptFunc(property, field, classScope, isSetter, checker);
2850 func->SetRange(field->Range());
2851 func->SetScope(functionScope);
2852 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2853 auto *methodIdent = property->Key()->AsIdentifier()->Clone(checker->ProgramAllocator(), nullptr);
2854 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2855 auto *funcExpr = checker->ProgramAllocNode<ir::FunctionExpression>(func);
2856 CHECK_NOT_NULL(funcExpr);
2857 funcExpr->SetRange(func->Range());
2858 func->AddFlag(ir::ScriptFunctionFlags::METHOD);
2859 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2860 auto *method = checker->ProgramAllocNode<ir::MethodDefinition>(
2861 isSetter ? ir::MethodDefinitionKind::SET : ir::MethodDefinitionKind::GET, methodIdent, funcExpr, modifierFlag,
2862 checker->ProgramAllocator(), false);
2863 auto *decl = checker->ProgramAllocator()->New<varbinder::FunctionDecl>(
2864 checker->ProgramAllocator(), property->Key()->AsIdentifier()->Name(), method);
2865 auto *var = checker->ProgramAllocator()->New<varbinder::LocalVariable>(decl, varbinder::VariableFlags::VAR);
2866 CHECK_NOT_NULL(var);
2867 var->AddFlag(varbinder::VariableFlags::METHOD);
2868
2869 methodIdent->SetVariable(var);
2870
2871 auto *methodId = method->Id();
2872 CHECK_NOT_NULL(methodId);
2873 methodId->SetMutator();
2874 method->SetRange(field->Range());
2875 auto *methodFunc = method->Function();
2876 CHECK_NOT_NULL(methodFunc);
2877 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2878 methodFunc->SetIdent(methodId->Clone(checker->ProgramAllocator(), nullptr));
2879 methodFunc->AddModifier(method->Modifiers());
2880 method->SetVariable(var);
2881 method->SetParent(field->Parent());
2882
2883 functionScope->BindNode(func);
2884
2885 auto classCtx = varbinder::LexicalScope<varbinder::ClassScope>::Enter(checker->VarBinder(), classScope);
2886 checker->VarBinder()->AsETSBinder()->ResolveMethodDefinition(method);
2887
2888 functionScope->BindName(classScope->Node()->AsClassDefinition()->InternalName());
2889 method->Check(checker);
2890
2891 return method;
2892 }
2893
GetImplementationClassProp(ETSChecker * checker,ir::ClassProperty * interfaceProp,ir::ClassProperty * originalProp,ETSObjectType * classType)2894 ir::ClassProperty *GetImplementationClassProp(ETSChecker *checker, ir::ClassProperty *interfaceProp,
2895 ir::ClassProperty *originalProp, ETSObjectType *classType)
2896 {
2897 bool isSuperOwner = ((originalProp->Modifiers() & ir::ModifierFlags::SUPER_OWNER) != 0U);
2898 if (!isSuperOwner) {
2899 auto *const classDef = classType->GetDeclNode()->AsClassDefinition();
2900 auto *const scope = checker->Scope()->AsClassScope();
2901 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2902 auto *const classProp = checker->ClassPropToImplementationProp(
2903 interfaceProp->Clone(checker->ProgramAllocator(), originalProp->Parent()), scope);
2904 classType->AddProperty<PropertyType::INSTANCE_FIELD>(classProp->Key()->Variable()->AsLocalVariable());
2905 classDef->Body().push_back(classProp);
2906 return classProp;
2907 }
2908
2909 auto *const classProp = classType
2910 ->GetProperty(interfaceProp->Key()->AsIdentifier()->Name(),
2911 PropertySearchFlags::SEARCH_ALL | PropertySearchFlags::SEARCH_IN_BASE)
2912 ->Declaration()
2913 ->Node()
2914 ->AsClassProperty();
2915 classProp->AddModifier(ir::ModifierFlags::SUPER_OWNER);
2916 return classProp;
2917 }
2918
SetupGetterSetterFlags(ir::ClassProperty * originalProp,ETSObjectType * classType,ir::MethodDefinition * getter,ir::MethodDefinition * setter,const bool inExternal)2919 void ETSChecker::SetupGetterSetterFlags(ir::ClassProperty *originalProp, ETSObjectType *classType,
2920 ir::MethodDefinition *getter, ir::MethodDefinition *setter,
2921 const bool inExternal)
2922 {
2923 auto getProgram = [](ir::AstNode *node) { return node->Range().start.Program(); };
2924
2925 auto *const classDef = classType->GetDeclNode()->AsClassDefinition();
2926 for (auto &method : {getter, setter}) {
2927 if (method == nullptr) {
2928 continue;
2929 }
2930
2931 const auto mflag = method == getter ? ir::ModifierFlags::GETTER : ir::ModifierFlags::SETTER;
2932 const auto tflag = method == getter ? TypeFlag::GETTER : TypeFlag::SETTER;
2933
2934 method->TsType()->AddTypeFlag(tflag);
2935 method->Variable()->SetTsType(method->TsType());
2936 auto *func = method->Function();
2937 ES2PANDA_ASSERT(func != nullptr);
2938 if (((originalProp->Modifiers() & mflag) != 0U)) {
2939 func->AddModifier(ir::ModifierFlags::OVERRIDE);
2940 }
2941
2942 if (inExternal && !getProgram(originalProp)->IsGenAbcForExternal()) {
2943 func->AddFlag(ir::ScriptFunctionFlags::EXTERNAL);
2944 }
2945
2946 if (originalProp->IsDeclare()) {
2947 method->AddModifier(ir::ModifierFlags::DECLARE);
2948 func->AddModifier(ir::ModifierFlags::DECLARE);
2949 }
2950 this->CheckOverride(func->Signature());
2951 method->SetParent(classDef);
2952 classType->AddProperty<checker::PropertyType::INSTANCE_METHOD>(method->Variable()->AsLocalVariable());
2953 }
2954 }
2955
GenerateGetterSetterPropertyAndMethod(ir::ClassProperty * originalProp,ETSObjectType * classType)2956 void ETSChecker::GenerateGetterSetterPropertyAndMethod(ir::ClassProperty *originalProp, ETSObjectType *classType)
2957 {
2958 auto *const classDef = classType->GetDeclNode()->AsClassDefinition();
2959 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2960 auto *interfaceProp = originalProp->Clone(ProgramAllocator(), originalProp->Parent());
2961 interfaceProp->ClearModifier(ir::ModifierFlags::GETTER_SETTER);
2962
2963 auto *const scope = Scope()->AsClassScope();
2964 scope->InstanceFieldScope()->EraseBinding(interfaceProp->Key()->AsIdentifier()->Name());
2965 interfaceProp->SetRange(originalProp->Range());
2966
2967 auto classCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(VarBinder(), scope);
2968 ReInitScopesForTypeAnnotation(this, interfaceProp->TypeAnnotation(), scope);
2969 compiler::InitScopesPhaseETS::RunExternalNode(interfaceProp->Value(), VarBinder());
2970
2971 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2972 auto *const classProp = GetImplementationClassProp(this, interfaceProp, originalProp, classType);
2973
2974 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2975 ir::MethodDefinition *getter = GenerateDefaultGetterSetter(interfaceProp, classProp, scope, false, this);
2976 classDef->Body().push_back(getter);
2977
2978 const auto &name = getter->Key()->AsIdentifier()->Name();
2979
2980 ir::MethodDefinition *setter =
2981 !classProp->IsConst()
2982 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
2983 ? GenerateDefaultGetterSetter(interfaceProp, classProp, Scope()->AsClassScope(), true, this)
2984 : nullptr;
2985
2986 auto *const methodScope = scope->InstanceMethodScope();
2987 auto *const decl = ProgramAllocator()->New<varbinder::FunctionDecl>(ProgramAllocator(), name, getter);
2988
2989 auto *var = methodScope->AddDecl(ProgramAllocator(), decl, ScriptExtension::ETS);
2990 if (var == nullptr) {
2991 auto *const prevDecl = methodScope->FindDecl(name);
2992 for (const auto &method : {getter, setter}) {
2993 if (method != nullptr) {
2994 prevDecl->Node()->AsMethodDefinition()->AddOverload(method);
2995 }
2996 }
2997 var = methodScope->FindLocal(name, varbinder::ResolveBindingOptions::BINDINGS);
2998 var->AddFlag(varbinder::VariableFlags::METHOD);
2999 }
3000
3001 getter->Function()->Id()->SetVariable(var);
3002
3003 SetupGetterSetterFlags(originalProp, classType, getter, setter, HasStatus(CheckerStatus::IN_EXTERNAL));
3004
3005 if (setter != nullptr && !setter->TsType()->IsTypeError()) {
3006 getter->Variable()->TsType()->AsETSFunctionType()->AddCallSignature(
3007 setter->TsType()->AsETSFunctionType()->CallSignatures()[0]);
3008 getter->AddOverload(setter);
3009 }
3010 }
3011
3012 // CC-OFFNXT(huge_method[C++], G.FUN.01-CPP) solid logic
TryTransformingToStaticInvoke(ir::Identifier * const ident,const Type * resolvedType)3013 bool ETSChecker::TryTransformingToStaticInvoke(ir::Identifier *const ident, const Type *resolvedType)
3014 {
3015 ES2PANDA_ASSERT(ident->Parent()->IsCallExpression());
3016 ES2PANDA_ASSERT(ident->Parent()->AsCallExpression()->Callee() == ident);
3017
3018 if (!resolvedType->IsETSObjectType()) {
3019 return false;
3020 }
3021
3022 auto className = ident->Name();
3023 std::string_view propertyName;
3024
3025 PropertySearchFlags searchFlag = PropertySearchFlags::SEARCH_IN_INTERFACES | PropertySearchFlags::SEARCH_IN_BASE |
3026 PropertySearchFlags::SEARCH_STATIC_METHOD;
3027 auto *instantiateMethod =
3028 resolvedType->AsETSObjectType()->GetProperty(compiler::Signatures::STATIC_INSTANTIATE_METHOD, searchFlag);
3029 auto *invokeMethod =
3030 resolvedType->AsETSObjectType()->GetProperty(compiler::Signatures::STATIC_INVOKE_METHOD, searchFlag);
3031 if (instantiateMethod != nullptr) {
3032 propertyName = compiler::Signatures::STATIC_INSTANTIATE_METHOD;
3033 } else if (invokeMethod != nullptr) {
3034 propertyName = compiler::Signatures::STATIC_INVOKE_METHOD;
3035 } else {
3036 LogError(diagnostic::NO_STATIC_INVOKE,
3037 {compiler::Signatures::STATIC_INVOKE_METHOD, compiler::Signatures::STATIC_INSTANTIATE_METHOD,
3038 className, className},
3039 ident->Start());
3040 return true;
3041 }
3042 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
3043 auto *classId = ProgramAllocNode<ir::Identifier>(className, ProgramAllocator());
3044 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
3045 auto *methodId = ProgramAllocNode<ir::Identifier>(propertyName, ProgramAllocator());
3046 if (propertyName == compiler::Signatures::STATIC_INSTANTIATE_METHOD) {
3047 methodId->SetVariable(instantiateMethod);
3048 } else if (propertyName == compiler::Signatures::STATIC_INVOKE_METHOD) {
3049 methodId->SetVariable(invokeMethod);
3050 }
3051
3052 auto *transformedCallee =
3053 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
3054 ProgramAllocNode<ir::MemberExpression>(classId, methodId, ir::MemberExpressionKind::PROPERTY_ACCESS, false,
3055 false);
3056
3057 classId->SetRange(ident->Range());
3058 methodId->SetRange(ident->Range());
3059 transformedCallee->SetRange(ident->Range());
3060
3061 auto *callExpr = ident->Parent()->AsCallExpression();
3062 transformedCallee->SetParent(callExpr);
3063 callExpr->SetCallee(transformedCallee);
3064
3065 if (instantiateMethod != nullptr) {
3066 auto lexScope {varbinder::LexicalScope<varbinder::Scope>::Enter(VarBinder(), compiler::NearestScope(callExpr))};
3067 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
3068 auto *argExpr = GenerateImplicitInstantiateArg(std::string(className));
3069
3070 argExpr->SetParent(callExpr);
3071 argExpr->SetRange(ident->Range());
3072
3073 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
3074 VarBinder()->AsETSBinder()->HandleCustomNodes(argExpr);
3075
3076 auto &arguments = callExpr->Arguments();
3077 arguments.insert(arguments.begin(), argExpr);
3078 }
3079
3080 return true;
3081 }
3082
ImportNamespaceObjectTypeAddReExportType(ir::ETSImportDeclaration * importDecl,checker::ETSObjectType * lastObjectType,ir::Identifier * ident)3083 void ETSChecker::ImportNamespaceObjectTypeAddReExportType(ir::ETSImportDeclaration *importDecl,
3084 checker::ETSObjectType *lastObjectType, ir::Identifier *ident)
3085 {
3086 for (auto item : VarBinder()->AsETSBinder()->ReExportImports()) {
3087 if (importDecl->ResolvedSource() != item->GetProgramPath().Mutf8()) {
3088 continue;
3089 }
3090 auto *reExportType = GetImportSpecifierObjectType(item->GetETSImportDeclarations(), ident);
3091 if (reExportType->IsTypeError()) {
3092 continue;
3093 }
3094 ES2PANDA_ASSERT(lastObjectType != nullptr);
3095 lastObjectType->AddReExports(reExportType->AsETSObjectType());
3096 for (auto node : importDecl->Specifiers()) {
3097 if (node->IsImportSpecifier()) {
3098 auto specifier = node->AsImportSpecifier();
3099 lastObjectType->AddReExportAlias(specifier->Imported()->Name(), specifier->Local()->Name());
3100 }
3101 }
3102 }
3103 }
3104
GetImportSpecifierObjectType(ir::ETSImportDeclaration * importDecl,ir::Identifier * ident)3105 Type *ETSChecker::GetImportSpecifierObjectType(ir::ETSImportDeclaration *importDecl, ir::Identifier *ident)
3106 {
3107 auto importPath = importDecl->ResolvedSource();
3108 parser::Program *program =
3109 SelectEntryOrExternalProgram(static_cast<varbinder::ETSBinder *>(VarBinder()), importPath);
3110 if (program == nullptr) {
3111 return GlobalTypeError();
3112 }
3113
3114 auto const moduleName = program->ModuleName();
3115 auto const internalName =
3116 util::UString(
3117 moduleName.Mutf8().append(compiler::Signatures::METHOD_SEPARATOR).append(compiler::Signatures::ETS_GLOBAL),
3118 ProgramAllocator())
3119 .View();
3120
3121 auto *moduleObjectType =
3122 ProgramAllocator()->New<ETSObjectType>(ProgramAllocator(), moduleName, internalName,
3123 std::make_tuple(ident, checker::ETSObjectFlags::CLASS, Relation()));
3124
3125 auto *rootDecl = ProgramAllocator()->New<varbinder::ClassDecl>(moduleName);
3126 varbinder::LocalVariable *rootVar =
3127 ProgramAllocator()->New<varbinder::LocalVariable>(rootDecl, varbinder::VariableFlags::NONE);
3128 ES2PANDA_ASSERT(rootVar != nullptr);
3129 rootVar->SetTsType(moduleObjectType);
3130
3131 ImportNamespaceObjectTypeAddReExportType(importDecl, moduleObjectType, ident);
3132 SetPropertiesForModuleObject(moduleObjectType, importPath,
3133 importDecl->Specifiers()[0]->IsImportNamespaceSpecifier() ? nullptr : importDecl);
3134 SetrModuleObjectTsType(ident, moduleObjectType);
3135
3136 return moduleObjectType;
3137 }
3138
FormNamedAccessMetadata(varbinder::Variable const * prop)3139 ETSChecker::NamedAccessMeta ETSChecker::FormNamedAccessMetadata(varbinder::Variable const *prop)
3140 {
3141 CHECK_NOT_NULL(prop);
3142 const auto *field = prop->Declaration()->Node()->AsClassProperty();
3143 const auto *owner = field->Parent()->AsClassDefinition();
3144 auto *fieldId = field->Id();
3145 CHECK_NOT_NULL(fieldId);
3146 return {owner->TsType()->AsETSObjectType(), field->TsType(), fieldId->Name()};
3147 }
3148
ETSObjectTypeDeclNode(ETSChecker * checker,ETSObjectType * const objectType)3149 void ETSChecker::ETSObjectTypeDeclNode(ETSChecker *checker, ETSObjectType *const objectType)
3150 {
3151 auto *declNode = objectType->AsETSObjectType()->GetDeclNode();
3152 if (declNode == nullptr) {
3153 return;
3154 }
3155
3156 if (declNode->IsClassDefinition() && !declNode->AsClassDefinition()->IsClassDefinitionChecked()) {
3157 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
3158 checker->CheckClassDefinition(declNode->AsClassDefinition());
3159 }
3160 }
3161
CreateExtensionAccessorCall(ETSChecker * checker,ir::MemberExpression * expr,ArenaVector<ir::Expression * > && args)3162 ir::CallExpression *ETSChecker::CreateExtensionAccessorCall(ETSChecker *checker, ir::MemberExpression *expr,
3163 ArenaVector<ir::Expression *> &&args)
3164 {
3165 ir::Expression *callExpr = nullptr;
3166 if (expr->Object()->IsETSNewClassInstanceExpression()) {
3167 args.insert(args.begin(), expr->Object());
3168 callExpr =
3169 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
3170 checker->ProgramAllocNode<ir::CallExpression>(expr->Property(), std::move(args), nullptr, false, false);
3171 } else {
3172 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
3173 callExpr = checker->ProgramAllocNode<ir::CallExpression>(expr, std::move(args), nullptr, false, false);
3174 }
3175 ES2PANDA_ASSERT(callExpr != nullptr);
3176 callExpr->SetRange(expr->Range());
3177 return callExpr->AsCallExpression();
3178 }
3179
CheckTypeParameterVariance(ir::ClassDefinition * classDef)3180 void ETSChecker::CheckTypeParameterVariance(ir::ClassDefinition *classDef)
3181 {
3182 if (classDef->TypeParams() == nullptr) {
3183 return;
3184 }
3185
3186 Context().SetContainingClass(classDef->TsType()->AsETSObjectType());
3187 auto checkVariance = [this](VarianceFlag varianceFlag, ir::Expression *expression, Type *type) {
3188 Relation()->Result(RelationResult::TRUE);
3189 Relation()->SetNode(expression);
3190 Relation()->CheckVarianceRecursively(type, varianceFlag);
3191 Relation()->SetNode(nullptr);
3192 };
3193
3194 for (auto *it : classDef->Body()) {
3195 if (!it->IsClassProperty() || it->AsClassProperty()->TypeAnnotation() == nullptr) {
3196 continue;
3197 }
3198 // Readonly Fields may have out type parameters, otherwise fields should be invariant type parameters
3199 checkVariance(it->AsClassProperty()->IsReadonly() ? VarianceFlag::COVARIANT : VarianceFlag::INVARIANT,
3200 it->AsClassProperty()->TypeAnnotation(), it->AsClassProperty()->TsType());
3201 }
3202
3203 for (auto *it : classDef->Body()) {
3204 if (!it->IsMethodDefinition() || it->AsMethodDefinition()->IsConstructor()) {
3205 continue;
3206 }
3207 // Methods may have out type parameters as return types, and in type parameters as parameter types,(in)=>out
3208 checkVariance(VarianceFlag::COVARIANT, it->AsMethodDefinition()->Id(), it->Check(this));
3209 }
3210
3211 if (classDef->Super() != nullptr) {
3212 checkVariance(VarianceFlag::COVARIANT, classDef->Super(), classDef->Super()->Check(this));
3213 }
3214
3215 for (auto *implement : classDef->Implements()) {
3216 checkVariance(VarianceFlag::COVARIANT, implement, implement->Expr()->AsTypeNode()->Check(this));
3217 }
3218 }
3219
SetPreferredTypeIfPossible(ir::Expression * const expr,Type * const targetType)3220 void ETSChecker::SetPreferredTypeIfPossible(ir::Expression *const expr, Type *const targetType)
3221 {
3222 // Object expression requires that its type be set by the context before checking. in this case, the target type
3223 // provides that context.
3224 if (expr->IsObjectExpression()) {
3225 expr->AsObjectExpression()->SetPreferredType(targetType);
3226 }
3227
3228 if (expr->IsArrayExpression()) {
3229 expr->AsArrayExpression()->SetPreferredType(targetType);
3230 }
3231 }
3232
IntersectSignatureSets(const checker::ETSFunctionType * left,const checker::ETSFunctionType * right)3233 checker::ETSFunctionType *ETSChecker::IntersectSignatureSets(const checker::ETSFunctionType *left,
3234 const checker::ETSFunctionType *right)
3235 {
3236 auto sameSig = [this](checker::Signature *leftSig, checker::Signature *rightSig) {
3237 auto relation = Relation();
3238 if ((leftSig->Flags() & ~checker::SignatureFlags::FINAL) !=
3239 (rightSig->Flags() & ~checker::SignatureFlags::FINAL)) {
3240 return false;
3241 }
3242 return relation->SignatureIsIdenticalTo(rightSig, leftSig);
3243 };
3244
3245 if (left->CallSignatures().size() > right->CallSignatures().size()) {
3246 std::swap(left, right);
3247 }
3248
3249 ArenaVector<checker::Signature *> intersection {ProgramAllocator()->Adapter()};
3250
3251 for (const auto sig : left->CallSignatures()) {
3252 auto found = right->FindSpecificSignature(
3253 [sig, &sameSig](checker::Signature *otherSig) { return sameSig(sig, otherSig); });
3254 if (found != nullptr) {
3255 intersection.push_back(found);
3256 }
3257 }
3258
3259 return CreateETSMethodType(left->Name(), std::move(intersection));
3260 }
3261
3262 } // namespace ark::es2panda::checker
3263