2021-10-13 20:08:48 +00:00
/*
2022-01-24 19:14:04 +00:00
* Copyright ( c ) 2021 - 2022 , Linus Groh < linusg @ serenityos . org >
2021-10-13 20:08:48 +00:00
*
* SPDX - License - Identifier : BSD - 2 - Clause
*/
# include <LibJS/Runtime/AbstractOperations.h>
# include <LibJS/Runtime/ShadowRealm.h>
# include <LibJS/Runtime/WrappedFunction.h>
namespace JS {
2024-11-14 15:01:23 +00:00
GC_DEFINE_ALLOCATOR ( WrappedFunction ) ;
2023-11-19 08:45:05 +00:00
2022-01-24 19:14:04 +00:00
// 3.1.1 WrappedFunctionCreate ( callerRealm: a Realm Record, Target: a function object, ), https://tc39.es/proposal-shadowrealm/#sec-wrappedfunctioncreate
2024-11-14 15:01:23 +00:00
ThrowCompletionOr < GC : : Ref < WrappedFunction > > WrappedFunction : : create ( Realm & realm , Realm & caller_realm , FunctionObject & target )
2021-10-13 20:08:48 +00:00
{
2022-08-15 23:20:49 +00:00
auto & vm = realm . vm ( ) ;
2022-01-24 19:14:04 +00:00
// 1. Let internalSlotsList be the internal slots listed in Table 2, plus [[Prototype]] and [[Extensible]].
2022-05-02 18:54:39 +00:00
// 2. Let wrapped be MakeBasicObject(internalSlotsList).
2022-01-24 19:14:04 +00:00
// 3. Set wrapped.[[Prototype]] to callerRealm.[[Intrinsics]].[[%Function.prototype%]].
// 4. Set wrapped.[[Call]] as described in 2.1.
// 5. Set wrapped.[[WrappedTargetFunction]] to Target.
// 6. Set wrapped.[[Realm]] to callerRealm.
2022-08-26 23:54:55 +00:00
auto & prototype = * caller_realm . intrinsics ( ) . function_prototype ( ) ;
2024-11-13 16:50:17 +00:00
auto wrapped = realm . create < WrappedFunction > ( caller_realm , target , prototype ) ;
2022-01-24 19:14:04 +00:00
2022-02-12 16:06:37 +00:00
// 7. Let result be CopyNameAndLength(wrapped, Target).
2022-08-21 16:58:23 +00:00
auto result = copy_name_and_length ( vm , * wrapped , target ) ;
2021-10-13 20:08:48 +00:00
2022-01-24 19:14:04 +00:00
// 8. If result is an Abrupt Completion, throw a TypeError exception.
if ( result . is_throw_completion ( ) )
2022-08-16 19:33:17 +00:00
return vm . throw_completion < TypeError > ( ErrorType : : WrappedFunctionCopyNameAndLengthThrowCompletion ) ;
2021-10-13 20:08:48 +00:00
2022-01-24 19:14:04 +00:00
// 9. Return wrapped.
2022-12-14 17:40:33 +00:00
return wrapped ;
2021-10-13 20:08:48 +00:00
}
// 2 Wrapped Function Exotic Objects, https://tc39.es/proposal-shadowrealm/#sec-wrapped-function-exotic-objects
WrappedFunction : : WrappedFunction ( Realm & realm , FunctionObject & wrapped_target_function , Object & prototype )
: FunctionObject ( prototype )
, m_wrapped_target_function ( wrapped_target_function )
, m_realm ( realm )
{
}
2022-07-31 10:13:33 +00:00
void WrappedFunction : : visit_edges ( Visitor & visitor )
{
Base : : visit_edges ( visitor ) ;
2023-02-26 23:09:02 +00:00
visitor . visit ( m_wrapped_target_function ) ;
visitor . visit ( m_realm ) ;
2022-07-31 10:13:33 +00:00
}
2021-10-13 20:08:48 +00:00
// 2.1 [[Call]] ( thisArgument, argumentsList ), https://tc39.es/proposal-shadowrealm/#sec-wrapped-function-exotic-objects-call-thisargument-argumentslist
2023-11-27 11:56:20 +00:00
ThrowCompletionOr < Value > WrappedFunction : : internal_call ( Value this_argument , ReadonlySpan < Value > arguments_list )
2021-10-13 20:08:48 +00:00
{
auto & vm = this - > vm ( ) ;
2022-07-31 10:13:33 +00:00
// 1. Let callerContext be the running execution context.
// NOTE: No-op, kept by the VM in its execution context stack.
// 2. Let calleeContext be PrepareForWrappedFunctionCall(F).
2024-05-31 13:12:33 +00:00
auto callee_context = ExecutionContext : : create ( ) ;
2023-11-27 15:45:45 +00:00
prepare_for_wrapped_function_call ( * this , * callee_context ) ;
2022-07-31 10:13:33 +00:00
// 3. Assert: calleeContext is now the running execution context.
2023-11-27 15:45:45 +00:00
VERIFY ( & vm . running_execution_context ( ) = = callee_context ) ;
2022-07-31 10:13:33 +00:00
2024-11-02 05:51:53 +00:00
// 4. Let result be Completion(OrdinaryWrappedFunctionCall(F, thisArgument, argumentsList)).
2022-07-31 10:13:33 +00:00
auto result = ordinary_wrapped_function_call ( * this , this_argument , arguments_list ) ;
// 5. Remove calleeContext from the execution context stack and restore callerContext as the running execution context.
vm . pop_execution_context ( ) ;
2024-11-02 05:51:53 +00:00
// 6. Return ? result.
2022-07-31 10:13:33 +00:00
return result ;
}
// 2.2 OrdinaryWrappedFunctionCall ( F: a wrapped function exotic object, thisArgument: an ECMAScript language value, argumentsList: a List of ECMAScript language values, ), https://tc39.es/proposal-shadowrealm/#sec-ordinary-wrapped-function-call
2023-11-27 11:56:20 +00:00
ThrowCompletionOr < Value > ordinary_wrapped_function_call ( WrappedFunction const & function , Value this_argument , ReadonlySpan < Value > arguments_list )
2022-07-31 10:13:33 +00:00
{
auto & vm = function . vm ( ) ;
2021-10-13 20:08:48 +00:00
// 1. Let target be F.[[WrappedTargetFunction]].
2022-07-31 10:13:33 +00:00
auto const & target = function . wrapped_target_function ( ) ;
2021-10-13 20:08:48 +00:00
// 2. Assert: IsCallable(target) is true.
VERIFY ( Value ( & target ) . is_function ( ) ) ;
2022-07-31 10:13:33 +00:00
// 3. Let callerRealm be F.[[Realm]].
auto * caller_realm = function . realm ( ) ;
2021-10-13 20:08:48 +00:00
2022-07-31 10:13:33 +00:00
// 4. NOTE: Any exception objects produced after this point are associated with callerRealm.
2022-08-21 18:24:32 +00:00
VERIFY ( vm . current_realm ( ) = = caller_realm ) ;
2021-10-13 20:08:48 +00:00
2022-07-31 10:13:33 +00:00
// 5. Let targetRealm be ? GetFunctionRealm(target).
2022-08-21 18:24:32 +00:00
auto * target_realm = TRY ( get_function_realm ( vm , target ) ) ;
2021-10-13 20:08:48 +00:00
// 6. Let wrappedArgs be a new empty List.
2024-11-14 15:01:23 +00:00
auto wrapped_args = GC : : MarkedVector < Value > { vm . heap ( ) } ;
2021-10-13 20:08:48 +00:00
wrapped_args . ensure_capacity ( arguments_list . size ( ) ) ;
// 7. For each element arg of argumentsList, do
2022-07-31 10:13:33 +00:00
for ( auto const & arg : arguments_list ) {
2021-10-13 20:08:48 +00:00
// a. Let wrappedValue be ? GetWrappedValue(targetRealm, arg).
2022-08-21 16:58:23 +00:00
auto wrapped_value = TRY ( get_wrapped_value ( vm , * target_realm , arg ) ) ;
2021-10-13 20:08:48 +00:00
// b. Append wrappedValue to wrappedArgs.
wrapped_args . append ( wrapped_value ) ;
}
// 8. Let wrappedThisArgument to ? GetWrappedValue(targetRealm, thisArgument).
2022-08-21 16:58:23 +00:00
auto wrapped_this_argument = TRY ( get_wrapped_value ( vm , * target_realm , this_argument ) ) ;
2021-10-13 20:08:48 +00:00
// 9. Let result be the Completion Record of Call(target, wrappedThisArgument, wrappedArgs).
2023-11-27 11:56:20 +00:00
auto result = call ( vm , & target , wrapped_this_argument , wrapped_args . span ( ) ) ;
2021-10-13 20:08:48 +00:00
// 10. If result.[[Type]] is normal or result.[[Type]] is return, then
if ( ! result . is_throw_completion ( ) ) {
// a. Return ? GetWrappedValue(callerRealm, result.[[Value]]).
2022-08-21 16:58:23 +00:00
return get_wrapped_value ( vm , * caller_realm , result . value ( ) ) ;
2021-10-13 20:08:48 +00:00
}
// 11. Else,
else {
// a. Throw a TypeError exception.
2022-08-16 19:33:17 +00:00
return vm . throw_completion < TypeError > ( ErrorType : : WrappedFunctionCallThrowCompletion ) ;
2021-10-13 20:08:48 +00:00
}
// NOTE: Also see "Editor's Note" in the spec regarding the TypeError above.
}
2022-07-31 10:13:33 +00:00
// 2.3 PrepareForWrappedFunctionCall ( F: a wrapped function exotic object, ), https://tc39.es/proposal-shadowrealm/#sec-prepare-for-wrapped-function-call
void prepare_for_wrapped_function_call ( WrappedFunction const & function , ExecutionContext & callee_context )
2021-10-13 20:08:48 +00:00
{
2022-07-31 10:13:33 +00:00
auto & vm = function . vm ( ) ;
2021-10-13 20:08:48 +00:00
2022-07-31 10:13:33 +00:00
// 1. Let callerContext be the running execution context.
auto const & caller_context = vm . running_execution_context ( ) ;
// 2. Let calleeContext be a new execution context.
// NOTE: In the specification, PrepareForWrappedFunctionCall "returns" a new callee execution context.
// To avoid heap allocations, we put our ExecutionContext objects on the C++ stack instead.
// Whoever calls us should put an ExecutionContext on their stack and pass that as the `callee_context`.
// 3. Set the Function of calleeContext to F.
callee_context . function = & const_cast < WrappedFunction & > ( function ) ;
// 4. Let calleeRealm be F.[[Realm]].
auto * callee_realm = function . realm ( ) ;
// 5. Set the Realm of calleeContext to calleeRealm.
callee_context . realm = callee_realm ;
// 6. Set the ScriptOrModule of calleeContext to null.
callee_context . script_or_module = { } ;
// 7. If callerContext is not already suspended, suspend callerContext.
// NOTE: We don't support this concept yet.
( void ) caller_context ;
// 8. Push calleeContext onto the execution context stack; calleeContext is now the running execution context.
vm . push_execution_context ( callee_context ) ;
// 9. NOTE: Any exception objects produced after this point are associated with calleeRealm.
// 10. Return calleeContext.
// NOTE: No-op, see NOTE after step 2.
2021-10-13 20:08:48 +00:00
}
}