diff --git a/Userland/Libraries/LibJS/CMakeLists.txt b/Userland/Libraries/LibJS/CMakeLists.txt index 57e2a2d626..59ec1ea8ab 100644 --- a/Userland/Libraries/LibJS/CMakeLists.txt +++ b/Userland/Libraries/LibJS/CMakeLists.txt @@ -131,6 +131,8 @@ set(SOURCES Runtime/SetIteratorPrototype.cpp Runtime/SetPrototype.cpp Runtime/ShadowRealm.cpp + Runtime/ShadowRealmConstructor.cpp + Runtime/ShadowRealmPrototype.cpp Runtime/Shape.cpp Runtime/StringConstructor.cpp Runtime/StringIterator.cpp diff --git a/Userland/Libraries/LibJS/Forward.h b/Userland/Libraries/LibJS/Forward.h index f4511a66a8..a17b4c43a8 100644 --- a/Userland/Libraries/LibJS/Forward.h +++ b/Userland/Libraries/LibJS/Forward.h @@ -43,6 +43,7 @@ __JS_ENUMERATE(Promise, promise, PromisePrototype, PromiseConstructor, void) \ __JS_ENUMERATE(RegExpObject, regexp, RegExpPrototype, RegExpConstructor, void) \ __JS_ENUMERATE(Set, set, SetPrototype, SetConstructor, void) \ + __JS_ENUMERATE(ShadowRealm, shadow_realm, ShadowRealmPrototype, ShadowRealmConstructor, void) \ __JS_ENUMERATE(StringObject, string, StringPrototype, StringConstructor, void) \ __JS_ENUMERATE(SymbolObject, symbol, SymbolPrototype, SymbolConstructor, void) \ __JS_ENUMERATE(WeakMap, weak_map, WeakMapPrototype, WeakMapConstructor, void) \ diff --git a/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp b/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp index 6a95a69de3..48ed9b3c8d 100644 --- a/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp +++ b/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp @@ -72,6 +72,8 @@ #include #include #include +#include +#include #include #include #include @@ -249,6 +251,7 @@ void GlobalObject::initialize_global_object() add_constructor(vm.names.Proxy, m_proxy_constructor, nullptr); add_constructor(vm.names.RegExp, m_regexp_constructor, m_regexp_prototype); add_constructor(vm.names.Set, m_set_constructor, m_set_prototype); + add_constructor(vm.names.ShadowRealm, m_shadow_realm_constructor, m_shadow_realm_prototype); add_constructor(vm.names.String, m_string_constructor, m_string_prototype); add_constructor(vm.names.Symbol, m_symbol_constructor, m_symbol_prototype); add_constructor(vm.names.WeakMap, m_weak_map_constructor, m_weak_map_prototype); diff --git a/Userland/Libraries/LibJS/Runtime/ShadowRealm.cpp b/Userland/Libraries/LibJS/Runtime/ShadowRealm.cpp index 3b46804760..f5ff6757e9 100644 --- a/Userland/Libraries/LibJS/Runtime/ShadowRealm.cpp +++ b/Userland/Libraries/LibJS/Runtime/ShadowRealm.cpp @@ -9,6 +9,20 @@ namespace JS { +ShadowRealm::ShadowRealm(Realm& shadow_realm, ExecutionContext execution_context, Object& prototype) + : Object(prototype) + , m_shadow_realm(shadow_realm) + , m_execution_context(move(execution_context)) +{ +} + +void ShadowRealm::visit_edges(Visitor& visitor) +{ + Base::visit_edges(visitor); + + visitor.visit(&m_shadow_realm); +} + // 3.1.3 GetWrappedValue ( callerRealm, value ), https://tc39.es/proposal-shadowrealm/#sec-getwrappedvalue ThrowCompletionOr get_wrapped_value(GlobalObject& global_object, Realm& caller_realm, Value value) { diff --git a/Userland/Libraries/LibJS/Runtime/ShadowRealm.h b/Userland/Libraries/LibJS/Runtime/ShadowRealm.h index 78c1b39637..d3639d05dd 100644 --- a/Userland/Libraries/LibJS/Runtime/ShadowRealm.h +++ b/Userland/Libraries/LibJS/Runtime/ShadowRealm.h @@ -7,10 +7,31 @@ #pragma once #include +#include #include namespace JS { +class ShadowRealm final : public Object { + JS_OBJECT(ShadowRealm, Object); + +public: + ShadowRealm(Realm&, ExecutionContext, Object& prototype); + virtual ~ShadowRealm() override = default; + + [[nodiscard]] Realm const& shadow_realm() const { return m_shadow_realm; } + [[nodiscard]] Realm& shadow_realm() { return m_shadow_realm; } + [[nodiscard]] ExecutionContext const& execution_context() const { return m_execution_context; } + [[nodiscard]] ExecutionContext& execution_context() { return m_execution_context; } + +private: + virtual void visit_edges(Visitor&) override; + + // 3.5 Properties of ShadowRealm Instances, https://tc39.es/proposal-shadowrealm/#sec-properties-of-shadowrealm-instances + Realm& m_shadow_realm; // [[ShadowRealm]] + ExecutionContext m_execution_context; // [[ExecutionContext]] +}; + ThrowCompletionOr get_wrapped_value(GlobalObject&, Realm& caller_realm, Value); } diff --git a/Userland/Libraries/LibJS/Runtime/ShadowRealmConstructor.cpp b/Userland/Libraries/LibJS/Runtime/ShadowRealmConstructor.cpp new file mode 100644 index 0000000000..8b83c85331 --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/ShadowRealmConstructor.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2021, Linus Groh + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include +#include + +namespace JS { + +// 3.2 The ShadowRealm Constructor, https://tc39.es/proposal-shadowrealm/#sec-shadowrealm-constructor +ShadowRealmConstructor::ShadowRealmConstructor(GlobalObject& global_object) + : NativeFunction(vm().names.ShadowRealm.as_string(), *global_object.function_prototype()) +{ +} + +void ShadowRealmConstructor::initialize(GlobalObject& global_object) +{ + auto& vm = this->vm(); + NativeFunction::initialize(global_object); + + // 3.3.1 ShadowRealm.prototype, https://tc39.es/proposal-shadowrealm/#sec-shadowrealm.prototype + define_direct_property(vm.names.prototype, global_object.shadow_realm_prototype(), 0); + + define_direct_property(vm.names.length, Value(0), Attribute::Configurable); +} + +// 3.2.1 ShadowRealm ( ), https://tc39.es/proposal-shadowrealm/#sec-shadowrealm +Value ShadowRealmConstructor::call() +{ + auto& vm = this->vm(); + + // 1. If NewTarget is undefined, throw a TypeError exception. + vm.throw_exception(global_object(), ErrorType::ConstructorWithoutNew, vm.names.ShadowRealm); + return {}; +} + +// 3.2.1 ShadowRealm ( ), https://tc39.es/proposal-shadowrealm/#sec-shadowrealm +Value ShadowRealmConstructor::construct(FunctionObject& new_target) +{ + auto& vm = this->vm(); + auto& global_object = this->global_object(); + + // 3. Let realmRec be CreateRealm(). + auto* realm = Realm::create(vm); + + // 5. Let context be a new execution context. + auto context = ExecutionContext { vm.heap() }; + + // 6. Set the Function of context to null. + context.function = nullptr; + + // 7. Set the Realm of context to realmRec. + context.realm = realm; + + // 8. Set the ScriptOrModule of context to null. + // FIXME: Our execution context struct currently does not track this item. + + // 2. Let O be ? OrdinaryCreateFromConstructor(NewTarget, "%ShadowRealm.prototype%", « [[ShadowRealm]], [[ExecutionContext]] »). + // 4. Set O.[[ShadowRealm]] to realmRec. + // 9. Set O.[[ExecutionContext]] to context. + auto* object = TRY_OR_DISCARD(ordinary_create_from_constructor(global_object, new_target, &GlobalObject::shadow_realm_prototype, *realm, move(context))); + + // 10. Perform ? SetRealmGlobalObject(realmRec, undefined, undefined). + auto* new_global_object = vm.heap().allocate_without_global_object(); + new_global_object->initialize_global_object(); + realm->set_global_object(*new_global_object, nullptr); + + // TODO: I don't think we should have these exactly like this, that doesn't work well with how + // we create global objects. Still, it should be possible to make a ShadowRealm with a + // non-LibJS GlobalObject somehow. + // 11. Perform ? SetDefaultGlobalBindings(O.[[ShadowRealm]]). + // 12. Perform ? HostInitializeShadowRealm(O.[[ShadowRealm]]). + + // 13. Return O. + return object; +} + +} diff --git a/Userland/Libraries/LibJS/Runtime/ShadowRealmConstructor.h b/Userland/Libraries/LibJS/Runtime/ShadowRealmConstructor.h new file mode 100644 index 0000000000..d84581ea9c --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/ShadowRealmConstructor.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2021, Linus Groh + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include + +namespace JS { + +class ShadowRealmConstructor final : public NativeFunction { + JS_OBJECT(ShadowRealmConstructor, NativeFunction); + +public: + explicit ShadowRealmConstructor(GlobalObject&); + virtual void initialize(GlobalObject&) override; + virtual ~ShadowRealmConstructor() override = default; + + virtual Value call() override; + virtual Value construct(FunctionObject& new_target) override; + +private: + virtual bool has_constructor() const override { return true; } +}; + +} diff --git a/Userland/Libraries/LibJS/Runtime/ShadowRealmPrototype.cpp b/Userland/Libraries/LibJS/Runtime/ShadowRealmPrototype.cpp new file mode 100644 index 0000000000..0f580107c6 --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/ShadowRealmPrototype.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2021, Linus Groh + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include +#include + +namespace JS { + +// 3.4 Properties of the ShadowRealm Prototype Object, https://tc39.es/proposal-shadowrealm/#sec-properties-of-the-shadowrealm-prototype-object +ShadowRealmPrototype::ShadowRealmPrototype(GlobalObject& global_object) + : PrototypeObject(*global_object.object_prototype()) +{ +} + +void ShadowRealmPrototype::initialize(GlobalObject& global_object) +{ + Object::initialize(global_object); +} + +} diff --git a/Userland/Libraries/LibJS/Runtime/ShadowRealmPrototype.h b/Userland/Libraries/LibJS/Runtime/ShadowRealmPrototype.h new file mode 100644 index 0000000000..8fda68cc99 --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/ShadowRealmPrototype.h @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2021, Linus Groh + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include + +namespace JS { + +class ShadowRealmPrototype final : public PrototypeObject { + JS_PROTOTYPE_OBJECT(ShadowRealmPrototype, ShadowRealm, ShadowRealm); + +public: + explicit ShadowRealmPrototype(GlobalObject&); + virtual void initialize(GlobalObject&) override; + virtual ~ShadowRealmPrototype() override = default; +}; + +} diff --git a/Userland/Libraries/LibJS/Tests/builtins/ShadowRealm/ShadowRealm.js b/Userland/Libraries/LibJS/Tests/builtins/ShadowRealm/ShadowRealm.js new file mode 100644 index 0000000000..325868f419 --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/ShadowRealm/ShadowRealm.js @@ -0,0 +1,20 @@ +describe("errors", () => { + test("called without new", () => { + expect(() => { + ShadowRealm(); + }).toThrowWithMessage(TypeError, "ShadowRealm constructor must be called with 'new'"); + }); +}); + +describe("normal behavior", () => { + test("length is 0", () => { + expect(ShadowRealm).toHaveLength(0); + }); + + test("basic functionality", () => { + const shadowRealm = new ShadowRealm(); + expect(typeof shadowRealm).toBe("object"); + expect(shadowRealm).toBeInstanceOf(ShadowRealm); + expect(Object.getPrototypeOf(shadowRealm)).toBe(ShadowRealm.prototype); + }); +});