FunctionPrototype.cpp 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179
  1. /*
  2. * Copyright (c) 2020-2023, Linus Groh <linusg@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #include <AK/Function.h>
  7. #include <AK/StringBuilder.h>
  8. #include <AK/TypeCasts.h>
  9. #include <LibGC/RootVector.h>
  10. #include <LibJS/Runtime/AbstractOperations.h>
  11. #include <LibJS/Runtime/BoundFunction.h>
  12. #include <LibJS/Runtime/ECMAScriptFunctionObject.h>
  13. #include <LibJS/Runtime/Error.h>
  14. #include <LibJS/Runtime/FunctionPrototype.h>
  15. #include <LibJS/Runtime/GlobalObject.h>
  16. #include <LibJS/Runtime/NativeFunction.h>
  17. #include <LibJS/Runtime/ShadowRealm.h>
  18. namespace JS {
  19. GC_DEFINE_ALLOCATOR(FunctionPrototype);
  20. FunctionPrototype::FunctionPrototype(Realm& realm)
  21. : FunctionObject(realm.intrinsics().object_prototype())
  22. {
  23. }
  24. void FunctionPrototype::initialize(Realm& realm)
  25. {
  26. auto& vm = this->vm();
  27. Base::initialize(realm);
  28. u8 attr = Attribute::Writable | Attribute::Configurable;
  29. define_native_function(realm, vm.names.apply, apply, 2, attr);
  30. define_native_function(realm, vm.names.bind, bind, 1, attr);
  31. define_native_function(realm, vm.names.call, call, 1, attr);
  32. define_native_function(realm, vm.names.toString, to_string, 0, attr);
  33. define_native_function(realm, vm.well_known_symbol_has_instance(), symbol_has_instance, 1, 0);
  34. define_direct_property(vm.names.length, Value(0), Attribute::Configurable);
  35. define_direct_property(vm.names.name, PrimitiveString::create(vm, String {}), Attribute::Configurable);
  36. }
  37. ThrowCompletionOr<Value> FunctionPrototype::internal_call(Value, ReadonlySpan<Value>)
  38. {
  39. // The Function prototype object:
  40. // - accepts any arguments and returns undefined when invoked.
  41. return js_undefined();
  42. }
  43. // 20.2.3.1 Function.prototype.apply ( thisArg, argArray ), https://tc39.es/ecma262/#sec-function.prototype.apply
  44. JS_DEFINE_NATIVE_FUNCTION(FunctionPrototype::apply)
  45. {
  46. // 1. Let func be the this value.
  47. auto function_value = vm.this_value();
  48. // 2. If IsCallable(func) is false, throw a TypeError exception.
  49. if (!function_value.is_function())
  50. return vm.throw_completion<TypeError>(ErrorType::NotAFunction, function_value.to_string_without_side_effects());
  51. auto& function = static_cast<FunctionObject&>(function_value.as_object());
  52. auto this_arg = vm.argument(0);
  53. auto arg_array = vm.argument(1);
  54. // 3. If argArray is undefined or null, then
  55. if (arg_array.is_nullish()) {
  56. // FIXME: a. Perform PrepareForTailCall().
  57. // b. Return ? Call(func, thisArg).
  58. return TRY(JS::call(vm, function, this_arg));
  59. }
  60. // 4. Let argList be ? CreateListFromArrayLike(argArray).
  61. auto arguments = TRY(create_list_from_array_like(vm, arg_array));
  62. // FIXME: 5. Perform PrepareForTailCall().
  63. // 6. Return ? Call(func, thisArg, argList).
  64. return TRY(JS::call(vm, function, this_arg, arguments.span()));
  65. }
  66. // 20.2.3.2 Function.prototype.bind ( thisArg, ...args ), https://tc39.es/ecma262/#sec-function.prototype.bind
  67. // 3.1.2.1 Function.prototype.bind ( thisArg, ...args ), https://tc39.es/proposal-shadowrealm/#sec-function.prototype.bind
  68. JS_DEFINE_NATIVE_FUNCTION(FunctionPrototype::bind)
  69. {
  70. auto& realm = *vm.current_realm();
  71. auto this_argument = vm.argument(0);
  72. // 1. Let Target be the this value.
  73. auto target_value = vm.this_value();
  74. // 2. If IsCallable(Target) is false, throw a TypeError exception.
  75. if (!target_value.is_function())
  76. return vm.throw_completion<TypeError>(ErrorType::NotAFunction, target_value.to_string_without_side_effects());
  77. auto& target = static_cast<FunctionObject&>(target_value.as_object());
  78. Vector<Value> arguments;
  79. if (vm.argument_count() > 1) {
  80. arguments.append(vm.running_execution_context().arguments.span().slice(1).data(), vm.argument_count() - 1);
  81. }
  82. // 3. Let F be ? BoundFunctionCreate(Target, thisArg, args).
  83. auto function = TRY(BoundFunction::create(realm, target, this_argument, move(arguments)));
  84. // 4. Let argCount be the number of elements in args.
  85. auto arg_count = vm.argument_count() > 0 ? vm.argument_count() - 1 : 0;
  86. // 5. Perform ? CopyNameAndLength(F, Target, "bound", argCount).
  87. TRY(copy_name_and_length(vm, *function, target, "bound"sv, arg_count));
  88. // 6. Return F.
  89. return function;
  90. }
  91. // 20.2.3.3 Function.prototype.call ( thisArg, ...args ), https://tc39.es/ecma262/#sec-function.prototype.call
  92. JS_DEFINE_NATIVE_FUNCTION(FunctionPrototype::call)
  93. {
  94. // 1. Let func be the this value.
  95. auto function_value = vm.this_value();
  96. // 2. If IsCallable(func) is false, throw a TypeError exception.
  97. if (!function_value.is_function())
  98. return vm.throw_completion<TypeError>(ErrorType::NotAFunction, function_value.to_string_without_side_effects());
  99. auto& function = static_cast<FunctionObject&>(function_value.as_object());
  100. // FIXME: 3. Perform PrepareForTailCall().
  101. auto this_arg = vm.argument(0);
  102. auto args = vm.argument_count() > 1 ? vm.running_execution_context().arguments.span().slice(1) : ReadonlySpan<Value> {};
  103. // 4. Return ? Call(func, thisArg, args).
  104. return TRY(JS::call(vm, function, this_arg, args));
  105. }
  106. // 20.2.3.5 Function.prototype.toString ( ), https://tc39.es/ecma262/#sec-function.prototype.tostring
  107. JS_DEFINE_NATIVE_FUNCTION(FunctionPrototype::to_string)
  108. {
  109. // 1. Let func be the this value.
  110. auto function_value = vm.this_value();
  111. // OPTIMIZATION: If func is not a function, bail out early. The order of this step is not observable.
  112. if (!function_value.is_function()) {
  113. // 5. Throw a TypeError exception.
  114. return vm.throw_completion<TypeError>(ErrorType::NotAnObjectOfType, "Function");
  115. }
  116. auto& function = function_value.as_function();
  117. // 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
  118. if (is<ECMAScriptFunctionObject>(function)) {
  119. // a. Return CodePointsToString(func.[[SourceText]]).
  120. return PrimitiveString::create(vm, static_cast<ECMAScriptFunctionObject&>(function).source_text());
  121. }
  122. // 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]].
  123. if (is<NativeFunction>(function)) {
  124. // NOTE: once we remove name(), the fallback here can simply be an empty string.
  125. auto const& native_function = static_cast<NativeFunction&>(function);
  126. auto const name = native_function.initial_name().value_or(native_function.name());
  127. return PrimitiveString::create(vm, ByteString::formatted("function {}() {{ [native code] }}", name));
  128. }
  129. // 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.
  130. // NOTE: ProxyObject, BoundFunction, WrappedFunction
  131. return PrimitiveString::create(vm, "function () { [native code] }"_string);
  132. }
  133. // 20.2.3.6 Function.prototype [ @@hasInstance ] ( V ), https://tc39.es/ecma262/#sec-function.prototype-@@hasinstance
  134. JS_DEFINE_NATIVE_FUNCTION(FunctionPrototype::symbol_has_instance)
  135. {
  136. // 1. Let F be the this value.
  137. // 2. Return ? OrdinaryHasInstance(F, V).
  138. return TRY(ordinary_has_instance(vm, vm.argument(0), vm.this_value()));
  139. }
  140. }