1/* 2 * Copyright (c) 2021 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_function.h" 17 18#include "ecmascript/interpreter/interpreter.h" 19#include "ecmascript/jspandafile/js_pandafile_manager.h" 20#include "ecmascript/js_arguments.h" 21#include "ecmascript/object_fast_operator-inl.h" 22 23namespace panda::ecmascript::builtins { 24// ecma 19.2.1 Function (p1, p2, ... , pn, body) 25JSTaggedValue BuiltinsFunction::FunctionConstructor(EcmaRuntimeCallInfo *argv) 26{ 27 // not support 28 JSThread *thread = argv->GetThread(); 29 BUILTINS_API_TRACE(thread, Function, Constructor); 30 [[maybe_unused]] EcmaHandleScope handleScope(thread); 31 THROW_TYPE_ERROR_AND_RETURN(thread, "Not support eval. Forbidden using new Function()/Function().", 32 JSTaggedValue::Exception()); 33} 34 35// ecma 19.2.3 The Function prototype object is itself a built-in function object. 36// When invoked, it accepts any arguments and returns undefined. 37JSTaggedValue BuiltinsFunction::FunctionPrototypeInvokeSelf([[maybe_unused]] EcmaRuntimeCallInfo *argv) 38{ 39 BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeInvokeSelf); 40 return JSTaggedValue::Undefined(); 41} 42namespace { 43static size_t MakeArgListWithHole(JSThread *thread, TaggedArray *argv, int length) 44{ 45 if (length <= 0) { 46 return 0; 47 } 48 uint32_t inputLength = static_cast<uint32_t>(length); 49 uint32_t arrayLength = argv->GetLength(); 50 if (inputLength > arrayLength) { 51 inputLength = arrayLength; 52 } 53 for (uint32_t index = 0; index < inputLength; ++index) { 54 JSTaggedValue value = argv->Get(thread, index); 55 if (value.IsHole()) { 56 argv->Set(thread, index, JSTaggedValue::Undefined()); 57 } 58 } 59 return static_cast<size_t>(inputLength); 60} 61 62static std::pair<TaggedArray*, size_t> BuildArgumentsListFast(JSThread *thread, 63 const JSHandle<JSTaggedValue> &arrayObj) 64{ 65 if (!arrayObj->HasStableElements(thread)) { 66 return std::make_pair(nullptr, 0); 67 } 68 if (arrayObj->IsStableJSArguments(thread)) { 69 JSHandle<JSArguments> argList = JSHandle<JSArguments>::Cast(arrayObj); 70 TaggedArray *elements = TaggedArray::Cast(argList->GetElements().GetTaggedObject()); 71 auto env = thread->GetEcmaVM()->GetGlobalEnv(); 72 if (argList->GetClass() != env->GetArgumentsClass().GetObject<JSHClass>()) { 73 return std::make_pair(nullptr, 0); 74 } 75 auto result = argList->GetPropertyInlinedProps(JSArguments::LENGTH_INLINE_PROPERTY_INDEX); 76 if (!result.IsInt()) { 77 return std::make_pair(nullptr, 0); 78 } 79 auto length = result.GetInt(); 80 size_t res = MakeArgListWithHole(thread, elements, length); 81 return std::make_pair(elements, res); 82 } else if (arrayObj->IsStableJSArray(thread)) { 83 JSHandle<JSArray> argList = JSHandle<JSArray>::Cast(arrayObj); 84 TaggedArray *elements = nullptr; 85 if (argList->GetElements().IsMutantTaggedArray()) { 86 JSHandle<JSObject> obj(arrayObj); 87 int elementsLength = static_cast<int>(ElementAccessor::GetElementsLength(obj)); 88 JSHandle<TaggedArray> newElements = thread->GetEcmaVM()->GetFactory()-> 89 NewTaggedArray(elementsLength, JSTaggedValue::Undefined()); 90 for (int i = 0; i < elementsLength; ++i) { 91 JSTaggedValue value = ElementAccessor::Get(obj, i); 92 newElements->Set(thread, i, value); 93 } 94 elements = *newElements; 95 } else { 96 elements = TaggedArray::Cast(argList->GetElements().GetTaggedObject()); 97 } 98 size_t length = argList->GetArrayLength(); 99 if (elements->GetLength() == 0 && length != 0) { 100 JSHandle<TaggedArray> array = 101 thread->GetEcmaVM()->GetFactory()->NewTaggedArray(length, JSTaggedValue::Undefined()); 102 return std::make_pair(*array, length); 103 } 104 size_t res = MakeArgListWithHole(thread, elements, length); 105 return std::make_pair(elements, res); 106 } else { 107 LOG_ECMA(FATAL) << "this branch is unreachable"; 108 UNREACHABLE(); 109 } 110} 111} // anonymous namespace 112 113// ecma 19.2.3.1 Function.prototype.apply (thisArg, argArray) 114JSTaggedValue BuiltinsFunction::FunctionPrototypeApply(EcmaRuntimeCallInfo *argv) 115{ 116 ASSERT(argv); 117 JSThread *thread = argv->GetThread(); 118 BUILTINS_API_TRACE(thread, Function, PrototypeApply); 119 [[maybe_unused]] EcmaHandleScope handleScope(thread); 120 121 JSHandle<JSTaggedValue> func = GetThis(argv); 122 JSHandle<JSTaggedValue> thisArg = GetCallArg(argv, 0); 123 JSHandle<JSTaggedValue> arrayObj = GetCallArg(argv, 1); 124 return FunctionPrototypeApplyInternal(thread, func, thisArg, arrayObj); 125} 126 127JSTaggedValue BuiltinsFunction::FunctionPrototypeApplyInternal(JSThread *thread, JSHandle<JSTaggedValue> func, 128 JSHandle<JSTaggedValue> thisArg, 129 JSHandle<JSTaggedValue> arrayObj) 130{ 131 [[maybe_unused]] EcmaHandleScope handleScope(thread); 132 // 1. If IsCallable(func) is false, throw a TypeError exception. 133 if (!func->IsCallable()) { 134 THROW_TYPE_ERROR_AND_RETURN(thread, "apply target is not callable", JSTaggedValue::Exception()); 135 } 136 137 // 2. If argArray is null or undefined, then 138 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined(); 139 if (arrayObj->IsUndefined()) { // null will also get undefined 140 // a. Return Call(func, thisArg). 141 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, thisArg, undefined, 0); 142 return JSFunction::Call(info); 143 } 144 // 3. Let argList be CreateListFromArrayLike(argArray). 145 std::pair<TaggedArray*, size_t> argumentsList = BuildArgumentsListFast(thread, arrayObj); 146 if (!argumentsList.first) { 147 JSHandle<JSTaggedValue> num = JSObject::CreateListFromArrayLike(thread, arrayObj); 148 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 149 JSHandle<TaggedArray> argList = JSHandle<TaggedArray>::Cast(num); 150 // 4. ReturnIfAbrupt(argList). 151 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 152 const uint32_t argsLength = argList->GetLength(); 153 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, thisArg, undefined, argsLength); 154 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 155 info->SetCallArg(argsLength, argList); 156 return JSFunction::Call(info); 157 } 158 // 6. Return Call(func, thisArg, argList). 159 const uint32_t argsLength = static_cast<uint32_t>(argumentsList.second); 160 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, thisArg, undefined, argsLength); 161 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 162 info->SetCallArg(argsLength, argumentsList.first); 163 return JSFunction::Call(info); 164} 165 166// ecma 19.2.3.2 Function.prototype.bind (thisArg , ...args) 167JSTaggedValue BuiltinsFunction::FunctionPrototypeBind(EcmaRuntimeCallInfo *argv) 168{ 169 ASSERT(argv); 170 BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeBind); 171 JSThread *thread = argv->GetThread(); 172 [[maybe_unused]] EcmaHandleScope handleScope(thread); 173 174 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 175 // 1. Let Target be the this value. 176 JSHandle<JSTaggedValue> target = GetThis(argv); 177 178 JSHandle<JSTaggedValue> thisArg = GetCallArg(argv, 0); 179 uint32_t argsLength = 0; 180 if (argv->GetArgsNumber() > 1) { 181 argsLength = argv->GetArgsNumber() - 1; 182 } 183 184 // 3. Let args be a new (possibly empty) List consisting of all of the argument 185 // values provided after thisArg in order. 186 JSHandle<TaggedArray> argsArray = factory->NewTaggedArray(argsLength); 187 for (uint32_t index = 0; index < argsLength; ++index) { 188 argsArray->Set(thread, index, GetCallArg(argv, index + 1)); 189 } 190 191 return FunctionPrototypeBindInternal(thread, target, thisArg, argsArray); 192} 193 194JSTaggedValue BuiltinsFunction::FunctionPrototypeBindInternal(JSThread *thread, JSHandle<JSTaggedValue> target, 195 JSHandle<JSTaggedValue> thisArg, 196 JSHandle<TaggedArray> argsArray) 197{ 198 [[maybe_unused]] EcmaHandleScope handleScope(thread); 199 200 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 201 // 2. If IsCallable(Target) is false, throw a TypeError exception. 202 if (!target->IsCallable()) { 203 THROW_TYPE_ERROR_AND_RETURN(thread, "bind target is not callable", JSTaggedValue::Exception()); 204 } 205 206 // 4. Let F be BoundFunctionCreate(Target, thisArg, args). 207 JSHandle<JSBoundFunction> boundFunction = factory->NewJSBoundFunction(target, thisArg, argsArray); 208 // 5. ReturnIfAbrupt(F) 209 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 210 211 auto globalConst = thread->GlobalConstants(); 212 JSTaggedValue functionLengthAccessor = globalConst->GetFunctionLengthAccessor(); 213 JSHandle<JSTaggedValue> lengthKey = globalConst->GetHandledLengthString(); 214 JSTaggedValue lengthProperty; 215 if (target->IsBoundFunction() || target->IsJSFunction()) { // fastpath 216 JSHandle<JSObject> obj(thread, target.GetTaggedValue()); 217 uint32_t numberOfInlinedProps = obj->GetJSHClass()->GetInlinedProperties(); 218 if (JSFunction::LENGTH_INLINE_PROPERTY_INDEX < numberOfInlinedProps) { 219 lengthProperty = obj->GetPropertyInlinedProps(JSFunction::LENGTH_INLINE_PROPERTY_INDEX); 220 } 221 } 222 if (lengthProperty.IsHole()) { 223 lengthProperty = ObjectFastOperator::FastGetPropertyByName( 224 thread, target.GetTaggedValue(), lengthKey.GetTaggedValue()); 225 } 226 227 if (!lengthProperty.IsAccessor() || functionLengthAccessor != lengthProperty) { 228 // 6. Let targetHasLength be HasOwnProperty(Target, "length"). 229 bool targetHasLength = JSTaggedValue::HasOwnProperty(thread, target, lengthKey); 230 // 7. ReturnIfAbrupt(targetHasLength). 231 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 232 233 double lengthValue = 0.0; 234 // 8. If targetHasLength is true, then 235 if (targetHasLength) { 236 // a. Let targetLen be Get(Target, "length"). 237 JSHandle<JSTaggedValue> targetLen = JSTaggedValue::GetProperty(thread, target, lengthKey).GetValue(); 238 // b. ReturnIfAbrupt(targetLen). 239 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 240 241 // c. If Type(targetLen) is not Number, let L be 0. 242 // d. Else, 243 // i. Let targetLen be ToInteger(targetLen). 244 // ii. Let L be the larger of 0 and the result of targetLen minus the number of elements of args. 245 if (targetLen->IsNumber()) { 246 // argv include thisArg 247 lengthValue = 248 std::max(0.0, JSTaggedValue::ToNumber(thread, targetLen).GetNumber() - 249 static_cast<double>(argsArray->GetLength())); 250 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 251 } 252 } 253 // 9. Else let L be 0. 254 255 // 10. Let status be DefinePropertyOrThrow(F, "length", PropertyDescriptor {[[Value]]: L, 256 // [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true}). 257 PropertyDescriptor desc(thread, JSHandle<JSTaggedValue>(thread, JSTaggedValue(lengthValue)), 258 false, false, true); 259 [[maybe_unused]] bool status = 260 JSTaggedValue::DefinePropertyOrThrow(thread, JSHandle<JSTaggedValue>(boundFunction), lengthKey, desc); 261 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 262 // 11. Assert: status is not an abrupt completion. 263 ASSERT_PRINT(status, "DefinePropertyOrThrow failed"); 264 } 265 266 JSTaggedValue functionNameAccessor = globalConst->GetFunctionNameAccessor(); 267 JSHandle<JSTaggedValue> nameKey = globalConst->GetHandledNameString(); 268 JSTaggedValue nameProperty; 269 if (target->IsBoundFunction() || target->IsJSFunction()) { // fastpath 270 JSHandle<JSObject> obj(thread, target.GetTaggedValue()); 271 uint32_t numberOfInlinedProps = obj->GetJSHClass()->GetInlinedProperties(); 272 if (JSFunction::NAME_INLINE_PROPERTY_INDEX < numberOfInlinedProps) { 273 nameProperty = obj->GetPropertyInlinedProps(JSFunction::NAME_INLINE_PROPERTY_INDEX); 274 } 275 } 276 if (nameProperty.IsHole()) { 277 nameProperty = ObjectFastOperator::FastGetPropertyByName( 278 thread, target.GetTaggedValue(), nameKey.GetTaggedValue()); 279 } 280 281 if (!nameProperty.IsAccessor() || functionNameAccessor != nameProperty) { 282 // 12. Let targetName be Get(Target, "name"). 283 JSHandle<JSTaggedValue> targetName = JSObject::GetProperty(thread, target, nameKey).GetValue(); 284 // 13. ReturnIfAbrupt(targetName). 285 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 286 287 JSHandle<JSTaggedValue> boundName = thread->GlobalConstants()->GetHandledBoundString(); 288 // 14. If Type(targetName) is not String, let targetName be the empty string. 289 // 15. Perform SetFunctionName(F, targetName, "bound"). 290 [[maybe_unused]] bool status; 291 if (!targetName->IsString()) { 292 JSHandle<JSTaggedValue> emptyString(factory->GetEmptyString()); 293 status = JSFunction::SetFunctionName(thread, JSHandle<JSFunctionBase>(boundFunction), 294 emptyString, boundName); 295 } else { 296 status = JSFunction::SetFunctionName(thread, JSHandle<JSFunctionBase>(boundFunction), 297 targetName, boundName); 298 } 299 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 300 // Assert: status is not an abrupt completion. 301 ASSERT_PRINT(status, "DefinePropertyOr failed"); 302 } 303 304 // 16. Return F. 305 return boundFunction.GetTaggedValue(); 306} 307 308// ecma 19.2.3.3 Function.prototype.call (thisArg , ...args) 309JSTaggedValue BuiltinsFunction::FunctionPrototypeCall(EcmaRuntimeCallInfo *argv) 310{ 311 ASSERT(argv); 312 BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeCall); 313 JSThread *thread = argv->GetThread(); 314 [[maybe_unused]] EcmaHandleScope handleScope(thread); 315 316 // 1. If IsCallable(func) is false, throw a TypeError exception. 317 if (!GetThis(argv)->IsCallable()) { 318 THROW_TYPE_ERROR_AND_RETURN(thread, "call target is not callable", JSTaggedValue::Exception()); 319 } 320 321 JSHandle<JSTaggedValue> func = GetThis(argv); 322 JSHandle<JSTaggedValue> thisArg = GetCallArg(argv, 0); 323 uint32_t argsLength = 0; 324 if (argv->GetArgsNumber() > 1) { 325 argsLength = argv->GetArgsNumber() - 1; 326 } 327 // 2. Let argList be an empty List. 328 // 3. If this method was called with more than one argument then in left to right order, 329 // starting with the second argument, append each argument as the last element of argList. 330 // 5. Return Call(func, thisArg, argList). 331 JSHandle<JSTaggedValue> undefined = thread->GlobalConstants()->GetHandledUndefined(); 332 EcmaRuntimeCallInfo *info = EcmaInterpreter::NewRuntimeCallInfo(thread, func, thisArg, undefined, argsLength); 333 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 334 info->SetCallArg(argsLength, 0, argv, 1); 335 return JSFunction::Call(info); 336} 337 338// ecma 19.2.3.5 Function.prototype.toString () 339JSTaggedValue BuiltinsFunction::FunctionPrototypeToString(EcmaRuntimeCallInfo *argv) 340{ 341 BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeToString); 342 JSThread *thread = argv->GetThread(); 343 [[maybe_unused]] EcmaHandleScope handleScope(thread); 344 JSHandle<JSTaggedValue> thisValue = GetThis(argv); 345 if (thisValue->IsJSObject() && thisValue->IsCallable()) { 346 JSHandle<Method> method; 347 if (thisValue->IsBoundFunction()) { 348 std::string str = "function () { [native code] }"; 349 return GetTaggedString(thread, str.c_str()); 350 } else { 351 JSHandle<JSFunction> func = JSHandle<JSFunction>::Cast(thisValue); 352 JSHandle<JSTaggedValue> methodHandle(thread, func->GetMethod()); 353 method = JSHandle<Method>::Cast(methodHandle); 354 } 355 if (method->IsNativeWithCallField()) { 356 JSHandle<JSTaggedValue> nameKey = thread->GlobalConstants()->GetHandledNameString(); 357 JSHandle<JSTaggedValue> name = JSObject::GetProperty(thread, thisValue, nameKey).GetValue(); 358 JSHandle<EcmaString> methodName = JSTaggedValue::ToString(thread, name); 359 RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread); 360 std::string nameStr = EcmaStringAccessor(methodName).ToStdString(); 361 std::string startStr = "function "; 362 std::string endStr = "() { [native code] }"; 363 startStr.append(nameStr).append(endStr); 364 return GetTaggedString(thread, startStr.c_str()); 365 } 366 DebugInfoExtractor *debugExtractor = 367 JSPandaFileManager::GetInstance()->GetJSPtExtractor(method->GetJSPandaFile()); 368 const std::string &sourceCode = debugExtractor->GetSourceCode(method->GetMethodId()); 369 if (!sourceCode.empty()) { 370 return GetTaggedString(thread, sourceCode.c_str()); 371 } else { 372 return GetTaggedString(thread, "Cannot get source code of funtion"); 373 } 374 } 375 376 THROW_TYPE_ERROR_AND_RETURN(thread, 377 "function.toString() target is incompatible object", JSTaggedValue::Exception()); 378} 379 380// ecma 19.2.3.6 Function.prototype[@@hasInstance] (V) 381JSTaggedValue BuiltinsFunction::FunctionPrototypeHasInstance(EcmaRuntimeCallInfo *argv) 382{ 383 BUILTINS_API_TRACE(argv->GetThread(), Function, PrototypeHasInstance); 384 [[maybe_unused]] EcmaHandleScope handleScope(argv->GetThread()); 385 // 1. Let F be the this value. 386 JSHandle<JSTaggedValue> thisValue = GetThis(argv); 387 // 2. Return OrdinaryHasInstance(F, V). 388 JSHandle<JSTaggedValue> arg = GetCallArg(argv, 0); 389 return JSFunction::OrdinaryHasInstance(argv->GetThread(), thisValue, arg) ? GetTaggedBoolean(true) 390 : GetTaggedBoolean(false); 391} 392} // namespace panda::ecmascript::builtins 393