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