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, ¶ms);
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, ¶ms, &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