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