From 25bcd36116df5438bdde164f14b2f81d1878abb2 Mon Sep 17 00:00:00 2001 From: Linus Groh Date: Fri, 8 Oct 2021 21:14:22 +0100 Subject: [PATCH] LibJS: Move prepare_for_ordinary_call() to ECMAScriptFunctionObject Now that it only needs to deal with ECMAScriptFunctionObject via internal_call() / internal_construct(), we can: - Remove the generic FunctionObject parameter - Move it from the VM to ECMAScriptFunctionObject - Make it private --- .../Runtime/ECMAScriptFunctionObject.cpp | 62 ++++++++++++++++++- .../LibJS/Runtime/ECMAScriptFunctionObject.h | 3 +- Userland/Libraries/LibJS/Runtime/VM.cpp | 56 ----------------- Userland/Libraries/LibJS/Runtime/VM.h | 1 - 4 files changed, 62 insertions(+), 60 deletions(-) diff --git a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp index 15b34eea3c..4c4d885c67 100644 --- a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp +++ b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp @@ -113,7 +113,7 @@ ThrowCompletionOr ECMAScriptFunctionObject::internal_call(Value this_argu callee_context.current_node = interpreter->current_node(); // 2. Let calleeContext be PrepareForOrdinaryCall(F, undefined). - vm.prepare_for_ordinary_call(*this, callee_context, nullptr); + prepare_for_ordinary_call(callee_context, nullptr); // NOTE: We throw if the end of the native stack is reached, so unlike in the spec this _does_ need an exception check. if (auto* exception = vm.exception()) @@ -188,7 +188,7 @@ ThrowCompletionOr ECMAScriptFunctionObject::internal_construct(MarkedVa callee_context.current_node = interpreter->current_node(); // 4. Let calleeContext be PrepareForOrdinaryCall(F, newTarget). - vm.prepare_for_ordinary_call(*this, callee_context, &new_target); + prepare_for_ordinary_call(callee_context, &new_target); // NOTE: We throw if the end of the native stack is reached, so unlike in the spec this _does_ need an exception check. if (auto* exception = vm.exception()) @@ -557,6 +557,64 @@ ThrowCompletionOr ECMAScriptFunctionObject::function_declaration_instantia return {}; } +// 10.2.1.1 PrepareForOrdinaryCall ( F, newTarget ), https://tc39.es/ecma262/#sec-prepareforordinarycall +void ECMAScriptFunctionObject::prepare_for_ordinary_call(ExecutionContext& callee_context, Object* new_target) +{ + auto& vm = this->vm(); + + // Non-standard + callee_context.is_strict_mode = m_strict; + + // 1. Let callerContext be the running execution context. + // 2. Let calleeContext be a new ECMAScript code execution context. + + // NOTE: In the specification, PrepareForOrdinaryCall "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 = this; + callee_context.function_name = m_name; + + // 4. Let calleeRealm be F.[[Realm]]. + auto* callee_realm = m_realm; + // NOTE: This non-standard fallback is needed until we can guarantee that literally + // every function has a realm - especially in LibWeb that's sometimes not the case + // when a function is created while no JS is running, as we currently need to rely on + // that (:acid2:, I know - see set_event_handler_attribute() for an example). + // If there's no 'current realm' either, we can't continue and crash. + if (!callee_realm) + callee_realm = vm.current_realm(); + VERIFY(callee_realm); + + // 5. Set the Realm of calleeContext to calleeRealm. + callee_context.realm = callee_realm; + + // 6. Set the ScriptOrModule of calleeContext to F.[[ScriptOrModule]]. + // FIXME: Our execution context struct currently does not track this item. + + // 7. Let localEnv be NewFunctionEnvironment(F, newTarget). + auto* local_environment = new_function_environment(new_target); + + // 8. Set the LexicalEnvironment of calleeContext to localEnv. + callee_context.lexical_environment = local_environment; + + // 9. Set the VariableEnvironment of calleeContext to localEnv. + callee_context.variable_environment = local_environment; + + // 10. Set the PrivateEnvironment of calleeContext to F.[[PrivateEnvironment]]. + // FIXME: We currently don't support private environments. + + // 11. If callerContext is not already suspended, suspend callerContext. + // FIXME: We don't have this concept yet. + + // 12. Push calleeContext onto the execution context stack; calleeContext is now the running execution context. + vm.push_execution_context(callee_context, global_object()); + + // 13. NOTE: Any exception objects produced after this point are associated with calleeRealm. + // 14. Return calleeContext. (See NOTE above about how contexts are allocated on the C++ stack.) +} + // 10.2.1.4 OrdinaryCallEvaluateBody ( F, argumentsList ), https://tc39.es/ecma262/#sec-ordinarycallevaluatebody Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body() { diff --git a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h index b3b82bafcb..d717328964 100644 --- a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h +++ b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h @@ -82,8 +82,9 @@ private: virtual FunctionEnvironment* new_function_environment(Object* new_target) override; virtual void visit_edges(Visitor&) override; - ThrowCompletionOr function_declaration_instantiation(Interpreter*); + void prepare_for_ordinary_call(ExecutionContext& callee_context, Object* new_target); Completion ordinary_call_evaluate_body(); + ThrowCompletionOr function_declaration_instantiation(Interpreter*); // Internal Slots of ECMAScript Function Objects, https://tc39.es/ecma262/#table-internal-slots-of-ecmascript-function-objects Environment* m_environment { nullptr }; // [[Environment]] diff --git a/Userland/Libraries/LibJS/Runtime/VM.cpp b/Userland/Libraries/LibJS/Runtime/VM.cpp index dbeb2eedf6..91ffa62ab8 100644 --- a/Userland/Libraries/LibJS/Runtime/VM.cpp +++ b/Userland/Libraries/LibJS/Runtime/VM.cpp @@ -525,62 +525,6 @@ Value VM::get_new_target() return verify_cast(env).new_target(); } -// 10.2.1.1 PrepareForOrdinaryCall ( F, newTarget ), https://tc39.es/ecma262/#sec-prepareforordinarycall -void VM::prepare_for_ordinary_call(FunctionObject& function, ExecutionContext& callee_context, Object* new_target) -{ - // Non-standard - callee_context.is_strict_mode = function.is_strict_mode(); - - // 1. Let callerContext be the running execution context. - // 2. Let calleeContext be a new ECMAScript code execution context. - - // NOTE: In the specification, PrepareForOrdinaryCall "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 = &function; - callee_context.function_name = function.name(); - - // 4. Let calleeRealm be F.[[Realm]]. - auto* callee_realm = function.realm(); - // NOTE: This non-standard fallback is needed until we can guarantee that literally - // every function has a realm - especially in LibWeb that's sometimes not the case - // when a function is created while no JS is running, as we currently need to rely on - // that (:acid2:, I know - see set_event_handler_attribute() for an example). - // If there's no 'current realm' either, we can't continue and crash. - if (!callee_realm) - callee_realm = vm.current_realm(); - VERIFY(callee_realm); - - // 5. Set the Realm of calleeContext to calleeRealm. - callee_context.realm = callee_realm; - - // 6. Set the ScriptOrModule of calleeContext to F.[[ScriptOrModule]]. - // FIXME: Our execution context struct currently does not track this item. - - // 7. Let localEnv be NewFunctionEnvironment(F, newTarget). - auto* local_environment = function.new_function_environment(new_target); - - // 8. Set the LexicalEnvironment of calleeContext to localEnv. - callee_context.lexical_environment = local_environment; - - // 9. Set the VariableEnvironment of calleeContext to localEnv. - callee_context.variable_environment = local_environment; - - // 10. Set the PrivateEnvironment of calleeContext to F.[[PrivateEnvironment]]. - // FIXME: We currently don't support private environments. - - // 11. If callerContext is not already suspended, suspend callerContext. - // FIXME: We don't have this concept yet. - - // 12. Push calleeContext onto the execution context stack; calleeContext is now the running execution context. - push_execution_context(callee_context, function.global_object()); - - // 13. NOTE: Any exception objects produced after this point are associated with calleeRealm. - // 14. Return calleeContext. (See NOTE above about how contexts are allocated on the C++ stack.) -} - // 10.2.1.2 OrdinaryCallBindThis ( F, calleeContext, thisArgument ), https://tc39.es/ecma262/#sec-ordinarycallbindthis void VM::ordinary_call_bind_this(FunctionObject& function, ExecutionContext& callee_context, Value this_argument) { diff --git a/Userland/Libraries/LibJS/Runtime/VM.h b/Userland/Libraries/LibJS/Runtime/VM.h index 49cdf62b57..8d75852b48 100644 --- a/Userland/Libraries/LibJS/Runtime/VM.h +++ b/Userland/Libraries/LibJS/Runtime/VM.h @@ -275,7 +275,6 @@ public: void restore_execution_context_stack(); // TODO: Move these elsewhere once only used for ECMAScriptFunctionObject. - void prepare_for_ordinary_call(FunctionObject&, ExecutionContext& callee_context, Object* new_target); void ordinary_call_bind_this(FunctionObject&, ExecutionContext&, Value this_argument); private: