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