123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179 |
- /*
- * Copyright (c) 2020-2023, Linus Groh <linusg@serenityos.org>
- *
- * SPDX-License-Identifier: BSD-2-Clause
- */
- #include <AK/Function.h>
- #include <AK/StringBuilder.h>
- #include <AK/TypeCasts.h>
- #include <LibGC/RootVector.h>
- #include <LibJS/Runtime/AbstractOperations.h>
- #include <LibJS/Runtime/BoundFunction.h>
- #include <LibJS/Runtime/ECMAScriptFunctionObject.h>
- #include <LibJS/Runtime/Error.h>
- #include <LibJS/Runtime/FunctionPrototype.h>
- #include <LibJS/Runtime/GlobalObject.h>
- #include <LibJS/Runtime/NativeFunction.h>
- #include <LibJS/Runtime/ShadowRealm.h>
- 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<Value> FunctionPrototype::internal_call(Value, ReadonlySpan<Value>)
- {
- // 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<TypeError>(ErrorType::NotAFunction, function_value.to_string_without_side_effects());
- auto& function = static_cast<FunctionObject&>(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<TypeError>(ErrorType::NotAFunction, target_value.to_string_without_side_effects());
- auto& target = static_cast<FunctionObject&>(target_value.as_object());
- Vector<Value> 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<TypeError>(ErrorType::NotAFunction, function_value.to_string_without_side_effects());
- auto& function = static_cast<FunctionObject&>(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<Value> {};
- // 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<TypeError>(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<ECMAScriptFunctionObject>(function)) {
- // a. Return CodePointsToString(func.[[SourceText]]).
- return PrimitiveString::create(vm, static_cast<ECMAScriptFunctionObject&>(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<NativeFunction>(function)) {
- // NOTE: once we remove name(), the fallback here can simply be an empty string.
- auto const& native_function = static_cast<NativeFunction&>(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()));
- }
- }
|