NativeFunction.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. /*
  2. * Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
  3. * Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
  4. *
  5. * SPDX-License-Identifier: BSD-2-Clause
  6. */
  7. #include <LibJS/Interpreter.h>
  8. #include <LibJS/Runtime/FunctionEnvironment.h>
  9. #include <LibJS/Runtime/GlobalObject.h>
  10. #include <LibJS/Runtime/NativeFunction.h>
  11. #include <LibJS/Runtime/Realm.h>
  12. #include <LibJS/Runtime/Value.h>
  13. namespace JS {
  14. // 10.3.3 CreateBuiltinFunction ( behaviour, length, name, additionalInternalSlotsList [ , realm [ , prototype [ , prefix ] ] ] ), https://tc39.es/ecma262/#sec-createbuiltinfunction
  15. // NOTE: This doesn't consider additionalInternalSlotsList, which is rarely used, and can either be implemented using only the `function` lambda, or needs a NativeFunction subclass.
  16. NonnullGCPtr<NativeFunction> NativeFunction::create(Realm& allocating_realm, SafeFunction<ThrowCompletionOr<Value>(VM&)> behaviour, i32 length, PropertyKey const& name, Optional<Realm*> realm, Optional<Object*> prototype, Optional<StringView> const& prefix)
  17. {
  18. auto& vm = allocating_realm.vm();
  19. // 1. If realm is not present, set realm to the current Realm Record.
  20. if (!realm.has_value())
  21. realm = vm.current_realm();
  22. // 2. If prototype is not present, set prototype to realm.[[Intrinsics]].[[%Function.prototype%]].
  23. if (!prototype.has_value())
  24. prototype = realm.value()->intrinsics().function_prototype();
  25. // 3. Let internalSlotsList be a List containing the names of all the internal slots that 10.3 requires for the built-in function object that is about to be created.
  26. // 4. Append to internalSlotsList the elements of additionalInternalSlotsList.
  27. // 5. Let func be a new built-in function object that, when called, performs the action described by behaviour using the provided arguments as the values of the corresponding parameters specified by behaviour. The new function object has internal slots whose names are the elements of internalSlotsList, and an [[InitialName]] internal slot.
  28. // 6. Set func.[[Prototype]] to prototype.
  29. // 7. Set func.[[Extensible]] to true.
  30. // 8. Set func.[[Realm]] to realm.
  31. // 9. Set func.[[InitialName]] to null.
  32. auto function = allocating_realm.heap().allocate<NativeFunction>(allocating_realm, move(behaviour), prototype.value(), *realm.value());
  33. // 10. Perform SetFunctionLength(func, length).
  34. function->set_function_length(length);
  35. // 11. If prefix is not present, then
  36. // a. Perform SetFunctionName(func, name).
  37. // 12. Else,
  38. // a. Perform SetFunctionName(func, name, prefix).
  39. function->set_function_name(name, prefix);
  40. // 13. Return func.
  41. return function;
  42. }
  43. NonnullGCPtr<NativeFunction> NativeFunction::create(Realm& realm, FlyString const& name, SafeFunction<ThrowCompletionOr<Value>(VM&)> function)
  44. {
  45. return realm.heap().allocate<NativeFunction>(realm, name, move(function), *realm.intrinsics().function_prototype());
  46. }
  47. NativeFunction::NativeFunction(SafeFunction<ThrowCompletionOr<Value>(VM&)> native_function, Object* prototype, Realm& realm)
  48. : FunctionObject(realm, prototype)
  49. , m_native_function(move(native_function))
  50. , m_realm(&realm)
  51. {
  52. }
  53. // FIXME: m_realm is supposed to be the realm argument of CreateBuiltinFunction, or the current
  54. // Realm Record. The former is not something that's commonly used or we support, the
  55. // latter is impossible as no ExecutionContext exists when most NativeFunctions are created...
  56. NativeFunction::NativeFunction(Object& prototype)
  57. : FunctionObject(prototype)
  58. , m_realm(&prototype.shape().realm())
  59. {
  60. }
  61. NativeFunction::NativeFunction(FlyString name, SafeFunction<ThrowCompletionOr<Value>(VM&)> native_function, Object& prototype)
  62. : FunctionObject(prototype)
  63. , m_name(move(name))
  64. , m_native_function(move(native_function))
  65. , m_realm(&prototype.shape().realm())
  66. {
  67. }
  68. NativeFunction::NativeFunction(FlyString name, Object& prototype)
  69. : FunctionObject(prototype)
  70. , m_name(move(name))
  71. , m_realm(&prototype.shape().realm())
  72. {
  73. }
  74. // NOTE: Do not attempt to DRY these, it's not worth it. The difference in return types (Value vs Object*),
  75. // called functions (call() vs construct(FunctionObject&)), and this value (passed vs uninitialized) make
  76. // these good candidates for a bit of code duplication :^)
  77. // 10.3.1 [[Call]] ( thisArgument, argumentsList ), https://tc39.es/ecma262/#sec-built-in-function-objects-call-thisargument-argumentslist
  78. ThrowCompletionOr<Value> NativeFunction::internal_call(Value this_argument, MarkedVector<Value> arguments_list)
  79. {
  80. auto& vm = this->vm();
  81. // 1. Let callerContext be the running execution context.
  82. auto& caller_context = vm.running_execution_context();
  83. // 2. If callerContext is not already suspended, suspend callerContext.
  84. // NOTE: We don't support this concept yet.
  85. // 3. Let calleeContext be a new execution context.
  86. ExecutionContext callee_context(heap());
  87. // 4. Set the Function of calleeContext to F.
  88. callee_context.function = this;
  89. callee_context.function_name = m_name;
  90. // 5. Let calleeRealm be F.[[Realm]].
  91. auto* callee_realm = m_realm;
  92. // NOTE: This non-standard fallback is needed until we can guarantee that literally
  93. // every function has a realm - especially in LibWeb that's sometimes not the case
  94. // when a function is created while no JS is running, as we currently need to rely on
  95. // that (:acid2:, I know - see set_event_handler_attribute() for an example).
  96. // If there's no 'current realm' either, we can't continue and crash.
  97. if (!callee_realm)
  98. callee_realm = vm.current_realm();
  99. VERIFY(callee_realm);
  100. // 6. Set the Realm of calleeContext to calleeRealm.
  101. callee_context.realm = callee_realm;
  102. // 7. Set the ScriptOrModule of calleeContext to null.
  103. // Note: This is already the default value.
  104. // 8. Perform any necessary implementation-defined initialization of calleeContext.
  105. callee_context.this_value = this_argument;
  106. callee_context.arguments.extend(move(arguments_list));
  107. callee_context.lexical_environment = caller_context.lexical_environment;
  108. callee_context.variable_environment = caller_context.variable_environment;
  109. // Note: Keeping the private environment is probably only needed because of async methods in classes
  110. // calling async_block_start which goes through a NativeFunction here.
  111. callee_context.private_environment = caller_context.private_environment;
  112. // NOTE: This is a LibJS specific hack for NativeFunction to inherit the strictness of its caller.
  113. callee_context.is_strict_mode = vm.in_strict_mode();
  114. if (auto* interpreter = vm.interpreter_if_exists())
  115. callee_context.current_node = interpreter->current_node();
  116. // </8.> --------------------------------------------------------------------------
  117. // 9. Push calleeContext onto the execution context stack; calleeContext is now the running execution context.
  118. TRY(vm.push_execution_context(callee_context, {}));
  119. // 10. Let result be the Completion Record that is the result of evaluating F in a manner that conforms to the specification of F. thisArgument is the this value, argumentsList provides the named parameters, and the NewTarget value is undefined.
  120. auto result = call();
  121. // 11. Remove calleeContext from the execution context stack and restore callerContext as the running execution context.
  122. vm.pop_execution_context();
  123. // 12. Return ? result.
  124. return result;
  125. }
  126. // 10.3.2 [[Construct]] ( argumentsList, newTarget ), https://tc39.es/ecma262/#sec-built-in-function-objects-construct-argumentslist-newtarget
  127. ThrowCompletionOr<NonnullGCPtr<Object>> NativeFunction::internal_construct(MarkedVector<Value> arguments_list, FunctionObject& new_target)
  128. {
  129. auto& vm = this->vm();
  130. // 1. Let callerContext be the running execution context.
  131. auto& caller_context = vm.running_execution_context();
  132. // 2. If callerContext is not already suspended, suspend callerContext.
  133. // NOTE: We don't support this concept yet.
  134. // 3. Let calleeContext be a new execution context.
  135. ExecutionContext callee_context(heap());
  136. // 4. Set the Function of calleeContext to F.
  137. callee_context.function = this;
  138. callee_context.function_name = m_name;
  139. // 5. Let calleeRealm be F.[[Realm]].
  140. auto* callee_realm = m_realm;
  141. // NOTE: This non-standard fallback is needed until we can guarantee that literally
  142. // every function has a realm - especially in LibWeb that's sometimes not the case
  143. // when a function is created while no JS is running, as we currently need to rely on
  144. // that (:acid2:, I know - see set_event_handler_attribute() for an example).
  145. // If there's no 'current realm' either, we can't continue and crash.
  146. if (!callee_realm)
  147. callee_realm = vm.current_realm();
  148. VERIFY(callee_realm);
  149. // 6. Set the Realm of calleeContext to calleeRealm.
  150. callee_context.realm = callee_realm;
  151. // 7. Set the ScriptOrModule of calleeContext to null.
  152. // Note: This is already the default value.
  153. // 8. Perform any necessary implementation-defined initialization of calleeContext.
  154. callee_context.arguments.extend(move(arguments_list));
  155. callee_context.lexical_environment = caller_context.lexical_environment;
  156. callee_context.variable_environment = caller_context.variable_environment;
  157. // NOTE: This is a LibJS specific hack for NativeFunction to inherit the strictness of its caller.
  158. callee_context.is_strict_mode = vm.in_strict_mode();
  159. if (auto* interpreter = vm.interpreter_if_exists())
  160. callee_context.current_node = interpreter->current_node();
  161. // </8.> --------------------------------------------------------------------------
  162. // 9. Push calleeContext onto the execution context stack; calleeContext is now the running execution context.
  163. TRY(vm.push_execution_context(callee_context, {}));
  164. // 10. Let result be the Completion Record that is the result of evaluating F in a manner that conforms to the specification of F. The this value is uninitialized, argumentsList provides the named parameters, and newTarget provides the NewTarget value.
  165. auto result = construct(new_target);
  166. // 11. Remove calleeContext from the execution context stack and restore callerContext as the running execution context.
  167. vm.pop_execution_context();
  168. // 12. Return ? result.
  169. return *TRY(result);
  170. }
  171. ThrowCompletionOr<Value> NativeFunction::call()
  172. {
  173. return m_native_function(vm());
  174. }
  175. ThrowCompletionOr<NonnullGCPtr<Object>> NativeFunction::construct(FunctionObject&)
  176. {
  177. // Needs to be overridden if [[Construct]] is needed.
  178. VERIFY_NOT_REACHED();
  179. }
  180. bool NativeFunction::is_strict_mode() const
  181. {
  182. return true;
  183. }
  184. }