/* * Copyright (c) 2020-2023, Linus Groh * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include #include #include #include #include #include namespace JS { GC_DEFINE_ALLOCATOR(FunctionPrototype); FunctionPrototype::FunctionPrototype(Realm& realm) : FunctionObject(realm.intrinsics().object_prototype()) { } void FunctionPrototype::initialize(Realm& realm) { auto& vm = this->vm(); Base::initialize(realm); u8 attr = Attribute::Writable | Attribute::Configurable; define_native_function(realm, vm.names.apply, apply, 2, attr); define_native_function(realm, vm.names.bind, bind, 1, attr); define_native_function(realm, vm.names.call, call, 1, attr); define_native_function(realm, vm.names.toString, to_string, 0, attr); define_native_function(realm, vm.well_known_symbol_has_instance(), symbol_has_instance, 1, 0); define_direct_property(vm.names.length, Value(0), Attribute::Configurable); define_direct_property(vm.names.name, PrimitiveString::create(vm, String {}), Attribute::Configurable); } ThrowCompletionOr FunctionPrototype::internal_call(Value, ReadonlySpan) { // The Function prototype object: // - accepts any arguments and returns undefined when invoked. return js_undefined(); } // 20.2.3.1 Function.prototype.apply ( thisArg, argArray ), https://tc39.es/ecma262/#sec-function.prototype.apply JS_DEFINE_NATIVE_FUNCTION(FunctionPrototype::apply) { // 1. Let func be the this value. auto function_value = vm.this_value(); // 2. If IsCallable(func) is false, throw a TypeError exception. if (!function_value.is_function()) return vm.throw_completion(ErrorType::NotAFunction, function_value.to_string_without_side_effects()); auto& function = static_cast(function_value.as_object()); auto this_arg = vm.argument(0); auto arg_array = vm.argument(1); // 3. If argArray is undefined or null, then if (arg_array.is_nullish()) { // FIXME: a. Perform PrepareForTailCall(). // b. Return ? Call(func, thisArg). return TRY(JS::call(vm, function, this_arg)); } // 4. Let argList be ? CreateListFromArrayLike(argArray). auto arguments = TRY(create_list_from_array_like(vm, arg_array)); // FIXME: 5. Perform PrepareForTailCall(). // 6. Return ? Call(func, thisArg, argList). return TRY(JS::call(vm, function, this_arg, arguments.span())); } // 20.2.3.2 Function.prototype.bind ( thisArg, ...args ), https://tc39.es/ecma262/#sec-function.prototype.bind // 3.1.2.1 Function.prototype.bind ( thisArg, ...args ), https://tc39.es/proposal-shadowrealm/#sec-function.prototype.bind JS_DEFINE_NATIVE_FUNCTION(FunctionPrototype::bind) { auto& realm = *vm.current_realm(); auto this_argument = vm.argument(0); // 1. Let Target be the this value. auto target_value = vm.this_value(); // 2. If IsCallable(Target) is false, throw a TypeError exception. if (!target_value.is_function()) return vm.throw_completion(ErrorType::NotAFunction, target_value.to_string_without_side_effects()); auto& target = static_cast(target_value.as_object()); Vector arguments; if (vm.argument_count() > 1) { arguments.append(vm.running_execution_context().arguments.span().slice(1).data(), vm.argument_count() - 1); } // 3. Let F be ? BoundFunctionCreate(Target, thisArg, args). auto function = TRY(BoundFunction::create(realm, target, this_argument, move(arguments))); // 4. Let argCount be the number of elements in args. auto arg_count = vm.argument_count() > 0 ? vm.argument_count() - 1 : 0; // 5. Perform ? CopyNameAndLength(F, Target, "bound", argCount). TRY(copy_name_and_length(vm, *function, target, "bound"sv, arg_count)); // 6. Return F. return function; } // 20.2.3.3 Function.prototype.call ( thisArg, ...args ), https://tc39.es/ecma262/#sec-function.prototype.call JS_DEFINE_NATIVE_FUNCTION(FunctionPrototype::call) { // 1. Let func be the this value. auto function_value = vm.this_value(); // 2. If IsCallable(func) is false, throw a TypeError exception. if (!function_value.is_function()) return vm.throw_completion(ErrorType::NotAFunction, function_value.to_string_without_side_effects()); auto& function = static_cast(function_value.as_object()); // FIXME: 3. Perform PrepareForTailCall(). auto this_arg = vm.argument(0); auto args = vm.argument_count() > 1 ? vm.running_execution_context().arguments.span().slice(1) : ReadonlySpan {}; // 4. Return ? Call(func, thisArg, args). return TRY(JS::call(vm, function, this_arg, args)); } // 20.2.3.5 Function.prototype.toString ( ), https://tc39.es/ecma262/#sec-function.prototype.tostring JS_DEFINE_NATIVE_FUNCTION(FunctionPrototype::to_string) { // 1. Let func be the this value. auto function_value = vm.this_value(); // OPTIMIZATION: If func is not a function, bail out early. The order of this step is not observable. if (!function_value.is_function()) { // 5. Throw a TypeError exception. return vm.throw_completion(ErrorType::NotAnObjectOfType, "Function"); } auto& function = function_value.as_function(); // 2. If Type(func) is Object and func has a [[SourceText]] internal slot and func.[[SourceText]] is a sequence of Unicode code points and HostHasSourceTextAvailable(func) is true, then if (is(function)) { // a. Return CodePointsToString(func.[[SourceText]]). return PrimitiveString::create(vm, static_cast(function).source_text()); } // 3. If func is a built-in function object, return an implementation-defined String source code representation of func. The representation must have the syntax of a NativeFunction. Additionally, if func has an [[InitialName]] internal slot and func.[[InitialName]] is a String, the portion of the returned String that would be matched by NativeFunctionAccessor[opt] PropertyName must be the value of func.[[InitialName]]. if (is(function)) { // NOTE: once we remove name(), the fallback here can simply be an empty string. auto const& native_function = static_cast(function); auto const name = native_function.initial_name().value_or(native_function.name()); return PrimitiveString::create(vm, ByteString::formatted("function {}() {{ [native code] }}", name)); } // 4. If Type(func) is Object and IsCallable(func) is true, return an implementation-defined String source code representation of func. The representation must have the syntax of a NativeFunction. // NOTE: ProxyObject, BoundFunction, WrappedFunction return PrimitiveString::create(vm, "function () { [native code] }"_string); } // 20.2.3.6 Function.prototype [ @@hasInstance ] ( V ), https://tc39.es/ecma262/#sec-function.prototype-@@hasinstance JS_DEFINE_NATIVE_FUNCTION(FunctionPrototype::symbol_has_instance) { // 1. Let F be the this value. // 2. Return ? OrdinaryHasInstance(F, V). return TRY(ordinary_has_instance(vm, vm.argument(0), vm.this_value())); } }