* Copyright (c) 2024-2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <iomanip>
#include <ostream>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include "assembler/assembly-program.h"
#include "assembly-function.h"
#include "es2panda.h"
#include "test/utils/asm_test.h"
namespace ark::pandasm {
std::ostream &operator<<(std::ostream &s, const Function &arg)
{
return s << "Function{" << std::quoted(arg.name) << "}";
}
}
namespace ark::es2panda::compiler::test {
class RestParameterTest : public ::test::utils::AsmTest {
public:
RestParameterTest() = default;
~RestParameterTest() override = default;
void CheckRestParameterFlag(std::string_view functionName, bool isStatic)
{
pandasm::Function *fn =
GetFunction(functionName, isStatic ? program_->functionStaticTable : program_->functionInstanceTable);
ASSERT_TRUE(fn != nullptr) << "Function '" << functionName << "' not found";
ASSERT_TRUE(HasRestParameterFlag(fn)) << "Function '" << fn->name << "' doesn't have ACC_VARARGS flag";
}
void CheckNoRestParameterFlag(std::string_view functionName, bool isStatic)
{
pandasm::Function *fn =
GetFunction(functionName, isStatic ? program_->functionStaticTable : program_->functionInstanceTable);
ASSERT_TRUE(fn != nullptr) << "Function '" << functionName << "' not found";
ASSERT_FALSE(HasRestParameterFlag(fn)) << "Function '" << fn->name << "' has ACC_VARARGS flag";
}
private:
bool HasRestParameterFlag(pandasm::Function *fn)
{
return (fn->metadata->GetAccessFlags() & ACC_VARARGS) != 0;
}
NO_COPY_SEMANTIC(RestParameterTest);
NO_MOVE_SEMANTIC(RestParameterTest);
};
TEST_F(RestParameterTest, function_without_rest_parameters_0)
{
SetCurrentProgram(R"(
function fn(): void {
}
)");
CheckNoRestParameterFlag("dummy.ETSGLOBAL.fn:void;", true);
}
TEST_F(RestParameterTest, function_without_rest_parameters_1)
{
SetCurrentProgram(R"(
function fn(args: FixedArray<int>): void {
}
)");
CheckNoRestParameterFlag("dummy.ETSGLOBAL.fn:std.core.Int[];void;", true);
}
TEST_F(RestParameterTest, function_without_rest_parameters_2)
{
SetCurrentProgram(R"(
function fn(arg0: int, args: FixedArray<String>): void {
}
)");
CheckNoRestParameterFlag("dummy.ETSGLOBAL.fn:i32;std.core.String[];void;", true);
}
TEST_F(RestParameterTest, function_with_rest_parameter_0)
{
SetCurrentProgram(R"(
function fn(...args: FixedArray<String>): void {
}
)");
CheckRestParameterFlag("dummy.ETSGLOBAL.fn:std.core.String[];void;", true);
}
TEST_F(RestParameterTest, function_with_rest_parameter_1)
{
SetCurrentProgram(R"(
function fn(o: Object, ...args: FixedArray<int>): void {
}
)");
CheckRestParameterFlag("dummy.ETSGLOBAL.fn:std.core.Object;std.core.Int[];void;", true);
}
TEST_F(RestParameterTest, class_method_without_rest_parameters_0)
{
SetCurrentProgram(R"(
class A {
fn() {};
}
)");
CheckNoRestParameterFlag("dummy.A.fn:void;", false);
}
TEST_F(RestParameterTest, class_method_without_rest_parameters_1)
{
SetCurrentProgram(R"(
class A {
fn(arg0: int) {};
}
)");
CheckNoRestParameterFlag("dummy.A.fn:i32;void;", false);
}
TEST_F(RestParameterTest, class_method_with_rest_parameters_0)
{
SetCurrentProgram(R"(
class A {
fn(...args: FixedArray<int>) {};
}
)");
CheckRestParameterFlag("dummy.A.fn:std.core.Int[];void;", false);
}
TEST_F(RestParameterTest, static_class_method_without_rest_parameters_0)
{
SetCurrentProgram(R"(
class A {
static fn() {};
}
)");
CheckNoRestParameterFlag("dummy.A.fn:void;", true);
}
TEST_F(RestParameterTest, static_class_method_without_rest_parameters_1)
{
SetCurrentProgram(R"(
class A {
static fn(arg0: int) {};
}
)");
CheckNoRestParameterFlag("dummy.A.fn:i32;void;", true);
}
TEST_F(RestParameterTest, static_class_method_with_rest_parameters_0)
{
SetCurrentProgram(R"(
class A {
static fn(...args: FixedArray<int>) {};
}
)");
CheckRestParameterFlag("dummy.A.fn:std.core.Int[];void;", true);
}
TEST_F(RestParameterTest, static_class_method_with_rest_parameters_1)
{
SetCurrentProgram(R"(
class A {
static fn(a: FixedArray<String>, ...args: FixedArray<int>) {};
}
)");
CheckRestParameterFlag("dummy.A.fn:std.core.String[];std.core.Int[];void;", true);
}
TEST_F(RestParameterTest, class_constructor_without_rest_parameters_0)
{
SetCurrentProgram(R"(
class A {
constructor() {};
}
)");
CheckNoRestParameterFlag("dummy.A.<ctor>:void;", false);
}
TEST_F(RestParameterTest, class_constructor_without_rest_parameters_1)
{
SetCurrentProgram(R"(
class A {
constructor(args: FixedArray<String>) {};
}
)");
CheckNoRestParameterFlag("dummy.A.<ctor>:std.core.String[];void;", false);
}
TEST_F(RestParameterTest, class_constructor_with_rest_parameters_0)
{
SetCurrentProgram(R"(
class A {
constructor(...args: FixedArray<int>) {};
}
)");
CheckRestParameterFlag("dummy.A.<ctor>:std.core.Int[];void;", false);
}
TEST_F(RestParameterTest, class_constructor_with_rest_parameters_1)
{
SetCurrentProgram(R"(
class A {
constructor(v0: long, ...args: FixedArray<String>) {};
}
)");
CheckRestParameterFlag("dummy.A.<ctor>:i64;std.core.String[];void;", false);
}
TEST_F(RestParameterTest, interface_without_rest_parameters_0)
{
SetCurrentProgram(R"(
interface A {
fn() {};
}
)");
CheckNoRestParameterFlag("dummy.A.fn:void;", false);
}
TEST_F(RestParameterTest, interface_without_rest_parameters_1)
{
SetCurrentProgram(R"(
interface A {
fn(args: FixedArray<String>) {};
}
)");
CheckNoRestParameterFlag("dummy.A.fn:std.core.String[];void;", false);
}
TEST_F(RestParameterTest, interface_with_rest_parameters_0)
{
SetCurrentProgram(R"(
interface A {
fn(...args: FixedArray<Object>) {};
}
)");
CheckRestParameterFlag("dummy.A.fn:std.core.Object[];void;", false);
}
TEST_F(RestParameterTest, interface_with_rest_parameters_1)
{
SetCurrentProgram(R"(
interface A {
fn(o: Object, ...args: FixedArray<String>) {};
}
)");
CheckRestParameterFlag("dummy.A.fn:std.core.Object;std.core.String[];void;", false);
}
TEST_F(RestParameterTest, lambda_without_rest_parameters_0)
{
SetCurrentProgram(R"(
let fn: ()=>int = (): int => {
return 1;
}
)");
CheckNoRestParameterFlag("dummy.ETSGLOBAL.lambda_invoke-0:i32;", true);
}
TEST_F(RestParameterTest, lambda_without_rest_parameters_1)
{
SetCurrentProgram(R"(
let fn: (args: FixedArray<long>)=>int = (args: FixedArray<long>): int => {
return 1;
}
)");
CheckNoRestParameterFlag("dummy.ETSGLOBAL.lambda_invoke-0:std.core.Long[];i32;", true);
}
TEST_F(RestParameterTest, abstract_function_without_rest_parameter_0)
{
SetCurrentProgram(R"(
abstract class A {
abstract fn(): void
}
)");
CheckNoRestParameterFlag("dummy.A.fn:void;", false);
}
TEST_F(RestParameterTest, abstract_function_without_rest_parameter_1)
{
SetCurrentProgram(R"(
abstract class A {
abstract fn(args: FixedArray<String>): void
}
)");
CheckNoRestParameterFlag("dummy.A.fn:std.core.String[];void;", false);
}
TEST_F(RestParameterTest, abstract_function_with_rest_parameter_0)
{
SetCurrentProgram(R"(
abstract class A {
abstract fn(...args: FixedArray<String>): void
}
)");
CheckRestParameterFlag("dummy.A.fn:std.core.String[];void;", false);
}
TEST_F(RestParameterTest, abstract_function_with_rest_parameter_1)
{
SetCurrentProgram(R"(
abstract class A {
abstract fn(v: int, ...args: FixedArray<String>): void
}
)");
CheckRestParameterFlag("dummy.A.fn:i32;std.core.String[];void;", false);
}
TEST_F(RestParameterTest, external_function_with_rest_parameter_0)
{
SetCurrentProgram(R"(
let o: Object[] = [1]
)");
using ::testing::Contains, ::testing::Key, ::testing::Eq;
EXPECT_THAT(program_->functionInstanceTable,
Contains(Key(Eq("std.core.Object[].<ctor>:std.core.Object[];i32;void;"))));
}
TEST_F(RestParameterTest, external_function_with_rest_parameter_1)
{
SetCurrentProgram(R"(
let v = Math.max(0.0, 1.0, 2.0) // Ensure the func is actually called and reference processed by unboxLowering.
)");
CheckRestParameterFlag("std.core.Math.max:std.core.Array;f64;", true);
}
}