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 "ETSchecker.h"
17
18#include "es2panda.h"
19#include "ir/base/classDefinition.h"
20#include "ir/expression.h"
21#include "ir/expressions/callExpression.h"
22#include "ir/ts/tsInterfaceDeclaration.h"
23#include "ir/statements/blockStatement.h"
24#include "varbinder/ETSBinder.h"
25#include "parser/program/program.h"
26#include "checker/ets/aliveAnalyzer.h"
27#include "checker/ets/assignAnalyzer.h"
28#include "checker/ets/etsWarningAnalyzer.h"
29#include "checker/types/globalTypesHolder.h"
30#include "ir/base/scriptFunction.h"
31#include "util/helpers.h"
32#include "evaluate/scopedDebugInfoPlugin.h"
33
34namespace ark::es2panda::checker {
35
36static util::StringView InitBuiltin(ETSChecker *checker, std::string_view signature)
37{
38    const auto varMap = checker->VarBinder()->TopScope()->Bindings();
39    const auto iterator = varMap.find(signature);
40    ASSERT(iterator != varMap.end());
41    auto *var = iterator->second;
42    Type *type {nullptr};
43    if (var->Declaration()->Node()->IsClassDefinition()) {
44        type = checker->BuildBasicClassProperties(var->Declaration()->Node()->AsClassDefinition());
45    } else {
46        ASSERT(var->Declaration()->Node()->IsTSInterfaceDeclaration());
47        type = checker->BuildBasicInterfaceProperties(var->Declaration()->Node()->AsTSInterfaceDeclaration());
48    }
49    checker->GetGlobalTypesHolder()->InitializeBuiltin(iterator->first, type);
50    return iterator->first;
51}
52
53static void SetupFunctionalInterface(ETSObjectType *type)
54{
55    type->AddObjectFlag(ETSObjectFlags::FUNCTIONAL);
56    auto *invoke = type->GetOwnProperty<PropertyType::INSTANCE_METHOD>(FUNCTIONAL_INTERFACE_INVOKE_METHOD_NAME);
57    auto *invokeType = invoke->TsType()->AsETSFunctionType();
58    ASSERT(invokeType->CallSignatures().size() == 1);
59    auto *signature = invokeType->CallSignatures()[0];
60    signature->AddSignatureFlag(SignatureFlags::FUNCTIONAL_INTERFACE_SIGNATURE);
61}
62
63static void SetupBuiltinMember(varbinder::Variable *var)
64{
65    auto *type = var->TsType();
66    if (type == nullptr || !type->IsETSObjectType()) {
67        return;
68    }
69}
70
71// NOLINTNEXTLINE(modernize-avoid-c-arrays)
72static constexpr std::string_view BUILTINS_TO_INIT[] = {
73    compiler::Signatures::BUILTIN_BOOLEAN_CLASS,
74    compiler::Signatures::BUILTIN_BYTE_CLASS,
75    compiler::Signatures::BUILTIN_CHAR_CLASS,
76    compiler::Signatures::BUILTIN_SHORT_CLASS,
77    compiler::Signatures::BUILTIN_INT_CLASS,
78    compiler::Signatures::BUILTIN_LONG_CLASS,
79    compiler::Signatures::BUILTIN_FLOAT_CLASS,
80    compiler::Signatures::BUILTIN_DOUBLE_CLASS,
81    compiler::Signatures::BUILTIN_FUNCTION0_CLASS,
82    compiler::Signatures::BUILTIN_FUNCTION1_CLASS,
83    compiler::Signatures::BUILTIN_FUNCTION2_CLASS,
84    compiler::Signatures::BUILTIN_FUNCTION3_CLASS,
85    compiler::Signatures::BUILTIN_FUNCTION4_CLASS,
86    compiler::Signatures::BUILTIN_FUNCTION5_CLASS,
87    compiler::Signatures::BUILTIN_FUNCTION6_CLASS,
88    compiler::Signatures::BUILTIN_FUNCTION7_CLASS,
89    compiler::Signatures::BUILTIN_FUNCTION8_CLASS,
90    compiler::Signatures::BUILTIN_FUNCTION9_CLASS,
91    compiler::Signatures::BUILTIN_FUNCTION10_CLASS,
92    compiler::Signatures::BUILTIN_FUNCTION11_CLASS,
93    compiler::Signatures::BUILTIN_FUNCTION12_CLASS,
94    compiler::Signatures::BUILTIN_FUNCTION13_CLASS,
95    compiler::Signatures::BUILTIN_FUNCTION14_CLASS,
96    compiler::Signatures::BUILTIN_FUNCTION15_CLASS,
97    compiler::Signatures::BUILTIN_FUNCTION16_CLASS,
98    compiler::Signatures::BUILTIN_FUNCTIONN_CLASS,
99    compiler::Signatures::BUILTIN_THROWING_FUNCTION0_CLASS,
100    compiler::Signatures::BUILTIN_THROWING_FUNCTION1_CLASS,
101    compiler::Signatures::BUILTIN_THROWING_FUNCTION2_CLASS,
102    compiler::Signatures::BUILTIN_THROWING_FUNCTION3_CLASS,
103    compiler::Signatures::BUILTIN_THROWING_FUNCTION4_CLASS,
104    compiler::Signatures::BUILTIN_THROWING_FUNCTION5_CLASS,
105    compiler::Signatures::BUILTIN_THROWING_FUNCTION6_CLASS,
106    compiler::Signatures::BUILTIN_THROWING_FUNCTION7_CLASS,
107    compiler::Signatures::BUILTIN_THROWING_FUNCTION8_CLASS,
108    compiler::Signatures::BUILTIN_THROWING_FUNCTION9_CLASS,
109    compiler::Signatures::BUILTIN_THROWING_FUNCTION10_CLASS,
110    compiler::Signatures::BUILTIN_THROWING_FUNCTION11_CLASS,
111    compiler::Signatures::BUILTIN_THROWING_FUNCTION12_CLASS,
112    compiler::Signatures::BUILTIN_THROWING_FUNCTION13_CLASS,
113    compiler::Signatures::BUILTIN_THROWING_FUNCTION14_CLASS,
114    compiler::Signatures::BUILTIN_THROWING_FUNCTION15_CLASS,
115    compiler::Signatures::BUILTIN_THROWING_FUNCTION16_CLASS,
116    compiler::Signatures::BUILTIN_THROWING_FUNCTIONN_CLASS,
117    compiler::Signatures::BUILTIN_RETHROWING_FUNCTION0_CLASS,
118    compiler::Signatures::BUILTIN_RETHROWING_FUNCTION1_CLASS,
119    compiler::Signatures::BUILTIN_RETHROWING_FUNCTION2_CLASS,
120    compiler::Signatures::BUILTIN_RETHROWING_FUNCTION3_CLASS,
121    compiler::Signatures::BUILTIN_RETHROWING_FUNCTION4_CLASS,
122    compiler::Signatures::BUILTIN_RETHROWING_FUNCTION5_CLASS,
123    compiler::Signatures::BUILTIN_RETHROWING_FUNCTION6_CLASS,
124    compiler::Signatures::BUILTIN_RETHROWING_FUNCTION7_CLASS,
125    compiler::Signatures::BUILTIN_RETHROWING_FUNCTION8_CLASS,
126    compiler::Signatures::BUILTIN_RETHROWING_FUNCTION9_CLASS,
127    compiler::Signatures::BUILTIN_RETHROWING_FUNCTION10_CLASS,
128    compiler::Signatures::BUILTIN_RETHROWING_FUNCTION11_CLASS,
129    compiler::Signatures::BUILTIN_RETHROWING_FUNCTION12_CLASS,
130    compiler::Signatures::BUILTIN_RETHROWING_FUNCTION13_CLASS,
131    compiler::Signatures::BUILTIN_RETHROWING_FUNCTION14_CLASS,
132    compiler::Signatures::BUILTIN_RETHROWING_FUNCTION15_CLASS,
133    compiler::Signatures::BUILTIN_RETHROWING_FUNCTION16_CLASS,
134    compiler::Signatures::BUILTIN_RETHROWING_FUNCTIONN_CLASS,
135};
136
137void ETSChecker::InitializeBuiltins(varbinder::ETSBinder *varbinder)
138{
139    if (HasStatus(CheckerStatus::BUILTINS_INITIALIZED)) {
140        return;
141    }
142
143    const auto varMap = varbinder->TopScope()->Bindings();
144
145    auto const objectName = InitBuiltin(this, compiler::Signatures::BUILTIN_OBJECT_CLASS);
146
147    for (auto sig : BUILTINS_TO_INIT) {
148        InitBuiltin(this, sig);
149    }
150
151    for (size_t id = static_cast<size_t>(GlobalTypeId::ETS_THROWING_FUNCTION0_CLASS), nargs = 0;
152         id <= static_cast<size_t>(GlobalTypeId::ETS_THROWING_FUNCTIONN_CLASS); id++, nargs++) {
153        auto *type = GetGlobalTypesHolder()
154                         ->GlobalFunctionBuiltinType(nargs, ir::ScriptFunctionFlags::THROWS)
155                         ->AsETSObjectType();
156        SetupFunctionalInterface(type);
157    }
158
159    for (size_t id = static_cast<size_t>(GlobalTypeId::ETS_RETHROWING_FUNCTION0_CLASS), nargs = 0;
160         id <= static_cast<size_t>(GlobalTypeId::ETS_RETHROWING_FUNCTIONN_CLASS); id++, nargs++) {
161        auto *type = GetGlobalTypesHolder()
162                         ->GlobalFunctionBuiltinType(nargs, ir::ScriptFunctionFlags::RETHROWS)
163                         ->AsETSObjectType();
164        SetupFunctionalInterface(type);
165        // note(gergocs): type->Interfaces().front() should be the same as the type in throwing functions
166        // and adding the functional flag to the interface should be deleted
167        type->Interfaces().front()->AddObjectFlag(ETSObjectFlags::FUNCTIONAL);
168    }
169
170    for (size_t id = static_cast<size_t>(GlobalTypeId::ETS_FUNCTION0_CLASS), nargs = 0;
171         id <= static_cast<size_t>(GlobalTypeId::ETS_FUNCTIONN_CLASS); id++, nargs++) {
172        auto *type =
173            GetGlobalTypesHolder()->GlobalFunctionBuiltinType(nargs, ir::ScriptFunctionFlags::NONE)->AsETSObjectType();
174        SetupFunctionalInterface(type);
175        // note(gergocs): type->Interfaces().front() should be the same as the type in rethrowing functions
176        // and adding the functional flag to the interface should be deleted
177        type->Interfaces().front()->AddObjectFlag(ETSObjectFlags::FUNCTIONAL);
178        type->Interfaces().front()->Interfaces().front()->AddObjectFlag(ETSObjectFlags::FUNCTIONAL);
179    }
180
181    for (const auto &[name, var] : varMap) {
182        (void)name;
183        SetupBuiltinMember(var);
184    }
185
186    for (const auto &[name, var] : varMap) {
187        if (name == objectName) {
188            continue;
189        }
190
191        if (var->HasFlag(varbinder::VariableFlags::BUILTIN_TYPE)) {
192            if (var->TsType() == nullptr) {
193                InitializeBuiltin(var, name);
194            } else {
195                GetGlobalTypesHolder()->InitializeBuiltin(name, var->TsType());
196            }
197        }
198    }
199
200    AddStatus(CheckerStatus::BUILTINS_INITIALIZED);
201}
202
203void ETSChecker::InitializeBuiltin(varbinder::Variable *var, const util::StringView &name)
204{
205    Type *type {nullptr};
206    if (var->Declaration()->Node()->IsClassDefinition()) {
207        type = BuildBasicClassProperties(var->Declaration()->Node()->AsClassDefinition());
208    } else {
209        ASSERT(var->Declaration()->Node()->IsTSInterfaceDeclaration());
210        type = BuildBasicInterfaceProperties(var->Declaration()->Node()->AsTSInterfaceDeclaration());
211    }
212    GetGlobalTypesHolder()->InitializeBuiltin(name, type);
213}
214
215bool ETSChecker::StartChecker(varbinder::VarBinder *varbinder, const CompilerOptions &options)
216{
217    Initialize(varbinder);
218
219    if (options.parseOnly) {
220        return false;
221    }
222
223    auto *etsBinder = varbinder->AsETSBinder();
224    InitializeBuiltins(etsBinder);
225
226    for (auto &entry : etsBinder->DynamicImportVars()) {
227        auto &data = entry.second;
228        if (data.import->IsPureDynamic()) {
229            data.variable->SetTsType(GlobalBuiltinDynamicType(data.import->Language()));
230        }
231    }
232
233    bool isEvalMode = (debugInfoPlugin_ != nullptr);
234    if (UNLIKELY(isEvalMode)) {
235        debugInfoPlugin_->PreCheck();
236    }
237
238    CheckProgram(Program(), true);
239
240    if (UNLIKELY(isEvalMode)) {
241        debugInfoPlugin_->PostCheck();
242    }
243
244    BuildDynamicImportClass();
245
246#ifndef NDEBUG
247    for (auto *func : varbinder->Functions()) {
248        ASSERT(!func->Node()->AsScriptFunction()->Scope()->InternalName().Empty());
249    }
250#endif
251
252    if (options.dumpCheckedAst) {
253        std::cout << Program()->Dump() << std::endl;
254    }
255
256    if (options.etsHasWarnings) {
257        CheckWarnings(Program(), options);
258    }
259
260    return !ErrorLogger()->IsAnyError();
261}
262
263evaluate::ScopedDebugInfoPlugin *ETSChecker::GetDebugInfoPlugin()
264{
265    return debugInfoPlugin_;
266}
267
268const evaluate::ScopedDebugInfoPlugin *ETSChecker::GetDebugInfoPlugin() const
269{
270    return debugInfoPlugin_;
271}
272
273void ETSChecker::SetDebugInfoPlugin(evaluate::ScopedDebugInfoPlugin *debugInfo)
274{
275    debugInfoPlugin_ = debugInfo;
276}
277
278void ETSChecker::CheckProgram(parser::Program *program, bool runAnalysis)
279{
280    auto *savedProgram = Program();
281    SetProgram(program);
282
283    for (auto &[_, extPrograms] : program->ExternalSources()) {
284        (void)_;
285        for (auto *extProg : extPrograms) {
286            checker::SavedCheckerContext savedContext(this, Context().Status(), Context().ContainingClass());
287            AddStatus(checker::CheckerStatus::IN_EXTERNAL);
288            CheckProgram(extProg, VarBinder()->IsGenStdLib());
289        }
290    }
291
292    ASSERT(Program()->Ast()->IsProgram());
293    Program()->Ast()->Check(this);
294
295    if (ErrorLogger()->IsAnyError()) {
296        return;
297    }
298
299    if (runAnalysis) {
300        AliveAnalyzer aliveAnalyzer(Program()->Ast(), this);
301        AssignAnalyzer(this).Analyze(Program()->Ast());
302    }
303
304    ASSERT(VarBinder()->AsETSBinder()->GetExternalRecordTable().find(program)->second);
305
306    SetProgram(savedProgram);
307}
308
309void ETSChecker::CheckWarnings(parser::Program *program, const CompilerOptions &options)
310{
311    const auto etsWarningCollection = options.etsWarningCollection;
312    for (const auto warning : etsWarningCollection) {
313        ETSWarningAnalyzer(Program()->Ast(), program, warning, options.etsWerror);
314    }
315}
316
317Type *ETSChecker::CheckTypeCached(ir::Expression *expr)
318{
319    if (expr->TsType() == nullptr) {
320        expr->SetTsType(expr->Check(this));
321    }
322
323    return expr->TsType();
324}
325
326template <typename... Args>
327ETSObjectType *ETSChecker::AsETSObjectType(Type *(GlobalTypesHolder::*typeFunctor)(Args...), Args... args) const
328{
329    auto *ret = (GetGlobalTypesHolder()->*typeFunctor)(args...);
330    return ret != nullptr ? ret->AsETSObjectType() : nullptr;
331}
332
333Type *ETSChecker::GlobalByteType() const
334{
335    return GetGlobalTypesHolder()->GlobalByteType();
336}
337
338Type *ETSChecker::GlobalShortType() const
339{
340    return GetGlobalTypesHolder()->GlobalShortType();
341}
342
343Type *ETSChecker::GlobalIntType() const
344{
345    return GetGlobalTypesHolder()->GlobalIntType();
346}
347
348Type *ETSChecker::GlobalLongType() const
349{
350    return GetGlobalTypesHolder()->GlobalLongType();
351}
352
353Type *ETSChecker::GlobalFloatType() const
354{
355    return GetGlobalTypesHolder()->GlobalFloatType();
356}
357
358Type *ETSChecker::GlobalDoubleType() const
359{
360    return GetGlobalTypesHolder()->GlobalDoubleType();
361}
362
363Type *ETSChecker::GlobalCharType() const
364{
365    return GetGlobalTypesHolder()->GlobalCharType();
366}
367
368Type *ETSChecker::GlobalETSBooleanType() const
369{
370    return GetGlobalTypesHolder()->GlobalETSBooleanType();
371}
372
373Type *ETSChecker::GlobalVoidType() const
374{
375    return GetGlobalTypesHolder()->GlobalETSVoidType();
376}
377
378Type *ETSChecker::GlobalETSNullType() const
379{
380    return GetGlobalTypesHolder()->GlobalETSNullType();
381}
382
383Type *ETSChecker::GlobalETSUndefinedType() const
384{
385    return GetGlobalTypesHolder()->GlobalETSUndefinedType();
386}
387
388Type *ETSChecker::GlobalETSStringLiteralType() const
389{
390    return GetGlobalTypesHolder()->GlobalETSStringLiteralType();
391}
392
393Type *ETSChecker::GlobalETSBigIntType() const
394{
395    return GetGlobalTypesHolder()->GlobalETSBigIntBuiltinType();
396}
397
398Type *ETSChecker::GlobalWildcardType() const
399{
400    return GetGlobalTypesHolder()->GlobalWildcardType();
401}
402
403ETSObjectType *ETSChecker::GlobalETSObjectType() const
404{
405    return AsETSObjectType(&GlobalTypesHolder::GlobalETSObjectType);
406}
407
408ETSUnionType *ETSChecker::GlobalETSNullishType() const
409{
410    auto *ret = (GetGlobalTypesHolder()->*&GlobalTypesHolder::GlobalETSNullishType)();
411    return ret != nullptr ? ret->AsETSUnionType() : nullptr;
412}
413
414ETSUnionType *ETSChecker::GlobalETSNullishObjectType() const
415{
416    auto *ret = (GetGlobalTypesHolder()->*&GlobalTypesHolder::GlobalETSNullishObjectType)();
417    return ret != nullptr ? ret->AsETSUnionType() : nullptr;
418}
419
420ETSObjectType *ETSChecker::GlobalBuiltinETSStringType() const
421{
422    return AsETSObjectType(&GlobalTypesHolder::GlobalETSStringBuiltinType);
423}
424
425ETSObjectType *ETSChecker::GlobalBuiltinETSBigIntType() const
426{
427    return AsETSObjectType(&GlobalTypesHolder::GlobalETSBigIntBuiltinType);
428}
429
430ETSObjectType *ETSChecker::GlobalBuiltinTypeType() const
431{
432    return AsETSObjectType(&GlobalTypesHolder::GlobalTypeBuiltinType);
433}
434
435ETSObjectType *ETSChecker::GlobalBuiltinExceptionType() const
436{
437    return AsETSObjectType(&GlobalTypesHolder::GlobalExceptionBuiltinType);
438}
439
440ETSObjectType *ETSChecker::GlobalBuiltinErrorType() const
441{
442    return AsETSObjectType(&GlobalTypesHolder::GlobalErrorBuiltinType);
443}
444
445ETSObjectType *ETSChecker::GlobalStringBuilderBuiltinType() const
446{
447    return AsETSObjectType(&GlobalTypesHolder::GlobalStringBuilderBuiltinType);
448}
449
450ETSObjectType *ETSChecker::GlobalBuiltinPromiseType() const
451{
452    return AsETSObjectType(&GlobalTypesHolder::GlobalPromiseBuiltinType);
453}
454
455ETSObjectType *ETSChecker::GlobalBuiltinJSRuntimeType() const
456{
457    return AsETSObjectType(&GlobalTypesHolder::GlobalJSRuntimeBuiltinType);
458}
459
460ETSObjectType *ETSChecker::GlobalBuiltinJSValueType() const
461{
462    return AsETSObjectType(&GlobalTypesHolder::GlobalJSValueBuiltinType);
463}
464
465ETSObjectType *ETSChecker::GlobalBuiltinFunctionType(size_t nargs, ir::ScriptFunctionFlags flags) const
466{
467    return AsETSObjectType(&GlobalTypesHolder::GlobalFunctionBuiltinType, nargs, flags);
468}
469
470size_t ETSChecker::GlobalBuiltinFunctionTypeVariadicThreshold() const
471{
472    return GetGlobalTypesHolder()->VariadicFunctionTypeThreshold();
473}
474
475ETSObjectType *ETSChecker::GlobalBuiltinDynamicType(Language lang) const
476{
477    if (lang.GetId() == Language::Id::JS) {
478        return GlobalBuiltinJSValueType();
479    }
480    return nullptr;
481}
482
483ETSObjectType *ETSChecker::GlobalBuiltinBoxType(Type *contents)
484{
485    switch (TypeKind(contents)) {
486        case TypeFlag::ETS_BOOLEAN:
487            return AsETSObjectType(&GlobalTypesHolder::GlobalBooleanBoxBuiltinType);
488        case TypeFlag::BYTE:
489            return AsETSObjectType(&GlobalTypesHolder::GlobalByteBoxBuiltinType);
490        case TypeFlag::CHAR:
491            return AsETSObjectType(&GlobalTypesHolder::GlobalCharBoxBuiltinType);
492        case TypeFlag::SHORT:
493            return AsETSObjectType(&GlobalTypesHolder::GlobalShortBoxBuiltinType);
494        case TypeFlag::INT:
495            return AsETSObjectType(&GlobalTypesHolder::GlobalIntBoxBuiltinType);
496        case TypeFlag::LONG:
497            return AsETSObjectType(&GlobalTypesHolder::GlobalLongBoxBuiltinType);
498        case TypeFlag::FLOAT:
499            return AsETSObjectType(&GlobalTypesHolder::GlobalFloatBoxBuiltinType);
500        case TypeFlag::DOUBLE:
501            return AsETSObjectType(&GlobalTypesHolder::GlobalDoubleBoxBuiltinType);
502        default: {
503            auto *base = AsETSObjectType(&GlobalTypesHolder::GlobalBoxBuiltinType);
504            auto *substitution = NewSubstitution();
505            substitution->emplace(base->TypeArguments()[0]->AsETSTypeParameter(), contents);
506            return base->Substitute(Relation(), substitution);
507        }
508    }
509}
510
511const checker::WrapperDesc &ETSChecker::PrimitiveWrapper() const
512{
513    return primitiveWrappers_.Wrappers();
514}
515
516GlobalArraySignatureMap &ETSChecker::GlobalArrayTypes()
517{
518    return globalArraySignatures_;
519}
520
521const GlobalArraySignatureMap &ETSChecker::GlobalArrayTypes() const
522{
523    return globalArraySignatures_;
524}
525
526Type *ETSChecker::GlobalTypeError() const
527{
528    return GetGlobalTypesHolder()->GlobalTypeError();
529}
530
531void ETSChecker::HandleUpdatedCallExpressionNode(ir::CallExpression *callExpr)
532{
533    VarBinder()->AsETSBinder()->HandleCustomNodes(callExpr);
534}
535
536Type *ETSChecker::SelectGlobalIntegerTypeForNumeric(Type *type)
537{
538    switch (ETSType(type)) {
539        case checker::TypeFlag::FLOAT: {
540            return GlobalIntType();
541        }
542        case checker::TypeFlag::DOUBLE: {
543            return GlobalLongType();
544        }
545        default: {
546            return type;
547        }
548    }
549}
550
551}  // namespace ark::es2panda::checker
552