• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &params](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