• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024-2025 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 #include <sstream>
18 
19 #include "checker/ets/typeRelationContext.h"
20 #include "compiler/lowering/scopesInit/scopesInitPhase.h"
21 #include "compiler/lowering/util.h"
22 #include "util/options.h"
23 
24 namespace ark::es2panda::compiler {
25 
26 struct LambdaInfo {
27     ir::ClassDeclaration *calleeClass = nullptr;
28     ir::TSInterfaceDeclaration *calleeInterface = nullptr;
29     ir::ScriptFunction *enclosingFunction = nullptr;
30     util::StringView name = "";
31     util::StringView originalFuncName = "";
32     ArenaSet<varbinder::Variable *> *capturedVars = nullptr;
33     ir::Expression *callReceiver = nullptr;
34     bool isFunctionReference = false;
35     checker::ETSObjectType *objType = nullptr;
36 };
37 
38 struct CalleeMethodInfo {
39     util::StringView calleeName;
40     ir::AstNode *body = nullptr;
41     checker::Type *forcedReturnType = nullptr;
42     ir::ModifierFlags auxModifierFlags = ir::ModifierFlags::NONE;
43     ir::ScriptFunctionFlags auxFunctionFlags = ir::ScriptFunctionFlags::NONE;
44 };
45 
46 struct LambdaClassInvokeInfo {
47     checker::Signature *lambdaSignature = nullptr;
48     ir::MethodDefinition *callee = nullptr;
49     ir::ClassDefinition *classDefinition = nullptr;
50     checker::Substitution *substitution = nullptr;
51     size_t arity = 0;
52     util::StringView restParameterIdentifier = "";
53     util::StringView restArgumentIdentifier = "";
54 };
55 
FindEnclosingClassAndFunction(ir::AstNode * ast)56 static std::pair<ir::ClassDeclaration *, ir::ScriptFunction *> FindEnclosingClassAndFunction(ir::AstNode *ast)
57 {
58     ir::ScriptFunction *function = nullptr;
59     for (ir::AstNode *curr = ast->Parent(); curr != nullptr; curr = curr->Parent()) {
60         if (curr->IsClassDeclaration()) {
61             return {curr->AsClassDeclaration(), function};
62         }
63         if (curr->IsScriptFunction()) {
64             function = curr->AsScriptFunction();
65         }
66     }
67     ES2PANDA_UNREACHABLE();
68 }
69 
CheckIfNeedThis(ir::ArrowFunctionExpression * lambda,checker::ETSChecker * checker)70 static bool CheckIfNeedThis(ir::ArrowFunctionExpression *lambda, checker::ETSChecker *checker)
71 {
72     auto *lambdaClass = ContainingClass(lambda);
73     return lambda->IsAnyChild([&checker, &lambdaClass](ir::AstNode *ast) {
74         return (ast->IsThisExpression() || ast->IsSuperExpression()) &&
75                checker->Relation()->IsIdenticalTo(lambdaClass, ContainingClass(ast));
76     });
77 }
78 
79 static size_t g_calleeCount = 0;
80 
81 // Make calleeCount behaviour predictable
ResetCalleeCount()82 static void ResetCalleeCount()
83 {
84     g_calleeCount = 0;
85 }
86 
CreateCalleeName(ArenaAllocator * allocator)87 static util::StringView CreateCalleeName(ArenaAllocator *allocator)
88 {
89     auto name = util::UString(util::StringView("lambda$invoke$"), allocator);
90     name.Append(std::to_string(g_calleeCount++));
91     return name.View();
92 }
93 
ProcessTypeParameterProperties(checker::ETSTypeParameter * oldTypeParam,checker::ETSTypeParameter * newTypeParam,ir::TSTypeParameter * newTypeParamNode,checker::Substitution * substitution,public_lib::Context * ctx)94 static void ProcessTypeParameterProperties(checker::ETSTypeParameter *oldTypeParam,
95                                            checker::ETSTypeParameter *newTypeParam,
96                                            ir::TSTypeParameter *newTypeParamNode, checker::Substitution *substitution,
97                                            public_lib::Context *ctx)
98 {
99     auto *allocator = ctx->allocator;
100     auto *checker = ctx->checker->AsETSChecker();
101 
102     if (auto *oldConstraint = oldTypeParam->GetConstraintType(); oldConstraint != nullptr) {
103         auto *newConstraint = oldConstraint->Substitute(checker->Relation(), substitution);
104         newTypeParam->SetConstraintType(newConstraint);
105         auto *newConstraintNode = allocator->New<ir::OpaqueTypeNode>(newConstraint, allocator);
106         newTypeParamNode->SetConstraint(newConstraintNode);
107         newConstraintNode->SetParent(newTypeParamNode);
108     }
109 
110     if (auto *oldDefault = oldTypeParam->GetDefaultType(); oldDefault != nullptr) {
111         auto *newDefault = oldDefault->Substitute(checker->Relation(), substitution);
112         newTypeParam->SetDefaultType(newDefault);
113         auto *newDefaultNode = allocator->New<ir::OpaqueTypeNode>(newDefault, allocator);
114         newTypeParamNode->SetDefaultType(newDefaultNode);
115         newDefaultNode->SetParent(newTypeParamNode);
116     }
117 }
118 
CloneTypeParams(public_lib::Context * ctx,ir::TSTypeParameterDeclaration * oldIrTypeParams,ir::ScriptFunction * enclosingFunction,varbinder::Scope * enclosingScope)119 static std::pair<ir::TSTypeParameterDeclaration *, checker::Substitution *> CloneTypeParams(
120     public_lib::Context *ctx, ir::TSTypeParameterDeclaration *oldIrTypeParams, ir::ScriptFunction *enclosingFunction,
121     varbinder::Scope *enclosingScope)
122 {
123     if (oldIrTypeParams == nullptr) {
124         return {nullptr, nullptr};
125     }
126 
127     auto *allocator = ctx->allocator;
128     auto *checker = ctx->checker->AsETSChecker();
129 
130     auto *newScope = allocator->New<varbinder::LocalScope>(allocator, enclosingScope);
131     auto newTypeParams = ArenaVector<checker::ETSTypeParameter *>(allocator->Adapter());
132     auto newTypeParamNodes = ArenaVector<ir::TSTypeParameter *>(allocator->Adapter());
133     auto *substitution = checker->NewSubstitution();
134     ES2PANDA_ASSERT(substitution != nullptr && newScope != nullptr);
135 
136     for (size_t ix = 0; ix < oldIrTypeParams->Params().size(); ix++) {
137         auto *oldTypeParamNode = oldIrTypeParams->Params()[ix];
138         auto *oldTypeParam = enclosingFunction->Signature()->TypeParams()[ix]->AsETSTypeParameter();
139         auto *newTypeParamId = allocator->New<ir::Identifier>(oldTypeParamNode->Name()->Name(), allocator);
140         auto *newTypeParamNode = util::NodeAllocator::ForceSetParent<ir::TSTypeParameter>(allocator, newTypeParamId,
141                                                                                           nullptr, nullptr, allocator);
142         auto *newTypeParam = allocator->New<checker::ETSTypeParameter>();
143         auto *newTypeParamDecl = allocator->New<varbinder::TypeParameterDecl>(newTypeParamId->Name());
144         auto *newTypeParamVar =
145             allocator->New<varbinder::LocalVariable>(newTypeParamDecl, varbinder::VariableFlags::TYPE_PARAMETER);
146         ES2PANDA_ASSERT(newTypeParam != nullptr && newTypeParamDecl != nullptr && newTypeParamVar != nullptr);
147         newTypeParam->SetDeclNode(newTypeParamNode);
148 
149         newTypeParamDecl->BindNode(newTypeParamNode);
150         newTypeParamVar->SetTsType(newTypeParam);
151         newScope->InsertBinding(newTypeParamId->Name(), newTypeParamVar);
152         newTypeParamId->SetVariable(newTypeParamVar);
153 
154         newTypeParams.push_back(newTypeParam);
155         newTypeParamNodes.push_back(newTypeParamNode);
156         substitution->emplace(oldTypeParam, newTypeParam);
157     }
158 
159     for (size_t ix = 0; ix < oldIrTypeParams->Params().size(); ix++) {
160         auto *oldTypeParam = enclosingFunction->Signature()->TypeParams()[ix]->AsETSTypeParameter();
161         ProcessTypeParameterProperties(oldTypeParam, newTypeParams[ix], newTypeParamNodes[ix], substitution, ctx);
162     }
163 
164     auto *newIrTypeParams = util::NodeAllocator::ForceSetParent<ir::TSTypeParameterDeclaration>(
165         allocator, std::move(newTypeParamNodes), oldIrTypeParams->RequiredParams());
166     ES2PANDA_ASSERT(newIrTypeParams != nullptr);
167     newIrTypeParams->SetScope(newScope);
168 
169     return {newIrTypeParams, substitution};
170 }
171 
172 using ParamsAndVarMap =
173     std::pair<ArenaVector<ir::Expression *>, ArenaMap<varbinder::Variable *, varbinder::Variable *>>;
174 
InitNewParameterVariable(varbinder::VarBinder * varBinder,ir::ETSParameterExpression * param,checker::Type * newParamType,varbinder::ParamScope * paramScope)175 inline static varbinder::Variable *InitNewParameterVariable(varbinder::VarBinder *varBinder,
176                                                             ir::ETSParameterExpression *param,
177                                                             checker::Type *newParamType,
178                                                             varbinder::ParamScope *paramScope)
179 {
180     ES2PANDA_ASSERT(param != nullptr);
181     auto *var = varBinder->AddParamDecl(param);
182     var->SetTsType(newParamType);
183     var->SetScope(paramScope);
184     param->SetVariable(var);
185     param->SetTsType(newParamType);
186     return var;
187 }
188 
CreateLambdaCalleeParameters(public_lib::Context * ctx,ir::ArrowFunctionExpression * lambda,ArenaSet<varbinder::Variable * > const & captured,varbinder::ParamScope * paramScope,checker::Substitution * substitution)189 ParamsAndVarMap CreateLambdaCalleeParameters(public_lib::Context *ctx, ir::ArrowFunctionExpression *lambda,
190                                              ArenaSet<varbinder::Variable *> const &captured,
191                                              varbinder::ParamScope *paramScope, checker::Substitution *substitution)
192 {
193     auto allocator = ctx->allocator;
194     auto checker = ctx->checker->AsETSChecker();
195     auto varBinder = ctx->checker->VarBinder();
196     auto resParams = ArenaVector<ir::Expression *>(allocator->Adapter());
197     auto varMap = ArenaMap<varbinder::Variable *, varbinder::Variable *>(allocator->Adapter());
198 
199     auto paramLexScope = varbinder::LexicalScope<varbinder::ParamScope>::Enter(varBinder, paramScope);
200 
201     for (auto capturedVar : captured) {
202         auto *newType = capturedVar->TsType()->Substitute(checker->Relation(), substitution);
203         auto newId = util::NodeAllocator::ForceSetParent<ir::Identifier>(
204             allocator, capturedVar->Name(), allocator->New<ir::OpaqueTypeNode>(newType, allocator), allocator);
205         auto param =
206             util::NodeAllocator::ForceSetParent<ir::ETSParameterExpression>(allocator, newId, false, allocator);
207         ES2PANDA_ASSERT(param != nullptr);
208         auto *var = InitNewParameterVariable(varBinder, param, newType, paramScope);
209         resParams.push_back(param);
210         varMap[capturedVar] = var;
211     }
212 
213     for (auto *oldParam : lambda->Function()->Params()) {
214         auto *oldParamType = oldParam->AsETSParameterExpression()->Ident()->TsType();
215         auto *newParamType = oldParamType->Substitute(checker->Relation(), substitution);
216         auto *newParam = oldParam->AsETSParameterExpression()->Clone(allocator, nullptr);
217         ES2PANDA_ASSERT(newParam != nullptr);
218         if (newParam->IsOptional()) {
219             newParam->SetOptional(false);
220             newParamType = checker->CreateETSUnionType({newParamType, checker->GlobalETSUndefinedType()});
221         }
222 
223         newParam->SetTypeAnnotation(allocator->New<ir::OpaqueTypeNode>(newParamType, allocator));
224         auto *var = InitNewParameterVariable(varBinder, newParam, newParamType, paramScope);
225         newParam->Ident()->SetTsType(newParamType);
226         if (newParam->IsRestParameter()) {
227             newParam->TypeAnnotation()->SetParent(newParam->RestParameter());
228             newParam->RestParameter()->SetTsType(newParamType);
229         } else {
230             newParam->TypeAnnotation()->SetParent(newParam->Ident());
231         }
232         resParams.push_back(newParam);
233         varMap[oldParam->AsETSParameterExpression()->Variable()] = var;
234 
235         if (newParam->TypeAnnotation()->IsETSFunctionType()) {
236             // Parameter can be a function with other parameters inside
237             // Restart varbinder to set correct scopes for inner parameters
238             InitScopesPhaseETS::RunExternalNode(newParam->TypeAnnotation(), varBinder);
239         }
240     }
241 
242     return {resParams, varMap};
243 }
244 
ProcessCalleeMethodBody(ir::AstNode * body,checker::ETSChecker * checker,varbinder::Scope * paramScope,checker::Substitution * substitution,ArenaMap<varbinder::Variable *,varbinder::Variable * > const & varMap)245 static void ProcessCalleeMethodBody(ir::AstNode *body, checker::ETSChecker *checker, varbinder::Scope *paramScope,
246                                     checker::Substitution *substitution,
247                                     ArenaMap<varbinder::Variable *, varbinder::Variable *> const &varMap)
248 {
249     if (body == nullptr) {
250         return;
251     }
252     body->Scope()->SetParent(paramScope);
253     body->IterateRecursively([&](ir::AstNode *node) {
254         if (node->IsIdentifier()) {
255             auto *id = node->AsIdentifier();
256             if (auto ref = varMap.find(id->Variable()); ref != varMap.end()) {
257                 id->SetVariable(ref->second);
258                 id->Check(checker);
259             }
260         }
261         if (substitution == nullptr) {
262             return;
263         }
264         if (node->IsTyped() && node->AsTyped()->TsType() != nullptr) {
265             node->AsTyped()->SetTsType(node->AsTyped()->TsType()->Substitute(checker->Relation(), substitution));
266             if (node->IsTSNonNullExpression()) {
267                 auto expr = node->AsTSNonNullExpression();
268                 expr->SetOriginalType(expr->OriginalType()->Substitute(checker->Relation(), substitution));
269             }
270         }
271         if (node->IsCallExpression()) {
272             node->AsCallExpression()->SetSignature(
273                 node->AsCallExpression()->Signature()->Substitute(checker->Relation(), substitution));
274         }
275         if (node->IsETSNewClassInstanceExpression()) {
276             node->AsETSNewClassInstanceExpression()->SetSignature(
277                 node->AsETSNewClassInstanceExpression()->GetSignature()->Substitute(checker->Relation(), substitution));
278         }
279         if (node->IsScriptFunction()) {
280             node->AsScriptFunction()->SetSignature(
281                 node->AsScriptFunction()->Signature()->Substitute(checker->Relation(), substitution));
282         }
283         if (node->IsVariableDeclarator()) {
284             auto *id = node->AsVariableDeclarator()->Id();
285             id->Variable()->SetTsType(id->Variable()->TsType()->Substitute(checker->Relation(), substitution));
286         }
287     });
288 }
289 
SetUpCalleeMethod(public_lib::Context * ctx,LambdaInfo const * info,CalleeMethodInfo const * cmInfo,ir::ScriptFunction * func,varbinder::Scope * scopeForMethod)290 static ir::MethodDefinition *SetUpCalleeMethod(public_lib::Context *ctx, LambdaInfo const *info,
291                                                CalleeMethodInfo const *cmInfo, ir::ScriptFunction *func,
292                                                varbinder::Scope *scopeForMethod)
293 {
294     auto *allocator = ctx->allocator;
295     auto *varBinder = ctx->checker->VarBinder()->AsETSBinder();
296 
297     auto *calleeClass = info->calleeClass;
298     auto *funcScope = func->Scope();
299     auto *paramScope = funcScope->ParamScope();
300     auto modifierFlags = ir::ModifierFlags::PUBLIC |
301                          (info->callReceiver != nullptr ? ir::ModifierFlags::NONE : ir::ModifierFlags::STATIC) |
302                          cmInfo->auxModifierFlags;
303 
304     auto *calleeNameId = allocator->New<ir::Identifier>(cmInfo->calleeName, allocator);
305     func->SetIdent(calleeNameId);
306     calleeNameId->SetParent(func);
307 
308     auto *calleeNameClone = calleeNameId->Clone(allocator, nullptr);
309     auto *funcExpr = util::NodeAllocator::ForceSetParent<ir::FunctionExpression>(allocator, func);
310     auto *method = util::NodeAllocator::ForceSetParent<ir::MethodDefinition>(
311         allocator, ir::MethodDefinitionKind::METHOD, calleeNameClone, funcExpr, modifierFlags, allocator, false);
312     calleeClass->Definition()->Body().push_back(method);
313     method->SetParent(calleeClass->Definition());
314 
315     auto *var =
316         std::get<1>(varBinder->NewVarDecl<varbinder::FunctionDecl>(func->Start(), allocator, cmInfo->calleeName, func));
317     var->AddFlag(varbinder::VariableFlags::METHOD);
318     var->SetScope(scopeForMethod);
319     func->Id()->SetVariable(var);
320     ES2PANDA_ASSERT(method->Id());
321     method->Id()->SetVariable(var);
322     if (info->callReceiver != nullptr) {
323         auto paramScopeCtx = varbinder::LexicalScope<varbinder::FunctionParamScope>::Enter(varBinder, paramScope);
324         varBinder->AddMandatoryParam(varbinder::TypedBinder::MANDATORY_PARAM_THIS);
325         calleeClass->Definition()->TsType()->AsETSObjectType()->AddProperty<checker::PropertyType::INSTANCE_METHOD>(
326             var->AsLocalVariable());
327     } else {
328         calleeClass->Definition()->TsType()->AsETSObjectType()->AddProperty<checker::PropertyType::STATIC_METHOD>(
329             var->AsLocalVariable());
330     }
331 
332     varbinder::BoundContext bctx {varBinder->GetRecordTable(), calleeClass->Definition(), true};
333     varBinder->ResolveReferencesForScopeWithContext(func, funcScope);
334 
335     auto checkerCtx = checker::SavedCheckerContext(ctx->checker, checker::CheckerStatus::IN_CLASS,
336                                                    calleeClass->Definition()->TsType()->AsETSObjectType());
337     method->Check(ctx->checker->AsETSChecker());
338 
339     return method;
340 }
341 
342 using ISS = ir::ScriptFunction::ScriptFunctionData;
CreateCalleeMethod(public_lib::Context * ctx,ir::ArrowFunctionExpression * lambda,LambdaInfo const * info,CalleeMethodInfo const * cmInfo)343 static ir::MethodDefinition *CreateCalleeMethod(public_lib::Context *ctx, ir::ArrowFunctionExpression *lambda,
344                                                 LambdaInfo const *info, CalleeMethodInfo const *cmInfo)
345 {
346     auto *allocator = ctx->allocator;
347     auto *varBinder = ctx->checker->VarBinder()->AsETSBinder();
348     auto *checker = ctx->checker->AsETSChecker();
349 
350     auto *classScope = info->calleeClass->Definition()->Scope()->AsClassScope();
351 
352     auto *oldTypeParams = (info->enclosingFunction != nullptr) ? info->enclosingFunction->TypeParams() : nullptr;
353     auto enclosingScope =
354         info->callReceiver != nullptr ? classScope->InstanceMethodScope() : classScope->StaticMethodScope();
355 
356     auto [newTypeParams, subst0] = CloneTypeParams(ctx, oldTypeParams, info->enclosingFunction, enclosingScope);
357     auto *substitution = subst0;  // NOTE(gogabr): needed to capture in a lambda later.
358     auto *scopeForMethod = newTypeParams != nullptr ? newTypeParams->Scope() : enclosingScope;
359 
360     auto lexScope = varbinder::LexicalScope<varbinder::LocalScope>::Enter(varBinder, enclosingScope);
361     auto paramScope = allocator->New<varbinder::FunctionParamScope>(allocator, scopeForMethod);
362 
363     auto [params, vMap] = CreateLambdaCalleeParameters(ctx, lambda, *info->capturedVars, paramScope, substitution);
364     auto varMap = std::move(vMap);
365 
366     auto *alternative = lambda->Function()->Signature()->ReturnType()->Substitute(checker->Relation(), substitution);
367     auto *returnType = cmInfo->forcedReturnType != nullptr ? cmInfo->forcedReturnType : alternative;
368     auto returnTypeAnnotation = allocator->New<ir::OpaqueTypeNode>(returnType, allocator);
369 
370     auto funcFlags = ir::ScriptFunctionFlags::METHOD | cmInfo->auxFunctionFlags;
371     auto modifierFlags = ir::ModifierFlags::PUBLIC |
372                          (info->callReceiver != nullptr ? ir::ModifierFlags::NONE : ir::ModifierFlags::STATIC) |
373                          cmInfo->auxModifierFlags;
374 
375     auto func = util::NodeAllocator::ForceSetParent<ir::ScriptFunction>(
376         allocator, allocator,
377         ISS {cmInfo->body,
378              ir::FunctionSignature(newTypeParams, std::move(params), returnTypeAnnotation,
379                                    lambda->Function()->HasReceiver()),
380              funcFlags, modifierFlags});
381     auto *funcScope = cmInfo->body == nullptr ? allocator->New<varbinder::FunctionScope>(allocator, paramScope)
382                                               : cmInfo->body->Scope()->AsFunctionScope();
383     ES2PANDA_ASSERT(funcScope);
384     funcScope->BindName(info->calleeClass->Definition()->TsType()->AsETSObjectType()->AssemblerName());
385     func->SetScope(funcScope);
386     ProcessCalleeMethodBody(cmInfo->body, checker, paramScope, substitution, varMap);
387 
388     for (auto *param : func->Params()) {
389         param->SetParent(func);
390     }
391 
392     // Bind the scopes
393     funcScope->BindNode(func);
394     paramScope->BindNode(func);
395     funcScope->AssignParamScope(paramScope);
396     paramScope->BindFunctionScope(funcScope);
397 
398     /* NOTE(gogabr): Why does function scope need to replicate bindings from param scope?.
399        Keeping it for now.
400     */
401     for (auto [ov, nv] : varMap) {
402         ES2PANDA_ASSERT(ov->Name() == nv->Name());
403         funcScope->EraseBinding(ov->Name());
404         funcScope->InsertBinding(ov->Name(), nv);
405     }
406 
407     return SetUpCalleeMethod(ctx, info, cmInfo, func, scopeForMethod);
408 }
409 
CreateCallee(public_lib::Context * ctx,ir::ArrowFunctionExpression * lambda,LambdaInfo const * info)410 static ir::MethodDefinition *CreateCallee(public_lib::Context *ctx, ir::ArrowFunctionExpression *lambda,
411                                           LambdaInfo const *info)
412 {
413     auto *allocator = ctx->allocator;
414     auto *checker = ctx->checker->AsETSChecker();
415     auto *body = lambda->Function()->Body()->AsBlockStatement();
416     auto calleeName = lambda->Function()->IsAsyncFunc()
417                           ? (util::UString {checker::ETSChecker::GetAsyncImplName(info->name), allocator}).View()
418                           : info->name;
419     auto *forcedReturnType = lambda->Function()->IsAsyncFunc() ? checker->GlobalETSAnyType() : nullptr;
420 
421     CalleeMethodInfo cmInfo;
422     cmInfo.calleeName = calleeName;
423     cmInfo.body = body;
424     cmInfo.forcedReturnType = forcedReturnType;
425     auto *method = CreateCalleeMethod(ctx, lambda, info, &cmInfo);
426 
427     if (lambda->Function()->IsAsyncFunc()) {
428         CalleeMethodInfo cmInfoAsync;
429         cmInfoAsync.calleeName = info->name;
430         cmInfoAsync.body = nullptr;
431         cmInfoAsync.forcedReturnType = nullptr;
432         cmInfoAsync.auxModifierFlags = ir::ModifierFlags::NATIVE;
433         cmInfoAsync.auxFunctionFlags = ir::ScriptFunctionFlags::ASYNC;
434         auto *asyncMethod = CreateCalleeMethod(ctx, lambda, info, &cmInfoAsync);
435         return asyncMethod;
436     }
437 
438     return method;
439 }
440 
441 // The name "=t" used in extension methods has special meaning for the code generator;
442 // avoid it as parameter and field name in our generated code.
AvoidMandatoryThis(util::StringView name)443 static util::StringView AvoidMandatoryThis(util::StringView name)
444 {
445     return (name == varbinder::TypedBinder::MANDATORY_PARAM_THIS) ? "$extensionThis" : name;
446 }
447 
CreateLambdaClassFields(public_lib::Context * ctx,ir::ClassDefinition * classDefinition,LambdaInfo const * info,checker::Substitution * substitution)448 static void CreateLambdaClassFields(public_lib::Context *ctx, ir::ClassDefinition *classDefinition,
449                                     LambdaInfo const *info, checker::Substitution *substitution)
450 {
451     auto *allocator = ctx->allocator;
452     auto *parser = ctx->parser->AsETSParser();
453     auto *checker = ctx->checker->AsETSChecker();
454     auto props = ArenaVector<ir::AstNode *>(allocator->Adapter());
455 
456     checker::Type *objectType = info->objType != nullptr
457                                     ? info->objType
458                                     : (info->calleeClass != nullptr ? info->calleeClass->Definition()->TsType()
459                                                                     : info->calleeInterface->TsType());
460 
461     if (info->callReceiver != nullptr) {
462         auto *outerThisDeclaration = parser->CreateFormattedClassFieldDefinition(
463             "@@I1: @@T2", "$this", objectType->Substitute(checker->Relation(), substitution));
464         props.push_back(outerThisDeclaration);
465     }
466 
467     for (auto *captured : *info->capturedVars) {
468         auto *varDeclaration = parser->CreateFormattedClassFieldDefinition(
469             "@@I1: @@T2", AvoidMandatoryThis(captured->Name()),
470             captured->TsType()->Substitute(checker->Relation(), substitution));
471         props.push_back(varDeclaration);
472     }
473 
474     classDefinition->AddProperties(std::move(props));
475 }
476 
CreateLambdaClassConstructor(public_lib::Context * ctx,ir::ClassDefinition * classDefinition,LambdaInfo const * info,checker::Substitution * substitution)477 static void CreateLambdaClassConstructor(public_lib::Context *ctx, ir::ClassDefinition *classDefinition,
478                                          LambdaInfo const *info, checker::Substitution *substitution)
479 {
480     auto *allocator = ctx->allocator;
481     auto *parser = ctx->parser->AsETSParser();
482     auto *checker = ctx->checker->AsETSChecker();
483 
484     auto params = ArenaVector<ir::Expression *>(allocator->Adapter());
485     auto makeParam = [checker, allocator, substitution, &params](util::StringView name, checker::Type *type) {
486         auto *substitutedType = type->Substitute(checker->Relation(), substitution);
487         auto *id = util::NodeAllocator::ForceSetParent<ir::Identifier>(
488             allocator, name, allocator->New<ir::OpaqueTypeNode>(substitutedType, allocator), allocator);
489         auto *param = util::NodeAllocator::ForceSetParent<ir::ETSParameterExpression>(allocator, id, false, allocator);
490         params.push_back(param);
491     };
492 
493     checker::Type *objectType = info->objType != nullptr
494                                     ? info->objType
495                                     : (info->calleeClass != nullptr ? info->calleeClass->Definition()->TsType()
496                                                                     : info->calleeInterface->TsType());
497 
498     if (info->callReceiver != nullptr) {
499         makeParam("$this", objectType);
500     }
501     for (auto *var : *info->capturedVars) {
502         makeParam(AvoidMandatoryThis(var->Name()), var->TsType());
503     }
504 
505     auto bodyStmts = ArenaVector<ir::Statement *>(allocator->Adapter());
506     auto makeStatement = [&parser, &bodyStmts](util::StringView name) {
507         auto adjustedName = AvoidMandatoryThis(name);
508         bodyStmts.push_back(parser->CreateFormattedStatement("this.@@I1 = @@I2", adjustedName, adjustedName));
509     };
510     if (info->callReceiver != nullptr) {
511         makeStatement("$this");
512     }
513     for (auto *var : *info->capturedVars) {
514         makeStatement(var->Name());
515     }
516 
517     auto *body = util::NodeAllocator::ForceSetParent<ir::BlockStatement>(allocator, allocator, std::move(bodyStmts));
518 
519     auto *constructorId = allocator->New<ir::Identifier>("constructor", allocator);
520 
521     auto *func = util::NodeAllocator::ForceSetParent<ir::ScriptFunction>(
522         allocator, allocator,
523         ir::ScriptFunction::ScriptFunctionData {body, ir::FunctionSignature(nullptr, std::move(params), nullptr),
524                                                 ir::ScriptFunctionFlags::CONSTRUCTOR |
525                                                     ir::ScriptFunctionFlags::IMPLICIT_SUPER_CALL_NEEDED});
526     ES2PANDA_ASSERT(func);
527     func->SetIdent(constructorId);
528     auto *funcExpr = util::NodeAllocator::ForceSetParent<ir::FunctionExpression>(allocator, func);
529 
530     auto *ctor = util::NodeAllocator::ForceSetParent<ir::MethodDefinition>(
531         allocator, ir::MethodDefinitionKind::CONSTRUCTOR, constructorId->Clone(allocator, nullptr), funcExpr,
532         ir::ModifierFlags::NONE, allocator, false);
533 
534     classDefinition->Body().push_back(ctor);
535     ctor->SetParent(classDefinition);
536 }
537 
538 // NOTE(vpukhov): requires the optimization based on the array type
539 // CC-OFFNXT(G.FUN.01, huge_method) solid logic
CreateRestArgumentsArrayReallocation(public_lib::Context * ctx,LambdaClassInvokeInfo const * lciInfo)540 static ArenaVector<ark::es2panda::ir::Statement *> CreateRestArgumentsArrayReallocation(
541     public_lib::Context *ctx, LambdaClassInvokeInfo const *lciInfo)
542 {
543     if (!lciInfo->lambdaSignature->HasRestParameter()) {
544         return ArenaVector<ir::Statement *>(ctx->allocator->Adapter());
545     }
546 
547     auto *allocator = ctx->allocator;
548     auto *parser = ctx->parser->AsETSParser();
549     auto *checker = ctx->checker->AsETSChecker();
550 
551     auto *restParameterType = lciInfo->lambdaSignature->RestVar()->TsType();
552     auto *restParameterSubstituteType = restParameterType->Substitute(checker->Relation(), lciInfo->substitution);
553     bool isFixedArray = restParameterSubstituteType->IsETSArrayType();
554     auto *elementType = checker->GetElementTypeOfArray(restParameterSubstituteType);
555     std::stringstream statements;
556     auto restParameterIndex = GenName(allocator).View();
557     auto spreadArrIterator = GenName(allocator).View();
558     ir::Statement *args = nullptr;
559     if (isFixedArray) {
560         auto tmpArray = GenName(allocator).View();
561         statements << "let @@I1: int = 0;";
562         if (elementType->IsETSReferenceType()) {
563             // NOTE(vpukhov): this is a clear null-safety violation that should be rewitten with a runtime intrinsic
564             statements << "let @@I2: FixedArray<@@T3 | undefined> = new (@@T4 | undefined)[@@I5.length];";
565         } else {
566             statements << "let @@I2: FixedArray<@@T3> = new (@@T4)[@@I5.length];";
567         }
568         statements << "let @@I6 = @@I7 as FixedArray<@@T8>;"
569                    // CC-OFFNXT(G.FMT.06) false positive
570                    << "for (let @@I9: @@T10 of @@I11){"
571                    // CC-OFFNXT(G.FMT.06) false positive
572                    << "    @@I12[@@I13] = @@I14 as @@T15 as @@T16;"
573                    // CC-OFFNXT(G.FMT.06) false positive
574                    << "    @@I17 = @@I18 + 1;"
575                    << "}";
576         args = parser->CreateFormattedStatement(
577             statements.str(), restParameterIndex, tmpArray, elementType, elementType, lciInfo->restParameterIdentifier,
578             lciInfo->restArgumentIdentifier, tmpArray, elementType, spreadArrIterator, checker->GlobalETSAnyType(),
579             lciInfo->restParameterIdentifier, lciInfo->restArgumentIdentifier, restParameterIndex, spreadArrIterator,
580             checker->MaybeBoxType(elementType), elementType, restParameterIndex, restParameterIndex);
581     } else {
582         auto *typeNode = allocator->New<ir::OpaqueTypeNode>(
583             checker->GetElementTypeOfArray(lciInfo->lambdaSignature->RestVar()->TsType()), allocator);
584         statements << "let @@I1: int = 0;"
585                    // CC-OFFNXT(G.FMT.06) false positive
586                    << "let @@I2 = new Array<@@T3>(@@I4.length);"
587                    // CC-OFFNXT(G.FMT.06) false positive
588                    << "for (let @@I5:@@T6 of @@I7){"
589                    // CC-OFFNXT(G.FMT.06) false positive
590                    << "    @@I8.$_set(@@I9, @@I10 as @@T11);"
591                    // CC-OFFNXT(G.FMT.06) false positive
592                    << "    @@I12 = @@I13 + 1;"
593                    // CC-OFFNXT(G.FMT.06) false positive
594                    << "}";
595         args = parser->CreateFormattedStatement(
596             statements.str(), restParameterIndex, lciInfo->restArgumentIdentifier, typeNode,
597             lciInfo->restParameterIdentifier, spreadArrIterator, checker->GlobalETSAnyType(),
598             lciInfo->restParameterIdentifier, lciInfo->restArgumentIdentifier, restParameterIndex, spreadArrIterator,
599             checker->MaybeBoxType(elementType), restParameterIndex, restParameterIndex);
600     }
601     ES2PANDA_ASSERT(args != nullptr);
602     return ArenaVector<ir::Statement *>(std::move(args->AsBlockStatement()->Statements()));
603 }
604 
CreateInvokeMethodRestParameter(public_lib::Context * ctx,LambdaClassInvokeInfo * lciInfo,ArenaVector<ir::Expression * > * params)605 static void CreateInvokeMethodRestParameter(public_lib::Context *ctx, LambdaClassInvokeInfo *lciInfo,
606                                             ArenaVector<ir::Expression *> *params)
607 {
608     auto *allocator = ctx->allocator;
609     auto *checker = ctx->checker->AsETSChecker();
610 
611     auto *restIdent = Gensym(allocator);
612     ES2PANDA_ASSERT(restIdent != nullptr);
613     lciInfo->restParameterIdentifier = restIdent->Name();
614     lciInfo->restArgumentIdentifier = GenName(allocator).View();
615     auto *spread = allocator->New<ir::SpreadElement>(ir::AstNodeType::REST_ELEMENT, allocator, restIdent);
616     ES2PANDA_ASSERT(spread != nullptr);
617     auto *arr = lciInfo->lambdaSignature->RestVar()->TsType()->IsETSTupleType()
618                     ? lciInfo->lambdaSignature->RestVar()->TsType()
619                     : checker->CreateETSArrayType(checker->GlobalETSAnyType());
620     auto *typeAnnotation = allocator->New<ir::OpaqueTypeNode>(arr, allocator);
621 
622     spread->SetTsTypeAnnotation(typeAnnotation);
623     spread->SetTsType(arr);
624     restIdent->SetTsType(arr);
625     auto *param = allocator->New<ir::ETSParameterExpression>(spread, nullptr, allocator);
626 
627     restIdent->SetParent(spread);
628     typeAnnotation->SetParent(spread);
629     spread->SetParent(param);
630     params->push_back(param);
631 }
632 
CreateCallArgumentsForLambdaClassInvoke(public_lib::Context * ctx,LambdaInfo const * info,LambdaClassInvokeInfo const * lciInfo,bool wrapToObject)633 static ArenaVector<ir::Expression *> CreateCallArgumentsForLambdaClassInvoke(public_lib::Context *ctx,
634                                                                              LambdaInfo const *info,
635                                                                              LambdaClassInvokeInfo const *lciInfo,
636                                                                              bool wrapToObject)
637 {
638     auto *allocator = ctx->allocator;
639     auto *parser = ctx->parser->AsETSParser();
640     auto *checker = ctx->checker->AsETSChecker();
641 
642     auto callArguments = ArenaVector<ir::Expression *>(allocator->Adapter());
643     for (auto *captured : *info->capturedVars) {
644         auto *arg = parser->CreateFormattedExpression("this.@@I1", AvoidMandatoryThis(captured->Name()));
645         callArguments.push_back(arg);
646     }
647     for (size_t idx = 0; idx < lciInfo->lambdaSignature->ArgCount(); ++idx) {
648         auto lambdaParam = lciInfo->lambdaSignature->Params().at(idx);
649         if (idx >= lciInfo->arity) {
650             callArguments.push_back(allocator->New<ir::UndefinedLiteral>());
651             continue;
652         }
653         auto argName = lambdaParam->Name();
654         auto *type = lambdaParam->TsType()->Substitute(checker->Relation(), lciInfo->substitution);
655         auto *arg = wrapToObject ? parser->CreateFormattedExpression("@@I1 as @@T2 as @@T3", argName,
656                                                                      checker->MaybeBoxType(type), type)
657                                  : allocator->New<ir::Identifier>(argName, allocator);
658         callArguments.push_back(arg);
659     }
660 
661     if (lciInfo->lambdaSignature->HasRestParameter()) {
662         auto *restIdent = allocator->New<ir::Identifier>(lciInfo->restArgumentIdentifier, allocator);
663         if (lciInfo->lambdaSignature->RestVar()->TsType()->IsETSArrayType()) {
664             auto *spread = allocator->New<ir::SpreadElement>(ir::AstNodeType::SPREAD_ELEMENT, allocator, restIdent);
665             restIdent->SetParent(spread);
666             callArguments.push_back(spread);
667         } else {
668             restIdent->AddAstNodeFlags(ir::AstNodeFlags::RESIZABLE_REST);
669             callArguments.push_back(restIdent);
670         }
671     }
672     return callArguments;
673 }
674 
CreateCallForLambdaClassInvoke(public_lib::Context * ctx,LambdaInfo const * info,LambdaClassInvokeInfo const * lciInfo,bool wrapToObject)675 static ir::CallExpression *CreateCallForLambdaClassInvoke(public_lib::Context *ctx, LambdaInfo const *info,
676                                                           LambdaClassInvokeInfo const *lciInfo, bool wrapToObject)
677 {
678     auto *allocator = ctx->allocator;
679     auto *parser = ctx->parser->AsETSParser();
680 
681     auto callArguments = CreateCallArgumentsForLambdaClassInvoke(ctx, info, lciInfo, wrapToObject);
682     ir::Expression *calleeReceiver;
683     if (info->callReceiver != nullptr) {
684         calleeReceiver = parser->CreateFormattedExpression("this.@@I1", "$this");
685     } else {
686         calleeReceiver = lciInfo->callee->Parent()->AsClassDefinition()->Ident()->Clone(allocator, nullptr);
687     }
688 
689     auto *calleeMemberExpr = util::NodeAllocator::ForceSetParent<ir::MemberExpression>(
690         allocator, calleeReceiver, lciInfo->callee->Key()->Clone(allocator, nullptr)->AsExpression(),
691         ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
692     auto *call = parser->CreateFormattedExpression("@@E1(@@[E2)", calleeMemberExpr, std::move(callArguments))
693                      ->AsCallExpression();
694 
695     if (lciInfo->classDefinition->TypeParams() != nullptr) {
696         auto typeArgs = ArenaVector<ir::TypeNode *>(allocator->Adapter());
697         for (auto *tp : lciInfo->classDefinition->TypeParams()->Params()) {
698             typeArgs.push_back(
699                 allocator->New<ir::OpaqueTypeNode>(tp->Name()->AsIdentifier()->Variable()->TsType(), allocator));
700         }
701         auto *typeArg =
702             util::NodeAllocator::ForceSetParent<ir::TSTypeParameterInstantiation>(allocator, std::move(typeArgs));
703         call->SetTypeParams(typeArg);
704         typeArg->SetParent(call);
705     }
706 
707     return call;
708 }
709 
CreateLambdaClassInvokeBody(public_lib::Context * ctx,LambdaInfo const * info,LambdaClassInvokeInfo const * lciInfo,bool wrapToObject)710 static ir::BlockStatement *CreateLambdaClassInvokeBody(public_lib::Context *ctx, LambdaInfo const *info,
711                                                        LambdaClassInvokeInfo const *lciInfo, bool wrapToObject)
712 {
713     auto *allocator = ctx->allocator;
714     auto *parser = ctx->parser->AsETSParser();
715     auto *checker = ctx->checker->AsETSChecker();
716     auto *anyType = checker->GlobalETSAnyType();
717 
718     auto *call = CreateCallForLambdaClassInvoke(ctx, info, lciInfo, wrapToObject);
719     auto bodyStmts = CreateRestArgumentsArrayReallocation(ctx, lciInfo);
720 
721     if (lciInfo->lambdaSignature->ReturnType() == checker->GlobalVoidType()) {
722         auto *callStmt = util::NodeAllocator::ForceSetParent<ir::ExpressionStatement>(allocator, call);
723         bodyStmts.push_back(callStmt);
724         if (wrapToObject) {
725             auto *returnStmt = util::NodeAllocator::ForceSetParent<ir::ReturnStatement>(
726                 allocator, allocator->New<ir::UndefinedLiteral>());
727             bodyStmts.push_back(returnStmt);
728         }
729     } else {
730         auto *returnExpr = wrapToObject ? parser->CreateFormattedExpression("@@E1 as @@T2", call, anyType) : call;
731         auto *returnStmt = util::NodeAllocator::ForceSetParent<ir::ReturnStatement>(allocator, returnExpr);
732         bodyStmts.push_back(returnStmt);
733     }
734 
735     return util::NodeAllocator::ForceSetParent<ir::BlockStatement>(allocator, allocator, std::move(bodyStmts));
736 }
737 
CreateLambdaClassInvokeMethod(public_lib::Context * ctx,LambdaInfo const * info,LambdaClassInvokeInfo * lciInfo,util::StringView methodName,bool wrapToObject)738 static void CreateLambdaClassInvokeMethod(public_lib::Context *ctx, LambdaInfo const *info,
739                                           LambdaClassInvokeInfo *lciInfo, util::StringView methodName,
740                                           bool wrapToObject)
741 {
742     auto *allocator = ctx->allocator;
743     auto *checker = ctx->checker->AsETSChecker();
744     auto *anyType = checker->GlobalETSAnyType();
745 
746     auto params = ArenaVector<ir::Expression *>(allocator->Adapter());
747     for (size_t idx = 0; idx < lciInfo->arity; ++idx) {
748         auto lparam = lciInfo->lambdaSignature->Params().at(idx);
749         auto *type = wrapToObject ? anyType : lparam->TsType()->Substitute(checker->Relation(), lciInfo->substitution);
750         auto *id = util::NodeAllocator::ForceSetParent<ir::Identifier>(
751             allocator, lparam->Name(), allocator->New<ir::OpaqueTypeNode>(type, allocator), allocator);
752         auto *param = util::NodeAllocator::ForceSetParent<ir::ETSParameterExpression>(allocator, id, false, allocator);
753         params.push_back(param);
754     }
755 
756     if (lciInfo->lambdaSignature->HasRestParameter()) {
757         CreateInvokeMethodRestParameter(ctx, lciInfo, &params);
758     }
759 
760     auto *returnType2 = allocator->New<ir::OpaqueTypeNode>(
761         wrapToObject ? anyType
762                      : lciInfo->lambdaSignature->ReturnType()->Substitute(checker->Relation(), lciInfo->substitution),
763         allocator);
764     bool hasReceiver = lciInfo->lambdaSignature->HasSignatureFlag(checker::SignatureFlags::EXTENSION_FUNCTION);
765     ir::ScriptFunctionFlags functionFlag = ir::ScriptFunctionFlags::METHOD;
766     auto *func = util::NodeAllocator::ForceSetParent<ir::ScriptFunction>(
767         allocator, allocator,
768         ir::ScriptFunction::ScriptFunctionData {
769             CreateLambdaClassInvokeBody(ctx, info, lciInfo, wrapToObject),
770             ir::FunctionSignature(nullptr, std::move(params), returnType2, hasReceiver), functionFlag});
771 
772     auto *invokeId = allocator->New<ir::Identifier>(methodName, allocator);
773     func->SetIdent(invokeId);
774 
775     auto *funcExpr = util::NodeAllocator::ForceSetParent<ir::FunctionExpression>(allocator, func);
776 
777     auto *invokeIdClone = invokeId->Clone(allocator, nullptr);
778     auto *invokeMethod = util::NodeAllocator::ForceSetParent<ir::MethodDefinition>(
779         allocator, ir::MethodDefinitionKind::METHOD, invokeIdClone, funcExpr, ir::ModifierFlags::NONE, allocator,
780         false);
781     ES2PANDA_ASSERT(!invokeMethod->IsStatic());
782 
783     lciInfo->classDefinition->Body().push_back(invokeMethod);
784     invokeMethod->SetParent(lciInfo->classDefinition);
785 }
786 
FunctionTypeToLambdaProviderType(checker::ETSChecker * checker,checker::Signature * signature)787 static checker::ETSObjectType *FunctionTypeToLambdaProviderType(checker::ETSChecker *checker,
788                                                                 checker::Signature *signature)
789 {
790     if (signature->RestVar() != nullptr) {
791         ES2PANDA_ASSERT(checker->GlobalBuiltinLambdaType(signature->ArgCount(), true));
792         return checker->GlobalBuiltinLambdaType(signature->ArgCount(), true)->AsETSObjectType();
793     }
794     // Note: FunctionN is not supported yet
795     if (signature->ArgCount() >= checker->GlobalBuiltinFunctionTypeVariadicThreshold()) {
796         return nullptr;
797     }
798     return checker->GlobalBuiltinLambdaType(signature->ArgCount(), false)->AsETSObjectType();
799 }
800 
801 // The `invoke` and `invoke0` of extension lambda class has two `this` identifier in parameter scope,
802 // first one is the lambdaClass itself and second one is the receiver class,
803 // the true `this` of the `invoke` and `invoke0` functionScope is the lambdaClass.
CorrectTheTrueThisForExtensionLambda(public_lib::Context * ctx,ir::ClassDeclaration * lambdaClass,size_t arity,bool hasRestParam)804 static void CorrectTheTrueThisForExtensionLambda(public_lib::Context *ctx, ir::ClassDeclaration *lambdaClass,
805                                                  size_t arity, bool hasRestParam)
806 {
807     auto *classScope = lambdaClass->Definition()->Scope();
808     ArenaVector<varbinder::Variable *> invokeFuncsOfLambda(ctx->Allocator()->Adapter());
809     auto invokeName = checker::FunctionalInterfaceInvokeName(arity, hasRestParam);
810     invokeFuncsOfLambda.emplace_back(
811         classScope->FindLocal(compiler::Signatures::LAMBDA_OBJECT_INVOKE, varbinder::ResolveBindingOptions::METHODS));
812     invokeFuncsOfLambda.emplace_back(
813         classScope->FindLocal(util::StringView(invokeName), varbinder::ResolveBindingOptions::METHODS));
814     for (auto *invokeFuncOfLambda : invokeFuncsOfLambda) {
815         if (invokeFuncOfLambda == nullptr) {
816             continue;
817         }
818         auto *scriptFunc = invokeFuncOfLambda->Declaration()
819                                ->AsFunctionDecl()
820                                ->Node()
821                                ->AsMethodDefinition()
822                                ->Value()
823                                ->AsFunctionExpression()
824                                ->Function();
825         if (!scriptFunc->Signature()->HasSignatureFlag(checker::SignatureFlags::EXTENSION_FUNCTION)) {
826             ES2PANDA_ASSERT(!scriptFunc->IsExtensionMethod());
827             continue;
828         }
829         ES2PANDA_ASSERT(scriptFunc->IsExtensionMethod());
830         auto *functionScope = scriptFunc->Scope();
831         auto *functionParamScope = scriptFunc->Scope()->ParamScope();
832         auto *theTrueThisVar = functionParamScope->Params()[0];
833         auto &bindings = const_cast<varbinder::Scope::VariableMap &>(functionScope->Bindings());
834         bindings.erase(varbinder::ETSBinder::MANDATORY_PARAM_THIS);
835         bindings.insert({varbinder::ETSBinder::MANDATORY_PARAM_THIS, theTrueThisVar});
836     }
837 }
838 
CreateEmptyLambdaClassDeclaration(public_lib::Context * ctx,LambdaInfo const * info,ir::TSTypeParameterDeclaration * newTypeParams,checker::ETSObjectType * fnInterface,checker::ETSObjectType * lambdaProviderClass)839 static ir::ClassDeclaration *CreateEmptyLambdaClassDeclaration(public_lib::Context *ctx, LambdaInfo const *info,
840                                                                ir::TSTypeParameterDeclaration *newTypeParams,
841                                                                checker::ETSObjectType *fnInterface,
842                                                                checker::ETSObjectType *lambdaProviderClass)
843 {
844     auto *allocator = ctx->allocator;
845     auto *parser = ctx->parser->AsETSParser();
846     auto *checker = ctx->checker->AsETSChecker();
847     auto *varBinder = ctx->checker->VarBinder()->AsETSBinder();
848 
849     auto lambdaClassName = util::UString {std::string_view {"LambdaObject-"}, allocator};
850 
851     util::StringView &objectName = info->calleeClass != nullptr ? info->calleeClass->Definition()->Ident()->Name()
852                                                                 : info->calleeInterface->Id()->Name();
853 
854     lambdaClassName.Append(objectName).Append("$").Append(info->name);
855 
856     ES2PANDA_ASSERT(lambdaProviderClass);
857     auto *providerTypeReference = checker->AllocNode<ir::ETSTypeReference>(
858         checker->AllocNode<ir::ETSTypeReferencePart>(
859             checker->AllocNode<ir::Identifier>(lambdaProviderClass->AsETSObjectType()->Name(), checker->Allocator()),
860             nullptr, nullptr, allocator),
861         allocator);
862 
863     std::stringstream stream;
864     stream << "@" << Signatures::NAMED_FUNCTION_OBJECT << "({name: \"" << info->originalFuncName
865            << "\"}) final class @@I1 extends @@T2 implements @@T3 {}";
866 
867     auto *classDeclaration =
868         parser->CreateFormattedTopLevelStatement(stream.str(), lambdaClassName, providerTypeReference, fnInterface)
869             ->AsClassDeclaration();
870     auto *classDefinition = classDeclaration->Definition();
871 
872     // Adjust the class definition compared to what the parser gives.
873     classDefinition->Body().clear();  // remove the default empty constructor
874     classDefinition->AddModifier(ir::ModifierFlags::PUBLIC | ir::ModifierFlags::FUNCTIONAL);
875     if (newTypeParams != nullptr) {
876         classDefinition->SetTypeParams(newTypeParams);
877         newTypeParams->SetParent(classDefinition);
878     }
879 
880     auto *program = varBinder->GetRecordTable()->Program();
881     program->Ast()->Statements().push_back(classDeclaration);
882     classDeclaration->SetParent(program->Ast());
883 
884     return classDeclaration;
885 }
886 
CreateLambdaClass(public_lib::Context * ctx,checker::ETSFunctionType * fntype,ir::MethodDefinition * callee,LambdaInfo const * info)887 static ir::ClassDeclaration *CreateLambdaClass(public_lib::Context *ctx, checker::ETSFunctionType *fntype,
888                                                ir::MethodDefinition *callee, LambdaInfo const *info)
889 {
890     auto *checker = ctx->checker->AsETSChecker();
891     auto *varBinder = ctx->checker->VarBinder()->AsETSBinder();
892 
893     auto *oldTypeParams = (info->enclosingFunction != nullptr) ? info->enclosingFunction->TypeParams() : nullptr;
894     auto [newTypeParams, subst0] =
895         CloneTypeParams(ctx, oldTypeParams, info->enclosingFunction, ctx->parserProgram->GlobalClassScope());
896     auto *substitution = subst0;  // NOTE(gogabr): needed to capture in a lambda later.
897 
898     auto fnInterface = fntype->Substitute(checker->Relation(), substitution)->ArrowToFunctionalInterface(checker);
899     auto lambdaProviderClass = FunctionTypeToLambdaProviderType(checker, fntype->ArrowSignature());
900 
901     auto lexScope = varbinder::LexicalScope<varbinder::Scope>::Enter(varBinder, ctx->parserProgram->GlobalClassScope());
902 
903     auto classDeclaration =
904         CreateEmptyLambdaClassDeclaration(ctx, info, newTypeParams, fnInterface, lambdaProviderClass);
905     auto classDefinition = classDeclaration->Definition();
906     if (info->isFunctionReference) {
907         ES2PANDA_ASSERT(callee->Function());
908         classDefinition->SetFunctionalReferenceReferencedMethod(callee);
909         classDefinition->SetModifiers(classDefinition->Modifiers() |
910                                       ir::ClassDefinitionModifiers::FUNCTIONAL_REFERENCE);
911     }
912 
913     CreateLambdaClassFields(ctx, classDefinition, info, substitution);
914     CreateLambdaClassConstructor(ctx, classDefinition, info, substitution);
915 
916     auto signature = fntype->ArrowSignature();
917 
918     LambdaClassInvokeInfo lciInfo;
919     lciInfo.callee = callee;
920     lciInfo.classDefinition = classDefinition;
921     lciInfo.substitution = substitution;
922     lciInfo.lambdaSignature = signature;
923 
924     for (size_t arity = signature->MinArgCount(); arity <= signature->ArgCount(); ++arity) {
925         lciInfo.arity = arity;
926         auto invokeMethodName =
927             util::UString {checker::FunctionalInterfaceInvokeName(arity, signature->HasRestParameter()), ctx->allocator}
928                 .View();
929         CreateLambdaClassInvokeMethod(ctx, info, &lciInfo, invokeMethodName, true);
930         // NOTE(vpukhov): for optional methods, the required invokeRk k={min, max-1} is not emitted
931     }
932     CreateLambdaClassInvokeMethod(ctx, info, &lciInfo, compiler::Signatures::LAMBDA_OBJECT_INVOKE, false);
933 
934     InitScopesPhaseETS::RunExternalNode(classDeclaration, varBinder);
935     varBinder->ResolveReferencesForScopeWithContext(classDeclaration, varBinder->TopScope());
936     classDeclaration->Check(checker);
937     CorrectTheTrueThisForExtensionLambda(ctx, classDeclaration, signature->MinArgCount(),
938                                          signature->HasRestParameter());
939     return classDeclaration;
940 }
941 
CreateConstructorCall(public_lib::Context * ctx,ir::AstNode * lambdaOrFuncRef,ir::ClassDeclaration * lambdaClass,LambdaInfo const * info)942 static ir::ETSNewClassInstanceExpression *CreateConstructorCall(public_lib::Context *ctx, ir::AstNode *lambdaOrFuncRef,
943                                                                 ir::ClassDeclaration *lambdaClass,
944                                                                 LambdaInfo const *info)
945 {
946     auto *allocator = ctx->allocator;
947     auto *varBinder = ctx->checker->VarBinder()->AsETSBinder();
948     auto *checker = ctx->checker->AsETSChecker();
949 
950     auto args = ArenaVector<ir::Expression *>(allocator->Adapter());
951     if (info->callReceiver != nullptr) {
952         args.push_back(info->callReceiver);
953     }
954     for (auto captured : *info->capturedVars) {
955         auto *id = allocator->New<ir::Identifier>(captured->Name(), allocator);
956         args.push_back(id);
957     }
958 
959     checker::ETSObjectType *constructedType = lambdaClass->Definition()->TsType()->AsETSObjectType();
960     if (info->enclosingFunction != nullptr) {
961         constructedType = constructedType->SubstituteArguments(checker->Relation(),
962                                                                info->enclosingFunction->Signature()->TypeParams());
963     }
964     auto *newExpr = util::NodeAllocator::ForceSetParent<ir::ETSNewClassInstanceExpression>(
965         allocator, allocator->New<ir::OpaqueTypeNode>(constructedType, allocator), std::move(args));
966     auto *lambdaOrFuncRefParent = lambdaOrFuncRef->Parent();
967     ES2PANDA_ASSERT(newExpr);
968     newExpr->SetParent(lambdaOrFuncRefParent);
969     // NOTE(dslynko, #19869): Required for correct debug-info generation
970     ES2PANDA_ASSERT(newExpr);
971     newExpr->SetRange(lambdaOrFuncRefParent != nullptr ? lambdaOrFuncRefParent->Range() : lambdaOrFuncRef->Range());
972 
973     auto *nearestScope = NearestScope(lambdaOrFuncRef);
974     auto lexScope = varbinder::LexicalScope<varbinder::Scope>::Enter(varBinder, nearestScope);
975     varBinder->ResolveReferencesForScopeWithContext(newExpr, nearestScope);
976 
977     checker::Type *objectType =
978         info->calleeClass != nullptr ? info->calleeClass->Definition()->TsType() : info->calleeInterface->TsType();
979     auto checkerCtx =
980         checker::SavedCheckerContext(ctx->checker, checker::CheckerStatus::IN_CLASS, objectType->AsETSObjectType());
981     auto scopeCtx = checker::ScopeContext(ctx->checker, nearestScope);
982     newExpr->Check(checker);
983 
984     return newExpr;
985 }
986 
ConvertLambda(public_lib::Context * ctx,ir::ArrowFunctionExpression * lambda)987 static ir::AstNode *ConvertLambda(public_lib::Context *ctx, ir::ArrowFunctionExpression *lambda)
988 {
989     auto *allocator = ctx->allocator;
990     auto *checker = ctx->checker->AsETSChecker();
991 
992     lambda->Check(checker);
993     ES2PANDA_ASSERT(lambda->TsType()->IsETSFunctionType());
994 
995     LambdaInfo info;
996     std::tie(info.calleeClass, info.enclosingFunction) = FindEnclosingClassAndFunction(lambda);
997     info.name = CreateCalleeName(allocator);
998 
999     if ((lambda->Parent() != nullptr) && lambda->Parent()->IsVariableDeclarator()) {
1000         info.originalFuncName = lambda->Parent()->AsVariableDeclarator()->Id()->AsIdentifier()->Name();
1001     } else if ((lambda->Parent() != nullptr) && lambda->Parent()->IsClassProperty()) {
1002         info.originalFuncName = lambda->Parent()->AsClassProperty()->Id()->Name();
1003     }
1004 
1005     auto capturedVars = FindCaptured(allocator, lambda);
1006     info.capturedVars = &capturedVars;
1007     info.callReceiver = CheckIfNeedThis(lambda, checker) ? allocator->New<ir::ThisExpression>() : nullptr;
1008     info.isFunctionReference = false;
1009 
1010     auto *callee = CreateCallee(ctx, lambda, &info);
1011     auto *lambdaType = lambda->TsType()->AsETSFunctionType();
1012     auto *lambdaClass = CreateLambdaClass(ctx, lambdaType, callee, &info);
1013     return CreateConstructorCall(ctx, lambda, lambdaClass, &info);
1014 }
1015 
GetWrappingLambdaParentFunction(public_lib::Context * ctx,ir::Expression * funcRef,checker::Signature * signature)1016 static ir::ScriptFunction *GetWrappingLambdaParentFunction(public_lib::Context *ctx, ir::Expression *funcRef,
1017                                                            checker::Signature *signature)
1018 {
1019     auto *allocator = ctx->allocator;
1020     ArenaVector<ir::Expression *> params {allocator->Adapter()};
1021     for (auto *p : signature->Params()) {
1022         params.push_back(util::NodeAllocator::ForceSetParent<ir::ETSParameterExpression>(
1023             allocator,
1024             allocator->New<ir::Identifier>(p->Name(), allocator->New<ir::OpaqueTypeNode>(p->TsType(), allocator),
1025                                            allocator),
1026             false, allocator));
1027     }
1028     auto *func = util::NodeAllocator::ForceSetParent<ir::ScriptFunction>(
1029         allocator, allocator,
1030         ir::ScriptFunction::ScriptFunctionData {
1031             nullptr,
1032             ir::FunctionSignature {nullptr, std::move(params),
1033                                    allocator->New<ir::OpaqueTypeNode>(signature->ReturnType(), allocator)},
1034             ir::ScriptFunctionFlags::ARROW});
1035     ES2PANDA_ASSERT(func != nullptr);
1036     ArenaVector<ir::Statement *> bodyStmts {allocator->Adapter()};
1037     ArenaVector<ir::Expression *> callArgs {allocator->Adapter()};
1038 
1039     for (auto *p : func->Params()) {
1040         ir::Identifier *clone = p->AsETSParameterExpression()->Ident()->Clone(allocator, nullptr);
1041         ES2PANDA_ASSERT(clone != nullptr);
1042         if (clone->IsIdentifier() && (clone->IsReference(ScriptExtension::ETS)) &&
1043             (clone->TypeAnnotation() != nullptr)) {
1044             clone->SetTsTypeAnnotation(nullptr);
1045         }
1046         callArgs.push_back(clone);
1047     }
1048     auto *callExpr = util::NodeAllocator::ForceSetParent<ir::CallExpression>(allocator, funcRef, std::move(callArgs),
1049                                                                              nullptr, false);
1050     ir::Statement *stmt;
1051     if (signature->ReturnType() == ctx->checker->AsETSChecker()->GlobalVoidType()) {
1052         stmt = util::NodeAllocator::ForceSetParent<ir::ExpressionStatement>(allocator, callExpr);
1053     } else {
1054         stmt = util::NodeAllocator::ForceSetParent<ir::ReturnStatement>(allocator, callExpr);
1055     }
1056     bodyStmts.push_back(stmt);
1057     func->SetBody(util::NodeAllocator::ForceSetParent<ir::BlockStatement>(allocator, allocator, std::move(bodyStmts)));
1058     ES2PANDA_ASSERT(func->Body());
1059     func->Body()->SetParent(func);
1060     return func;
1061 }
1062 
CreateWrappingLambda(public_lib::Context * ctx,ir::Expression * funcRef)1063 static ir::ArrowFunctionExpression *CreateWrappingLambda(public_lib::Context *ctx, ir::Expression *funcRef)
1064 {
1065     auto *allocator = ctx->allocator;
1066     auto *varBinder = ctx->checker->VarBinder()->AsETSBinder();
1067     ES2PANDA_ASSERT(funcRef->TsType()->IsETSArrowType());
1068     auto signature = funcRef->TsType()->AsETSFunctionType()->ArrowSignature();
1069 
1070     auto *parent = funcRef->Parent();
1071     auto *func = GetWrappingLambdaParentFunction(ctx, funcRef, signature);
1072 
1073     auto *lambda = util::NodeAllocator::ForceSetParent<ir::ArrowFunctionExpression>(allocator, func, allocator);
1074     ES2PANDA_ASSERT(lambda);
1075     lambda->SetParent(parent);
1076 
1077     auto *nearestScope = NearestScope(lambda);
1078     auto lexScope = varbinder::LexicalScope<varbinder::Scope>::Enter(varBinder, nearestScope);
1079     InitScopesPhaseETS::RunExternalNode(lambda, varBinder);
1080     varBinder->ResolveReferencesForScopeWithContext(lambda, nearestScope);
1081 
1082     auto [enclosingClass, _] = FindEnclosingClassAndFunction(parent);
1083 
1084     auto checkerCtx = checker::SavedCheckerContext(ctx->checker, checker::CheckerStatus::IN_CLASS,
1085                                                    enclosingClass->Definition()->TsType()->AsETSObjectType());
1086     auto scopeCtx = checker::ScopeContext(ctx->checker, nearestScope);
1087     lambda->Check(ctx->checker->AsETSChecker());
1088 
1089     return lambda;
1090 }
1091 
GenerateLambdaInfoForFunctionReference(public_lib::Context * ctx,ir::Expression * funcRef,ir::MethodDefinition * method)1092 static LambdaInfo GenerateLambdaInfoForFunctionReference(public_lib::Context *ctx, ir::Expression *funcRef,
1093                                                          ir::MethodDefinition *method)
1094 {
1095     auto *allocator = ctx->allocator;
1096     LambdaInfo info;
1097     if (method->Parent()->Parent()->IsClassDeclaration()) {
1098         info.calleeClass = method->Parent()->Parent()->AsClassDeclaration();
1099     } else if (method->Parent()->Parent()->IsTSInterfaceDeclaration()) {
1100         info.calleeInterface = method->Parent()->Parent()->AsTSInterfaceDeclaration();
1101     } else {
1102         ES2PANDA_UNREACHABLE();
1103     }
1104     info.enclosingFunction = nullptr;
1105     info.name = CreateCalleeName(allocator);
1106     info.originalFuncName = method->Id()->Name();
1107     info.capturedVars = allocator->New<ArenaSet<varbinder::Variable *>>(allocator->Adapter());
1108     info.isFunctionReference = true;
1109     if (method->IsStatic()) {
1110         info.callReceiver = nullptr;
1111     } else {
1112         ES2PANDA_ASSERT(funcRef->IsMemberExpression());
1113         info.callReceiver = funcRef->AsMemberExpression()->Object();
1114     }
1115     if (funcRef->IsMemberExpression()) {
1116         info.objType = funcRef->AsMemberExpression()->ObjType();
1117     }
1118     return info;
1119 }
1120 
ConvertFunctionReference(public_lib::Context * ctx,ir::Expression * funcRef)1121 static ir::AstNode *ConvertFunctionReference(public_lib::Context *ctx, ir::Expression *funcRef)
1122 {
1123     ES2PANDA_ASSERT(funcRef->IsIdentifier() ||
1124                     (funcRef->IsMemberExpression() &&
1125                      funcRef->AsMemberExpression()->Kind() == ir::MemberExpressionKind::PROPERTY_ACCESS &&
1126                      funcRef->AsMemberExpression()->Property()->IsIdentifier()));
1127     varbinder::Variable *var;
1128     if (funcRef->IsIdentifier()) {
1129         var = funcRef->AsIdentifier()->Variable();
1130     } else {
1131         auto *mexpr = funcRef->AsMemberExpression();
1132         // NOTE(gogabr): mexpr->PropVar() is a synthetic variable wwith no reference to the method definition. Why?
1133         auto refVar = ctx->checker->AsETSChecker()->GetTargetRef(mexpr);
1134         auto flags = checker::PropertySearchFlags::SEARCH_IN_BASE |
1135                      checker::PropertySearchFlags::DISALLOW_SYNTHETIC_METHOD_CREATION;
1136         if (refVar != nullptr && refVar->HasFlag(varbinder::VariableFlags::CLASS)) {
1137             flags |= checker::PropertySearchFlags::SEARCH_STATIC_METHOD;
1138         } else {
1139             flags |= (checker::PropertySearchFlags::SEARCH_INSTANCE_METHOD |
1140                       checker::PropertySearchFlags::SEARCH_STATIC_METHOD);
1141         }
1142         var =
1143             mexpr->Object()->TsType()->AsETSObjectType()->GetProperty(mexpr->Property()->AsIdentifier()->Name(), flags);
1144         ES2PANDA_ASSERT(var != nullptr);
1145     }
1146 
1147     ES2PANDA_ASSERT(var->Declaration()->Node()->IsMethodDefinition());
1148     auto *method = var->Declaration()->Node()->AsMethodDefinition();
1149 
1150     if (method->IsPrivate() || method->IsProtected()) {
1151         // Direct reference to method will be impossible from the lambda class, so replace func ref with a lambda
1152         // that will translate to a proxy method
1153         auto *lam = CreateWrappingLambda(ctx, funcRef);
1154         return lam == nullptr ? funcRef : ConvertLambda(ctx, lam);
1155     }
1156 
1157     LambdaInfo info = GenerateLambdaInfoForFunctionReference(ctx, funcRef, method);
1158 
1159     ES2PANDA_ASSERT(funcRef->TsType()->IsETSArrowType());
1160     auto *lambdaClass = CreateLambdaClass(ctx, funcRef->TsType()->AsETSFunctionType(), method, &info);
1161     auto *constructorCall = CreateConstructorCall(ctx, funcRef, lambdaClass, &info);
1162     ES2PANDA_ASSERT(constructorCall);
1163     if (constructorCall->TsType()->IsETSObjectType()) {
1164         constructorCall->TsType()->AsETSObjectType()->AddObjectFlag(checker::ETSObjectFlags::FUNCTIONAL_REFERENCE);
1165     }
1166 
1167     return constructorCall;
1168 }
1169 
IsVariableOriginalAccessor(const varbinder::Variable * var)1170 static bool IsVariableOriginalAccessor(const varbinder::Variable *var)
1171 {
1172     return checker::ETSChecker::IsVariableGetterSetter(var) && !(checker::ETSChecker::IsVariableExtensionAccessor(var));
1173 }
1174 
IsFunctionOrMethodCall(checker::ETSChecker * checker,ir::CallExpression const * node)1175 static bool IsFunctionOrMethodCall(checker::ETSChecker *checker, ir::CallExpression const *node)
1176 {
1177     auto const *callee = node->Callee();
1178     if (callee->TsType() != nullptr && callee->TsType()->IsETSExtensionFuncHelperType()) {
1179         return true;
1180     }
1181 
1182     // NOTE: Skip if invoke pattern Union.<method>()
1183     // Not skip if invoke pattern Union.<field>() where field is of ETSArrowType
1184     if (callee->IsMemberExpression()) {
1185         auto me = callee->AsMemberExpression();
1186         ES2PANDA_ASSERT(me->TsType() != nullptr);
1187         if (me->Object()->TsType() != nullptr && checker->GetApparentType(me->Object()->TsType())->IsETSUnionType() &&
1188             me->TsType()->IsETSMethodType()) {
1189             return true;
1190         }
1191     }
1192 
1193     varbinder::Variable *var = nullptr;
1194     if (callee->IsMemberExpression() &&
1195         (callee->AsMemberExpression()->Kind() & ir::MemberExpressionKind::PROPERTY_ACCESS) != 0) {
1196         var = callee->AsMemberExpression()->Property()->Variable();
1197     } else if (callee->IsIdentifier()) {
1198         var = callee->AsIdentifier()->Variable();
1199     }
1200 
1201     return var != nullptr && !IsVariableOriginalAccessor(var) && (var->Flags() & varbinder::VariableFlags::METHOD) != 0;
1202 }
1203 
InsertInvokeCall(public_lib::Context * ctx,ir::CallExpression * call)1204 static ir::AstNode *InsertInvokeCall(public_lib::Context *ctx, ir::CallExpression *call)
1205 {
1206     auto *allocator = ctx->allocator;
1207     auto *checker = ctx->checker->AsETSChecker();
1208     auto *varBinder = checker->VarBinder()->AsETSBinder();
1209 
1210     auto *oldCallee = call->Callee();
1211     auto *oldType = checker->GetApparentType(oldCallee->TsType());
1212     ES2PANDA_ASSERT(oldType != nullptr);
1213     size_t arity = call->Arguments().size();
1214     auto *ifaceType = oldType->IsETSObjectType()
1215                           ? oldType->AsETSObjectType()
1216                           : oldType->AsETSFunctionType()->ArrowToFunctionalInterfaceDesiredArity(checker, arity);
1217     ES2PANDA_ASSERT(ifaceType != nullptr);
1218     if (ifaceType->IsETSDynamicType()) {
1219         return call;
1220     }
1221     bool hasRestParam =
1222         oldType->IsETSFunctionType() && oldType->AsETSFunctionType()->ArrowSignature()->HasRestParameter();
1223     util::StringView invokeMethodName =
1224         util::UString {checker::FunctionalInterfaceInvokeName(arity, hasRestParam), allocator}.View();
1225     auto *prop = ifaceType->GetProperty(invokeMethodName, checker::PropertySearchFlags::SEARCH_INSTANCE_METHOD |
1226                                                               checker::PropertySearchFlags::SEARCH_IN_INTERFACES);
1227     ES2PANDA_ASSERT(prop != nullptr);
1228     auto *invoke0Id = allocator->New<ir::Identifier>(invokeMethodName, allocator);
1229     ES2PANDA_ASSERT(invoke0Id != nullptr);
1230     invoke0Id->SetTsType(prop->TsType());
1231     invoke0Id->SetVariable(prop);
1232 
1233     auto *newCallee = util::NodeAllocator::ForceSetParent<ir::MemberExpression>(
1234         allocator, oldCallee, invoke0Id, ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
1235     ES2PANDA_ASSERT(newCallee != nullptr);
1236     newCallee->SetTsType(prop->TsType());
1237     newCallee->SetObjectType(ifaceType);
1238 
1239     call->SetCallee(newCallee);
1240     call->SetSignature(prop->TsType()->AsETSFunctionType()->CallSignatures()[0]);
1241 
1242     /* NOTE(gogabr): argument types may have been spoiled by widening/narrowing conversions.
1243        Repair them here.
1244        In the future, make sure those conversions behave appropriately.
1245     */
1246     for (auto *arg : call->Arguments()) {
1247         auto boxingFlags = arg->GetBoxingUnboxingFlags();
1248         Recheck(ctx->phaseManager, varBinder, checker, arg);
1249         arg->SetBoxingUnboxingFlags(boxingFlags);
1250     }
1251 
1252     return call;
1253 }
1254 
IsRedirectingConstructorCall(ir::CallExpression * expr)1255 static bool IsRedirectingConstructorCall(ir::CallExpression *expr)
1256 {
1257     return expr->Callee()->IsThisExpression() || expr->Callee()->IsSuperExpression();
1258 }
1259 
IsInCalleePosition(ir::Expression * expr)1260 static bool IsInCalleePosition(ir::Expression *expr)
1261 {
1262     return expr->Parent()->IsCallExpression() && expr->Parent()->AsCallExpression()->Callee() == expr;
1263 }
1264 
IsEnumFunctionCall(const ir::Identifier * const id)1265 static bool IsEnumFunctionCall(const ir::Identifier *const id)
1266 {
1267     if (id->Parent() != nullptr && id->Parent()->IsMemberExpression()) {
1268         const auto *const expr = id->Parent()->AsMemberExpression();
1269         if (expr->Object()->TsType()->IsETSEnumType()) {
1270             return true;
1271         }
1272     }
1273 
1274     return false;
1275 }
1276 
IsValidFunctionDeclVar(const varbinder::Variable * const var)1277 static bool IsValidFunctionDeclVar(const varbinder::Variable *const var)
1278 {
1279     // Note: If a function is accessor, then no need to build lambda class.
1280     return var != nullptr && var->Declaration() != nullptr && var->Declaration()->IsFunctionDecl() &&
1281            !var->TsType()->HasTypeFlag(checker::TypeFlag::GETTER_SETTER);
1282 }
1283 
BuildLambdaClassWhenNeeded(public_lib::Context * ctx,ir::AstNode * node)1284 static ir::AstNode *BuildLambdaClassWhenNeeded(public_lib::Context *ctx, ir::AstNode *node)
1285 {
1286     if (node->IsArrowFunctionExpression()) {
1287         return ConvertLambda(ctx, node->AsArrowFunctionExpression());
1288     }
1289 
1290     if (node->IsIdentifier()) {
1291         auto *id = node->AsIdentifier();
1292         auto *var = id->Variable();
1293         // We are running this lowering only for ETS files
1294         // so it is correct to pass ETS extension here to isReference()
1295         if (id->IsReference(ScriptExtension::ETS) && id->TsType() != nullptr && id->TsType()->IsETSFunctionType() &&
1296             !IsInCalleePosition(id) && !IsEnumFunctionCall(id) && IsValidFunctionDeclVar(var)) {
1297             return ConvertFunctionReference(ctx, id);
1298         }
1299     }
1300     if (node->IsMemberExpression()) {
1301         auto *mexpr = node->AsMemberExpression();
1302         if (mexpr->Kind() == ir::MemberExpressionKind::PROPERTY_ACCESS && mexpr->TsType() != nullptr &&
1303             mexpr->TsType()->IsETSFunctionType() && mexpr->Object()->TsType()->IsETSObjectType()) {
1304             ES2PANDA_ASSERT(mexpr->Property()->IsIdentifier());
1305             auto *var = mexpr->Object()->TsType()->AsETSObjectType()->GetProperty(
1306                 mexpr->Property()->AsIdentifier()->Name(),
1307                 checker::PropertySearchFlags::SEARCH_INSTANCE_METHOD |
1308                     checker::PropertySearchFlags::SEARCH_STATIC_METHOD | checker::PropertySearchFlags::SEARCH_IN_BASE |
1309                     checker::PropertySearchFlags::DISALLOW_SYNTHETIC_METHOD_CREATION);
1310             if (IsValidFunctionDeclVar(var) && !IsInCalleePosition(mexpr)) {
1311                 return ConvertFunctionReference(ctx, mexpr);
1312             }
1313         }
1314     }
1315     return node;
1316 }
1317 
LowerTypeNodeIfNeeded(public_lib::Context * ctx,ir::AstNode * node)1318 static ir::AstNode *LowerTypeNodeIfNeeded(public_lib::Context *ctx, ir::AstNode *node)
1319 {
1320     if (!node->IsExpression() || !node->AsExpression()->IsTypeNode()) {
1321         return node;
1322     }
1323 
1324     auto type = node->AsExpression()->AsTypeNode()->TsType();
1325     if (type == nullptr || !type->IsETSArrowType()) {
1326         return node;
1327     }
1328 
1329     auto allocator = ctx->allocator;
1330     auto checker = ctx->checker->AsETSChecker();
1331 
1332     auto newTypeNode =
1333         allocator->New<ir::OpaqueTypeNode>(type->AsETSFunctionType()->ArrowToFunctionalInterface(checker), allocator);
1334     newTypeNode->SetParent(node->Parent());
1335     return newTypeNode;
1336 }
1337 
PerformForModule(public_lib::Context * ctx,parser::Program * program)1338 bool LambdaConversionPhase::PerformForModule(public_lib::Context *ctx, parser::Program *program)
1339 {
1340     auto *varBinder = ctx->checker->VarBinder()->AsETSBinder();
1341     varbinder::RecordTableContext bctx {varBinder, program == ctx->parserProgram ? nullptr : program};
1342     parser::SavedFormattingFileName savedFormattingName(ctx->parser->AsETSParser(), "lambda-conversion");
1343 
1344     // For reproducibility of results when several compilation sessions are executed during
1345     // the same process's lifetime.
1346     if (program == ctx->parserProgram &&
1347         ctx->config->options->GetCompilationMode() != CompilationMode::GEN_ABC_FOR_EXTERNAL_SOURCE) {
1348         ResetCalleeCount();
1349     }
1350 
1351     program->Ast()->TransformChildrenRecursivelyPostorder(
1352         [ctx](ir::AstNode *node) { return BuildLambdaClassWhenNeeded(ctx, node); }, Name());
1353 
1354     program->Ast()->TransformChildrenRecursivelyPreorder(
1355         [ctx](ir::AstNode *node) { return LowerTypeNodeIfNeeded(ctx, node); }, Name());
1356 
1357     auto insertInvokeIfNeeded = [ctx](ir::AstNode *node) {
1358         if (node->IsCallExpression() &&
1359             !IsFunctionOrMethodCall(ctx->checker->AsETSChecker(), node->AsCallExpression()) &&
1360             !IsRedirectingConstructorCall(node->AsCallExpression())) {
1361             return InsertInvokeCall(ctx, node->AsCallExpression());
1362         }
1363         return node;
1364     };
1365 
1366     // at this moment, the AST in subexpressions is not consistent, so the preorder is chosen
1367     program->Ast()->TransformChildrenRecursivelyPreorder(insertInvokeIfNeeded, Name());
1368 
1369     return true;
1370 }
1371 
PostconditionForModule(public_lib::Context * ctx,parser::Program const * program)1372 bool LambdaConversionPhase::PostconditionForModule([[maybe_unused]] public_lib::Context *ctx,
1373                                                    parser::Program const *program)
1374 {
1375     return !program->Ast()->IsAnyChild([](ir::AstNode const *node) { return node->IsArrowFunctionExpression(); });
1376 }
1377 
1378 }  // namespace ark::es2panda::compiler
1379