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, ¶ms](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, ¶ms);
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