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 
203     return {resParams, varMap};
204 }
205 
ProcessCalleeMethodBody(ir::AstNode *body, checker::ETSChecker *checker, varbinder::Scope *paramScope, checker::Substitution *substitution, ArenaMap<varbinder::Variable *, varbinder::Variable *> const &varMap)206 static 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 
SetUpCalleeMethod(public_lib::Context *ctx, LambdaInfo const *info, CalleeMethodInfo const *cmInfo, ir::ScriptFunction *func, varbinder::Scope *scopeForMethod, varbinder::Variable *variable)246 static 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 
GetAndApplyFunctionScope(public_lib::Context *ctx, LambdaInfo const *info, CalleeMethodInfo const *cmInfo, varbinder::ParamScope *paramScope, ir::ScriptFunction *func)304 static 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 
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)324 static 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 
CreateCalleeDefault(public_lib::Context *ctx, ir::ArrowFunctionExpression *lambda, LambdaInfo const *info)393 static 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 
CreateArgsForOptionalCall(public_lib::Context *ctx, ir::ArrowFunctionExpression *lambda, ir::MethodDefinition *defaultMethod)424 static 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 
CreateFunctionBody(public_lib::Context *ctx, ir::ArrowFunctionExpression *lambda, ir::MethodDefinition *defaultMethod)459 static 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 
CreateCallee(public_lib::Context *ctx, ir::ArrowFunctionExpression *lambda, LambdaInfo const *info, ir::MethodDefinition *defaultMethod, size_t limit = std::numeric_limits<size_t>::max())499 static 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.
AvoidMandatoryThis(util::StringView name)534 static util::StringView AvoidMandatoryThis(util::StringView name)
535 {
536     return (name == varbinder::TypedBinder::MANDATORY_PARAM_THIS) ? "$extensionThis" : name;
537 }
538 
CreateLambdaClassFields(public_lib::Context *ctx, ir::ClassDefinition *classDefinition, LambdaInfo const *info, checker::Substitution *substitution)539 static 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 
CreateLambdaClassConstructor(public_lib::Context *ctx, ir::ClassDefinition *classDefinition, LambdaInfo const *info, checker::Substitution *substitution)564 static 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, &params](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 
CreateCallForLambdaClassInvoke(public_lib::Context *ctx, LambdaInfo const *info, LambdaClassInvokeInfo const *lciInfo, bool wrapToObject)620 static 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 
CreateLambdaClassInvoke(public_lib::Context *ctx, LambdaInfo const *info, LambdaClassInvokeInfo const *lciInfo, util::StringView methodName, bool wrapToObject)668 static 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 
BuildLambdaClass(public_lib::Context *ctx, ArenaVector<checker::Signature *> &lambdaSigs, checker::Substitution *substitution, ArenaVector<checker::Type *> &funcInterfaces)726 static 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 
CreateLambdaClass(public_lib::Context *ctx, ArenaVector<checker::Signature *> &lambdaSigs, ir::MethodDefinition *callee, LambdaInfo const *info)747 static 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 
CreateConstructorCall(public_lib::Context *ctx, ir::AstNode *lambdaOrFuncRef, ir::ClassDeclaration *lambdaClass, LambdaInfo const *info)809 static 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 
ConvertLambda(public_lib::Context *ctx, ir::ArrowFunctionExpression *lambda)850 static 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 
GuessSignature(checker::ETSChecker *checker, ir::Expression *ast)879 static 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 
CreateWrappingLambda(public_lib::Context *ctx, ir::Expression *funcRef)925 static 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 
ConvertFunctionReference(public_lib::Context *ctx, ir::Expression *funcRef)984 static 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 
IsFunctionOrMethodCall(ir::AstNode const *node)1035 static 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 
InsertInvokeCall(public_lib::Context *ctx, ir::CallExpression *call)1060 static 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 
IsRedirectingConstructorCall(ir::CallExpression *expr)1106 static bool IsRedirectingConstructorCall(ir::CallExpression *expr)
1107 {
1108     return expr->Callee()->IsThisExpression() || expr->Callee()->IsSuperExpression();
1109 }
1110 
IsInCalleePosition(ir::Expression *expr)1111 static bool IsInCalleePosition(ir::Expression *expr)
1112 {
1113     return expr->Parent()->IsCallExpression() && expr->Parent()->AsCallExpression()->Callee() == expr;
1114 }
1115 
BuildLambdaClassWhenNeeded(public_lib::Context *ctx, ir::AstNode *node)1116 static 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 
CallPerformForExtSources(LambdaConversionPhase *phase, public_lib::Context *ctx, parser::Program *program)1147 static 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 
Perform(public_lib::Context *ctx, parser::Program *program)1159 bool 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 
Postcondition([[maybe_unused]] public_lib::Context *ctx, parser::Program const *program)1188 bool 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