• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include <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 
48 namespace ark::es2panda::checker {
49 
ProcessCheckerNode(ETSChecker * checker,ir::AstNode * node)50 void 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 
ProcessScopesNode(ETSChecker * checker,ir::AstNode * node)73 void 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 
AddParam(util::StringView name,ir::TypeNode * type)85 ir::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 
97 template <typename T>
CreateDynamicCallIntrinsic(ir::Expression * callee,const ArenaVector<T * > & arguments,Language lang)98 ir::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 
ToString(ETSChecker * checker,const ArenaVector<ir::Expression * > & arguments,std::stringstream & ss)153 static 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 
ToString(ETSChecker * checker,const ArenaVector<varbinder::LocalVariable * > & arguments,std::stringstream & ss)162 static 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 
172 template <typename T>
ResolveDynamicCallExpression(ir::Expression * callee,const ArenaVector<T * > & arguments,Language lang,bool isConstruct)173 Signature *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 
222 template Signature *ETSChecker::ResolveDynamicCallExpression<ir::Expression>(
223     ir::Expression *callee, const ArenaVector<ir::Expression *> &arguments, Language lang, bool isConstruct);
224 
225 template Signature *ETSChecker::ResolveDynamicCallExpression<varbinder::LocalVariable>(
226     ir::Expression *callee, const ArenaVector<varbinder::LocalVariable *> &arguments, Language lang, bool isConstruct);
227 
CreateStaticScriptFunction(ClassInitializerBuilder const & builder)228 std::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 
CreateScriptFunction(ClassInitializerBuilder const & builder)257 std::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 
CreateClassStaticInitializer(const ClassInitializerBuilder & builder,ETSObjectType * type)282 ir::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 
CreateClassInstanceInitializer(const ClassInitializerBuilder & builder,ETSObjectType * type)298 ir::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 
CreateDynamicCallClassInitializer(Language lang,bool isConstruct)314 ir::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         ArenaVector<ir::Expression *> callParams(Allocator()->Adapter());
333 
334         std::stringstream ss;
335         auto name = isConstruct ? compiler::Signatures::Dynamic::NewClass(lang)
336                                 : compiler::Signatures::Dynamic::CallClass(lang);
337 
338         ss << compiler::Signatures::CLASS_REF_BEGIN;
339         if (!VarBinder()->Program()->OmitModuleName()) {
340             std::string moduleString(VarBinder()->Program()->ModuleName());
341             std::replace(moduleString.begin(), moduleString.end(), *compiler::Signatures::METHOD_SEPARATOR.begin(),
342                          *compiler::Signatures::NAMESPACE_SEPARATOR.begin());
343             ss << moduleString << compiler::Signatures::NAMESPACE_SEPARATOR;
344         }
345         ss << name << compiler::Signatures::MANGLE_SEPARATOR;
346 
347         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
348         auto *className = AllocNode<ir::StringLiteral>(util::UString(ss.str(), Allocator()).View());
349         callParams.push_back(className);
350 
351         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
352         auto *initCall = AllocNode<ir::CallExpression>(callee, std::move(callParams), nullptr, false);
353 
354         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
355         statements->push_back(AllocNode<ir::ExpressionStatement>(initCall));
356     });
357 }
358 
BuildClass(util::StringView name,const ClassBuilder & builder)359 ir::ClassDeclaration *ETSChecker::BuildClass(util::StringView name, const ClassBuilder &builder)
360 {
361     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
362     auto *classId = AllocNode<ir::Identifier>(name, Allocator());
363 
364     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
365     auto *classDef = AllocNode<ir::ClassDefinition>(Allocator(), classId, ir::ClassDefinitionModifiers::CLASS_DECL,
366                                                     ir::ModifierFlags::NONE, Language(Language::Id::ETS));
367 
368     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
369     auto *classDecl = AllocNode<ir::ClassDeclaration>(classDef, Allocator());
370 
371     VarBinder()->Program()->Ast()->Statements().push_back(classDecl);
372     classDecl->SetParent(VarBinder()->Program()->Ast());
373 
374     varbinder::BoundContext boundCtx(VarBinder()->AsETSBinder()->GetGlobalRecordTable(), classDef);
375 
376     ArenaVector<ir::AstNode *> classBody(Allocator()->Adapter());
377 
378     builder(&classBody);
379 
380     classDef->AddProperties(std::move(classBody));
381 
382     ProcessScopesNode(this, classDecl);
383     ProcessCheckerNode(this, classDecl);
384     return classDecl;
385 }
386 
CreateStaticReadonlyField(const char * name)387 ir::ClassProperty *ETSChecker::CreateStaticReadonlyField(const char *name)
388 {
389     auto *fieldIdent = AllocNode<ir::Identifier>(name, Allocator());
390     // NOTE: remove const when readonly is properly supported
391     auto flags =
392         ir::ModifierFlags::STATIC | ir::ModifierFlags::PRIVATE | ir::ModifierFlags::READONLY | ir::ModifierFlags::CONST;
393     auto *field = AllocNode<ir::ClassProperty>(
394         fieldIdent, nullptr, AllocNode<ir::ETSPrimitiveType>(ir::PrimitiveType::INT), flags, Allocator(), false);
395 
396     return field;
397 }
398 
GetDynamicClass(Language lang,bool isConstruct)399 ir::ClassDeclaration *ETSChecker::GetDynamicClass(Language lang, bool isConstruct)
400 {
401     auto &klasses = dynamicClasses_[static_cast<size_t>(isConstruct)];
402     if (klasses.count(lang) != 0U) {
403         return klasses[lang];
404     }
405     auto className =
406         isConstruct ? compiler::Signatures::Dynamic::NewClass(lang) : compiler::Signatures::Dynamic::CallClass(lang);
407     auto klass = BuildClass(className, [this, lang, isConstruct](ArenaVector<ir::AstNode *> *classBody) {
408         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
409         classBody->push_back(CreateStaticReadonlyField("qname_start_from"));
410         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
411         classBody->push_back(CreateDynamicCallClassInitializer(lang, isConstruct));
412     });
413     klasses.emplace(lang, klass);
414     return klass;
415 }
416 
ClassInitializerFromImport(ir::ETSImportDeclaration * import,ArenaVector<ir::Statement * > * statements)417 void ETSChecker::ClassInitializerFromImport(ir::ETSImportDeclaration *import, ArenaVector<ir::Statement *> *statements)
418 {
419     auto builtin = compiler::Signatures::Dynamic::LoadModuleBuiltin(import->Language());
420     auto [builtin_class_name, builtin_method_name] = util::Helpers::SplitSignature(builtin);
421     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
422     auto *classId = AllocNode<ir::Identifier>(builtin_class_name, Allocator());
423     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
424     auto *methodId = AllocNode<ir::Identifier>(builtin_method_name, Allocator());
425     methodId->SetReference();
426     auto *callee =
427         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
428         AllocNode<ir::MemberExpression>(classId, methodId, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
429 
430     // Note(rsipka): this check could be avoided with appropriate language extensions
431     ArenaVector<ir::Expression *> callParams(Allocator()->Adapter());
432     if (ark::os::file::File::IsRegularFile(import->ResolvedSource()->Str().Mutf8())) {
433         callParams.push_back(AllocNode<ir::StringLiteral>(
434             util::UString(ark::os::RemoveExtension(import->ResolvedSource()->Str().Mutf8()), Allocator()).View()));
435     } else {
436         callParams.push_back(import->ResolvedSource());
437     }
438     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
439     auto *loadCall = AllocNode<ir::CallExpression>(callee, std::move(callParams), nullptr, false);
440     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
441     auto *moduleClassId = AllocNode<ir::Identifier>(compiler::Signatures::DYNAMIC_MODULE_CLASS, Allocator());
442     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
443     auto *fieldId = AllocNode<ir::Identifier>(import->AssemblerName(), Allocator());
444     fieldId->SetReference();
445     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
446     auto *property = AllocNode<ir::MemberExpression>(moduleClassId, fieldId, ir::MemberExpressionKind::PROPERTY_ACCESS,
447                                                      false, false);
448 
449     auto *initializer =
450         AllocNode<ir::AssignmentExpression>(property, loadCall, lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
451     statements->push_back(AllocNode<ir::ExpressionStatement>(initializer));
452 }
453 
CreateDynamicModuleClassInitializer(const std::vector<ir::ETSImportDeclaration * > & imports)454 ir::ClassStaticBlock *ETSChecker::CreateDynamicModuleClassInitializer(
455     const std::vector<ir::ETSImportDeclaration *> &imports)
456 {
457     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
458     return CreateClassStaticInitializer([this, imports](ArenaVector<ir::Statement *> *statements,
459                                                         [[maybe_unused]] ArenaVector<ir::Expression *> *params) {
460         for (auto *import : imports) {
461             ClassInitializerFromImport(import, statements);
462         }
463     });
464 }
465 
CreateClassMethod(const std::string_view name,ir::ScriptFunctionFlags funcFlags,ir::ModifierFlags modifierFlags,const MethodBuilder & builder)466 ir::MethodDefinition *ETSChecker::CreateClassMethod(const std::string_view name, ir::ScriptFunctionFlags funcFlags,
467                                                     ir::ModifierFlags modifierFlags, const MethodBuilder &builder)
468 {
469     ArenaVector<ir::Expression *> params(Allocator()->Adapter());
470     auto *id = AllocNode<ir::Identifier>(name, Allocator());
471 
472     ArenaVector<ir::Statement *> statements(Allocator()->Adapter());
473     Type *returnType = nullptr;
474 
475     builder(&statements, &params, &returnType);
476 
477     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
478     auto *body = AllocNode<ir::BlockStatement>(Allocator(), std::move(statements));
479     auto funcSignature = ir::FunctionSignature(
480         nullptr, std::move(params), returnType == nullptr ? nullptr : AllocNode<ir::OpaqueTypeNode>(returnType));
481     auto *func = AllocNode<ir::ScriptFunction>(
482         Allocator(), ir::ScriptFunction::ScriptFunctionData {body, std::move(funcSignature), funcFlags, modifierFlags});
483 
484     func->SetIdent(id);
485 
486     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
487     auto *funcExpr = AllocNode<ir::FunctionExpression>(func);
488     auto *method =
489         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHt resetint)
490         AllocNode<ir::MethodDefinition>(ir::MethodDefinitionKind::METHOD, func->Id()->Clone(Allocator(), nullptr),
491                                         funcExpr, modifierFlags, Allocator(), false);
492 
493     return method;
494 }
495 
CreateDynamicModuleClassInitMethod()496 ir::MethodDefinition *ETSChecker::CreateDynamicModuleClassInitMethod()
497 {
498     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
499     return CreateClassMethod(compiler::Signatures::DYNAMIC_MODULE_CLASS_INIT, ir::ScriptFunctionFlags::METHOD,
500                              ir::ModifierFlags::PUBLIC | ir::ModifierFlags::STATIC,
501                              [this]([[maybe_unused]] ArenaVector<ir::Statement *> *statements,
502                                     [[maybe_unused]] ArenaVector<ir::Expression *> *params,
503                                     Type **returnType) { *returnType = GlobalVoidType(); });
504 }
505 
CreateLambdaObjectClassInvokeMethod(Signature * invokeSignature,ir::TypeNode * retTypeAnnotation)506 ir::MethodDefinition *ETSChecker::CreateLambdaObjectClassInvokeMethod(Signature *invokeSignature,
507                                                                       ir::TypeNode *retTypeAnnotation)
508 {
509     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
510     return CreateClassMethod(
511         compiler::Signatures::LAMBDA_OBJECT_INVOKE, ir::ScriptFunctionFlags::METHOD, ir::ModifierFlags::PUBLIC,
512         [this, invokeSignature, retTypeAnnotation](ArenaVector<ir::Statement *> *statements,
513                                                    ArenaVector<ir::Expression *> *params, Type **returnType) {
514             util::UString thisParamName(std::string("this"), Allocator());
515             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
516             ir::ETSParameterExpression *thisParam = AddParam(thisParamName.View(), nullptr);
517             params->push_back(thisParam);
518 
519             ArenaVector<ir::Expression *> callParams(Allocator()->Adapter());
520             for (auto *invokeParam : invokeSignature->Params()) {
521                 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
522                 auto paramName =
523                     util::UString(std::string("p") + std::to_string(callParams.size()), Allocator()).View();
524                 auto *param = AddParam(paramName, AllocNode<ir::OpaqueTypeNode>(invokeParam->TsType()));
525                 params->push_back(param);
526                 callParams.push_back(param->Clone(Allocator(), nullptr));
527             }
528 
529             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
530             auto *properyId = AllocNode<ir::Identifier>("jsvalue_lambda", Allocator());
531             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
532             auto *callee = AllocNode<ir::MemberExpression>(thisParam->Clone(Allocator(), nullptr), properyId,
533                                                            ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
534             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
535             auto *callLambda = AllocNode<ir::CallExpression>(callee, std::move(callParams), nullptr, false);
536 
537             auto *castToRetTypeExpr =
538                 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
539                 AllocNode<ir::TSAsExpression>(callLambda, retTypeAnnotation->Clone(Allocator(), nullptr), false);
540             castToRetTypeExpr->SetTsType(invokeSignature->ReturnType());
541             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
542             auto *retStatement = AllocNode<ir::ReturnStatement>(castToRetTypeExpr);
543             statements->push_back(retStatement);
544 
545             *returnType = invokeSignature->ReturnType();
546         });
547 }
548 
EmitDynamicModuleClassInitCall()549 void ETSChecker::EmitDynamicModuleClassInitCall()
550 {
551     auto *globalClass = VarBinder()->Program()->GlobalClass();
552     auto &body = globalClass->Body();
553     auto it = std::find_if(body.begin(), body.end(), [](ir::AstNode *node) { return node->IsClassStaticBlock(); });
554 
555     ASSERT(it != body.end());
556 
557     auto *staticBlock = (*it)->AsClassStaticBlock();
558     auto *cctorBody = staticBlock->Function()->Body()->AsBlockStatement();
559 
560     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
561     auto *classId = AllocNode<ir::Identifier>(compiler::Signatures::DYNAMIC_MODULE_CLASS, Allocator());
562     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
563     auto *methodId = AllocNode<ir::Identifier>(compiler::Signatures::DYNAMIC_MODULE_CLASS_INIT, Allocator());
564     methodId->SetReference();
565     auto *callee =
566         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
567         AllocNode<ir::MemberExpression>(classId, methodId, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
568 
569     ArenaVector<ir::Expression *> callParams(Allocator()->Adapter());
570     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
571     auto *initCall = AllocNode<ir::CallExpression>(callee, std::move(callParams), nullptr, false);
572 
573     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
574     auto *const node = AllocNode<ir::ExpressionStatement>(initCall);
575     node->SetParent(cctorBody);
576     cctorBody->Statements().push_back(node);
577 
578     ProcessScopesNode(this, node);
579     ProcessCheckerNode(this, node);
580 }
581 
BuildDynamicImportClass()582 void ETSChecker::BuildDynamicImportClass()
583 {
584     auto dynamicImports = VarBinder()->AsETSBinder()->DynamicImports();
585     if (dynamicImports.empty()) {
586         return;
587     }
588 
589     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
590     BuildClass(
591         compiler::Signatures::DYNAMIC_MODULE_CLASS, [this, dynamicImports](ArenaVector<ir::AstNode *> *classBody) {
592             std::unordered_set<util::StringView> fields;
593             std::vector<ir::ETSImportDeclaration *> imports;
594 
595             for (auto *import : dynamicImports) {
596                 auto source = import->Source()->Str();
597                 if (fields.find(source) != fields.cend()) {
598                     continue;
599                 }
600 
601                 auto assemblyName = std::string(source);
602                 std::replace_if(
603                     assemblyName.begin(), assemblyName.end(), [](char c) { return std::isalnum(c) == 0; }, '_');
604                 assemblyName.append(std::to_string(fields.size()));
605 
606                 import->AssemblerName() = util::UString(assemblyName, Allocator()).View();
607                 fields.insert(import->AssemblerName());
608                 imports.push_back(import);
609 
610                 auto *fieldIdent = AllocNode<ir::Identifier>(import->AssemblerName(), Allocator());
611                 // NOTE: remove const when readonly is properly supported
612                 auto flags = ir::ModifierFlags::STATIC | ir::ModifierFlags::PUBLIC | ir::ModifierFlags::READONLY |
613                              ir::ModifierFlags::CONST;
614                 auto *field = AllocNode<ir::ClassProperty>(
615                     fieldIdent, nullptr, AllocNode<ir::OpaqueTypeNode>(GlobalBuiltinDynamicType(import->Language())),
616                     flags, Allocator(), false);
617 
618                 classBody->push_back(field);
619             }
620 
621             classBody->push_back(CreateDynamicModuleClassInitializer(imports));
622             classBody->push_back(CreateDynamicModuleClassInitMethod());
623         });
624     EmitDynamicModuleClassInitCall();
625 }
626 
CreateLambdaObjectClassInitializer(ETSObjectType * functionalInterface)627 ir::MethodDefinition *ETSChecker::CreateLambdaObjectClassInitializer(ETSObjectType *functionalInterface)
628 {
629     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
630     return CreateClassInstanceInitializer(
631         [this](ArenaVector<ir::Statement *> *statements, ArenaVector<ir::Expression *> *params) {
632             ir::ETSParameterExpression *thisParam = AddParam(varbinder::VarBinder::MANDATORY_PARAM_THIS, nullptr);
633             params->push_back(thisParam);
634 
635             util::UString jsvalueParamName(std::string("jsvalue_param"), Allocator());
636             ir::ETSParameterExpression *jsvalueParam =
637                 AddParam(jsvalueParamName.View(), AllocNode<ir::OpaqueTypeNode>(GlobalBuiltinJSValueType()));
638             params->push_back(jsvalueParam);
639             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
640             auto *moduleClassId = AllocNode<ir::Identifier>(varbinder::VarBinder::MANDATORY_PARAM_THIS, Allocator());
641             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
642             auto *fieldId = AllocNode<ir::Identifier>("jsvalue_lambda", Allocator());
643             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
644             auto *property = AllocNode<ir::MemberExpression>(moduleClassId, fieldId,
645                                                              ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
646             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
647             auto *initializer = AllocNode<ir::AssignmentExpression>(property, jsvalueParam->Clone(Allocator(), nullptr),
648                                                                     lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
649             // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
650             statements->push_back(AllocNode<ir::ExpressionStatement>(initializer));
651         },
652         functionalInterface);
653 }
654 
BuildLambdaObjectClass(ETSObjectType * functionalInterface,ir::TypeNode * retTypeAnnotation)655 void ETSChecker::BuildLambdaObjectClass(ETSObjectType *functionalInterface, ir::TypeNode *retTypeAnnotation)
656 {
657     auto *invokeMethod = functionalInterface->GetOwnProperty<checker::PropertyType::INSTANCE_METHOD>("invoke");
658     auto *invokeSignature = invokeMethod->TsType()->AsETSFunctionType()->CallSignatures()[0];
659 
660     std::stringstream ss;
661     ss << compiler::Signatures::LAMBDA_OBJECT;
662     ToString(this, invokeSignature->Params(), ss);
663     auto syntheticLambdaObjName = ss.str();
664     if (dynamicLambdaSignatureCache_.count(syntheticLambdaObjName) != 0) {
665         functionalInterface->AddConstructSignature(dynamicLambdaSignatureCache_[syntheticLambdaObjName]);
666         return;
667     }
668 
669     auto buildBody = [this, invokeSignature, retTypeAnnotation,
670                       functionalInterface](ArenaVector<ir::AstNode *> *classBody) {
671         auto assemblyName = "jsvalue_lambda";
672         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
673         auto *fieldIdent = AllocNode<ir::Identifier>(assemblyName, Allocator());
674         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
675         auto *field =
676             AllocNode<ir::ClassProperty>(fieldIdent, nullptr, AllocNode<ir::OpaqueTypeNode>(GlobalBuiltinJSValueType()),
677                                          ir::ModifierFlags::PRIVATE, Allocator(), false);
678         classBody->push_back(field);
679         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
680         classBody->push_back(CreateLambdaObjectClassInitializer(functionalInterface));
681         // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
682         classBody->push_back(CreateLambdaObjectClassInvokeMethod(invokeSignature, retTypeAnnotation));
683     };
684 
685     // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
686     BuildClass(util::StringView(syntheticLambdaObjName), buildBody);
687 
688     dynamicLambdaSignatureCache_[syntheticLambdaObjName] = functionalInterface->ConstructSignatures()[0];
689 }
690 
691 }  // namespace ark::es2panda::checker
692