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
26namespace ark::es2panda::compiler::test {
27
28class RestParameterTest : public testing::Test {
29public:
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
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
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
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
67private:
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
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
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
104private:
105    std::unique_ptr<pandasm::Program> program_ {};
106};
107
108// === Function ===
109TEST_F(RestParameterTest, function_without_rest_parameters_0)
110{
111    SetCurrentProgram(R"(
112        function fn(): void {
113        }
114    )");
115    CheckNoRestParameterFlag("ETSGLOBAL.fn:void;");
116}
117
118TEST_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
127TEST_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
136TEST_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
145TEST_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 ===
155TEST_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
165TEST_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
175TEST_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 ===
186TEST_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
196TEST_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
206TEST_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
216TEST_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 ===
227TEST_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
237TEST_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
247TEST_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
257TEST_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 ===
268TEST_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
278TEST_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
288TEST_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
298TEST_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 ===
309TEST_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
319TEST_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 ===
330TEST_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
340TEST_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
350TEST_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
360TEST_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 ===
371TEST_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
377TEST_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