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