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 "ecmascript/builtins/builtins_boolean.h"
17#include "ecmascript/builtins/builtins_function.h"
18#include "ecmascript/builtins/builtins_shared_async_function.h"
19#include "ecmascript/builtins/builtins_shared_function.h"
20
21#include "ecmascript/ecma_runtime_call_info.h"
22#include "ecmascript/ecma_string.h"
23#include "ecmascript/ecma_vm.h"
24#include "ecmascript/global_env.h"
25#include "ecmascript/js_array.h"
26#include "ecmascript/js_function.h"
27#include "ecmascript/js_object-inl.h"
28
29#include "ecmascript/object_factory.h"
30#include "ecmascript/tagged_array-inl.h"
31#include "ecmascript/tests/test_helper.h"
32
33using namespace panda::ecmascript;
34using namespace panda::ecmascript::builtins;
35using BuiltinsBase = panda::ecmascript::base::BuiltinsBase;
36using JSArray = panda::ecmascript::JSArray;
37
38namespace panda::test {
39class BuiltinsSharedFunctionTest : public BaseTestWithScope<false> {
40};
41
42// native function for test apply and call
43JSTaggedValue TestFunctionApplyAndCall(EcmaRuntimeCallInfo *argv)
44{
45    JSThread *thread = argv->GetThread();
46    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
47
48    int result = 0;
49    for (uint32_t index = 0; index < argv->GetArgsNumber(); ++index) {
50        result += BuiltinsBase::GetCallArg(argv, index)->GetInt();
51    }
52    JSHandle<JSTaggedValue> thisValue(BuiltinsBase::GetThis(argv));
53
54    JSTaggedValue testA = JSObject::GetProperty(thread, thisValue,
55        JSHandle<JSTaggedValue>(factory->NewFromASCII("test_builtins_function_a"))).GetValue().GetTaggedValue();
56    JSTaggedValue testB = JSObject::GetProperty(thread, thisValue,
57        JSHandle<JSTaggedValue>(factory->NewFromASCII("test_builtins_function_b"))).GetValue().GetTaggedValue();
58
59    result = result + testA.GetInt() + testB.GetInt();
60    return BuiltinsBase::GetTaggedInt(result);
61}
62
63enum class AlgorithmType {
64    PROTOTYPE_APPLY,
65    PROTOTYPE_BIND,
66    PROTOTYPE_CALL,
67};
68
69static JSTaggedValue FunctionAlgorithm(JSThread *thread, JSHandle<JSFunction> &thisArg,
70                                       std::vector<JSTaggedValue> &args, uint32_t argLen,
71                                       AlgorithmType type = AlgorithmType::PROTOTYPE_APPLY)
72{
73    auto ecmaRuntimeCallInfos = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), argLen);
74    ecmaRuntimeCallInfos->SetFunction(JSTaggedValue::Undefined());
75    ecmaRuntimeCallInfos->SetThis(thisArg.GetTaggedValue());
76    for (size_t i = 0; i < args.size(); i++) {
77        ecmaRuntimeCallInfos->SetCallArg(i, args[i]);
78    }
79    auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfos);
80    JSTaggedValue result;
81    switch (type) {
82        case AlgorithmType::PROTOTYPE_BIND:
83            result = BuiltinsFunction::FunctionPrototypeBind(ecmaRuntimeCallInfos);
84            break;
85        case AlgorithmType::PROTOTYPE_APPLY:
86            result = BuiltinsFunction::FunctionPrototypeApply(ecmaRuntimeCallInfos);
87            break;
88        case AlgorithmType::PROTOTYPE_CALL:
89            result = BuiltinsFunction::FunctionPrototypeCall(ecmaRuntimeCallInfos);
90            break;
91        default:
92            break;
93    }
94    TestHelper::TearDownFrame(thread, prev);
95    return result;
96}
97
98// func Constructor
99HWTEST_F_L0(BuiltinsSharedFunctionTest, FunctionConstructor)
100{
101    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
102    JSTaggedValue result = BuiltinsSharedFunction::SharedFunctionConstructor(ecmaRuntimeCallInfo);
103    ASSERT_EQ(result.GetRawData(), JSTaggedValue::Exception().GetRawData());
104}
105
106// async func Constructor
107HWTEST_F_L0(BuiltinsSharedFunctionTest, AsyncFunctionConstructor)
108{
109    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
110    JSTaggedValue result = BuiltinsSharedAsyncFunction::SharedAsyncFunctionConstructor(ecmaRuntimeCallInfo);
111    ASSERT_EQ(result.GetRawData(), JSTaggedValue::Exception().GetRawData());
112}
113
114// func.apply(thisArg)
115HWTEST_F_L0(BuiltinsSharedFunctionTest, FunctionPrototypeApply)
116{
117    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
118    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
119    // ecma 19.2.3.1: func
120    JSHandle<JSFunction> func = factory->NewSFunction(env, reinterpret_cast<void *>(TestFunctionApplyAndCall));
121
122    // ecma 19.2.3.1: thisArg
123    JSHandle<JSObject> thisArg(thread, env->GetGlobalObject());
124    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(thisArg),
125                          JSHandle<JSTaggedValue>(factory->NewFromASCII("test_builtins_function_a")),
126                          JSHandle<JSTaggedValue>(thread, JSTaggedValue(1)));
127    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(thisArg),
128                          JSHandle<JSTaggedValue>(factory->NewFromASCII("test_builtins_function_b")),
129                          JSHandle<JSTaggedValue>(thread, JSTaggedValue(2)));
130
131    std::vector<JSTaggedValue> args{thisArg.GetTaggedValue()};
132    auto result = FunctionAlgorithm(thread, func, args, 6, AlgorithmType::PROTOTYPE_APPLY);
133
134    ASSERT_EQ(result.GetRawData(), JSTaggedValue(3).GetRawData());
135
136    JSObject::DeleteProperty(thread, (thisArg),
137                             JSHandle<JSTaggedValue>(factory->NewFromASCII("test_builtins_function_a")));
138    JSObject::DeleteProperty(thread, (thisArg),
139                             JSHandle<JSTaggedValue>(factory->NewFromASCII("test_builtins_function_b")));
140}
141
142// func.apply(thisArg, argArray)
143HWTEST_F_L0(BuiltinsSharedFunctionTest, FunctionPrototypeApply1)
144{
145    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
146    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
147
148    // ecma 19.2.3.1: func
149    JSHandle<JSFunction> func = factory->NewSFunction(env, reinterpret_cast<void *>(TestFunctionApplyAndCall));
150
151    // ecma 19.2.3.1: thisArg
152    JSHandle<JSObject> thisArg(thread, env->GetGlobalObject());
153    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(thisArg),
154                          JSHandle<JSTaggedValue>(factory->NewFromASCII("test_builtins_function_a")),
155                          JSHandle<JSTaggedValue>(thread, JSTaggedValue(10)));
156    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(thisArg),
157                          JSHandle<JSTaggedValue>(factory->NewFromASCII("test_builtins_function_b")),
158                          JSHandle<JSTaggedValue>(thread, JSTaggedValue(20)));
159
160    // ecma 19.2.3.1: argArray
161    JSHandle<JSObject> array(JSArray::ArrayCreate(thread, JSTaggedNumber(2)));
162    PropertyDescriptor desc(thread, JSHandle<JSTaggedValue>(thread, JSTaggedValue(30)));
163    JSArray::DefineOwnProperty(thread, array, JSHandle<JSTaggedValue>(thread, JSTaggedValue(0)), desc);
164
165    PropertyDescriptor desc1(thread, JSHandle<JSTaggedValue>(thread, JSTaggedValue(40)));
166    JSArray::DefineOwnProperty(thread, array, JSHandle<JSTaggedValue>(thread, JSTaggedValue(1)), desc1);
167
168    std::vector<JSTaggedValue> args{thisArg.GetTaggedValue(), array.GetTaggedValue()};
169    auto result = FunctionAlgorithm(thread, func, args, 8, AlgorithmType::PROTOTYPE_APPLY);
170
171    ASSERT_EQ(result.GetRawData(), JSTaggedValue(100).GetRawData());
172
173    JSObject::DeleteProperty(thread, (thisArg),
174                             JSHandle<JSTaggedValue>(factory->NewFromASCII("test_builtins_function_a")));
175    JSObject::DeleteProperty(thread, (thisArg),
176                             JSHandle<JSTaggedValue>(factory->NewFromASCII("test_builtins_function_b")));
177}
178
179// target.bind(thisArg)
180HWTEST_F_L0(BuiltinsSharedFunctionTest, FunctionPrototypeBind)
181{
182    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
183    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
184
185    JSHandle<JSFunction> target = factory->NewSFunction(env);
186    JSHandle<JSObject> thisArg(thread, env->GetGlobalObject());
187    std::vector<JSTaggedValue> args{thisArg.GetTaggedValue()};
188    auto result = FunctionAlgorithm(thread, target, args, 6, AlgorithmType::PROTOTYPE_BIND);
189    ASSERT_TRUE(result.IsECMAObject());
190
191    JSHandle<JSBoundFunction> resultFunc(thread, reinterpret_cast<TaggedObject *>(result.GetRawData()));
192    // test BoundTarget
193    ASSERT_EQ(resultFunc->GetBoundTarget(), target.GetTaggedValue());
194    // test BoundThis
195    ASSERT_EQ(resultFunc->GetBoundThis(), thisArg.GetTaggedValue());
196    // test BoundArguments
197    JSHandle<TaggedArray> array(thread, resultFunc->GetBoundArguments());
198    ASSERT_EQ(array->GetLength(), 0U);
199}
200
201// target.bind(thisArg, 123, "helloworld")
202HWTEST_F_L0(BuiltinsSharedFunctionTest, FunctionPrototypeBind1)
203{
204    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
205    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
206
207    JSHandle<JSFunction> target = factory->NewSFunction(env);
208    JSHandle<JSObject> thisArg(thread, env->GetGlobalObject());
209    JSHandle<EcmaString> str = factory->NewFromASCII("helloworld");
210
211    std::vector<JSTaggedValue> args{thisArg.GetTaggedValue(), JSTaggedValue(static_cast<int32_t>(123)),
212        str.GetTaggedValue()};
213    auto result = FunctionAlgorithm(thread, target, args, 10, AlgorithmType::PROTOTYPE_BIND);
214    ASSERT_TRUE(result.IsECMAObject());
215
216    JSHandle<JSBoundFunction> resultFunc(thread, reinterpret_cast<TaggedObject *>(result.GetRawData()));
217    // test BoundTarget
218    ASSERT_EQ(resultFunc->GetBoundTarget(), target.GetTaggedValue());
219    // test BoundThis
220    ASSERT_EQ(resultFunc->GetBoundThis(), thisArg.GetTaggedValue());
221    // test BoundArguments
222    JSHandle<TaggedArray> array(thread, resultFunc->GetBoundArguments());
223    ASSERT_EQ(array->GetLength(), 2U);
224    JSTaggedValue elem = array->Get(0);
225    JSTaggedValue elem1 = array->Get(1);
226    ASSERT_EQ(elem.GetRawData(), JSTaggedValue(123).GetRawData());
227
228    ASSERT_EQ(elem1.GetRawData(), str.GetTaggedType());
229    ASSERT_TRUE(elem1.IsString());
230}
231
232// target.bind(thisArg, 123, "helloworld") set target_name = EmptyString()
233HWTEST_F_L0(BuiltinsSharedFunctionTest, FunctionPrototypeBind2)
234{
235    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
236    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
237
238    JSHandle<JSFunction> target = factory->NewJSFunction(env);
239    PropertyDescriptor nameDesc(thread, JSHandle<JSTaggedValue>(thread, JSTaggedValue(123)), false, false, true);
240    JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle<JSTaggedValue>(target),
241                                         thread->GlobalConstants()->GetHandledNameString(), nameDesc);
242    JSFunction::SetFunctionLength(thread, target, JSTaggedValue(5));
243
244    JSHandle<JSObject> thisArg(thread, env->GetGlobalObject());
245    JSHandle<EcmaString> str = factory->NewFromASCII("helloworld");
246    std::vector<JSTaggedValue> args{thisArg.GetTaggedValue(), JSTaggedValue(static_cast<int32_t>(123)),
247        str.GetTaggedValue()};
248    auto result = FunctionAlgorithm(thread, target, args, 10, AlgorithmType::PROTOTYPE_BIND);
249    ASSERT_TRUE(result.IsECMAObject());
250
251    JSHandle<JSBoundFunction> resultFunc(thread, reinterpret_cast<TaggedObject *>(result.GetRawData()));
252    // test BoundThis
253    ASSERT_EQ(resultFunc->GetBoundThis(), thisArg.GetTaggedValue());
254    // test BoundTarget
255    ASSERT_EQ(resultFunc->GetBoundTarget(), target.GetTaggedValue());
256    // test BoundArguments
257    JSHandle<TaggedArray> array(thread, resultFunc->GetBoundArguments());
258    ASSERT_EQ(array->GetLength(), 2U);
259    JSTaggedValue elem = array->Get(0);
260    JSTaggedValue elem1 = array->Get(1);
261    ASSERT_EQ(elem.GetRawData(), JSTaggedValue(123).GetRawData());
262
263    ASSERT_TRUE(elem1.IsString());
264    ASSERT_EQ(elem1.GetRawData(), str.GetTaggedType());
265}
266
267// func.call(thisArg)
268HWTEST_F_L0(BuiltinsSharedFunctionTest, FunctionPrototypeCall)
269{
270    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
271    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
272
273    // ecma 19.2.3.3: func
274    JSHandle<JSFunction> func = factory->NewSFunction(env, reinterpret_cast<void *>(TestFunctionApplyAndCall));
275    // ecma 19.2.3.3: thisArg
276    JSHandle<JSObject> thisArg(thread, env->GetGlobalObject());
277    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(thisArg),
278                          JSHandle<JSTaggedValue>(factory->NewFromASCII("test_builtins_function_a")),
279                          JSHandle<JSTaggedValue>(thread, JSTaggedValue(1)));
280    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(thisArg),
281                          JSHandle<JSTaggedValue>(factory->NewFromASCII("test_builtins_function_b")),
282                          JSHandle<JSTaggedValue>(thread, JSTaggedValue(2)));
283    std::vector<JSTaggedValue> args{thisArg.GetTaggedValue()};
284    auto result = FunctionAlgorithm(thread, func, args, 6, AlgorithmType::PROTOTYPE_CALL);
285    ASSERT_EQ(result.GetRawData(), JSTaggedValue(3).GetRawData());
286
287    JSObject::DeleteProperty(thread, (thisArg),
288                             JSHandle<JSTaggedValue>(factory->NewFromASCII("test_builtins_function_a")));
289    JSObject::DeleteProperty(thread, (thisArg),
290                             JSHandle<JSTaggedValue>(factory->NewFromASCII("test_builtins_function_b")));
291}
292
293// func.call(thisArg, 123, 456, 789)
294HWTEST_F_L0(BuiltinsSharedFunctionTest, FunctionPrototypeCall1)
295{
296    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
297    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
298
299    // ecma 19.2.3.3: func
300    JSHandle<JSFunction> func = factory->NewSFunction(env, reinterpret_cast<void *>(TestFunctionApplyAndCall));
301    // ecma 19.2.3.3: thisArg
302    JSHandle<JSObject> thisArg(thread, env->GetGlobalObject());
303    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(thisArg),
304                          JSHandle<JSTaggedValue>(factory->NewFromASCII("test_builtins_function_a")),
305                          JSHandle<JSTaggedValue>(thread, JSTaggedValue(1)));
306    JSObject::SetProperty(thread, JSHandle<JSTaggedValue>(thisArg),
307                          JSHandle<JSTaggedValue>(factory->NewFromASCII("test_builtins_function_b")),
308                          JSHandle<JSTaggedValue>(thread, JSTaggedValue(2)));
309
310    // func thisArg ...args
311    std::vector<JSTaggedValue> args{thisArg.GetTaggedValue(), JSTaggedValue(static_cast<int32_t>(123)),
312                                JSTaggedValue(static_cast<int32_t>(456)), JSTaggedValue(static_cast<int32_t>(789))};
313    auto result = FunctionAlgorithm(thread, func, args, 12, AlgorithmType::PROTOTYPE_CALL);
314
315    ASSERT_EQ(result.GetRawData(), JSTaggedValue(1371).GetRawData());
316
317    JSObject::DeleteProperty(thread, (thisArg),
318                             JSHandle<JSTaggedValue>(factory->NewFromASCII("test_builtins_function_a")));
319    JSObject::DeleteProperty(thread, (thisArg),
320                             JSHandle<JSTaggedValue>(factory->NewFromASCII("test_builtins_function_b")));
321}
322
323HWTEST_F_L0(BuiltinsSharedFunctionTest, FunctionPrototypeHasInstance)
324{
325    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
326    JSHandle<JSFunction> booleanCtor(env->GetBooleanFunction());
327
328    auto ecmaRuntimeCallInfo1 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue(*booleanCtor), 6);
329    ecmaRuntimeCallInfo1->SetFunction(booleanCtor.GetTaggedValue());
330    ecmaRuntimeCallInfo1->SetThis(JSTaggedValue::Undefined());
331    ecmaRuntimeCallInfo1->SetCallArg(0, JSTaggedValue(static_cast<int32_t>(123)));
332
333    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo1);
334    JSTaggedValue result = BuiltinsBoolean::BooleanConstructor(ecmaRuntimeCallInfo1);
335    TestHelper::TearDownFrame(thread, prev);
336
337    JSHandle<JSObject> booleanInstance(thread, result);
338
339    auto ecmaRuntimeCallInfo2 = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 6);
340    ecmaRuntimeCallInfo2->SetFunction(JSTaggedValue::Undefined());
341    ecmaRuntimeCallInfo2->SetThis(booleanCtor.GetTaggedValue());
342    ecmaRuntimeCallInfo2->SetCallArg(0, booleanInstance.GetTaggedValue());
343
344    prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo2);
345    EXPECT_TRUE(BuiltinsFunction::FunctionPrototypeHasInstance(ecmaRuntimeCallInfo2).GetRawData());
346    TestHelper::TearDownFrame(thread, prev);
347}
348
349/**
350 * @tc.name: FunctionPrototypeToString
351 * @tc.desc: Create msgs through "CreateEcmaRuntimeCallInfo" function, Set ArgsNumber and CallArg, then call
352 *           the "FunctionPrototypeToString" function to get the result of Function.prototype.call.toString().
353 * @tc.type: FUNC
354 * @tc.require: issueI5INW1
355 */
356HWTEST_F_L0(BuiltinsSharedFunctionTest, FunctionPrototypeToString)
357{
358    JSHandle<GlobalEnv> env = thread->GetEcmaVM()->GetGlobalEnv();
359    ObjectFactory *factory = thread->GetEcmaVM()->GetFactory();
360    JSHandle<JSFunction> func = factory->NewSFunction(
361                                env, reinterpret_cast<void *>(BuiltinsFunction::FunctionPrototypeCall));
362
363    auto ecmaRuntimeCallInfo = TestHelper::CreateEcmaRuntimeCallInfo(thread, JSTaggedValue::Undefined(), 4);
364    ecmaRuntimeCallInfo->SetFunction(JSTaggedValue::Undefined());
365    ecmaRuntimeCallInfo->SetThis(func.GetTaggedValue());
366
367    [[maybe_unused]] auto prev = TestHelper::SetupFrame(thread, ecmaRuntimeCallInfo);
368    JSTaggedValue result = BuiltinsFunction::FunctionPrototypeToString(ecmaRuntimeCallInfo);
369    ASSERT_TRUE(result.IsString());
370    JSHandle<EcmaString> resultHandle(thread, reinterpret_cast<EcmaString *>(result.GetRawData()));
371    JSHandle<EcmaString> test = factory->NewFromASCII("function undefined() { [native code] }");
372    ASSERT_EQ(EcmaStringAccessor::Compare(instance, resultHandle, test), 0);
373}
374}  // namespace panda::test
375