• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 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 "restArgsLowering.h"
17 #include "compiler/lowering/util.h"
18 #include "ir/astNode.h"
19 #include "ir/expressions/arrayExpression.h"
20 
21 #include <checker/ETSchecker.h>
22 #include <sstream>
23 
24 namespace ark::es2panda::compiler {
25 
26 using AstNodePtr = ir::AstNode *;
27 
CreateRestArgsBlockExpression(public_lib::Context * context,ir::SpreadElement * spreadElement)28 static ir::BlockExpression *CreateRestArgsBlockExpression(public_lib::Context *context,
29                                                           ir::SpreadElement *spreadElement)
30 {
31     auto *allocator = context->allocator;
32     auto *parser = context->parser->AsETSParser();
33     auto *checker = context->checker->AsETSChecker();
34 
35     ArenaVector<ir::Statement *> blockStatements(allocator->Adapter());
36     const auto arraySymbol = Gensym(allocator);
37     const auto argumentSymbol = Gensym(allocator);
38     const auto iteratorIndex = Gensym(allocator);
39     const auto iteratorSymbol = Gensym(allocator);
40     const auto elementType = checker->GetElementTypeOfArray(spreadElement->Argument()->TsType());
41     auto *typeNode = allocator->New<ir::OpaqueTypeNode>(elementType, allocator);
42     blockStatements.push_back(
43         parser->CreateFormattedStatement("let @@I1 = @@E2;", argumentSymbol, spreadElement->Argument()));
44     blockStatements.push_back(parser->CreateFormattedStatement("let @@I1 = 0;", iteratorIndex));
45     blockStatements.push_back(parser->CreateFormattedStatement("let @@I1 = new Array<@@T2>(@@I3.length);", arraySymbol,
46                                                                typeNode, argumentSymbol->Clone(allocator, nullptr)));
47     std::vector<ir::AstNode *> args;
48     std::stringstream ss;
49     ss << "for (let @@I1 of @@I2){";
50     args.emplace_back(iteratorSymbol);
51     args.emplace_back(argumentSymbol->Clone(allocator, nullptr));
52     ss << "@@I3[@@I4] = @@I5;";
53     args.emplace_back(arraySymbol->Clone(allocator, nullptr));
54     ES2PANDA_ASSERT(iteratorIndex != nullptr);
55     args.emplace_back(iteratorIndex->Clone(allocator, nullptr));
56     args.emplace_back(iteratorSymbol->Clone(allocator, nullptr));
57     ss << "@@I6 = @@I7 + 1;";
58     args.emplace_back(iteratorIndex->Clone(allocator, nullptr));
59     args.emplace_back(iteratorIndex->Clone(allocator, nullptr));
60     ss << "}";
61     ir::Statement *loopStatement = parser->CreateFormattedStatement(ss.str(), args);
62 
63     blockStatements.push_back(loopStatement);
64     blockStatements.push_back(parser->CreateFormattedStatement("@@I1", arraySymbol->Clone(allocator, nullptr)));
65     auto *blockExpr = util::NodeAllocator::ForceSetParent<ir::BlockExpression>(allocator, std::move(blockStatements));
66     return blockExpr;
67 }
68 
ConvertSpreadToBlockExpression(public_lib::Context * context,ir::SpreadElement * spreadElement)69 static ir::BlockExpression *ConvertSpreadToBlockExpression(public_lib::Context *context,
70                                                            ir::SpreadElement *spreadElement)
71 {
72     auto *blockExpression = CreateRestArgsBlockExpression(context, spreadElement);
73     ES2PANDA_ASSERT(blockExpression != nullptr);
74     blockExpression->SetParent(spreadElement->Parent());
75     blockExpression->SetRange(spreadElement->Range());
76 
77     for (auto *statement : blockExpression->Statements()) {
78         SetSourceRangesRecursively(statement, spreadElement->Range());
79     }
80     return blockExpression;
81 }
82 
ShouldProcessRestParameters(checker::Signature * signature,const ArenaVector<ir::Expression * > & arguments)83 static bool ShouldProcessRestParameters(checker::Signature *signature, const ArenaVector<ir::Expression *> &arguments)
84 {
85     return signature != nullptr && signature->HasRestParameter() && !signature->RestVar()->TsType()->IsETSArrayType() &&
86            arguments.size() >= signature->Params().size() && !signature->RestVar()->TsType()->IsETSTupleType();
87 }
88 
CreateRestArgsArray(public_lib::Context * context,ArenaVector<ir::Expression * > & arguments,checker::Signature * signature)89 static ir::Expression *CreateRestArgsArray(public_lib::Context *context, ArenaVector<ir::Expression *> &arguments,
90                                            checker::Signature *signature)
91 {
92     auto *allocator = context->allocator;
93     auto *parser = context->parser->AsETSParser();
94     auto *checker = context->checker->AsETSChecker();
95 
96     // Handle single spread element case
97     const size_t extraArgs = arguments.size() - signature->Params().size();
98     if (extraArgs == 1 && arguments.back()->IsSpreadElement()) {
99         return ConvertSpreadToBlockExpression(context, arguments.back()->AsSpreadElement());
100     }
101     // Determine array type
102     checker::Type *arrayType = signature->RestVar()->TsType();
103     auto *type = checker->AllocNode<ir::OpaqueTypeNode>(checker->GetElementTypeOfArray(arrayType), allocator);
104     if (extraArgs == 0) {
105         return parser->CreateFormattedExpression("new Array<@@T1>(0)", type);
106     }
107 
108     ArenaVector<ir::Expression *> copiedArguments(arguments.begin() + signature->Params().size(), arguments.end(),
109                                                   allocator->Adapter());
110 
111     std::stringstream ss;
112     auto *genSymIdent = Gensym(allocator);
113     ss << "let @@I1 : FixedArray<@@T2> = @@E3;";
114     ss << "Array.from<@@T4>(@@I5);";
115     auto *arrayExpr = checker->AllocNode<ir::ArrayExpression>(std::move(copiedArguments), allocator);
116     ES2PANDA_ASSERT(type != nullptr);
117     auto *loweringResult =
118         parser->CreateFormattedExpression(ss.str(), genSymIdent, type, arrayExpr, type->Clone(allocator, nullptr),
119                                           genSymIdent->Clone(allocator, nullptr));
120     return loweringResult;
121 }
122 
RebuildCallExpression(public_lib::Context * context,ir::CallExpression * originalCall,checker::Signature * signature,ir::Expression * restArgsArray)123 static ir::CallExpression *RebuildCallExpression(public_lib::Context *context, ir::CallExpression *originalCall,
124                                                  checker::Signature *signature, ir::Expression *restArgsArray)
125 {
126     auto *allocator = context->allocator;
127     auto *varbinder = context->checker->VarBinder()->AsETSBinder();
128     ArenaVector<ir::Expression *> newArgs(allocator->Adapter());
129 
130     for (size_t i = 0; i < signature->Params().size(); ++i) {
131         newArgs.push_back(originalCall->Arguments()[i]);
132         newArgs[i]->SetBoxingUnboxingFlags(ir::BoxingUnboxingFlags::NONE);
133     }
134 
135     newArgs.push_back(restArgsArray);
136 
137     auto *newCall = util::NodeAllocator::ForceSetParent<ir::CallExpression>(allocator, originalCall->Callee(),
138                                                                             std::move(newArgs), nullptr, false);
139     ES2PANDA_ASSERT(newCall != nullptr);
140     restArgsArray->SetParent(newCall);
141     newCall->SetParent(originalCall->Parent());
142     newCall->AddModifier(originalCall->Modifiers());
143     newCall->AddBoxingUnboxingFlags(originalCall->GetBoxingUnboxingFlags());
144     newCall->SetTypeParams(originalCall->TypeParams());
145     newCall->AddAstNodeFlags(ir::AstNodeFlags::RESIZABLE_REST);
146 
147     auto *scope = NearestScope(newCall->Parent());
148     auto bscope = varbinder::LexicalScope<varbinder::Scope>::Enter(varbinder, scope);
149     CheckLoweredNode(context->checker->VarBinder()->AsETSBinder(), context->checker->AsETSChecker(), newCall);
150     newCall->RemoveAstNodeFlags(ir::AstNodeFlags::RESIZABLE_REST);
151     return newCall;
152 }
153 
RebuildNewClassInstanceExpression(public_lib::Context * context,ir::ETSNewClassInstanceExpression * originalCall,checker::Signature * signature,ir::Expression * restArgsArray)154 static ir::ETSNewClassInstanceExpression *RebuildNewClassInstanceExpression(
155     public_lib::Context *context, ir::ETSNewClassInstanceExpression *originalCall, checker::Signature *signature,
156     ir::Expression *restArgsArray)
157 {
158     auto *allocator = context->allocator;
159     ArenaVector<ir::Expression *> newArgs(allocator->Adapter());
160 
161     for (size_t i = 0; i < signature->Params().size(); ++i) {
162         newArgs.push_back(originalCall->GetArguments()[i]);
163     }
164 
165     newArgs.push_back(restArgsArray);
166 
167     auto *newCall = util::NodeAllocator::ForceSetParent<ir::ETSNewClassInstanceExpression>(
168         allocator, originalCall->GetTypeRef()->Clone(allocator, nullptr)->AsETSTypeReference(), std::move(newArgs));
169 
170     restArgsArray->SetParent(newCall);
171     newCall->SetParent(originalCall->Parent());
172     newCall->AddModifier(originalCall->Modifiers());
173     newCall->AddBoxingUnboxingFlags(originalCall->GetBoxingUnboxingFlags());
174     auto *scope = NearestScope(newCall->Parent());
175     auto bscope = varbinder::LexicalScope<varbinder::Scope>::Enter(context->checker->VarBinder()->AsETSBinder(), scope);
176     CheckLoweredNode(context->checker->VarBinder()->AsETSBinder(), context->checker->AsETSChecker(), newCall);
177     return newCall;
178 }
179 
TransformCallConstructWithRestArgs(ir::ETSNewClassInstanceExpression * expr,public_lib::Context * context)180 ir::ETSNewClassInstanceExpression *RestArgsLowering::TransformCallConstructWithRestArgs(
181     ir::ETSNewClassInstanceExpression *expr, public_lib::Context *context)
182 {
183     checker::Signature *signature = expr->GetSignature();
184     if (!ShouldProcessRestParameters(signature, expr->GetArguments())) {
185         return expr;
186     }
187 
188     auto *restArgsArray = CreateRestArgsArray(context, expr->GetArguments(), signature);
189     restArgsArray->AddAstNodeFlags(ir::AstNodeFlags::RESIZABLE_REST);
190 
191     return RebuildNewClassInstanceExpression(context, expr, signature, restArgsArray);
192 }
193 
TransformCallExpressionWithRestArgs(ir::CallExpression * callExpr,public_lib::Context * context)194 ir::CallExpression *RestArgsLowering::TransformCallExpressionWithRestArgs(ir::CallExpression *callExpr,
195                                                                           public_lib::Context *context)
196 {
197     checker::Type *calleeType = callExpr->Callee()->TsType();
198     if (calleeType == nullptr || calleeType->IsETSDynamicType() || calleeType->IsETSArrowType()) {
199         return callExpr;
200     }
201 
202     checker::Signature *signature = callExpr->Signature();
203     if (!ShouldProcessRestParameters(signature, callExpr->Arguments())) {
204         return callExpr;
205     }
206 
207     auto *restArgsArray = CreateRestArgsArray(context, callExpr->Arguments(), signature);
208     restArgsArray->AddAstNodeFlags(ir::AstNodeFlags::RESIZABLE_REST);
209 
210     return RebuildCallExpression(context, callExpr, signature, restArgsArray);
211 }
212 
PerformForModule(public_lib::Context * ctx,parser::Program * program)213 bool RestArgsLowering::PerformForModule(public_lib::Context *ctx, parser::Program *program)
214 {
215     program->Ast()->TransformChildrenRecursively(
216         [this, ctx](ir::AstNode *node) -> AstNodePtr {
217             if (node->IsCallExpression()) {
218                 return TransformCallExpressionWithRestArgs(node->AsCallExpression(), ctx);
219             }
220             if (node->IsETSNewClassInstanceExpression()) {
221                 return TransformCallConstructWithRestArgs(node->AsETSNewClassInstanceExpression(), ctx);
222             }
223             return node;
224         },
225         Name());
226     return true;
227 }
228 }  // namespace ark::es2panda::compiler