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