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