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 <utility>
17#include "checker/ETSchecker.h"
18
19#include "compiler/lowering/util.h"
20#include "varbinder/declaration.h"
21#include "varbinder/varbinder.h"
22#include "varbinder/ETSBinder.h"
23#include "checker/types/ets/etsDynamicFunctionType.h"
24#include "checker/ets/dynamic/dynamicCall.h"
25#include "compiler/lowering/scopesInit/scopesInitPhase.h"
26#include "ir/base/classProperty.h"
27#include "ir/base/classStaticBlock.h"
28#include "ir/base/methodDefinition.h"
29#include "ir/base/scriptFunction.h"
30#include "ir/expressions/assignmentExpression.h"
31#include "ir/expressions/callExpression.h"
32#include "ir/expressions/functionExpression.h"
33#include "ir/expressions/identifier.h"
34#include "ir/expressions/thisExpression.h"
35#include "ir/expressions/memberExpression.h"
36#include "ir/ets/etsPrimitiveType.h"
37#include "ir/ts/tsAsExpression.h"
38#include "ir/statements/blockStatement.h"
39#include "ir/statements/classDeclaration.h"
40#include "ir/statements/expressionStatement.h"
41#include "ir/statements/returnStatement.h"
42#include "parser/program/program.h"
43#include "util/helpers.h"
44#include "util/language.h"
45#include "generated/signatures.h"
46#include "ir/ets/etsParameterExpression.h"
47
48namespace ark::es2panda::checker {
49
50void ProcessCheckerNode(ETSChecker *checker, ir::AstNode *node)
51{
52    auto scope = compiler::NearestScope(node);
53    if (scope->IsGlobalScope()) {
54        // NOTE(aleksisch): All classes are contained in ETSGlobal class scope (not just Global scope),
55        // however it's parent is ETSScript. It should be fixed
56        scope = checker->VarBinder()->Program()->GlobalClassScope();
57    }
58
59    auto expressionCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(checker->VarBinder(), scope);
60    checker->VarBinder()->AsETSBinder()->ResolveReference(node);
61
62    if (node->IsMethodDefinition()) {
63        // NOTE(aleksisch): This should be done in varbinder,
64        // however right now checker do it when called on ClassDefinition
65        auto method = node->AsMethodDefinition();
66        auto func = method->Value()->AsFunctionExpression()->Function();
67        func->Id()->SetVariable(method->Id()->Variable());
68    }
69    ScopeContext checkerScope(checker, scope);
70    node->Check(checker);
71}
72
73void ProcessScopesNode(ETSChecker *checker, ir::AstNode *node)
74{
75    auto *scope = compiler::NearestScope(node);
76    if (scope->IsGlobalScope()) {
77        // NOTE(aleksisch): All classes are contained in ETSGlobal scope,
78        // however it's parent is ETSScript (not ETSGlobal). It should be fixed
79        scope = checker->VarBinder()->Program()->GlobalClassScope();
80    }
81    auto expressionCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(checker->VarBinder(), scope);
82    compiler::InitScopesPhaseETS::RunExternalNode(node, checker->VarBinder());
83}
84
85ir::ETSParameterExpression *ETSChecker::AddParam(util::StringView name, ir::TypeNode *type)
86{
87    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
88    auto *paramIdent = AllocNode<ir::Identifier>(name, Allocator());
89    if (type != nullptr) {
90        paramIdent->SetTsTypeAnnotation(type);
91        type->SetParent(paramIdent);
92    }
93    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
94    return AllocNode<ir::ETSParameterExpression>(paramIdent, nullptr);
95}
96
97template <typename T>
98ir::MethodDefinition *ETSChecker::CreateDynamicCallIntrinsic(ir::Expression *callee, const ArenaVector<T *> &arguments,
99                                                             Language lang)
100{
101    ArenaVector<ir::Expression *> params(Allocator()->Adapter());
102
103    auto dynamicTypeNode = AllocNode<ir::OpaqueTypeNode>(GlobalBuiltinDynamicType(lang));
104    auto intTypeNode = AllocNode<ir::ETSPrimitiveType>(ir::PrimitiveType::INT);
105
106    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
107    auto *objParam = AddParam("obj", dynamicTypeNode);
108    params.push_back(objParam);
109
110    ir::ETSParameterExpression *param2;
111    if (!DynamicCall::IsByValue(VarBinder()->AsETSBinder(), callee)) {
112        // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
113        param2 = AddParam("qname_start", intTypeNode);
114        params.push_back(param2);
115        // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
116        param2 = AddParam("qname_len", intTypeNode->Clone(Allocator(), nullptr));
117    } else {
118        // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
119        param2 = AddParam("this", dynamicTypeNode->Clone(Allocator(), nullptr));
120    }
121
122    params.push_back(param2);
123
124    for (size_t i = 0; i < arguments.size(); i++) {
125        util::UString paramName("p" + std::to_string(i), Allocator());
126        auto paramType = arguments[i]->TsType()->IsLambdaObject()
127                             ? dynamicTypeNode->Clone(Allocator(), nullptr)
128                             : AllocNode<ir::OpaqueTypeNode>(arguments[i]->TsType());
129        // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
130        params.emplace_back(AddParam(paramName.View(), paramType));
131    }
132
133    auto funcSignature =
134        ir::FunctionSignature(nullptr, std::move(params), dynamicTypeNode->Clone(Allocator(), nullptr));
135    auto *func = AllocNode<ir::ScriptFunction>(
136        Allocator(), ir::ScriptFunction::ScriptFunctionData {nullptr, std::move(funcSignature),
137                                                             ir::ScriptFunctionFlags::METHOD, ir::ModifierFlags::NONE});
138
139    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
140    auto *name = AllocNode<ir::Identifier>("invoke", Allocator());
141    func->SetIdent(name);
142
143    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
144    auto *funcExpr = AllocNode<ir::FunctionExpression>(func);
145
146    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
147    auto *method = AllocNode<ir::MethodDefinition>(
148        ir::MethodDefinitionKind::METHOD, func->Id()->Clone(Allocator(), nullptr), funcExpr,
149        ir::ModifierFlags::PUBLIC | ir::ModifierFlags::NATIVE | ir::ModifierFlags::STATIC, Allocator(), false);
150    return method;
151}
152
153static void ToString(ETSChecker *checker, const ArenaVector<ir::Expression *> &arguments, std::stringstream &ss)
154{
155    for (auto *arg : arguments) {
156        auto *type = arg->Check(checker);
157        ss << "-";
158        type->ToString(ss);
159    }
160}
161
162static void ToString([[maybe_unused]] ETSChecker *checker, const ArenaVector<varbinder::LocalVariable *> &arguments,
163                     std::stringstream &ss)
164{
165    for (auto *arg : arguments) {
166        auto *type = arg->TsType();
167        ss << "-";
168        type->ToString(ss);
169    }
170}
171
172template <typename T>
173Signature *ETSChecker::ResolveDynamicCallExpression(ir::Expression *callee, const ArenaVector<T *> &arguments,
174                                                    Language lang, bool isConstruct)
175{
176    auto &dynamicIntrinsics = *DynamicCallIntrinsics(isConstruct);
177
178    auto mapIt = dynamicIntrinsics.find(lang);
179    if (mapIt == dynamicIntrinsics.cend()) {
180        std::tie(mapIt, std::ignore) = dynamicIntrinsics.emplace(lang, Allocator()->Adapter());
181    }
182
183    auto &map = mapIt->second;
184
185    std::stringstream ss;
186    ss << "dyncall";
187    if (DynamicCall::IsByValue(VarBinder()->AsETSBinder(), callee)) {
188        ss << "-byvalue";
189    } else {
190        const auto callNames = DynamicCall::ResolveCall(VarBinder()->AsETSBinder(), callee);
191        DynamicCallNames(isConstruct)->try_emplace(callNames.name, 0);
192    }
193
194    ToString(this, arguments, ss);
195
196    auto key = ss.str();
197    auto it = map.find(util::StringView(key));
198    if (it == map.end()) {
199        auto klass = GetDynamicClass(lang, isConstruct);
200        // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
201        auto *method = CreateDynamicCallIntrinsic(callee, arguments, lang);
202        auto props = ArenaVector<ir::AstNode *>(Allocator()->Adapter());
203        props.emplace_back(method);
204        klass->Definition()->AddProperties(std::move(props));
205
206        {
207            auto prevClass = VarBinder()->AsETSBinder()->GetGlobalRecordTable()->ClassDefinition();
208            VarBinder()->AsETSBinder()->GetGlobalRecordTable()->SetClassDefinition(klass->Definition());
209            ProcessScopesNode(this, method);
210            ProcessCheckerNode(this, method);
211            VarBinder()->AsETSBinder()->GetGlobalRecordTable()->SetClassDefinition(prevClass);
212        }
213        method->Function()->Signature()->SetReturnType(GlobalBuiltinDynamicType(lang));
214
215        map.emplace(util::UString(key, Allocator()).View(), method->Function());
216        return method->Function()->Signature();
217    }
218
219    return it->second->Signature();
220}
221
222template Signature *ETSChecker::ResolveDynamicCallExpression<ir::Expression>(
223    ir::Expression *callee, const ArenaVector<ir::Expression *> &arguments, Language lang, bool isConstruct);
224
225template Signature *ETSChecker::ResolveDynamicCallExpression<varbinder::LocalVariable>(
226    ir::Expression *callee, const ArenaVector<varbinder::LocalVariable *> &arguments, Language lang, bool isConstruct);
227
228std::pair<ir::ScriptFunction *, ir::Identifier *> ETSChecker::CreateStaticScriptFunction(
229    ClassInitializerBuilder const &builder)
230{
231    ArenaVector<ir::Statement *> statements(Allocator()->Adapter());
232    ArenaVector<ir::Expression *> params(Allocator()->Adapter());
233
234    ir::ScriptFunction *func;
235    ir::Identifier *id;
236
237    builder(&statements, nullptr);
238    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
239    auto *body = AllocNode<ir::BlockStatement>(Allocator(), std::move(statements));
240    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
241    id = AllocNode<ir::Identifier>(compiler::Signatures::CCTOR, Allocator());
242    auto signature = ir::FunctionSignature(nullptr, std::move(params), nullptr);
243    // clang-format off
244    func = AllocNode<ir::ScriptFunction>(
245        Allocator(), ir::ScriptFunction::ScriptFunctionData {
246                        body,
247                        std::move(signature),
248                        ir::ScriptFunctionFlags::STATIC_BLOCK | ir::ScriptFunctionFlags::EXPRESSION,
249                        ir::ModifierFlags::STATIC,
250                     });
251    // clang-format on
252    func->SetIdent(id);
253
254    return std::make_pair(func, id);
255}
256
257std::pair<ir::ScriptFunction *, ir::Identifier *> ETSChecker::CreateScriptFunction(
258    ClassInitializerBuilder const &builder)
259{
260    ArenaVector<ir::Statement *> statements(Allocator()->Adapter());
261    ArenaVector<ir::Expression *> params(Allocator()->Adapter());
262
263    ir::ScriptFunction *func;
264    ir::Identifier *id;
265
266    builder(&statements, &params);
267    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
268    auto *body = AllocNode<ir::BlockStatement>(Allocator(), std::move(statements));
269    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
270    id = AllocNode<ir::Identifier>(compiler::Signatures::CTOR, Allocator());
271    auto funcSignature = ir::FunctionSignature(nullptr, std::move(params), nullptr);
272    func = AllocNode<ir::ScriptFunction>(Allocator(),
273                                         ir::ScriptFunction::ScriptFunctionData {
274                                             body, std::move(funcSignature),
275                                             ir::ScriptFunctionFlags::CONSTRUCTOR | ir::ScriptFunctionFlags::EXPRESSION,
276                                             ir::ModifierFlags::PUBLIC});
277    func->SetIdent(id);
278
279    return std::make_pair(func, id);
280}
281
282ir::ClassStaticBlock *ETSChecker::CreateClassStaticInitializer(const ClassInitializerBuilder &builder,
283                                                               [[maybe_unused]] ETSObjectType *type)
284{
285    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
286    auto [func, id] = CreateStaticScriptFunction(builder);
287
288    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
289    auto *funcExpr = AllocNode<ir::FunctionExpression>(func);
290
291    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
292    auto *staticBlock = AllocNode<ir::ClassStaticBlock>(funcExpr, Allocator());
293    staticBlock->AddModifier(ir::ModifierFlags::STATIC);
294
295    return staticBlock;
296}
297
298ir::MethodDefinition *ETSChecker::CreateClassInstanceInitializer(const ClassInitializerBuilder &builder,
299                                                                 [[maybe_unused]] ETSObjectType *type)
300{
301    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
302    auto [func, id] = CreateScriptFunction(builder);
303
304    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
305    auto *funcExpr = AllocNode<ir::FunctionExpression>(func);
306
307    auto *ctor =
308        // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
309        AllocNode<ir::MethodDefinition>(ir::MethodDefinitionKind::CONSTRUCTOR, id->Clone(Allocator(), nullptr),
310                                        funcExpr, ir::ModifierFlags::NONE, Allocator(), false);
311    return ctor;
312}
313
314ir::ClassStaticBlock *ETSChecker::CreateDynamicCallClassInitializer(Language lang, bool isConstruct)
315{
316    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
317    return CreateClassStaticInitializer([this, lang,
318                                         isConstruct](ArenaVector<ir::Statement *> *statements,
319                                                      [[maybe_unused]] ArenaVector<ir::Expression *> *params) {
320        auto [builtin_class_name, builtin_method_name] =
321            util::Helpers::SplitSignature(isConstruct ? compiler::Signatures::Dynamic::InitNewClassBuiltin(lang)
322                                                      : compiler::Signatures::Dynamic::InitCallClassBuiltin(lang));
323        // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
324        auto *classId = AllocNode<ir::Identifier>(builtin_class_name, Allocator());
325        // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
326        auto *methodId = AllocNode<ir::Identifier>(builtin_method_name, Allocator());
327        methodId->SetReference();
328        // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
329        auto *callee =
330            AllocNode<ir::MemberExpression>(classId, methodId, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
331
332        // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
333        auto *initCall = AllocNode<ir::CallExpression>(callee, ArenaVector<ir::Expression *>(Allocator()->Adapter()),
334                                                       nullptr, false);
335
336        // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
337        statements->push_back(AllocNode<ir::ExpressionStatement>(initCall));
338    });
339}
340
341ir::ClassDeclaration *ETSChecker::BuildClass(util::StringView name, const ClassBuilder &builder)
342{
343    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
344    auto *classId = AllocNode<ir::Identifier>(name, Allocator());
345
346    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
347    auto *classDef = AllocNode<ir::ClassDefinition>(Allocator(), classId, ir::ClassDefinitionModifiers::CLASS_DECL,
348                                                    ir::ModifierFlags::NONE, Language(Language::Id::ETS));
349
350    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
351    auto *classDecl = AllocNode<ir::ClassDeclaration>(classDef, Allocator());
352
353    VarBinder()->Program()->Ast()->Statements().push_back(classDecl);
354    classDecl->SetParent(VarBinder()->Program()->Ast());
355
356    varbinder::BoundContext boundCtx(VarBinder()->AsETSBinder()->GetGlobalRecordTable(), classDef);
357
358    ArenaVector<ir::AstNode *> classBody(Allocator()->Adapter());
359
360    builder(&classBody);
361
362    classDef->AddProperties(std::move(classBody));
363
364    ProcessScopesNode(this, classDecl);
365    ProcessCheckerNode(this, classDecl);
366    return classDecl;
367}
368
369ir::ClassProperty *ETSChecker::CreateStaticReadonlyField(const char *name)
370{
371    auto *fieldIdent = AllocNode<ir::Identifier>(name, Allocator());
372    // NOTE: remove const when readonly is properly supported
373    auto flags =
374        ir::ModifierFlags::STATIC | ir::ModifierFlags::PRIVATE | ir::ModifierFlags::READONLY | ir::ModifierFlags::CONST;
375    auto *field = AllocNode<ir::ClassProperty>(
376        fieldIdent, nullptr, AllocNode<ir::ETSPrimitiveType>(ir::PrimitiveType::INT), flags, Allocator(), false);
377
378    return field;
379}
380
381ir::ClassDeclaration *ETSChecker::GetDynamicClass(Language lang, bool isConstruct)
382{
383    auto &klasses = dynamicClasses_[static_cast<size_t>(isConstruct)];
384    if (klasses.count(lang) != 0U) {
385        return klasses[lang];
386    }
387    auto className =
388        isConstruct ? compiler::Signatures::Dynamic::NewClass(lang) : compiler::Signatures::Dynamic::CallClass(lang);
389    auto klass = BuildClass(className, [this, lang, isConstruct](ArenaVector<ir::AstNode *> *classBody) {
390        // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
391        classBody->push_back(CreateStaticReadonlyField("qname_start_from"));
392        // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
393        classBody->push_back(CreateDynamicCallClassInitializer(lang, isConstruct));
394    });
395    klasses.emplace(lang, klass);
396    return klass;
397}
398
399void ETSChecker::ClassInitializerFromImport(ir::ETSImportDeclaration *import, ArenaVector<ir::Statement *> *statements)
400{
401    auto builtin = compiler::Signatures::Dynamic::LoadModuleBuiltin(import->Language());
402    auto [builtin_class_name, builtin_method_name] = util::Helpers::SplitSignature(builtin);
403    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
404    auto *classId = AllocNode<ir::Identifier>(builtin_class_name, Allocator());
405    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
406    auto *methodId = AllocNode<ir::Identifier>(builtin_method_name, Allocator());
407    methodId->SetReference();
408    auto *callee =
409        // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
410        AllocNode<ir::MemberExpression>(classId, methodId, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
411
412    // Note(rsipka): this check could be avoided with appropriate language extensions
413    ArenaVector<ir::Expression *> callParams(Allocator()->Adapter());
414    if (ark::os::file::File::IsRegularFile(import->ResolvedSource()->Str().Mutf8())) {
415        callParams.push_back(AllocNode<ir::StringLiteral>(
416            util::UString(ark::os::RemoveExtension(import->ResolvedSource()->Str().Mutf8()), Allocator()).View()));
417    } else {
418        callParams.push_back(import->ResolvedSource());
419    }
420    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
421    auto *loadCall = AllocNode<ir::CallExpression>(callee, std::move(callParams), nullptr, false);
422    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
423    auto *moduleClassId = AllocNode<ir::Identifier>(compiler::Signatures::DYNAMIC_MODULE_CLASS, Allocator());
424    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
425    auto *fieldId = AllocNode<ir::Identifier>(import->AssemblerName(), Allocator());
426    fieldId->SetReference();
427    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
428    auto *property = AllocNode<ir::MemberExpression>(moduleClassId, fieldId, ir::MemberExpressionKind::PROPERTY_ACCESS,
429                                                     false, false);
430
431    auto *initializer =
432        AllocNode<ir::AssignmentExpression>(property, loadCall, lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
433    statements->push_back(AllocNode<ir::ExpressionStatement>(initializer));
434}
435
436ir::ClassStaticBlock *ETSChecker::CreateDynamicModuleClassInitializer(
437    const std::vector<ir::ETSImportDeclaration *> &imports)
438{
439    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
440    return CreateClassStaticInitializer([this, imports](ArenaVector<ir::Statement *> *statements,
441                                                        [[maybe_unused]] ArenaVector<ir::Expression *> *params) {
442        for (auto *import : imports) {
443            ClassInitializerFromImport(import, statements);
444        }
445    });
446}
447
448ir::MethodDefinition *ETSChecker::CreateClassMethod(const std::string_view name, ir::ScriptFunctionFlags funcFlags,
449                                                    ir::ModifierFlags modifierFlags, const MethodBuilder &builder)
450{
451    ArenaVector<ir::Expression *> params(Allocator()->Adapter());
452    auto *id = AllocNode<ir::Identifier>(name, Allocator());
453
454    ArenaVector<ir::Statement *> statements(Allocator()->Adapter());
455    Type *returnType = nullptr;
456
457    builder(&statements, &params, &returnType);
458
459    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
460    auto *body = AllocNode<ir::BlockStatement>(Allocator(), std::move(statements));
461    auto funcSignature = ir::FunctionSignature(
462        nullptr, std::move(params), returnType == nullptr ? nullptr : AllocNode<ir::OpaqueTypeNode>(returnType));
463    auto *func = AllocNode<ir::ScriptFunction>(
464        Allocator(), ir::ScriptFunction::ScriptFunctionData {body, std::move(funcSignature), funcFlags, modifierFlags});
465
466    func->SetIdent(id);
467
468    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
469    auto *funcExpr = AllocNode<ir::FunctionExpression>(func);
470    auto *method =
471        // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHt resetint)
472        AllocNode<ir::MethodDefinition>(ir::MethodDefinitionKind::METHOD, func->Id()->Clone(Allocator(), nullptr),
473                                        funcExpr, modifierFlags, Allocator(), false);
474
475    return method;
476}
477
478ir::MethodDefinition *ETSChecker::CreateDynamicModuleClassInitMethod()
479{
480    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
481    return CreateClassMethod(compiler::Signatures::DYNAMIC_MODULE_CLASS_INIT, ir::ScriptFunctionFlags::METHOD,
482                             ir::ModifierFlags::PUBLIC | ir::ModifierFlags::STATIC,
483                             [this]([[maybe_unused]] ArenaVector<ir::Statement *> *statements,
484                                    [[maybe_unused]] ArenaVector<ir::Expression *> *params,
485                                    Type **returnType) { *returnType = GlobalVoidType(); });
486}
487
488ir::MethodDefinition *ETSChecker::CreateLambdaObjectClassInvokeMethod(Signature *invokeSignature,
489                                                                      ir::TypeNode *retTypeAnnotation)
490{
491    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
492    return CreateClassMethod(
493        compiler::Signatures::LAMBDA_OBJECT_INVOKE, ir::ScriptFunctionFlags::METHOD, ir::ModifierFlags::PUBLIC,
494        [this, invokeSignature, retTypeAnnotation](ArenaVector<ir::Statement *> *statements,
495                                                   ArenaVector<ir::Expression *> *params, Type **returnType) {
496            util::UString thisParamName(std::string("this"), Allocator());
497            // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
498            ir::ETSParameterExpression *thisParam = AddParam(thisParamName.View(), nullptr);
499            params->push_back(thisParam);
500
501            ArenaVector<ir::Expression *> callParams(Allocator()->Adapter());
502            for (auto *invokeParam : invokeSignature->Params()) {
503                // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
504                auto paramName =
505                    util::UString(std::string("p") + std::to_string(callParams.size()), Allocator()).View();
506                auto *param = AddParam(paramName, AllocNode<ir::OpaqueTypeNode>(invokeParam->TsType()));
507                params->push_back(param);
508                callParams.push_back(param->Clone(Allocator(), nullptr));
509            }
510
511            // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
512            auto *properyId = AllocNode<ir::Identifier>("jsvalue_lambda", Allocator());
513            // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
514            auto *callee = AllocNode<ir::MemberExpression>(thisParam->Clone(Allocator(), nullptr), properyId,
515                                                           ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
516            // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
517            auto *callLambda = AllocNode<ir::CallExpression>(callee, std::move(callParams), nullptr, false);
518
519            auto *castToRetTypeExpr =
520                // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
521                AllocNode<ir::TSAsExpression>(callLambda, retTypeAnnotation->Clone(Allocator(), nullptr), false);
522            castToRetTypeExpr->SetTsType(invokeSignature->ReturnType());
523            // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
524            auto *retStatement = AllocNode<ir::ReturnStatement>(castToRetTypeExpr);
525            statements->push_back(retStatement);
526
527            *returnType = invokeSignature->ReturnType();
528        });
529}
530
531void ETSChecker::EmitDynamicModuleClassInitCall()
532{
533    auto *globalClass = VarBinder()->Program()->GlobalClass();
534    auto &body = globalClass->Body();
535    auto it = std::find_if(body.begin(), body.end(), [](ir::AstNode *node) { return node->IsClassStaticBlock(); });
536
537    ASSERT(it != body.end());
538
539    auto *staticBlock = (*it)->AsClassStaticBlock();
540    auto *cctorBody = staticBlock->Function()->Body()->AsBlockStatement();
541
542    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
543    auto *classId = AllocNode<ir::Identifier>(compiler::Signatures::DYNAMIC_MODULE_CLASS, Allocator());
544    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
545    auto *methodId = AllocNode<ir::Identifier>(compiler::Signatures::DYNAMIC_MODULE_CLASS_INIT, Allocator());
546    methodId->SetReference();
547    auto *callee =
548        // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
549        AllocNode<ir::MemberExpression>(classId, methodId, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
550
551    ArenaVector<ir::Expression *> callParams(Allocator()->Adapter());
552    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
553    auto *initCall = AllocNode<ir::CallExpression>(callee, std::move(callParams), nullptr, false);
554
555    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
556    auto *const node = AllocNode<ir::ExpressionStatement>(initCall);
557    node->SetParent(cctorBody);
558    cctorBody->Statements().push_back(node);
559
560    ProcessScopesNode(this, node);
561    ProcessCheckerNode(this, node);
562}
563
564void ETSChecker::BuildDynamicImportClass()
565{
566    auto dynamicImports = VarBinder()->AsETSBinder()->DynamicImports();
567    if (dynamicImports.empty()) {
568        return;
569    }
570
571    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
572    BuildClass(
573        compiler::Signatures::DYNAMIC_MODULE_CLASS, [this, dynamicImports](ArenaVector<ir::AstNode *> *classBody) {
574            std::unordered_set<util::StringView> fields;
575            std::vector<ir::ETSImportDeclaration *> imports;
576
577            for (auto *import : dynamicImports) {
578                auto source = import->Source()->Str();
579                if (fields.find(source) != fields.cend()) {
580                    continue;
581                }
582
583                auto assemblyName = std::string(source);
584                std::replace_if(
585                    assemblyName.begin(), assemblyName.end(), [](char c) { return std::isalnum(c) == 0; }, '_');
586                assemblyName.append(std::to_string(fields.size()));
587
588                import->AssemblerName() = util::UString(assemblyName, Allocator()).View();
589                fields.insert(import->AssemblerName());
590                imports.push_back(import);
591
592                auto *fieldIdent = AllocNode<ir::Identifier>(import->AssemblerName(), Allocator());
593                // NOTE: remove const when readonly is properly supported
594                auto flags = ir::ModifierFlags::STATIC | ir::ModifierFlags::PUBLIC | ir::ModifierFlags::READONLY |
595                             ir::ModifierFlags::CONST;
596                auto *field = AllocNode<ir::ClassProperty>(
597                    fieldIdent, nullptr, AllocNode<ir::OpaqueTypeNode>(GlobalBuiltinDynamicType(import->Language())),
598                    flags, Allocator(), false);
599
600                classBody->push_back(field);
601            }
602
603            classBody->push_back(CreateDynamicModuleClassInitializer(imports));
604            classBody->push_back(CreateDynamicModuleClassInitMethod());
605        });
606    EmitDynamicModuleClassInitCall();
607}
608
609ir::MethodDefinition *ETSChecker::CreateLambdaObjectClassInitializer(ETSObjectType *functionalInterface)
610{
611    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
612    return CreateClassInstanceInitializer(
613        [this](ArenaVector<ir::Statement *> *statements, ArenaVector<ir::Expression *> *params) {
614            ir::ETSParameterExpression *thisParam = AddParam(varbinder::VarBinder::MANDATORY_PARAM_THIS, nullptr);
615            params->push_back(thisParam);
616
617            util::UString jsvalueParamName(std::string("jsvalue_param"), Allocator());
618            ir::ETSParameterExpression *jsvalueParam =
619                AddParam(jsvalueParamName.View(), AllocNode<ir::OpaqueTypeNode>(GlobalBuiltinJSValueType()));
620            params->push_back(jsvalueParam);
621            // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
622            auto *moduleClassId = AllocNode<ir::Identifier>(varbinder::VarBinder::MANDATORY_PARAM_THIS, Allocator());
623            // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
624            auto *fieldId = AllocNode<ir::Identifier>("jsvalue_lambda", Allocator());
625            // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
626            auto *property = AllocNode<ir::MemberExpression>(moduleClassId, fieldId,
627                                                             ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
628            // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
629            auto *initializer = AllocNode<ir::AssignmentExpression>(property, jsvalueParam->Clone(Allocator(), nullptr),
630                                                                    lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
631            // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
632            statements->push_back(AllocNode<ir::ExpressionStatement>(initializer));
633        },
634        functionalInterface);
635}
636
637void ETSChecker::BuildLambdaObjectClass(ETSObjectType *functionalInterface, ir::TypeNode *retTypeAnnotation)
638{
639    auto *invokeMethod = functionalInterface->GetOwnProperty<checker::PropertyType::INSTANCE_METHOD>("invoke");
640    auto *invokeSignature = invokeMethod->TsType()->AsETSFunctionType()->CallSignatures()[0];
641
642    std::stringstream ss;
643    ss << compiler::Signatures::LAMBDA_OBJECT;
644    ToString(this, invokeSignature->Params(), ss);
645    auto syntheticLambdaObjName = ss.str();
646    if (dynamicLambdaSignatureCache_.count(syntheticLambdaObjName) != 0) {
647        functionalInterface->AddConstructSignature(dynamicLambdaSignatureCache_[syntheticLambdaObjName]);
648        return;
649    }
650
651    auto buildBody = [this, invokeSignature, retTypeAnnotation,
652                      functionalInterface](ArenaVector<ir::AstNode *> *classBody) {
653        auto assemblyName = "jsvalue_lambda";
654        // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
655        auto *fieldIdent = AllocNode<ir::Identifier>(assemblyName, Allocator());
656        // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
657        auto *field =
658            AllocNode<ir::ClassProperty>(fieldIdent, nullptr, AllocNode<ir::OpaqueTypeNode>(GlobalBuiltinJSValueType()),
659                                         ir::ModifierFlags::PRIVATE, Allocator(), false);
660        classBody->push_back(field);
661        // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
662        classBody->push_back(CreateLambdaObjectClassInitializer(functionalInterface));
663        // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
664        classBody->push_back(CreateLambdaObjectClassInvokeMethod(invokeSignature, retTypeAnnotation));
665    };
666
667    // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
668    BuildClass(util::StringView(syntheticLambdaObjName), buildBody);
669
670    dynamicLambdaSignatureCache_[syntheticLambdaObjName] = functionalInterface->ConstructSignatures()[0];
671}
672
673}  // namespace ark::es2panda::checker
674