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