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
33 using namespace panda::ecmascript;
34 using namespace panda::ecmascript::builtins;
35 using BuiltinsBase = panda::ecmascript::base::BuiltinsBase;
36 using JSArray = panda::ecmascript::JSArray;
37
38 namespace panda::test {
39 class BuiltinsSharedFunctionTest : public BaseTestWithScope<false> {
40 };
41
42 // native function for test apply and call
TestFunctionApplyAndCall(EcmaRuntimeCallInfo * argv)43 JSTaggedValue 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
63 enum class AlgorithmType {
64 PROTOTYPE_APPLY,
65 PROTOTYPE_BIND,
66 PROTOTYPE_CALL,
67 };
68
FunctionAlgorithm(JSThread * thread,JSHandle<JSFunction> & thisArg,std::vector<JSTaggedValue> & args,uint32_t argLen,AlgorithmType type=AlgorithmType::PROTOTYPE_APPLY)69 static 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
HWTEST_F_L0(BuiltinsSharedFunctionTest,FunctionConstructor)99 HWTEST_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
HWTEST_F_L0(BuiltinsSharedFunctionTest,AsyncFunctionConstructor)107 HWTEST_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)
HWTEST_F_L0(BuiltinsSharedFunctionTest,FunctionPrototypeApply)115 HWTEST_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)
HWTEST_F_L0(BuiltinsSharedFunctionTest,FunctionPrototypeApply1)143 HWTEST_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)
HWTEST_F_L0(BuiltinsSharedFunctionTest,FunctionPrototypeBind)180 HWTEST_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")
HWTEST_F_L0(BuiltinsSharedFunctionTest,FunctionPrototypeBind1)202 HWTEST_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()
HWTEST_F_L0(BuiltinsSharedFunctionTest,FunctionPrototypeBind2)233 HWTEST_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)
HWTEST_F_L0(BuiltinsSharedFunctionTest,FunctionPrototypeCall)268 HWTEST_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)
HWTEST_F_L0(BuiltinsSharedFunctionTest,FunctionPrototypeCall1)294 HWTEST_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
HWTEST_F_L0(BuiltinsSharedFunctionTest,FunctionPrototypeHasInstance)323 HWTEST_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 */
HWTEST_F_L0(BuiltinsSharedFunctionTest,FunctionPrototypeToString)356 HWTEST_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