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 <gtest/gtest.h>
17 
18 #include "assembler/assembly-program.h"
19 #include "es2panda.h"
20 #include "generated/signatures.h"
21 #include "libpandabase/mem/mem.h"
22 #include "macros.h"
23 #include "mem/pool_manager.h"
24 #include "util/options.h"
25 
26 namespace ark::es2panda::compiler::test {
27 
28 class RestParameterTest : public testing::Test {
29 public:
RestParameterTest()30     RestParameterTest()
31     {
32         const auto compilerSize = 268435456;
33 
34         mem::MemConfig::Initialize(0, 0, compilerSize, 0, 0, 0);
35         PoolManager::Initialize(PoolType::MMAP);
36     }
37     ~RestParameterTest() override
38     {
39         PoolManager::Finalize();
40         mem::MemConfig::Finalize();
41     }
42 
SetCurrentProgram(std::string_view src)43     void SetCurrentProgram(std::string_view src)
44     {
45         int argc = 1;
46         const char *argv = "../../../../bin/es2panda";  // NOLINT(modernize-avoid-c-arrays)
47         static constexpr std::string_view FILE_NAME = "dummy.sts";
48 
49         program_ = GetProgram(argc, &argv, FILE_NAME, src);
50         ASSERT_NE(program_.get(), nullptr);
51     }
52 
CheckRestParameterFlag(std::string_view functionName)53     void CheckRestParameterFlag(std::string_view functionName)
54     {
55         pandasm::Function *fn = GetFunction(functionName);
56         ASSERT_TRUE(fn != nullptr) << "Function '" << functionName << "' not found";
57         ASSERT_TRUE(HasRestParameterFlag(fn)) << "Function '" << fn->name << "' doesn't have ACC_VARARGS flag";
58     }
59 
CheckNoRestParameterFlag(std::string_view functionName)60     void CheckNoRestParameterFlag(std::string_view functionName)
61     {
62         pandasm::Function *fn = GetFunction(functionName);
63         ASSERT_TRUE(fn != nullptr) << "Function '" << functionName << "' not found";
64         ASSERT_FALSE(HasRestParameterFlag(fn)) << "Function '" << fn->name << "' has ACC_VARARGS flag";
65     }
66 
67 private:
HasRestParameterFlag(pandasm::Function *fn)68     bool HasRestParameterFlag(pandasm::Function *fn)
69     {
70         return (fn->metadata->GetAccessFlags() & ACC_VARARGS) != 0;
71     }
72 
73     NO_COPY_SEMANTIC(RestParameterTest);
74     NO_MOVE_SEMANTIC(RestParameterTest);
75 
GetProgram(int argc, const char **argv, std::string_view fileName, std::string_view src)76     static std::unique_ptr<pandasm::Program> GetProgram(int argc, const char **argv, std::string_view fileName,
77                                                         std::string_view src)
78     {
79         auto options = std::make_unique<es2panda::util::Options>();
80         if (!options->Parse(argc, argv)) {
81             std::cerr << options->ErrorMsg() << std::endl;
82             return nullptr;
83         }
84 
85         Logger::ComponentMask mask {};
86         mask.set(Logger::Component::ES2PANDA);
87         Logger::InitializeStdLogging(Logger::LevelFromString(options->LogLevel()), mask);
88 
89         es2panda::Compiler compiler(options->Extension(), options->ThreadCount());
90         es2panda::SourceFile input(fileName, src, options->ParseModule());
91 
92         return std::unique_ptr<pandasm::Program>(compiler.Compile(input, *options));
93     }
94 
GetFunction(std::string_view functionName)95     pandasm::Function *GetFunction(std::string_view functionName)
96     {
97         auto it = program_->functionTable.find(functionName.data());
98         if (it == program_->functionTable.end()) {
99             return nullptr;
100         }
101         return &it->second;
102     }
103 
104 private:
105     std::unique_ptr<pandasm::Program> program_ {};
106 };
107 
108 // === Function ===
TEST_F(RestParameterTest, function_without_rest_parameters_0)109 TEST_F(RestParameterTest, function_without_rest_parameters_0)
110 {
111     SetCurrentProgram(R"(
112         function fn(): void {
113         }
114     )");
115     CheckNoRestParameterFlag("ETSGLOBAL.fn:void;");
116 }
117 
TEST_F(RestParameterTest, function_without_rest_parameters_1)118 TEST_F(RestParameterTest, function_without_rest_parameters_1)
119 {
120     SetCurrentProgram(R"(
121         function fn(args: int[]): void {
122         }
123     )");
124     CheckNoRestParameterFlag("ETSGLOBAL.fn:i32[];void;");
125 }
126 
TEST_F(RestParameterTest, function_without_rest_parameters_2)127 TEST_F(RestParameterTest, function_without_rest_parameters_2)
128 {
129     SetCurrentProgram(R"(
130         function fn(arg0: int, args: String[]): void {
131         }
132     )");
133     CheckNoRestParameterFlag("ETSGLOBAL.fn:i32;std.core.String[];void;");
134 }
135 
TEST_F(RestParameterTest, function_with_rest_parameter_0)136 TEST_F(RestParameterTest, function_with_rest_parameter_0)
137 {
138     SetCurrentProgram(R"(
139         function fn(...args: String[]): void {
140         }
141     )");
142     CheckRestParameterFlag("ETSGLOBAL.fn:std.core.String[];void;");
143 }
144 
TEST_F(RestParameterTest, function_with_rest_parameter_1)145 TEST_F(RestParameterTest, function_with_rest_parameter_1)
146 {
147     SetCurrentProgram(R"(
148         function fn(o: Object, ...args: int[]): void {
149         }
150     )");
151     CheckRestParameterFlag("ETSGLOBAL.fn:std.core.Object;i32[];void;");
152 }
153 
154 // === Method of class ===
TEST_F(RestParameterTest, class_method_without_rest_parameters_0)155 TEST_F(RestParameterTest, class_method_without_rest_parameters_0)
156 {
157     SetCurrentProgram(R"(
158         class A {
159             fn() {};
160         }
161     )");
162     CheckNoRestParameterFlag("A.fn:void;");
163 }
164 
TEST_F(RestParameterTest, class_method_without_rest_parameters_1)165 TEST_F(RestParameterTest, class_method_without_rest_parameters_1)
166 {
167     SetCurrentProgram(R"(
168         class A {
169             fn(arg0: int) {};
170         }
171     )");
172     CheckNoRestParameterFlag("A.fn:i32;void;");
173 }
174 
TEST_F(RestParameterTest, class_method_with_rest_parameters_0)175 TEST_F(RestParameterTest, class_method_with_rest_parameters_0)
176 {
177     SetCurrentProgram(R"(
178         class A {
179             fn(...args: int[]) {};
180         }
181     )");
182     CheckRestParameterFlag("A.fn:i32[];void;");
183 }
184 
185 // === Static method of class ===
TEST_F(RestParameterTest, static_class_method_without_rest_parameters_0)186 TEST_F(RestParameterTest, static_class_method_without_rest_parameters_0)
187 {
188     SetCurrentProgram(R"(
189         class A {
190             static fn() {};
191         }
192     )");
193     CheckNoRestParameterFlag("A.fn:void;");
194 }
195 
TEST_F(RestParameterTest, static_class_method_without_rest_parameters_1)196 TEST_F(RestParameterTest, static_class_method_without_rest_parameters_1)
197 {
198     SetCurrentProgram(R"(
199         class A {
200             static fn(arg0: int) {};
201         }
202     )");
203     CheckNoRestParameterFlag("A.fn:i32;void;");
204 }
205 
TEST_F(RestParameterTest, static_class_method_with_rest_parameters_0)206 TEST_F(RestParameterTest, static_class_method_with_rest_parameters_0)
207 {
208     SetCurrentProgram(R"(
209         class A {
210             static fn(...args: int[]) {};
211         }
212     )");
213     CheckRestParameterFlag("A.fn:i32[];void;");
214 }
215 
TEST_F(RestParameterTest, static_class_method_with_rest_parameters_1)216 TEST_F(RestParameterTest, static_class_method_with_rest_parameters_1)
217 {
218     SetCurrentProgram(R"(
219         class A {
220             static fn(a: String[], ...args: int[]) {};
221         }
222     )");
223     CheckRestParameterFlag("A.fn:std.core.String[];i32[];void;");
224 }
225 
226 // === Constructor of class ===
TEST_F(RestParameterTest, class_constructor_without_rest_parameters_0)227 TEST_F(RestParameterTest, class_constructor_without_rest_parameters_0)
228 {
229     SetCurrentProgram(R"(
230         class A {
231             constructor() {};
232         }
233     )");
234     CheckNoRestParameterFlag("A.<ctor>:void;");
235 }
236 
TEST_F(RestParameterTest, class_constructor_without_rest_parameters_1)237 TEST_F(RestParameterTest, class_constructor_without_rest_parameters_1)
238 {
239     SetCurrentProgram(R"(
240         class A {
241             constructor(args: String[]) {};
242         }
243     )");
244     CheckNoRestParameterFlag("A.<ctor>:std.core.String[];void;");
245 }
246 
TEST_F(RestParameterTest, class_constructor_with_rest_parameters_0)247 TEST_F(RestParameterTest, class_constructor_with_rest_parameters_0)
248 {
249     SetCurrentProgram(R"(
250         class A {
251             constructor(...args: int[]) {};
252         }
253     )");
254     CheckRestParameterFlag("A.<ctor>:i32[];void;");
255 }
256 
TEST_F(RestParameterTest, class_constructor_with_rest_parameters_1)257 TEST_F(RestParameterTest, class_constructor_with_rest_parameters_1)
258 {
259     SetCurrentProgram(R"(
260         class A {
261             constructor(v0: long, ...args: String[]) {};
262         }
263     )");
264     CheckRestParameterFlag("A.<ctor>:i64;std.core.String[];void;");
265 }
266 
267 // === Method of interface ===
TEST_F(RestParameterTest, interface_without_rest_parameters_0)268 TEST_F(RestParameterTest, interface_without_rest_parameters_0)
269 {
270     SetCurrentProgram(R"(
271         interface A {
272             fn() {};
273         }
274     )");
275     CheckNoRestParameterFlag("A.fn:void;");
276 }
277 
TEST_F(RestParameterTest, interface_without_rest_parameters_1)278 TEST_F(RestParameterTest, interface_without_rest_parameters_1)
279 {
280     SetCurrentProgram(R"(
281         interface A {
282             fn(args: String[]) {};
283         }
284     )");
285     CheckNoRestParameterFlag("A.fn:std.core.String[];void;");
286 }
287 
TEST_F(RestParameterTest, interface_with_rest_parameters_0)288 TEST_F(RestParameterTest, interface_with_rest_parameters_0)
289 {
290     SetCurrentProgram(R"(
291         interface A {
292             fn(...args: Object[]) {};
293         }
294     )");
295     CheckRestParameterFlag("A.fn:std.core.Object[];void;");
296 }
297 
TEST_F(RestParameterTest, interface_with_rest_parameters_1)298 TEST_F(RestParameterTest, interface_with_rest_parameters_1)
299 {
300     SetCurrentProgram(R"(
301         interface A {
302             fn(o: Object, ...args: String[]) {};
303         }
304     )");
305     CheckRestParameterFlag("A.fn:std.core.Object;std.core.String[];void;");
306 }
307 
308 // === Lambda method ===
TEST_F(RestParameterTest, lambda_without_rest_parameters_0)309 TEST_F(RestParameterTest, lambda_without_rest_parameters_0)
310 {
311     SetCurrentProgram(R"(
312         let fn: ()=>int = (): int => {
313             return 1;
314         }
315     )");
316     CheckNoRestParameterFlag("LambdaObject-ETSGLOBAL$lambda$invoke$0.invoke:i32;");
317 }
318 
TEST_F(RestParameterTest, lambda_without_rest_parameters_1)319 TEST_F(RestParameterTest, lambda_without_rest_parameters_1)
320 {
321     SetCurrentProgram(R"(
322         let fn: (args: long[])=>int = (args: long[]): int => {
323             return 1;
324         }
325     )");
326     CheckNoRestParameterFlag("LambdaObject-ETSGLOBAL$lambda$invoke$0.invoke:i64[];i32;");
327 }
328 
329 // === Abstract method of abstract class ===
TEST_F(RestParameterTest, abstract_function_without_rest_parameter_0)330 TEST_F(RestParameterTest, abstract_function_without_rest_parameter_0)
331 {
332     SetCurrentProgram(R"(
333         abstract class A {
334             abstract fn(): void
335         }
336     )");
337     CheckNoRestParameterFlag("A.fn:void;");
338 }
339 
TEST_F(RestParameterTest, abstract_function_without_rest_parameter_1)340 TEST_F(RestParameterTest, abstract_function_without_rest_parameter_1)
341 {
342     SetCurrentProgram(R"(
343         abstract class A {
344             abstract fn(args: String[]): void
345         }
346     )");
347     CheckNoRestParameterFlag("A.fn:std.core.String[];void;");
348 }
349 
TEST_F(RestParameterTest, abstract_function_with_rest_parameter_0)350 TEST_F(RestParameterTest, abstract_function_with_rest_parameter_0)
351 {
352     SetCurrentProgram(R"(
353         abstract class A {
354             abstract fn(...args: String[]): void
355         }
356     )");
357     CheckRestParameterFlag("A.fn:std.core.String[];void;");
358 }
359 
TEST_F(RestParameterTest, abstract_function_with_rest_parameter_1)360 TEST_F(RestParameterTest, abstract_function_with_rest_parameter_1)
361 {
362     SetCurrentProgram(R"(
363         abstract class A {
364             abstract fn(v: int, ...args: String[]): void
365         }
366     )");
367     CheckRestParameterFlag("A.fn:i32;std.core.String[];void;");
368 }
369 
370 // === External methods ===
TEST_F(RestParameterTest, external_function_with_rest_parameter_0)371 TEST_F(RestParameterTest, external_function_with_rest_parameter_0)
372 {
373     SetCurrentProgram("");
374     CheckRestParameterFlag("std.core.LambdaValue.invoke:std.core.Object[];std.core.Object;");
375 }
376 
TEST_F(RestParameterTest, external_function_with_rest_parameter_1)377 TEST_F(RestParameterTest, external_function_with_rest_parameter_1)
378 {
379     SetCurrentProgram("");
380     CheckRestParameterFlag("escompat.Math.max:f64[];f64;");
381 }
382 
383 }  // namespace ark::es2panda::compiler::test
384