1/* 2 * Copyright (c) 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 "lambdaLowering.h" 17 18#include "checker/ets/typeRelationContext.h" 19#include "compiler/lowering/scopesInit/scopesInitPhase.h" 20#include "compiler/lowering/util.h" 21 22namespace ark::es2panda::compiler { 23 24struct LambdaInfo { 25 ir::ClassDeclaration *calleeClass = nullptr; 26 ir::ScriptFunction *enclosingFunction = nullptr; 27 util::StringView name = ""; 28 ArenaSet<varbinder::Variable *> *capturedVars = nullptr; 29 ir::Expression *callReceiver = nullptr; 30}; 31 32struct CalleeMethodInfo { 33 util::StringView calleeName; 34 ir::AstNode *body = nullptr; 35 checker::Type *forcedReturnType = nullptr; 36 ir::ModifierFlags auxModifierFlags = ir::ModifierFlags::NONE; 37 ir::ScriptFunctionFlags auxFunctionFlags = ir::ScriptFunctionFlags::NONE; 38}; 39 40struct LambdaClassInvokeInfo { 41 checker::Signature *lambdaSignature = nullptr; 42 ir::MethodDefinition *callee = nullptr; 43 ir::ClassDefinition *classDefinition = nullptr; 44 checker::Substitution *substitution = nullptr; 45}; 46 47struct CalleeParameterInfo { 48 ir::ArrowFunctionExpression *lambda = nullptr; 49 ArenaSet<varbinder::Variable *> const &captured; 50 varbinder::ParamScope *paramScope = nullptr; 51 checker::Substitution *substitution = nullptr; 52 size_t limit = 0; 53}; 54 55static std::pair<ir::ClassDeclaration *, ir::ScriptFunction *> FindEnclosingClassAndFunction(ir::AstNode *ast) 56{ 57 ir::ScriptFunction *function = nullptr; 58 for (ir::AstNode *curr = ast->Parent(); curr != nullptr; curr = curr->Parent()) { 59 if (curr->IsClassDeclaration()) { 60 return {curr->AsClassDeclaration(), function}; 61 } 62 if (curr->IsScriptFunction()) { 63 function = curr->AsScriptFunction(); 64 } 65 } 66 UNREACHABLE(); 67} 68 69static bool CheckIfNeedThis(ir::ArrowFunctionExpression const *lambda) 70{ 71 return lambda->IsAnyChild([](ir::AstNode *ast) { return ast->IsThisExpression(); }); 72} 73 74static size_t g_calleeCount = 0; 75 76// Make calleeCount behaviour predictable 77static void ResetCalleeCount() 78{ 79 g_calleeCount = 0; 80} 81 82static util::StringView CreateCalleeName(ArenaAllocator *allocator) 83{ 84 auto name = util::UString(util::StringView("lambda$invoke$"), allocator); 85 name.Append(std::to_string(g_calleeCount++)); 86 return name.View(); 87} 88 89static std::pair<ir::TSTypeParameterDeclaration *, checker::Substitution *> CloneTypeParams( 90 public_lib::Context *ctx, ir::TSTypeParameterDeclaration *oldIrTypeParams, ir::ScriptFunction *enclosingFunction, 91 varbinder::Scope *enclosingScope) 92{ 93 if (oldIrTypeParams == nullptr) { 94 return {nullptr, nullptr}; 95 } 96 97 auto *allocator = ctx->allocator; 98 auto *checker = ctx->checker->AsETSChecker(); 99 100 auto *newScope = allocator->New<varbinder::LocalScope>(allocator, enclosingScope); 101 auto newTypeParams = ArenaVector<checker::ETSTypeParameter *>(allocator->Adapter()); 102 auto newTypeParamNodes = ArenaVector<ir::TSTypeParameter *>(allocator->Adapter()); 103 auto *substitution = checker->NewSubstitution(); 104 105 for (size_t ix = 0; ix < oldIrTypeParams->Params().size(); ix++) { 106 auto *oldTypeParamNode = oldIrTypeParams->Params()[ix]; 107 auto *oldTypeParam = enclosingFunction->Signature()->TypeParams()[ix]->AsETSTypeParameter(); 108 auto *newTypeParamId = allocator->New<ir::Identifier>(oldTypeParamNode->Name()->Name(), allocator); 109 auto *newTypeParamNode = 110 util::NodeAllocator::ForceSetParent<ir::TSTypeParameter>(allocator, newTypeParamId, nullptr, nullptr); 111 auto *newTypeParam = allocator->New<checker::ETSTypeParameter>(); 112 newTypeParam->SetDeclNode(newTypeParamNode); 113 114 auto *newTypeParamDecl = allocator->New<varbinder::TypeParameterDecl>(newTypeParamId->Name()); 115 newTypeParamDecl->BindNode(newTypeParamNode); 116 auto *newTypeParamVar = 117 allocator->New<varbinder::LocalVariable>(newTypeParamDecl, varbinder::VariableFlags::TYPE_PARAMETER); 118 119 newTypeParamVar->SetTsType(newTypeParam); 120 newScope->InsertBinding(newTypeParamId->Name(), newTypeParamVar); 121 newTypeParamId->SetVariable(newTypeParamVar); 122 123 newTypeParams.push_back(newTypeParam); 124 newTypeParamNodes.push_back(newTypeParamNode); 125 substitution->emplace(oldTypeParam, newTypeParam); 126 } 127 128 for (size_t ix = 0; ix < oldIrTypeParams->Params().size(); ix++) { 129 auto *oldTypeParam = enclosingFunction->Signature()->TypeParams()[ix]->AsETSTypeParameter(); 130 131 if (auto *oldConstraint = oldTypeParam->GetConstraintType(); oldConstraint != nullptr) { 132 auto *newConstraint = oldConstraint->Substitute(checker->Relation(), substitution); 133 newTypeParams[ix]->SetConstraintType(newConstraint); 134 newTypeParamNodes[ix]->SetConstraint(allocator->New<ir::OpaqueTypeNode>(newConstraint)); 135 newTypeParamNodes[ix]->Constraint()->SetParent(newTypeParamNodes[ix]); 136 } 137 if (auto *oldDefault = oldTypeParam->GetDefaultType(); oldDefault != nullptr) { 138 auto *newDefault = oldDefault->Substitute(checker->Relation(), substitution); 139 newTypeParams[ix]->SetDefaultType(newDefault); 140 newTypeParamNodes[ix]->SetDefaultType(allocator->New<ir::OpaqueTypeNode>(newDefault)); 141 newTypeParamNodes[ix]->DefaultType()->SetParent(newTypeParamNodes[ix]); 142 } 143 } 144 145 auto *newIrTypeParams = util::NodeAllocator::ForceSetParent<ir::TSTypeParameterDeclaration>( 146 allocator, std::move(newTypeParamNodes), oldIrTypeParams->RequiredParams()); 147 newIrTypeParams->SetScope(newScope); 148 149 return {newIrTypeParams, substitution}; 150} 151 152using ParamsAndVarMap = 153 std::pair<ArenaVector<ir::Expression *>, ArenaMap<varbinder::Variable *, varbinder::Variable *>>; 154ParamsAndVarMap CreateLambdaCalleeParameters(public_lib::Context *ctx, const CalleeParameterInfo &calleeParameterInfo) 155{ 156 auto allocator = ctx->allocator; 157 auto checker = ctx->checker->AsETSChecker(); 158 auto varBinder = ctx->checker->VarBinder(); 159 auto resParams = ArenaVector<ir::Expression *>(allocator->Adapter()); 160 auto varMap = ArenaMap<varbinder::Variable *, varbinder::Variable *>(allocator->Adapter()); 161 162 auto paramLexScope = 163 varbinder::LexicalScope<varbinder::ParamScope>::Enter(varBinder, calleeParameterInfo.paramScope); 164 165 for (auto capturedVar : calleeParameterInfo.captured) { 166 auto *newType = capturedVar->TsType()->Substitute(checker->Relation(), calleeParameterInfo.substitution); 167 auto newId = util::NodeAllocator::ForceSetParent<ir::Identifier>( 168 allocator, capturedVar->Name(), allocator->New<ir::OpaqueTypeNode>(newType), allocator); 169 auto param = util::NodeAllocator::ForceSetParent<ir::ETSParameterExpression>(allocator, newId, nullptr); 170 auto [_, var] = varBinder->AddParamDecl(param); 171 (void)_; 172 var->SetTsType(newType); 173 var->SetScope(calleeParameterInfo.paramScope); 174 param->SetVariable(var); 175 param->SetTsType(newType); 176 resParams.push_back(param); 177 varMap[capturedVar] = var; 178 } 179 180 size_t i = 0; 181 182 for (auto *oldParam : calleeParameterInfo.lambda->Function()->Params()) { 183 if (i > calleeParameterInfo.limit) { 184 break; 185 } 186 187 auto *oldParamType = oldParam->AsETSParameterExpression()->Ident()->TypeAnnotation()->TsType(); 188 auto *newParamType = oldParamType->Substitute(checker->Relation(), calleeParameterInfo.substitution); 189 auto *newParam = oldParam->AsETSParameterExpression()->Clone(allocator, nullptr); 190 newParam->Ident()->SetVariable(nullptr); // Remove the cloned variable. 191 auto [_, var] = varBinder->AddParamDecl(newParam); 192 (void)_; 193 var->SetTsType(newParamType); 194 var->SetScope(calleeParameterInfo.paramScope); 195 newParam->SetVariable(var); 196 newParam->SetTsType(newParamType); 197 newParam->Ident()->SetTsType(newParamType); 198 resParams.push_back(newParam); 199 varMap[oldParam->AsETSParameterExpression()->Variable()] = var; 200 i++; 201 } 202 203 return {resParams, varMap}; 204} 205 206static void ProcessCalleeMethodBody(ir::AstNode *body, checker::ETSChecker *checker, varbinder::Scope *paramScope, 207 checker::Substitution *substitution, 208 ArenaMap<varbinder::Variable *, varbinder::Variable *> const &varMap) 209{ 210 if (body == nullptr || body->Scope() == nullptr) { 211 return; 212 } 213 body->Scope()->SetParent(paramScope); 214 body->IterateRecursively([&](ir::AstNode *node) { 215 if (node->IsIdentifier()) { 216 auto *id = node->AsIdentifier(); 217 if (auto ref = varMap.find(id->Variable()); ref != varMap.end()) { 218 id->SetVariable(ref->second); 219 } 220 } 221 if (substitution == nullptr) { 222 return; 223 } 224 if (node->IsTyped() && node->AsTyped()->TsType() != nullptr) { 225 node->AsTyped()->SetTsType(node->AsTyped()->TsType()->Substitute(checker->Relation(), substitution)); 226 } 227 if (node->IsCallExpression()) { 228 node->AsCallExpression()->SetSignature( 229 node->AsCallExpression()->Signature()->Substitute(checker->Relation(), substitution)); 230 } 231 if (node->IsETSNewClassInstanceExpression()) { 232 node->AsETSNewClassInstanceExpression()->SetSignature( 233 node->AsETSNewClassInstanceExpression()->GetSignature()->Substitute(checker->Relation(), substitution)); 234 } 235 if (node->IsScriptFunction()) { 236 node->AsScriptFunction()->SetSignature( 237 node->AsScriptFunction()->Signature()->Substitute(checker->Relation(), substitution)); 238 } 239 if (node->IsVariableDeclarator()) { 240 auto *id = node->AsVariableDeclarator()->Id(); 241 id->Variable()->SetTsType(id->Variable()->TsType()->Substitute(checker->Relation(), substitution)); 242 } 243 }); 244} 245 246static ir::MethodDefinition *SetUpCalleeMethod(public_lib::Context *ctx, LambdaInfo const *info, 247 CalleeMethodInfo const *cmInfo, ir::ScriptFunction *func, 248 varbinder::Scope *scopeForMethod, varbinder::Variable *variable) 249{ 250 auto *allocator = ctx->allocator; 251 auto *varBinder = ctx->checker->VarBinder()->AsETSBinder(); 252 253 auto *calleeClass = info->calleeClass; 254 auto *funcScope = func->Scope(); 255 auto *paramScope = funcScope->ParamScope(); 256 auto modifierFlags = ir::ModifierFlags::PUBLIC | 257 (info->callReceiver != nullptr ? ir::ModifierFlags::NONE : ir::ModifierFlags::STATIC) | 258 cmInfo->auxModifierFlags; 259 260 auto *calleeNameId = allocator->New<ir::Identifier>(cmInfo->calleeName, allocator); 261 func->SetIdent(calleeNameId); 262 calleeNameId->SetParent(func); 263 264 auto *calleeNameClone = calleeNameId->Clone(allocator, nullptr); 265 auto *funcExpr = util::NodeAllocator::ForceSetParent<ir::FunctionExpression>(allocator, func); 266 auto *method = util::NodeAllocator::ForceSetParent<ir::MethodDefinition>( 267 allocator, ir::MethodDefinitionKind::METHOD, calleeNameClone, funcExpr, modifierFlags, allocator, false); 268 calleeClass->Definition()->Body().push_back(method); 269 method->SetParent(calleeClass->Definition()); 270 271 if (variable == nullptr) { 272 auto [_, var] = 273 varBinder->NewVarDecl<varbinder::FunctionDecl>(func->Start(), allocator, cmInfo->calleeName, func); 274 (void)_; 275 var->AddFlag(varbinder::VariableFlags::METHOD); 276 var->SetScope(scopeForMethod); 277 func->Id()->SetVariable(var); 278 method->Id()->SetVariable(var); 279 if (info->callReceiver != nullptr) { 280 auto paramScopeCtx = varbinder::LexicalScope<varbinder::FunctionParamScope>::Enter(varBinder, paramScope); 281 varBinder->AddMandatoryParam(varbinder::TypedBinder::MANDATORY_PARAM_THIS); 282 calleeClass->Definition()->TsType()->AsETSObjectType()->AddProperty<checker::PropertyType::INSTANCE_METHOD>( 283 var->AsLocalVariable()); 284 } else { 285 calleeClass->Definition()->TsType()->AsETSObjectType()->AddProperty<checker::PropertyType::STATIC_METHOD>( 286 var->AsLocalVariable()); 287 } 288 289 varbinder::BoundContext bctx {varBinder->GetRecordTable(), calleeClass->Definition(), true}; 290 varBinder->ResolveReferencesForScopeWithContext(func, funcScope); 291 292 auto checkerCtx = checker::SavedCheckerContext(ctx->checker, checker::CheckerStatus::IN_CLASS, 293 calleeClass->Definition()->TsType()->AsETSObjectType()); 294 method->Check(ctx->checker->AsETSChecker()); 295 } else { 296 func->Id()->SetVariable(variable); 297 method->Id()->SetVariable(variable); 298 method->Function()->AddFlag(ir::ScriptFunctionFlags::OVERLOAD); 299 } 300 301 return method; 302} 303 304static varbinder::FunctionScope *GetAndApplyFunctionScope(public_lib::Context *ctx, LambdaInfo const *info, 305 CalleeMethodInfo const *cmInfo, 306 varbinder::ParamScope *paramScope, ir::ScriptFunction *func) 307{ 308 auto *allocator = ctx->allocator; 309 auto *funcScope = cmInfo->body == nullptr ? allocator->New<varbinder::FunctionScope>(allocator, paramScope) 310 : (cmInfo->body->Scope() == nullptr 311 ? allocator->New<varbinder::FunctionScope>(allocator, paramScope) 312 : cmInfo->body->Scope()->AsFunctionScope()); 313 funcScope->BindName(info->calleeClass->Definition()->TsType()->AsETSObjectType()->AssemblerName()); 314 func->SetScope(funcScope); 315 316 if (cmInfo->body != nullptr) { 317 cmInfo->body->AsBlockStatement()->SetScope(funcScope); 318 cmInfo->body->SetParent(func); 319 } 320 321 return funcScope; 322} 323 324static ir::MethodDefinition *CreateCalleeMethod(public_lib::Context *ctx, ir::ArrowFunctionExpression *lambda, 325 LambdaInfo const *info, CalleeMethodInfo const *cmInfo, 326 size_t limit = std::numeric_limits<size_t>::max(), 327 varbinder::Variable *variable = nullptr) 328{ 329 auto *allocator = ctx->allocator; 330 auto *varBinder = ctx->checker->VarBinder()->AsETSBinder(); 331 auto *checker = ctx->checker->AsETSChecker(); 332 333 auto *classScope = info->calleeClass->Definition()->Scope()->AsClassScope(); 334 335 auto *oldTypeParams = (info->enclosingFunction != nullptr) ? info->enclosingFunction->TypeParams() : nullptr; 336 auto enclosingScope = 337 info->callReceiver != nullptr ? classScope->InstanceMethodScope() : classScope->StaticMethodScope(); 338 339 auto [newTypeParams, subst0] = CloneTypeParams(ctx, oldTypeParams, info->enclosingFunction, enclosingScope); 340 auto *substitution = subst0; // NOTE(gogabr): needed to capture in a lambda later. 341 auto *scopeForMethod = newTypeParams != nullptr ? newTypeParams->Scope() : enclosingScope; 342 343 auto lexScope = varbinder::LexicalScope<varbinder::LocalScope>::Enter(varBinder, enclosingScope); 344 auto paramScope = allocator->New<varbinder::FunctionParamScope>(allocator, scopeForMethod); 345 346 CalleeParameterInfo cpi {lambda, *info->capturedVars, paramScope, substitution, limit}; 347 348 auto [params, vMap] = CreateLambdaCalleeParameters(ctx, cpi); 349 auto varMap = std::move(vMap); 350 351 auto *returnType = 352 cmInfo->forcedReturnType != nullptr 353 ? cmInfo->forcedReturnType 354 : lambda->Function()->Signature()->ReturnType()->Substitute(checker->Relation(), substitution); 355 auto returnTypeAnnotation = allocator->New<ir::OpaqueTypeNode>(returnType); 356 357 auto funcFlags = ir::ScriptFunctionFlags::METHOD | cmInfo->auxFunctionFlags; 358 auto modifierFlags = ir::ModifierFlags::PUBLIC | 359 (info->callReceiver != nullptr ? ir::ModifierFlags::NONE : ir::ModifierFlags::STATIC) | 360 cmInfo->auxModifierFlags; 361 362 auto func = util::NodeAllocator::ForceSetParent<ir::ScriptFunction>( 363 allocator, allocator, 364 ir::ScriptFunction::ScriptFunctionData { 365 cmInfo->body, ir::FunctionSignature(newTypeParams, std::move(params), returnTypeAnnotation), funcFlags, 366 modifierFlags}); 367 auto funcScope = GetAndApplyFunctionScope(ctx, info, cmInfo, paramScope, func); 368 ProcessCalleeMethodBody(cmInfo->body, checker, paramScope, substitution, varMap); 369 370 for (auto *param : func->Params()) { 371 param->SetParent(func); 372 } 373 374 // Bind the scopes 375 funcScope->BindNode(func); 376 paramScope->BindNode(func); 377 funcScope->AssignParamScope(paramScope); 378 paramScope->BindFunctionScope(funcScope); 379 380 /* NOTE(gogabr): Why does function scope need to replicate bindings from param scope?. 381 Keeping it for now. 382 */ 383 for (auto [ov, nv] : varMap) { 384 ASSERT(ov->Name() == nv->Name()); 385 auto name = ov->Name(); 386 funcScope->EraseBinding(name); 387 funcScope->InsertBinding(name, nv); 388 } 389 390 return SetUpCalleeMethod(ctx, info, cmInfo, func, scopeForMethod, variable); 391} 392 393static ir::MethodDefinition *CreateCalleeDefault(public_lib::Context *ctx, ir::ArrowFunctionExpression *lambda, 394 LambdaInfo const *info) 395{ 396 auto *allocator = ctx->allocator; 397 auto *checker = ctx->checker->AsETSChecker(); 398 auto *body = lambda->Function()->Body()->AsBlockStatement(); 399 auto calleeName = lambda->Function()->IsAsyncFunc() 400 ? (util::UString {checker::ETSChecker::GetAsyncImplName(info->name), allocator}).View() 401 : info->name; 402 auto *forcedReturnType = lambda->Function()->IsAsyncFunc() ? checker->GlobalETSNullishObjectType() : nullptr; 403 404 CalleeMethodInfo cmInfo; 405 cmInfo.calleeName = calleeName; 406 cmInfo.body = body; 407 cmInfo.forcedReturnType = forcedReturnType; 408 auto *method = CreateCalleeMethod(ctx, lambda, info, &cmInfo); 409 410 if (lambda->Function()->IsAsyncFunc()) { 411 CalleeMethodInfo cmInfoAsync; 412 cmInfoAsync.calleeName = info->name; 413 cmInfoAsync.body = nullptr; 414 cmInfoAsync.forcedReturnType = nullptr; 415 cmInfoAsync.auxModifierFlags = ir::ModifierFlags::NATIVE; 416 cmInfoAsync.auxFunctionFlags = ir::ScriptFunctionFlags::ASYNC; 417 auto *asyncMethod = CreateCalleeMethod(ctx, lambda, info, &cmInfoAsync); 418 return asyncMethod; 419 } 420 421 return method; 422} 423 424static ArenaVector<ir::Expression *> CreateArgsForOptionalCall(public_lib::Context *ctx, 425 ir::ArrowFunctionExpression *lambda, 426 ir::MethodDefinition *defaultMethod) 427{ 428 auto *checker = ctx->checker->AsETSChecker(); 429 430 ArenaVector<ir::Expression *> funcCallArgs(checker->Allocator()->Adapter()); 431 funcCallArgs.reserve(defaultMethod->Function()->Params().size()); 432 433 size_t i = 0; 434 435 for (auto *param : defaultMethod->Function()->Params()) { 436 if (!param->AsETSParameterExpression()->IsDefault()) { 437 auto *paramName = 438 param->AsETSParameterExpression()->Ident()->Clone(checker->Allocator(), nullptr)->AsIdentifier(); 439 funcCallArgs.push_back(paramName); 440 } else { 441 break; 442 } 443 i++; 444 } 445 446 for (; i < lambda->Function()->Params().size(); i++) { 447 auto *param = lambda->Function()->Params()[i]->AsETSParameterExpression(); 448 if (param->Initializer() == nullptr) { 449 checker->ThrowTypeError({"Expected initializer for parameter ", param->Ident()->Name(), "."}, 450 param->Start()); 451 } else { 452 funcCallArgs.push_back(param->Initializer()); 453 } 454 } 455 456 return funcCallArgs; 457} 458 459static ir::BlockStatement *CreateFunctionBody(public_lib::Context *ctx, ir::ArrowFunctionExpression *lambda, 460 ir::MethodDefinition *defaultMethod) 461{ 462 auto *checker = ctx->checker->AsETSChecker(); 463 ArenaVector<ir::Statement *> statements(checker->Allocator()->Adapter()); 464 ir::CallExpression *callExpression = nullptr; 465 ir::Expression *id = nullptr; 466 ir::Expression *accessor = nullptr; 467 auto *const callee = checker->AllocNode<ir::Identifier>(defaultMethod->Id()->Name(), checker->Allocator()); 468 callee->SetReference(); 469 470 if (defaultMethod->IsStatic() && defaultMethod->Parent()->IsClassDefinition() && 471 (!defaultMethod->Parent()->AsClassDefinition()->IsGlobal())) { 472 id = checker->AllocNode<ir::Identifier>(defaultMethod->Parent()->AsClassDefinition()->Ident()->Name(), 473 checker->Allocator()); 474 id->AsIdentifier()->SetReference(); 475 accessor = checker->AllocNode<ir::MemberExpression>(id, callee, ir::MemberExpressionKind::PROPERTY_ACCESS, 476 false, false); 477 callee->SetParent(accessor); 478 } 479 480 callExpression = checker->AllocNode<ir::CallExpression>(accessor != nullptr ? accessor : callee, 481 CreateArgsForOptionalCall(ctx, lambda, defaultMethod), 482 nullptr, false, false); 483 callee->SetParent(callExpression); 484 callExpression->SetSignature(defaultMethod->Function()->Signature()); 485 486 if ((defaultMethod->Function()->ReturnTypeAnnotation() != nullptr) || 487 ((defaultMethod->Function()->AsScriptFunction()->Flags() & ir::ScriptFunctionFlags::HAS_RETURN) != 0)) { 488 statements.push_back(checker->AllocNode<ir::ReturnStatement>(callExpression)); 489 } else { 490 statements.push_back(checker->AllocNode<ir::ExpressionStatement>(callExpression)); 491 } 492 493 auto blockStatement = checker->AllocNode<ir::BlockStatement>(checker->Allocator(), std::move(statements)); 494 blockStatement->Statements().front()->SetParent(blockStatement); 495 496 return blockStatement; 497} 498 499static ir::MethodDefinition *CreateCallee(public_lib::Context *ctx, ir::ArrowFunctionExpression *lambda, 500 LambdaInfo const *info, ir::MethodDefinition *defaultMethod, 501 size_t limit = std::numeric_limits<size_t>::max()) 502{ 503 auto *allocator = ctx->allocator; 504 auto *checker = ctx->checker->AsETSChecker(); 505 506 auto calleeName = lambda->Function()->IsAsyncFunc() 507 ? (util::UString {checker::ETSChecker::GetAsyncImplName(info->name), allocator}).View() 508 : info->name; 509 auto *forcedReturnType = lambda->Function()->IsAsyncFunc() ? checker->GlobalETSNullishObjectType() : nullptr; 510 511 CalleeMethodInfo cmInfo; 512 cmInfo.calleeName = calleeName; 513 cmInfo.body = CreateFunctionBody(ctx, lambda, defaultMethod); 514 cmInfo.forcedReturnType = forcedReturnType; 515 516 auto *method = CreateCalleeMethod(ctx, lambda, info, &cmInfo, limit, defaultMethod->Id()->Variable()); 517 518 if (lambda->Function()->IsAsyncFunc()) { 519 CalleeMethodInfo cmInfoAsync; 520 cmInfoAsync.calleeName = info->name; 521 cmInfoAsync.body = nullptr; 522 cmInfoAsync.forcedReturnType = nullptr; 523 cmInfoAsync.auxModifierFlags = ir::ModifierFlags::NATIVE; 524 cmInfoAsync.auxFunctionFlags = ir::ScriptFunctionFlags::ASYNC; 525 auto *asyncMethod = CreateCalleeMethod(ctx, lambda, info, &cmInfoAsync); 526 return asyncMethod; 527 } 528 529 return method; 530} 531 532// The name "=t" used in extension methods has special meaning for the code generator; 533// avoid it as parameter and field name in our generated code. 534static util::StringView AvoidMandatoryThis(util::StringView name) 535{ 536 return (name == varbinder::TypedBinder::MANDATORY_PARAM_THIS) ? "$extensionThis" : name; 537} 538 539static void CreateLambdaClassFields(public_lib::Context *ctx, ir::ClassDefinition *classDefinition, 540 LambdaInfo const *info, checker::Substitution *substitution) 541{ 542 auto *allocator = ctx->allocator; 543 auto *parser = ctx->parser->AsETSParser(); 544 auto *checker = ctx->checker->AsETSChecker(); 545 auto props = ArenaVector<ir::AstNode *>(allocator->Adapter()); 546 547 if (info->callReceiver != nullptr) { 548 auto *outerThisDeclaration = parser->CreateFormattedClassFieldDefinition( 549 "@@I1: @@T2", "$this", 550 info->calleeClass->Definition()->TsType()->Substitute(checker->Relation(), substitution)); 551 props.push_back(outerThisDeclaration); 552 } 553 554 for (auto *captured : *info->capturedVars) { 555 auto *varDeclaration = parser->CreateFormattedClassFieldDefinition( 556 "@@I1: @@T2", AvoidMandatoryThis(captured->Name()), 557 captured->TsType()->Substitute(checker->Relation(), substitution)); 558 props.push_back(varDeclaration); 559 } 560 561 classDefinition->AddProperties(std::move(props)); 562} 563 564static void CreateLambdaClassConstructor(public_lib::Context *ctx, ir::ClassDefinition *classDefinition, 565 LambdaInfo const *info, checker::Substitution *substitution) 566{ 567 auto *allocator = ctx->allocator; 568 auto *parser = ctx->parser->AsETSParser(); 569 auto *checker = ctx->checker->AsETSChecker(); 570 571 auto params = ArenaVector<ir::Expression *>(allocator->Adapter()); 572 auto makeParam = [checker, allocator, substitution, ¶ms](util::StringView name, checker::Type *type) { 573 auto *substitutedType = type->Substitute(checker->Relation(), substitution); 574 auto *id = util::NodeAllocator::ForceSetParent<ir::Identifier>( 575 allocator, name, allocator->New<ir::OpaqueTypeNode>(substitutedType), allocator); 576 auto *param = util::NodeAllocator::ForceSetParent<ir::ETSParameterExpression>(allocator, id, nullptr); 577 params.push_back(param); 578 }; 579 580 if (info->callReceiver != nullptr) { 581 makeParam("$this", info->calleeClass->Definition()->TsType()); 582 } 583 for (auto *var : *info->capturedVars) { 584 makeParam(AvoidMandatoryThis(var->Name()), var->TsType()); 585 } 586 587 auto bodyStmts = ArenaVector<ir::Statement *>(allocator->Adapter()); 588 auto makeStatement = [&parser, &bodyStmts](util::StringView name) { 589 auto adjustedName = AvoidMandatoryThis(name); 590 auto *statement = parser->CreateFormattedStatement("this.@@I1 = @@I2", adjustedName, adjustedName); 591 bodyStmts.push_back(statement); 592 }; 593 if (info->callReceiver != nullptr) { 594 makeStatement("$this"); 595 } 596 for (auto *var : *info->capturedVars) { 597 makeStatement(var->Name()); 598 } 599 auto *body = util::NodeAllocator::ForceSetParent<ir::BlockStatement>(allocator, allocator, std::move(bodyStmts)); 600 601 auto *constructorId = allocator->New<ir::Identifier>("constructor", allocator); 602 auto *constructorIdClone = constructorId->Clone(allocator, nullptr); 603 604 auto *func = util::NodeAllocator::ForceSetParent<ir::ScriptFunction>( 605 allocator, allocator, 606 ir::ScriptFunction::ScriptFunctionData {body, ir::FunctionSignature(nullptr, std::move(params), nullptr), 607 ir::ScriptFunctionFlags::CONSTRUCTOR | 608 ir::ScriptFunctionFlags::IMPLICIT_SUPER_CALL_NEEDED}); 609 func->SetIdent(constructorId); 610 auto *funcExpr = util::NodeAllocator::ForceSetParent<ir::FunctionExpression>(allocator, func); 611 612 auto *ctor = util::NodeAllocator::ForceSetParent<ir::MethodDefinition>( 613 allocator, ir::MethodDefinitionKind::CONSTRUCTOR, constructorIdClone, funcExpr, ir::ModifierFlags::NONE, 614 allocator, false); 615 616 classDefinition->Body().push_back(ctor); 617 ctor->SetParent(classDefinition); 618} 619 620static ir::CallExpression *CreateCallForLambdaClassInvoke(public_lib::Context *ctx, LambdaInfo const *info, 621 LambdaClassInvokeInfo const *lciInfo, bool wrapToObject) 622{ 623 auto *allocator = ctx->allocator; 624 auto *parser = ctx->parser->AsETSParser(); 625 auto *checker = ctx->checker->AsETSChecker(); 626 627 auto callArguments = ArenaVector<ir::Expression *>(allocator->Adapter()); 628 for (auto *captured : *info->capturedVars) { 629 auto *arg = parser->CreateFormattedExpression("this.@@I1", AvoidMandatoryThis(captured->Name())); 630 callArguments.push_back(arg); 631 } 632 for (auto *lambdaParam : lciInfo->lambdaSignature->Params()) { 633 auto argName = lambdaParam->Name(); 634 auto *type = lambdaParam->TsType()->Substitute(checker->Relation(), lciInfo->substitution); 635 auto *arg = wrapToObject ? parser->CreateFormattedExpression("@@I1 as @@T2 as @@T3", argName, 636 checker->MaybePromotedBuiltinType(type), type) 637 : allocator->New<ir::Identifier>(argName, allocator); 638 callArguments.push_back(arg); 639 } 640 641 ir::Expression *calleeReceiver; 642 if (info->callReceiver != nullptr) { 643 calleeReceiver = parser->CreateFormattedExpression("this.@@I1", "$this"); 644 } else { 645 calleeReceiver = lciInfo->callee->Parent()->AsClassDefinition()->Ident()->Clone(allocator, nullptr); 646 } 647 648 auto *calleeMemberExpr = util::NodeAllocator::ForceSetParent<ir::MemberExpression>( 649 allocator, calleeReceiver, lciInfo->callee->Key()->Clone(allocator, nullptr)->AsExpression(), 650 ir::MemberExpressionKind::PROPERTY_ACCESS, false, false); 651 auto *call = parser->CreateFormattedExpression("@@E1(@@[E2)", calleeMemberExpr, std::move(callArguments)) 652 ->AsCallExpression(); 653 654 if (lciInfo->classDefinition->TypeParams() != nullptr) { 655 auto typeArgs = ArenaVector<ir::TypeNode *>(allocator->Adapter()); 656 for (auto *tp : lciInfo->classDefinition->TypeParams()->Params()) { 657 typeArgs.push_back(allocator->New<ir::OpaqueTypeNode>(tp->Name()->AsIdentifier()->Variable()->TsType())); 658 } 659 auto *typeArg = 660 util::NodeAllocator::ForceSetParent<ir::TSTypeParameterInstantiation>(allocator, std::move(typeArgs)); 661 call->SetTypeParams(typeArg); 662 typeArg->SetParent(call); 663 } 664 665 return call; 666} 667 668static void CreateLambdaClassInvoke(public_lib::Context *ctx, LambdaInfo const *info, 669 LambdaClassInvokeInfo const *lciInfo, util::StringView methodName, 670 bool wrapToObject) 671{ 672 auto *allocator = ctx->allocator; 673 auto *parser = ctx->parser->AsETSParser(); 674 auto *checker = ctx->checker->AsETSChecker(); 675 auto *anyType = checker->GlobalETSNullishObjectType(); 676 677 auto params = ArenaVector<ir::Expression *>(allocator->Adapter()); 678 for (auto *lparam : lciInfo->lambdaSignature->Params()) { 679 auto *type = wrapToObject ? anyType : lparam->TsType()->Substitute(checker->Relation(), lciInfo->substitution); 680 auto *id = util::NodeAllocator::ForceSetParent<ir::Identifier>( 681 allocator, lparam->Name(), allocator->New<ir::OpaqueTypeNode>(type), allocator); 682 auto *param = util::NodeAllocator::ForceSetParent<ir::ETSParameterExpression>(allocator, id, nullptr); 683 params.push_back(param); 684 } 685 686 auto *call = CreateCallForLambdaClassInvoke(ctx, info, lciInfo, wrapToObject); 687 688 auto bodyStmts = ArenaVector<ir::Statement *>(allocator->Adapter()); 689 if (lciInfo->lambdaSignature->ReturnType() == checker->GlobalVoidType()) { 690 auto *callStmt = util::NodeAllocator::ForceSetParent<ir::ExpressionStatement>(allocator, call); 691 bodyStmts.push_back(callStmt); 692 if (wrapToObject) { 693 auto *returnStmt = util::NodeAllocator::ForceSetParent<ir::ReturnStatement>( 694 allocator, allocator->New<ir::UndefinedLiteral>()); 695 bodyStmts.push_back(returnStmt); 696 } 697 } else { 698 auto *returnExpr = wrapToObject ? parser->CreateFormattedExpression("@@E1 as @@T2", call, anyType) : call; 699 auto *returnStmt = util::NodeAllocator::ForceSetParent<ir::ReturnStatement>(allocator, returnExpr); 700 bodyStmts.push_back(returnStmt); 701 } 702 703 auto body = util::NodeAllocator::ForceSetParent<ir::BlockStatement>(allocator, allocator, std::move(bodyStmts)); 704 auto *returnType2 = allocator->New<ir::OpaqueTypeNode>( 705 wrapToObject ? anyType 706 : lciInfo->lambdaSignature->ReturnType()->Substitute(checker->Relation(), lciInfo->substitution)); 707 auto *func = util::NodeAllocator::ForceSetParent<ir::ScriptFunction>( 708 allocator, allocator, 709 ir::ScriptFunction::ScriptFunctionData {body, ir::FunctionSignature(nullptr, std::move(params), returnType2), 710 ir::ScriptFunctionFlags::METHOD}); 711 712 auto *invokeId = allocator->New<ir::Identifier>(methodName, allocator); 713 func->SetIdent(invokeId); 714 715 auto *funcExpr = util::NodeAllocator::ForceSetParent<ir::FunctionExpression>(allocator, func); 716 717 auto *invokeIdClone = invokeId->Clone(allocator, nullptr); 718 auto *invokeMethod = util::NodeAllocator::ForceSetParent<ir::MethodDefinition>( 719 allocator, ir::MethodDefinitionKind::METHOD, invokeIdClone, funcExpr, ir::ModifierFlags::NONE, allocator, 720 false); 721 722 lciInfo->classDefinition->Body().push_back(invokeMethod); 723 invokeMethod->SetParent(lciInfo->classDefinition); 724} 725 726static std::string BuildLambdaClass(public_lib::Context *ctx, ArenaVector<checker::Signature *> &lambdaSigs, 727 checker::Substitution *substitution, ArenaVector<checker::Type *> &funcInterfaces) 728{ 729 auto *checker = ctx->checker->AsETSChecker(); 730 731 std::string stringBuilder = "final class @@I1 implements "; 732 constexpr uint8_t OFF_SET = 2; 733 734 for (size_t i = 0; i < lambdaSigs.size(); i++) { 735 funcInterfaces.push_back(checker->FunctionTypeToFunctionalInterfaceType( 736 lambdaSigs[i]->Substitute(checker->Relation(), substitution))); 737 stringBuilder += "@@T" + std::to_string(i + OFF_SET) + ", "; 738 } 739 740 stringBuilder.pop_back(); 741 stringBuilder.pop_back(); 742 stringBuilder += " {}"; 743 744 return stringBuilder; 745} 746 747static ir::ClassDeclaration *CreateLambdaClass(public_lib::Context *ctx, ArenaVector<checker::Signature *> &lambdaSigs, 748 ir::MethodDefinition *callee, LambdaInfo const *info) 749{ 750 auto *allocator = ctx->allocator; 751 auto *parser = ctx->parser->AsETSParser(); 752 auto *checker = ctx->checker->AsETSChecker(); 753 auto *varBinder = ctx->checker->VarBinder()->AsETSBinder(); 754 755 auto *oldTypeParams = (info->enclosingFunction != nullptr) ? info->enclosingFunction->TypeParams() : nullptr; 756 auto [newTypeParams, subst0] = 757 CloneTypeParams(ctx, oldTypeParams, info->enclosingFunction, ctx->parserProgram->GlobalClassScope()); 758 auto *substitution = subst0; // NOTE(gogabr): needed to capture in a lambda later. 759 760 auto lexScope = varbinder::LexicalScope<varbinder::Scope>::Enter(varBinder, ctx->parserProgram->GlobalClassScope()); 761 762 auto lambdaClassName = util::UString {std::string_view {"LambdaObject-"}, allocator}; 763 lambdaClassName.Append(info->calleeClass->Definition()->Ident()->Name()); 764 lambdaClassName.Append("$"); 765 lambdaClassName.Append(info->name); 766 767 ArenaVector<checker::Type *> funcInterfaces(allocator->Adapter()); 768 769 auto *classDeclaration = 770 parser 771 ->CreateFormattedTopLevelStatement(BuildLambdaClass(ctx, lambdaSigs, substitution, funcInterfaces), 772 lambdaClassName, funcInterfaces) 773 ->AsClassDeclaration(); 774 auto *classDefinition = classDeclaration->Definition(); 775 776 // Adjust the class definition compared to what the parser gives. 777 classDefinition->Body().clear(); // remove the default empty constructor 778 classDefinition->AddModifier(ir::ModifierFlags::PUBLIC | ir::ModifierFlags::FUNCTIONAL); 779 if (newTypeParams != nullptr) { 780 classDefinition->SetTypeParams(newTypeParams); 781 newTypeParams->SetParent(classDefinition); 782 } 783 784 auto *program = varBinder->GetRecordTable()->Program(); 785 program->Ast()->Statements().push_back(classDeclaration); 786 classDeclaration->SetParent(program->Ast()); 787 788 CreateLambdaClassFields(ctx, classDefinition, info, substitution); 789 CreateLambdaClassConstructor(ctx, classDefinition, info, substitution); 790 791 LambdaClassInvokeInfo lciInfo; 792 lciInfo.callee = callee; 793 lciInfo.classDefinition = classDefinition; 794 lciInfo.substitution = substitution; 795 796 for (auto it : lambdaSigs) { 797 lciInfo.lambdaSignature = it; 798 CreateLambdaClassInvoke(ctx, info, &lciInfo, "invoke0", true); 799 CreateLambdaClassInvoke(ctx, info, &lciInfo, "invoke", false); 800 } 801 802 InitScopesPhaseETS::RunExternalNode(classDeclaration, varBinder); 803 varBinder->ResolveReferencesForScopeWithContext(classDeclaration, varBinder->TopScope()); 804 classDeclaration->Check(checker); 805 806 return classDeclaration; 807} 808 809static ir::ETSNewClassInstanceExpression *CreateConstructorCall(public_lib::Context *ctx, ir::AstNode *lambdaOrFuncRef, 810 ir::ClassDeclaration *lambdaClass, 811 LambdaInfo const *info) 812{ 813 auto *allocator = ctx->allocator; 814 auto *varBinder = ctx->checker->VarBinder()->AsETSBinder(); 815 auto *checker = ctx->checker->AsETSChecker(); 816 817 auto args = ArenaVector<ir::Expression *>(allocator->Adapter()); 818 if (info->callReceiver != nullptr) { 819 args.push_back(info->callReceiver); 820 } 821 for (auto captured : *info->capturedVars) { 822 auto *id = allocator->New<ir::Identifier>(captured->Name(), allocator); 823 args.push_back(id); 824 } 825 826 checker::ETSObjectType *constructedType = lambdaClass->Definition()->TsType()->AsETSObjectType(); 827 if (info->enclosingFunction != nullptr) { 828 constructedType = constructedType->SubstituteArguments(checker->Relation(), 829 info->enclosingFunction->Signature()->TypeParams()); 830 } 831 auto *newExpr = util::NodeAllocator::ForceSetParent<ir::ETSNewClassInstanceExpression>( 832 allocator, allocator->New<ir::OpaqueTypeNode>(constructedType), std::move(args), nullptr); 833 auto *lambdaOrFuncRefParent = lambdaOrFuncRef->Parent(); 834 newExpr->SetParent(lambdaOrFuncRefParent); 835 // NOTE(dslynko, #19869): Required for correct debug-info generation 836 newExpr->SetRange(lambdaOrFuncRefParent != nullptr ? lambdaOrFuncRefParent->Range() : lambdaOrFuncRef->Range()); 837 838 auto *nearestScope = NearestScope(lambdaOrFuncRef); 839 auto lexScope = varbinder::LexicalScope<varbinder::Scope>::Enter(varBinder, nearestScope); 840 varBinder->ResolveReferencesForScopeWithContext(newExpr, nearestScope); 841 842 auto checkerCtx = checker::SavedCheckerContext(ctx->checker, checker::CheckerStatus::IN_CLASS, 843 info->calleeClass->Definition()->TsType()->AsETSObjectType()); 844 auto scopeCtx = checker::ScopeContext(ctx->checker, nearestScope); 845 newExpr->Check(checker); 846 847 return newExpr; 848} 849 850static ir::AstNode *ConvertLambda(public_lib::Context *ctx, ir::ArrowFunctionExpression *lambda) 851{ 852 auto *allocator = ctx->allocator; 853 854 auto firstDefaultIndex = lambda->Function()->DefaultParamIndex(); 855 856 LambdaInfo info; 857 std::tie(info.calleeClass, info.enclosingFunction) = FindEnclosingClassAndFunction(lambda); 858 info.name = CreateCalleeName(allocator); 859 auto capturedVars = FindCaptured(allocator, lambda); 860 info.capturedVars = &capturedVars; 861 info.callReceiver = CheckIfNeedThis(lambda) ? allocator->New<ir::ThisExpression>() : nullptr; 862 863 auto *callee = CreateCalleeDefault(ctx, lambda, &info); 864 865 if (firstDefaultIndex < lambda->Function()->Params().size()) { 866 for (size_t i = firstDefaultIndex; i < lambda->Function()->Params().size(); i++) { 867 auto overload = CreateCallee(ctx, lambda, &info, callee, firstDefaultIndex); 868 callee->AddOverload(overload); 869 } 870 } 871 872 ASSERT(lambda->TsType()->IsETSFunctionType()); 873 auto *lambdaType = lambda->TsType()->AsETSFunctionType(); 874 auto *lambdaClass = CreateLambdaClass(ctx, lambdaType->CallSignatures(), callee, &info); 875 auto *constructorCall = CreateConstructorCall(ctx, lambda, lambdaClass, &info); 876 return constructorCall; 877} 878 879static checker::Signature *GuessSignature(checker::ETSChecker *checker, ir::Expression *ast) 880{ 881 ASSERT(ast->TsType()->IsETSFunctionType()); 882 auto *type = ast->TsType()->AsETSFunctionType(); 883 884 if (type->CallSignatures().size() == 1) { 885 return type->CallSignatures()[0]; 886 } 887 888 if (!ast->Parent()->IsCallExpression()) { 889 checker->ThrowTypeError( 890 std::initializer_list<checker::TypeErrorMessageElement> {"Cannot deduce call signature"}, ast->Start()); 891 } 892 893 auto &args = ast->Parent()->AsCallExpression()->Arguments(); 894 for (size_t ix = 0; ix < args.size(); ix++) { 895 if (args[ix] != ast) { 896 continue; 897 } 898 899 auto *argType = ast->Parent()->AsCallExpression()->Signature()->Params()[ix]->TsType(); 900 checker::Signature *sigFound = nullptr; 901 902 for (auto *sig : type->CallSignatures()) { 903 auto *tmpFunType = checker->Allocator()->New<checker::ETSFunctionType>("", sig, checker->Allocator()); 904 checker::AssignmentContext actx { 905 checker->Relation(), ast, tmpFunType, argType, ast->Start(), {}, checker::TypeRelationFlag::NO_THROW}; 906 if (!actx.IsAssignable()) { 907 continue; 908 } 909 if (sigFound != nullptr) { 910 // ambiguiuty 911 checker->ThrowTypeError( 912 std::initializer_list<checker::TypeErrorMessageElement> {"Cannot deduce call signature"}, 913 ast->Start()); 914 } 915 sigFound = sig; 916 } 917 if (sigFound != nullptr) { 918 return sigFound; 919 } 920 } 921 922 checker->ThrowTypeError({"Cannot deduce call signature"}, ast->Start()); 923} 924 925static ir::ArrowFunctionExpression *CreateWrappingLambda(public_lib::Context *ctx, ir::Expression *funcRef) 926{ 927 auto *allocator = ctx->allocator; 928 auto *varBinder = ctx->checker->VarBinder()->AsETSBinder(); 929 auto *signature = GuessSignature(ctx->checker->AsETSChecker(), funcRef); 930 931 auto *parent = funcRef->Parent(); 932 933 ArenaVector<ir::Expression *> params {allocator->Adapter()}; 934 for (auto *p : signature->Params()) { 935 params.push_back(util::NodeAllocator::ForceSetParent<ir::ETSParameterExpression>( 936 allocator, 937 allocator->New<ir::Identifier>(p->Name(), allocator->New<ir::OpaqueTypeNode>(p->TsType()), allocator), 938 nullptr)); 939 } 940 auto *func = util::NodeAllocator::ForceSetParent<ir::ScriptFunction>( 941 allocator, allocator, 942 ir::ScriptFunction::ScriptFunctionData { 943 nullptr, 944 ir::FunctionSignature {nullptr, std::move(params), 945 allocator->New<ir::OpaqueTypeNode>(signature->ReturnType())}, 946 ir::ScriptFunctionFlags::ARROW}); 947 948 ArenaVector<ir::Statement *> bodyStmts {allocator->Adapter()}; 949 ArenaVector<ir::Expression *> callArgs {allocator->Adapter()}; 950 951 for (auto *p : func->Params()) { 952 callArgs.push_back(p->AsETSParameterExpression()->Ident()->Clone(allocator, nullptr)); 953 } 954 auto *callExpr = util::NodeAllocator::ForceSetParent<ir::CallExpression>(allocator, funcRef, std::move(callArgs), 955 nullptr, false); 956 ir::Statement *stmt; 957 if (signature->ReturnType() == ctx->checker->AsETSChecker()->GlobalVoidType()) { 958 stmt = util::NodeAllocator::ForceSetParent<ir::ExpressionStatement>(allocator, callExpr); 959 } else { 960 stmt = util::NodeAllocator::ForceSetParent<ir::ReturnStatement>(allocator, callExpr); 961 } 962 bodyStmts.push_back(stmt); 963 func->SetBody(util::NodeAllocator::ForceSetParent<ir::BlockStatement>(allocator, allocator, std::move(bodyStmts))); 964 func->Body()->SetParent(func); 965 auto *lambda = util::NodeAllocator::ForceSetParent<ir::ArrowFunctionExpression>(allocator, func); 966 lambda->SetParent(parent); 967 968 auto *nearestScope = NearestScope(lambda); 969 auto lexScope = varbinder::LexicalScope<varbinder::Scope>::Enter(varBinder, nearestScope); 970 InitScopesPhaseETS::RunExternalNode(lambda, varBinder); 971 varBinder->ResolveReferencesForScopeWithContext(lambda, nearestScope); 972 973 auto [enclosingClass, enclosingFun] = FindEnclosingClassAndFunction(parent); 974 (void)enclosingFun; 975 976 auto checkerCtx = checker::SavedCheckerContext(ctx->checker, checker::CheckerStatus::IN_CLASS, 977 enclosingClass->Definition()->TsType()->AsETSObjectType()); 978 auto scopeCtx = checker::ScopeContext(ctx->checker, nearestScope); 979 lambda->Check(ctx->checker->AsETSChecker()); 980 981 return lambda; 982} 983 984static ir::AstNode *ConvertFunctionReference(public_lib::Context *ctx, ir::Expression *funcRef) 985{ 986 auto *allocator = ctx->allocator; 987 ASSERT(funcRef->IsIdentifier() || 988 (funcRef->IsMemberExpression() && 989 funcRef->AsMemberExpression()->Kind() == ir::MemberExpressionKind::PROPERTY_ACCESS && 990 funcRef->AsMemberExpression()->Property()->IsIdentifier())); 991 varbinder::Variable *var; 992 if (funcRef->IsIdentifier()) { 993 var = funcRef->AsIdentifier()->Variable(); 994 } else { 995 auto *mexpr = funcRef->AsMemberExpression(); 996 // NOTE(gogabr): mexpr->PropVar() is a synthetic variable wwith no reference to the method definition. Why? 997 var = mexpr->Object()->TsType()->AsETSObjectType()->GetProperty( 998 mexpr->Property()->AsIdentifier()->Name(), 999 checker::PropertySearchFlags::SEARCH_INSTANCE_METHOD | checker::PropertySearchFlags::SEARCH_STATIC_METHOD | 1000 checker::PropertySearchFlags::DISALLOW_SYNTHETIC_METHOD_CREATION); 1001 ASSERT(var != nullptr); 1002 } 1003 1004 ASSERT(var->Declaration()->Node()->IsMethodDefinition()); 1005 auto *method = var->Declaration()->Node()->AsMethodDefinition(); 1006 1007 if (method->IsPrivate() || method->IsProtected()) { 1008 // Direct reference to method will be impossible from the lambda class, so replace func ref with a lambda 1009 // that will translate to a proxy method 1010 auto *lam = CreateWrappingLambda(ctx, funcRef); 1011 return ConvertLambda(ctx, lam); 1012 } 1013 1014 LambdaInfo info; 1015 info.calleeClass = method->Parent()->Parent()->AsClassDeclaration(); 1016 info.enclosingFunction = nullptr; 1017 info.name = CreateCalleeName(allocator); 1018 auto emptySet = ArenaSet<varbinder::Variable *>(allocator->Adapter()); 1019 info.capturedVars = &emptySet; 1020 if (method->IsStatic()) { 1021 info.callReceiver = nullptr; 1022 } else { 1023 ASSERT(funcRef->IsMemberExpression()); 1024 info.callReceiver = funcRef->AsMemberExpression()->Object(); 1025 } 1026 1027 auto *signature = GuessSignature(ctx->checker->AsETSChecker(), funcRef); 1028 ArenaVector<checker::Signature *> signatures(allocator->Adapter()); 1029 signatures.push_back(signature); 1030 auto *lambdaClass = CreateLambdaClass(ctx, signatures, method, &info); 1031 auto *constructorCall = CreateConstructorCall(ctx, funcRef, lambdaClass, &info); 1032 return constructorCall; 1033} 1034 1035static bool IsFunctionOrMethodCall(ir::AstNode const *node) 1036{ 1037 ASSERT(node->IsCallExpression()); 1038 auto const *callee = node->AsCallExpression()->Callee(); 1039 1040 if (callee->TsType() != nullptr && callee->TsType()->IsETSExtensionFuncHelperType()) { 1041 return true; 1042 } 1043 1044 if (callee->IsMemberExpression() && callee->AsMemberExpression()->Object()->TsType() != nullptr && 1045 (callee->AsMemberExpression()->Object()->TsType()->IsETSEnumType())) { 1046 return true; 1047 } 1048 1049 varbinder::Variable *var = nullptr; 1050 if (callee->IsMemberExpression() && 1051 callee->AsMemberExpression()->Kind() == ir::MemberExpressionKind::PROPERTY_ACCESS) { 1052 var = callee->AsMemberExpression()->Property()->Variable(); 1053 } else if (callee->IsIdentifier()) { 1054 var = callee->AsIdentifier()->Variable(); 1055 } 1056 return var != nullptr && !checker::ETSChecker::IsVariableGetterSetter(var) && 1057 (var->Flags() & varbinder::VariableFlags::METHOD) != 0; 1058} 1059 1060static ir::AstNode *InsertInvokeCall(public_lib::Context *ctx, ir::CallExpression *call) 1061{ 1062 auto *allocator = ctx->allocator; 1063 auto *checker = ctx->checker->AsETSChecker(); 1064 auto *varBinder = checker->VarBinder()->AsETSBinder(); 1065 1066 auto *oldCallee = call->Callee(); 1067 auto *ifaceType = oldCallee->TsType() != nullptr && oldCallee->TsType()->IsETSObjectType() 1068 ? oldCallee->TsType()->AsETSObjectType() 1069 : checker->FunctionTypeToFunctionalInterfaceType(call->Signature()); 1070 if (ifaceType->IsETSDynamicType()) { 1071 return call; 1072 } 1073 auto *prop = ifaceType->GetProperty(checker::FUNCTIONAL_INTERFACE_INVOKE_METHOD_NAME, 1074 checker::PropertySearchFlags::SEARCH_INSTANCE_METHOD | 1075 checker::PropertySearchFlags::SEARCH_IN_INTERFACES); 1076 ASSERT(prop != nullptr); 1077 auto *invoke0Id = allocator->New<ir::Identifier>(checker::FUNCTIONAL_INTERFACE_INVOKE_METHOD_NAME, allocator); 1078 invoke0Id->SetTsType(prop->TsType()); 1079 invoke0Id->SetVariable(prop); 1080 1081 auto *newCallee = util::NodeAllocator::ForceSetParent<ir::MemberExpression>( 1082 allocator, oldCallee, invoke0Id, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false); 1083 newCallee->SetTsType(prop->TsType()); 1084 newCallee->SetObjectType(ifaceType); 1085 1086 call->SetCallee(newCallee); 1087 call->SetSignature(prop->TsType()->AsETSFunctionType()->CallSignatures()[0]); 1088 1089 /* NOTE(gogabr): argument types may have been spoiled by widening/narrowing conversions. 1090 Repair them here. 1091 In the future, make sure those conversions behave appropriately. 1092 */ 1093 for (auto *arg : call->Arguments()) { 1094 auto boxingFlags = arg->GetBoxingUnboxingFlags(); 1095 Recheck(varBinder, checker, arg); 1096 arg->SetBoxingUnboxingFlags(boxingFlags); 1097 // NOTE (psiket) Temporal solution 1098 if (arg->TsType()->IsETSEnumType()) { 1099 arg->SetBoxingUnboxingFlags(ir::BoxingUnboxingFlags::BOX_TO_ENUM); 1100 } 1101 } 1102 1103 return call; 1104} 1105 1106static bool IsRedirectingConstructorCall(ir::CallExpression *expr) 1107{ 1108 return expr->Callee()->IsThisExpression() || expr->Callee()->IsSuperExpression(); 1109} 1110 1111static bool IsInCalleePosition(ir::Expression *expr) 1112{ 1113 return expr->Parent()->IsCallExpression() && expr->Parent()->AsCallExpression()->Callee() == expr; 1114} 1115 1116static ir::AstNode *BuildLambdaClassWhenNeeded(public_lib::Context *ctx, ir::AstNode *node) 1117{ 1118 if (node->IsArrowFunctionExpression()) { 1119 return ConvertLambda(ctx, node->AsArrowFunctionExpression()); 1120 } 1121 if (node->IsIdentifier()) { 1122 auto *id = node->AsIdentifier(); 1123 auto *var = id->Variable(); 1124 if (id->IsReference() && id->TsType() != nullptr && id->TsType()->IsETSFunctionType() && var != nullptr && 1125 var->Declaration()->IsFunctionDecl() && !IsInCalleePosition(id)) { 1126 return ConvertFunctionReference(ctx, id); 1127 } 1128 } 1129 if (node->IsMemberExpression()) { 1130 auto *mexpr = node->AsMemberExpression(); 1131 if (mexpr->Kind() == ir::MemberExpressionKind::PROPERTY_ACCESS && mexpr->TsType() != nullptr && 1132 mexpr->TsType()->IsETSFunctionType() && mexpr->Object()->TsType()->IsETSObjectType()) { 1133 ASSERT(mexpr->Property()->IsIdentifier()); 1134 auto *var = mexpr->Object()->TsType()->AsETSObjectType()->GetProperty( 1135 mexpr->Property()->AsIdentifier()->Name(), 1136 checker::PropertySearchFlags::SEARCH_INSTANCE_METHOD | 1137 checker::PropertySearchFlags::SEARCH_STATIC_METHOD | 1138 checker::PropertySearchFlags::DISALLOW_SYNTHETIC_METHOD_CREATION); 1139 if (var != nullptr && var->Declaration()->IsFunctionDecl() && !IsInCalleePosition(mexpr)) { 1140 return ConvertFunctionReference(ctx, mexpr); 1141 } 1142 } 1143 } 1144 return node; 1145} 1146 1147static void CallPerformForExtSources(LambdaConversionPhase *phase, public_lib::Context *ctx, parser::Program *program) 1148{ 1149 auto *varBinder = ctx->checker->VarBinder()->AsETSBinder(); 1150 for (auto &[_, extPrograms] : program->ExternalSources()) { 1151 (void)_; 1152 for (auto *extProg : extPrograms) { 1153 varbinder::RecordTableContext bctx {varBinder, extProg}; 1154 phase->Perform(ctx, extProg); 1155 } 1156 } 1157} 1158 1159bool LambdaConversionPhase::Perform(public_lib::Context *ctx, parser::Program *program) 1160{ 1161 parser::SavedFormattingFileName savedFormattingName(ctx->parser->AsETSParser(), "lambda-conversion"); 1162 1163 // For reproducibility of results when several compilation sessions are executed during 1164 // the same process's lifetime. 1165 if (program == ctx->parserProgram) { 1166 ResetCalleeCount(); 1167 } 1168 1169 if (ctx->config->options->CompilerOptions().compilationMode == CompilationMode::GEN_STD_LIB) { 1170 CallPerformForExtSources(this, ctx, program); 1171 } 1172 1173 program->Ast()->TransformChildrenRecursivelyPostorder( 1174 [ctx](ir::AstNode *node) { return BuildLambdaClassWhenNeeded(ctx, node); }, Name()); 1175 1176 auto insertInvokeIfNeeded = [ctx](ir::AstNode *node) { 1177 if (node->IsCallExpression() && !IsFunctionOrMethodCall(node) && 1178 !IsRedirectingConstructorCall(node->AsCallExpression())) { 1179 return InsertInvokeCall(ctx, node->AsCallExpression()); 1180 } 1181 return node; 1182 }; 1183 program->Ast()->TransformChildrenRecursively(insertInvokeIfNeeded, Name()); 1184 1185 return true; 1186} 1187 1188bool LambdaConversionPhase::Postcondition([[maybe_unused]] public_lib::Context *ctx, parser::Program const *program) 1189{ 1190 return !program->Ast()->IsAnyChild([](ir::AstNode const *node) { return node->IsArrowFunctionExpression(); }); 1191} 1192 1193} // namespace ark::es2panda::compiler 1194