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