LibJS: Implement a more general InitializeHostDefinedRealm AO
The existing implementation of this AO lives in Interpreter::create(), which makes it impossible to use without also constructing an Interpreter. This patch adds a new Realm::initialize_host_defined_realm() and takes the global object and global this customization steps as Function callback objects. This will be used by LibWeb to create realms during Document construction.
This commit is contained in:
parent
e756b5450d
commit
8a03b17007
Notes:
sideshowbarker
2024-07-17 08:25:25 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/8a03b17007
3 changed files with 78 additions and 10 deletions
|
@ -1,13 +1,16 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2022, Andreas Kling <kling@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/TypeCasts.h>
|
||||
#include <LibJS/Runtime/GlobalEnvironment.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/Realm.h>
|
||||
#include <LibJS/Runtime/VM.h>
|
||||
#include <LibJS/Heap/DeferGC.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
|
@ -17,25 +20,89 @@ Realm* Realm::create(VM& vm)
|
|||
return vm.heap().allocate_without_global_object<Realm>();
|
||||
}
|
||||
|
||||
// 9.3.3 SetRealmGlobalObject ( realmRec, globalObj, thisValue ), https://tc39.es/ecma262/#sec-setrealmglobalobject
|
||||
void Realm::set_global_object(GlobalObject& global_object, Object* this_value)
|
||||
// 9.6 InitializeHostDefinedRealm ( ), https://tc39.es/ecma262/#sec-initializehostdefinedrealm
|
||||
ThrowCompletionOr<NonnullOwnPtr<ExecutionContext>> Realm::initialize_host_defined_realm(VM& vm, Function<Value(Realm&)> create_global_object, Function<Value(Realm&)> create_global_this_value)
|
||||
{
|
||||
// NOTE: Step 1 is not supported, the global object must be allocated elsewhere.
|
||||
DeferGC defer_gc(vm.heap());
|
||||
|
||||
// 1. Let realm be CreateRealm().
|
||||
auto* realm = Realm::create(vm);
|
||||
|
||||
// 2. Let newContext be a new execution context.
|
||||
auto new_context = make<ExecutionContext>(vm.heap());
|
||||
|
||||
// 3. Set the Function of newContext to null.
|
||||
new_context->function = nullptr;
|
||||
|
||||
// 4. Set the Realm of newContext to realm.
|
||||
new_context->realm = realm;
|
||||
|
||||
// 5. Set the ScriptOrModule of newContext to null.
|
||||
new_context->script_or_module = {};
|
||||
|
||||
// 6. Push newContext onto the execution context stack; newContext is now the running execution context.
|
||||
vm.push_execution_context(*new_context);
|
||||
|
||||
// 7. If the host requires use of an exotic object to serve as realm's global object,
|
||||
// let global be such an object created in a host-defined manner.
|
||||
// Otherwise, let global be undefined, indicating that an ordinary object should be created as the global object.
|
||||
Value global;
|
||||
if (create_global_object)
|
||||
global = create_global_object(*realm);
|
||||
else
|
||||
global = js_undefined();
|
||||
|
||||
// 8. If the host requires that the this binding in realm's global scope return an object other than the global object,
|
||||
// let thisValue be such an object created in a host-defined manner.
|
||||
// Otherwise, let thisValue be undefined, indicating that realm's global this binding should be the global object.
|
||||
Value this_value;
|
||||
if (create_global_this_value)
|
||||
this_value = create_global_this_value(*realm);
|
||||
else
|
||||
this_value = js_undefined();
|
||||
|
||||
// 9. Perform SetRealmGlobalObject(realm, global, thisValue).
|
||||
realm->set_global_object(global, this_value);
|
||||
|
||||
// NOTE: Steps 10 & 11 are somewhat ad-hoc, since we store intrinsics on the global object.
|
||||
|
||||
// 10. Let globalObj be ? SetDefaultGlobalBindings(realm).
|
||||
// 11. Create any host-defined global object properties on globalObj.
|
||||
realm->global_object().initialize_global_object();
|
||||
|
||||
// 12. Return unused.
|
||||
return new_context;
|
||||
}
|
||||
|
||||
// 9.3.3 SetRealmGlobalObject ( realmRec, globalObj, thisValue ), https://tc39.es/ecma262/#sec-setrealmglobalobject
|
||||
void Realm::set_global_object(Value global_object, Value this_value)
|
||||
{
|
||||
// 1. If globalObj is undefined, then
|
||||
if (global_object.is_undefined()) {
|
||||
// NOTE: Step 1 is not supported, the global object must be allocated elsewhere.
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
// 2. Assert: Type(globalObj) is Object.
|
||||
VERIFY(global_object.is_object());
|
||||
VERIFY(is<GlobalObject>(global_object.as_object()));
|
||||
|
||||
// Non-standard
|
||||
global_object.set_associated_realm(*this);
|
||||
verify_cast<GlobalObject>(global_object.as_object()).set_associated_realm(*this);
|
||||
|
||||
// 3. If thisValue is undefined, set thisValue to globalObj.
|
||||
if (!this_value)
|
||||
this_value = &global_object;
|
||||
if (this_value.is_undefined())
|
||||
this_value = global_object;
|
||||
|
||||
// Non-standard
|
||||
VERIFY(this_value.is_object());
|
||||
|
||||
// 4. Set realmRec.[[GlobalObject]] to globalObj.
|
||||
m_global_object = &global_object;
|
||||
m_global_object = &verify_cast<GlobalObject>(global_object.as_object());
|
||||
|
||||
// 5. Let newGlobalEnv be NewGlobalEnvironment(globalObj, thisValue).
|
||||
// 6. Set realmRec.[[GlobalEnv]] to newGlobalEnv.
|
||||
m_global_environment = global_object.heap().allocate_without_global_object<GlobalEnvironment>(global_object, *this_value);
|
||||
m_global_environment = m_global_object->heap().allocate_without_global_object<GlobalEnvironment>(verify_cast<GlobalObject>(global_object.as_object()), this_value.as_object());
|
||||
|
||||
// 7. Return unused.
|
||||
}
|
||||
|
|
|
@ -26,8 +26,9 @@ public:
|
|||
Realm() = default;
|
||||
|
||||
static Realm* create(VM&);
|
||||
static ThrowCompletionOr<NonnullOwnPtr<ExecutionContext>> initialize_host_defined_realm(VM&, Function<Value(Realm&)> create_global_object, Function<Value(Realm&)> create_global_this_value);
|
||||
|
||||
void set_global_object(GlobalObject&, Object* this_value = nullptr);
|
||||
void set_global_object(Value global_object, Value this_value);
|
||||
|
||||
[[nodiscard]] GlobalObject& global_object() const { return *m_global_object; }
|
||||
[[nodiscard]] GlobalEnvironment& global_environment() const { return *m_global_environment; }
|
||||
|
|
|
@ -64,7 +64,7 @@ ThrowCompletionOr<Object*> ShadowRealmConstructor::construct(FunctionObject& new
|
|||
|
||||
// 10. Perform ? SetRealmGlobalObject(realmRec, undefined, undefined).
|
||||
auto* new_global_object = vm.heap().allocate_without_global_object<GlobalObject>(*realm);
|
||||
realm->set_global_object(*new_global_object, nullptr);
|
||||
realm->set_global_object(new_global_object, js_undefined());
|
||||
new_global_object->initialize_global_object();
|
||||
|
||||
// TODO: I don't think we should have these exactly like this, that doesn't work well with how
|
||||
|
|
Loading…
Add table
Reference in a new issue