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, ¶ms);
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 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
328 auto *callee =
329 AllocNode<ir::MemberExpression>(classId, methodId, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
330
331 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
332 auto *initCall = AllocNode<ir::CallExpression>(callee, ArenaVector<ir::Expression *>(Allocator()->Adapter()),
333 nullptr, false);
334
335 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
336 statements->push_back(AllocNode<ir::ExpressionStatement>(initCall));
337 });
338 }
339
BuildClass(util::StringView name,const ClassBuilder & builder)340 ir::ClassDeclaration *ETSChecker::BuildClass(util::StringView name, const ClassBuilder &builder)
341 {
342 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
343 auto *classId = AllocNode<ir::Identifier>(name, Allocator());
344
345 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
346 auto *classDef = AllocNode<ir::ClassDefinition>(Allocator(), classId, ir::ClassDefinitionModifiers::CLASS_DECL,
347 ir::ModifierFlags::NONE, Language(Language::Id::ETS));
348
349 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
350 auto *classDecl = AllocNode<ir::ClassDeclaration>(classDef, Allocator());
351
352 VarBinder()->Program()->Ast()->Statements().push_back(classDecl);
353 classDecl->SetParent(VarBinder()->Program()->Ast());
354
355 varbinder::BoundContext boundCtx(VarBinder()->AsETSBinder()->GetGlobalRecordTable(), classDef);
356
357 ArenaVector<ir::AstNode *> classBody(Allocator()->Adapter());
358
359 builder(&classBody);
360
361 classDef->AddProperties(std::move(classBody));
362
363 ProcessScopesNode(this, classDecl);
364 ProcessCheckerNode(this, classDecl);
365 return classDecl;
366 }
367
CreateStaticReadonlyField(const char * name)368 ir::ClassProperty *ETSChecker::CreateStaticReadonlyField(const char *name)
369 {
370 auto *fieldIdent = AllocNode<ir::Identifier>(name, Allocator());
371 auto flags = ir::ModifierFlags::STATIC | ir::ModifierFlags::PRIVATE | ir::ModifierFlags::READONLY;
372 auto *field = AllocNode<ir::ClassProperty>(
373 fieldIdent, nullptr, AllocNode<ir::ETSPrimitiveType>(ir::PrimitiveType::INT), flags, Allocator(), false);
374
375 return field;
376 }
377
GetDynamicClass(Language lang,bool isConstruct)378 ir::ClassDeclaration *ETSChecker::GetDynamicClass(Language lang, bool isConstruct)
379 {
380 auto &klasses = dynamicClasses_[static_cast<size_t>(isConstruct)];
381 if (klasses.count(lang) != 0U) {
382 return klasses[lang];
383 }
384 auto className =
385 isConstruct ? compiler::Signatures::Dynamic::NewClass(lang) : compiler::Signatures::Dynamic::CallClass(lang);
386 auto klass = BuildClass(className, [this, lang, isConstruct](ArenaVector<ir::AstNode *> *classBody) {
387 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
388 classBody->push_back(CreateStaticReadonlyField("qname_start_from"));
389 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
390 classBody->push_back(CreateDynamicCallClassInitializer(lang, isConstruct));
391 });
392 klasses.emplace(lang, klass);
393 return klass;
394 }
395
ClassInitializerFromImport(ir::ETSImportDeclaration * import,ArenaVector<ir::Statement * > * statements)396 void ETSChecker::ClassInitializerFromImport(ir::ETSImportDeclaration *import, ArenaVector<ir::Statement *> *statements)
397 {
398 auto builtin = compiler::Signatures::Dynamic::LoadModuleBuiltin(import->Language());
399 auto [builtin_class_name, builtin_method_name] = util::Helpers::SplitSignature(builtin);
400 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
401 auto *classId = AllocNode<ir::Identifier>(builtin_class_name, Allocator());
402 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
403 auto *methodId = AllocNode<ir::Identifier>(builtin_method_name, Allocator());
404 auto *callee =
405 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
406 AllocNode<ir::MemberExpression>(classId, methodId, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
407
408 // Note(rsipka): this check could be avoided with appropriate language extensions
409 ArenaVector<ir::Expression *> callParams(Allocator()->Adapter());
410 if (ark::os::file::File::IsRegularFile(import->ResolvedSource()->Str().Mutf8())) {
411 callParams.push_back(AllocNode<ir::StringLiteral>(
412 util::UString(ark::os::RemoveExtension(import->ResolvedSource()->Str().Mutf8()), Allocator()).View()));
413 } else {
414 callParams.push_back(import->ResolvedSource());
415 }
416 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
417 auto *loadCall = AllocNode<ir::CallExpression>(callee, std::move(callParams), nullptr, false);
418 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
419 auto *moduleClassId = AllocNode<ir::Identifier>(compiler::Signatures::DYNAMIC_MODULE_CLASS, Allocator());
420 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
421 auto *fieldId = AllocNode<ir::Identifier>(import->AssemblerName(), Allocator());
422 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
423 auto *property = AllocNode<ir::MemberExpression>(moduleClassId, fieldId, ir::MemberExpressionKind::PROPERTY_ACCESS,
424 false, false);
425
426 auto *initializer =
427 AllocNode<ir::AssignmentExpression>(property, loadCall, lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
428 statements->push_back(AllocNode<ir::ExpressionStatement>(initializer));
429 }
430
CreateDynamicModuleClassInitializer(const std::vector<ir::ETSImportDeclaration * > & imports)431 ir::ClassStaticBlock *ETSChecker::CreateDynamicModuleClassInitializer(
432 const std::vector<ir::ETSImportDeclaration *> &imports)
433 {
434 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
435 return CreateClassStaticInitializer([this, imports](ArenaVector<ir::Statement *> *statements,
436 [[maybe_unused]] ArenaVector<ir::Expression *> *params) {
437 for (auto *import : imports) {
438 ClassInitializerFromImport(import, statements);
439 }
440 });
441 }
442
CreateClassMethod(const std::string_view name,ir::ScriptFunctionFlags funcFlags,ir::ModifierFlags modifierFlags,const MethodBuilder & builder)443 ir::MethodDefinition *ETSChecker::CreateClassMethod(const std::string_view name, ir::ScriptFunctionFlags funcFlags,
444 ir::ModifierFlags modifierFlags, const MethodBuilder &builder)
445 {
446 ArenaVector<ir::Expression *> params(Allocator()->Adapter());
447 auto *id = AllocNode<ir::Identifier>(name, Allocator());
448
449 ArenaVector<ir::Statement *> statements(Allocator()->Adapter());
450 Type *returnType = nullptr;
451
452 builder(&statements, ¶ms, &returnType);
453
454 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
455 auto *body = AllocNode<ir::BlockStatement>(Allocator(), std::move(statements));
456 auto funcSignature = ir::FunctionSignature(
457 nullptr, std::move(params), returnType == nullptr ? nullptr : AllocNode<ir::OpaqueTypeNode>(returnType));
458 auto *func = AllocNode<ir::ScriptFunction>(
459 Allocator(), ir::ScriptFunction::ScriptFunctionData {body, std::move(funcSignature), funcFlags, modifierFlags});
460
461 func->SetIdent(id);
462
463 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
464 auto *funcExpr = AllocNode<ir::FunctionExpression>(func);
465 auto *method =
466 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHt resetint)
467 AllocNode<ir::MethodDefinition>(ir::MethodDefinitionKind::METHOD, func->Id()->Clone(Allocator(), nullptr),
468 funcExpr, modifierFlags, Allocator(), false);
469
470 return method;
471 }
472
CreateDynamicModuleClassInitMethod()473 ir::MethodDefinition *ETSChecker::CreateDynamicModuleClassInitMethod()
474 {
475 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
476 return CreateClassMethod(compiler::Signatures::DYNAMIC_MODULE_CLASS_INIT, ir::ScriptFunctionFlags::METHOD,
477 ir::ModifierFlags::PUBLIC | ir::ModifierFlags::STATIC,
478 [this]([[maybe_unused]] ArenaVector<ir::Statement *> *statements,
479 [[maybe_unused]] ArenaVector<ir::Expression *> *params,
480 Type **returnType) { *returnType = GlobalVoidType(); });
481 }
482
CreateLambdaObjectClassInvokeMethod(Signature * invokeSignature,ir::TypeNode * retTypeAnnotation)483 ir::MethodDefinition *ETSChecker::CreateLambdaObjectClassInvokeMethod(Signature *invokeSignature,
484 ir::TypeNode *retTypeAnnotation)
485 {
486 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
487 return CreateClassMethod(
488 compiler::Signatures::LAMBDA_OBJECT_INVOKE, ir::ScriptFunctionFlags::METHOD, ir::ModifierFlags::PUBLIC,
489 [this, invokeSignature, retTypeAnnotation](ArenaVector<ir::Statement *> *statements,
490 ArenaVector<ir::Expression *> *params, Type **returnType) {
491 util::UString thisParamName(std::string("this"), Allocator());
492 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
493 ir::ETSParameterExpression *thisParam = AddParam(thisParamName.View(), nullptr);
494 params->push_back(thisParam);
495
496 ArenaVector<ir::Expression *> callParams(Allocator()->Adapter());
497 for (auto *invokeParam : invokeSignature->Params()) {
498 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
499 auto paramName =
500 util::UString(std::string("p") + std::to_string(callParams.size()), Allocator()).View();
501 auto *param = AddParam(paramName, AllocNode<ir::OpaqueTypeNode>(invokeParam->TsType()));
502 params->push_back(param);
503 callParams.push_back(param->Clone(Allocator(), nullptr));
504 }
505
506 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
507 auto *properyId = AllocNode<ir::Identifier>("jsvalue_lambda", Allocator());
508 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
509 auto *callee = AllocNode<ir::MemberExpression>(thisParam->Clone(Allocator(), nullptr), properyId,
510 ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
511 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
512 auto *callLambda = AllocNode<ir::CallExpression>(callee, std::move(callParams), nullptr, false);
513
514 auto *castToRetTypeExpr =
515 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
516 AllocNode<ir::TSAsExpression>(callLambda, retTypeAnnotation->Clone(Allocator(), nullptr), false);
517 castToRetTypeExpr->SetTsType(invokeSignature->ReturnType());
518 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
519 auto *retStatement = AllocNode<ir::ReturnStatement>(castToRetTypeExpr);
520 statements->push_back(retStatement);
521
522 *returnType = invokeSignature->ReturnType();
523 });
524 }
525
EmitDynamicModuleClassInitCall()526 void ETSChecker::EmitDynamicModuleClassInitCall()
527 {
528 auto *globalClass = VarBinder()->Program()->GlobalClass();
529 auto &body = globalClass->Body();
530 auto it = std::find_if(body.begin(), body.end(), [](ir::AstNode *node) { return node->IsClassStaticBlock(); });
531
532 ASSERT(it != body.end());
533
534 auto *staticBlock = (*it)->AsClassStaticBlock();
535 auto *cctorBody = staticBlock->Function()->Body()->AsBlockStatement();
536
537 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
538 auto *classId = AllocNode<ir::Identifier>(compiler::Signatures::DYNAMIC_MODULE_CLASS, Allocator());
539 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
540 auto *methodId = AllocNode<ir::Identifier>(compiler::Signatures::DYNAMIC_MODULE_CLASS_INIT, Allocator());
541 auto *callee =
542 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
543 AllocNode<ir::MemberExpression>(classId, methodId, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
544
545 ArenaVector<ir::Expression *> callParams(Allocator()->Adapter());
546 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
547 auto *initCall = AllocNode<ir::CallExpression>(callee, std::move(callParams), nullptr, false);
548
549 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
550 auto *const node = AllocNode<ir::ExpressionStatement>(initCall);
551 node->SetParent(cctorBody);
552 cctorBody->Statements().push_back(node);
553
554 ProcessScopesNode(this, node);
555 ProcessCheckerNode(this, node);
556 }
557
BuildDynamicImportClass()558 void ETSChecker::BuildDynamicImportClass()
559 {
560 auto dynamicImports = VarBinder()->AsETSBinder()->DynamicImports();
561 if (dynamicImports.empty()) {
562 return;
563 }
564
565 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
566 BuildClass(
567 compiler::Signatures::DYNAMIC_MODULE_CLASS, [this, dynamicImports](ArenaVector<ir::AstNode *> *classBody) {
568 std::unordered_set<util::StringView> fields;
569 std::vector<ir::ETSImportDeclaration *> imports;
570
571 for (auto *import : dynamicImports) {
572 auto source = import->Source()->Str();
573 if (fields.find(source) != fields.cend()) {
574 continue;
575 }
576
577 auto assemblyName = std::string(source);
578 std::replace_if(
579 assemblyName.begin(), assemblyName.end(), [](char c) { return std::isalnum(c) == 0; }, '_');
580 assemblyName.append(std::to_string(fields.size()));
581
582 import->AssemblerName() = util::UString(assemblyName, Allocator()).View();
583 fields.insert(import->AssemblerName());
584 imports.push_back(import);
585
586 auto *fieldIdent = AllocNode<ir::Identifier>(import->AssemblerName(), Allocator());
587 auto flags = ir::ModifierFlags::STATIC | ir::ModifierFlags::PUBLIC | ir::ModifierFlags::READONLY;
588 auto *field = AllocNode<ir::ClassProperty>(
589 fieldIdent, nullptr, AllocNode<ir::OpaqueTypeNode>(GlobalBuiltinDynamicType(import->Language())),
590 flags, Allocator(), false);
591
592 classBody->push_back(field);
593 }
594
595 classBody->push_back(CreateDynamicModuleClassInitializer(imports));
596 classBody->push_back(CreateDynamicModuleClassInitMethod());
597 });
598 EmitDynamicModuleClassInitCall();
599 }
600
CreateLambdaObjectClassInitializer(ETSObjectType * functionalInterface)601 ir::MethodDefinition *ETSChecker::CreateLambdaObjectClassInitializer(ETSObjectType *functionalInterface)
602 {
603 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
604 return CreateClassInstanceInitializer(
605 [this](ArenaVector<ir::Statement *> *statements, ArenaVector<ir::Expression *> *params) {
606 ir::ETSParameterExpression *thisParam = AddParam(varbinder::VarBinder::MANDATORY_PARAM_THIS, nullptr);
607 params->push_back(thisParam);
608
609 util::UString jsvalueParamName(std::string("jsvalue_param"), Allocator());
610 ir::ETSParameterExpression *jsvalueParam =
611 AddParam(jsvalueParamName.View(), AllocNode<ir::OpaqueTypeNode>(GlobalBuiltinJSValueType()));
612 params->push_back(jsvalueParam);
613 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
614 auto *moduleClassId = AllocNode<ir::Identifier>(varbinder::VarBinder::MANDATORY_PARAM_THIS, Allocator());
615 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
616 auto *fieldId = AllocNode<ir::Identifier>("jsvalue_lambda", Allocator());
617 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
618 auto *property = AllocNode<ir::MemberExpression>(moduleClassId, fieldId,
619 ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
620 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
621 auto *initializer = AllocNode<ir::AssignmentExpression>(property, jsvalueParam->Clone(Allocator(), nullptr),
622 lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
623 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
624 statements->push_back(AllocNode<ir::ExpressionStatement>(initializer));
625 },
626 functionalInterface);
627 }
628
BuildLambdaObjectClass(ETSObjectType * functionalInterface,ir::TypeNode * retTypeAnnotation)629 void ETSChecker::BuildLambdaObjectClass(ETSObjectType *functionalInterface, ir::TypeNode *retTypeAnnotation)
630 {
631 auto *invokeMethod = functionalInterface->GetOwnProperty<checker::PropertyType::INSTANCE_METHOD>("invoke");
632 auto *invokeSignature = invokeMethod->TsType()->AsETSFunctionType()->CallSignatures()[0];
633
634 std::stringstream ss;
635 ss << compiler::Signatures::LAMBDA_OBJECT;
636 ToString(this, invokeSignature->Params(), ss);
637 auto syntheticLambdaObjName = ss.str();
638 if (dynamicLambdaSignatureCache_.count(syntheticLambdaObjName) != 0) {
639 functionalInterface->AddConstructSignature(dynamicLambdaSignatureCache_[syntheticLambdaObjName]);
640 return;
641 }
642
643 auto buildBody = [this, invokeSignature, retTypeAnnotation,
644 functionalInterface](ArenaVector<ir::AstNode *> *classBody) {
645 auto assemblyName = "jsvalue_lambda";
646 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
647 auto *fieldIdent = AllocNode<ir::Identifier>(assemblyName, Allocator());
648 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
649 auto *field =
650 AllocNode<ir::ClassProperty>(fieldIdent, nullptr, AllocNode<ir::OpaqueTypeNode>(GlobalBuiltinJSValueType()),
651 ir::ModifierFlags::PRIVATE, Allocator(), false);
652 classBody->push_back(field);
653 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
654 classBody->push_back(CreateLambdaObjectClassInitializer(functionalInterface));
655 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
656 classBody->push_back(CreateLambdaObjectClassInvokeMethod(invokeSignature, retTypeAnnotation));
657 };
658
659 // SUPPRESS_CSA_NEXTLINE(alpha.core.AllocatorETSCheckerHint)
660 BuildClass(util::StringView(syntheticLambdaObjName), buildBody);
661
662 dynamicLambdaSignatureCache_[syntheticLambdaObjName] = functionalInterface->ConstructSignatures()[0];
663 }
664
665 } // namespace ark::es2panda::checker
666