diff options
author | davidot <davidot@serenityos.org> | 2022-12-06 02:10:01 +0100 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2023-01-23 09:56:50 +0000 |
commit | 3353cf68f1940e0ade51091fc99cbb124067aecf (patch) | |
tree | 9ed5e33618008ca11538f8c07c49880f3c9c3237 | |
parent | 0d8bab82f01cb85709dea182fc288aadb7215811 (diff) | |
download | serenity-3353cf68f1940e0ade51091fc99cbb124067aecf.zip |
LibJS: Add SuppressedError{, Prototype, Constructor}
14 files changed, 300 insertions, 0 deletions
diff --git a/Userland/Libraries/LibJS/CMakeLists.txt b/Userland/Libraries/LibJS/CMakeLists.txt index a9f5381144..9650dc2082 100644 --- a/Userland/Libraries/LibJS/CMakeLists.txt +++ b/Userland/Libraries/LibJS/CMakeLists.txt @@ -193,6 +193,9 @@ set(SOURCES Runtime/StringIteratorPrototype.cpp Runtime/StringObject.cpp Runtime/StringPrototype.cpp + Runtime/SuppressedError.cpp + Runtime/SuppressedErrorConstructor.cpp + Runtime/SuppressedErrorPrototype.cpp Runtime/Symbol.cpp Runtime/SymbolConstructor.cpp Runtime/SymbolObject.cpp diff --git a/Userland/Libraries/LibJS/Forward.h b/Userland/Libraries/LibJS/Forward.h index 6e656ccfc4..626936eb19 100644 --- a/Userland/Libraries/LibJS/Forward.h +++ b/Userland/Libraries/LibJS/Forward.h @@ -39,6 +39,7 @@ __JS_ENUMERATE(Set, set, SetPrototype, SetConstructor, void) \ __JS_ENUMERATE(ShadowRealm, shadow_realm, ShadowRealmPrototype, ShadowRealmConstructor, void) \ __JS_ENUMERATE(StringObject, string, StringPrototype, StringConstructor, void) \ + __JS_ENUMERATE(SuppressedError, suppressed_error, SuppressedErrorPrototype, SuppressedErrorConstructor, void) \ __JS_ENUMERATE(SymbolObject, symbol, SymbolPrototype, SymbolConstructor, void) \ __JS_ENUMERATE(WeakMap, weak_map, WeakMapPrototype, WeakMapConstructor, void) \ __JS_ENUMERATE(WeakRef, weak_ref, WeakRefPrototype, WeakRefConstructor, void) \ diff --git a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h index 34f7dfed23..ed742088a0 100644 --- a/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h +++ b/Userland/Libraries/LibJS/Runtime/CommonPropertyNames.h @@ -489,6 +489,7 @@ namespace JS { P(substring) \ P(subtract) \ P(sup) \ + P(suppressed) \ P(supportedLocalesOf) \ P(supportedValuesOf) \ P(symmetricDifference) \ diff --git a/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp b/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp index 83f29af207..73a925d3f1 100644 --- a/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp +++ b/Userland/Libraries/LibJS/Runtime/GlobalObject.cpp @@ -63,6 +63,7 @@ #include <LibJS/Runtime/Shape.h> #include <LibJS/Runtime/StringConstructor.h> #include <LibJS/Runtime/StringPrototype.h> +#include <LibJS/Runtime/SuppressedErrorConstructor.h> #include <LibJS/Runtime/SymbolConstructor.h> #include <LibJS/Runtime/Temporal/CalendarConstructor.h> #include <LibJS/Runtime/Temporal/DurationConstructor.h> @@ -154,6 +155,7 @@ Object& set_default_global_bindings(Realm& realm) global.define_intrinsic_accessor(vm.names.Set, attr, [](auto& realm) -> Value { return realm.intrinsics().set_constructor(); }); global.define_intrinsic_accessor(vm.names.ShadowRealm, attr, [](auto& realm) -> Value { return realm.intrinsics().shadow_realm_constructor(); }); global.define_intrinsic_accessor(vm.names.String, attr, [](auto& realm) -> Value { return realm.intrinsics().string_constructor(); }); + global.define_intrinsic_accessor(vm.names.SuppressedError, attr, [](auto& realm) -> Value { return realm.intrinsics().suppressed_error_constructor(); }); global.define_intrinsic_accessor(vm.names.Symbol, attr, [](auto& realm) -> Value { return realm.intrinsics().symbol_constructor(); }); global.define_intrinsic_accessor(vm.names.SyntaxError, attr, [](auto& realm) -> Value { return realm.intrinsics().syntax_error_constructor(); }); global.define_intrinsic_accessor(vm.names.TypeError, attr, [](auto& realm) -> Value { return realm.intrinsics().type_error_constructor(); }); diff --git a/Userland/Libraries/LibJS/Runtime/Intrinsics.cpp b/Userland/Libraries/LibJS/Runtime/Intrinsics.cpp index 70b82a470f..3d229eaa5e 100644 --- a/Userland/Libraries/LibJS/Runtime/Intrinsics.cpp +++ b/Userland/Libraries/LibJS/Runtime/Intrinsics.cpp @@ -89,6 +89,8 @@ #include <LibJS/Runtime/StringConstructor.h> #include <LibJS/Runtime/StringIteratorPrototype.h> #include <LibJS/Runtime/StringPrototype.h> +#include <LibJS/Runtime/SuppressedErrorConstructor.h> +#include <LibJS/Runtime/SuppressedErrorPrototype.h> #include <LibJS/Runtime/SymbolConstructor.h> #include <LibJS/Runtime/SymbolPrototype.h> #include <LibJS/Runtime/Temporal/CalendarConstructor.h> diff --git a/Userland/Libraries/LibJS/Runtime/SuppressedError.cpp b/Userland/Libraries/LibJS/Runtime/SuppressedError.cpp new file mode 100644 index 0000000000..b067f5b985 --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/SuppressedError.cpp @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2022, David Tuin <davidot@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <LibJS/Runtime/Error.h> +#include <LibJS/Runtime/GlobalObject.h> +#include <LibJS/Runtime/SuppressedError.h> + +namespace JS { + +NonnullGCPtr<SuppressedError> SuppressedError::create(Realm& realm) +{ + return *realm.heap().allocate<SuppressedError>(realm, *realm.intrinsics().suppressed_error_prototype()); +} + +SuppressedError::SuppressedError(Object& prototype) + : Error(prototype) +{ +} + +} diff --git a/Userland/Libraries/LibJS/Runtime/SuppressedError.h b/Userland/Libraries/LibJS/Runtime/SuppressedError.h new file mode 100644 index 0000000000..9839cd2a9f --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/SuppressedError.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2022, David Tuin <davidot@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <LibJS/Runtime/Error.h> + +namespace JS { + +class SuppressedError : public Error { + JS_OBJECT(SuppressedError, Error); + +public: + static NonnullGCPtr<SuppressedError> create(Realm&); + virtual ~SuppressedError() override = default; + +private: + explicit SuppressedError(Object& prototype); +}; + +} diff --git a/Userland/Libraries/LibJS/Runtime/SuppressedErrorConstructor.cpp b/Userland/Libraries/LibJS/Runtime/SuppressedErrorConstructor.cpp new file mode 100644 index 0000000000..74ac04741f --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/SuppressedErrorConstructor.cpp @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2022, David Tuin <davidot@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <LibJS/Runtime/AbstractOperations.h> +#include <LibJS/Runtime/Array.h> +#include <LibJS/Runtime/ErrorConstructor.h> +#include <LibJS/Runtime/GlobalObject.h> +#include <LibJS/Runtime/IteratorOperations.h> +#include <LibJS/Runtime/SuppressedError.h> +#include <LibJS/Runtime/SuppressedErrorConstructor.h> + +namespace JS { + +SuppressedErrorConstructor::SuppressedErrorConstructor(Realm& realm) + : NativeFunction(static_cast<Object&>(*realm.intrinsics().error_constructor())) +{ +} + +void SuppressedErrorConstructor::initialize(Realm& realm) +{ + auto& vm = this->vm(); + NativeFunction::initialize(realm); + + // 10.1.4.2.1 SuppressedError.prototype, https://tc39.es/proposal-explicit-resource-management/#sec-suppressederror.prototype + define_direct_property(vm.names.prototype, realm.intrinsics().suppressed_error_prototype(), 0); + + define_direct_property(vm.names.length, Value(3), Attribute::Configurable); +} + +// 10.1.4.1.1 SuppressedError ( error, suppressed, message [ , options ] ), https://tc39.es/proposal-explicit-resource-management/#sec-suppressederror +ThrowCompletionOr<Value> SuppressedErrorConstructor::call() +{ + // 1. If NewTarget is undefined, let newTarget be the active function object; else let newTarget be NewTarget. + return TRY(construct(*this)); +} + +// 10.1.4.1.1 SuppressedError ( error, suppressed, message [ , options ] ), https://tc39.es/proposal-explicit-resource-management/#sec-suppressederror +ThrowCompletionOr<NonnullGCPtr<Object>> SuppressedErrorConstructor::construct(FunctionObject& new_target) +{ + auto& vm = this->vm(); + auto error = vm.argument(0); + auto suppressed = vm.argument(1); + auto message = vm.argument(2); + auto options = vm.argument(3); + + // 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%SuppressedError.prototype%", ยซ [[ErrorData]] ยป). + auto suppressed_error = TRY(ordinary_create_from_constructor<SuppressedError>(vm, new_target, &Intrinsics::suppressed_error_prototype)); + + // 3. If message is not undefined, then + if (!message.is_undefined()) { + // a. Let msg be ? ToString(message). + auto msg = TRY(message.to_string(vm)); + + // b. Perform CreateNonEnumerableDataPropertyOrThrow(O, "message", msg). + suppressed_error->create_non_enumerable_data_property_or_throw(vm.names.message, PrimitiveString::create(vm, move(msg))); + } + + // 4. Perform ? InstallErrorCause(O, options). + TRY(suppressed_error->install_error_cause(options)); + + // 5. Perform ! DefinePropertyOrThrow(O, "error", PropertyDescriptor { [[Configurable]]: true, [[Enumerable]]: false, [[Writable]]: true, [[Value]]: error }). + MUST(suppressed_error->define_property_or_throw(vm.names.error, { .value = error, .writable = true, .enumerable = false, .configurable = true })); + + // 6. Perform ! DefinePropertyOrThrow(O, "suppressed", PropertyDescriptor { [[Configurable]]: true, [[Enumerable]]: false, [[Writable]]: true, [[Value]]: suppressed }). + MUST(suppressed_error->define_property_or_throw(vm.names.suppressed, { .value = suppressed, .writable = true, .enumerable = false, .configurable = true })); + + // 7. Return O. + return suppressed_error; +} + +} diff --git a/Userland/Libraries/LibJS/Runtime/SuppressedErrorConstructor.h b/Userland/Libraries/LibJS/Runtime/SuppressedErrorConstructor.h new file mode 100644 index 0000000000..f27672ed20 --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/SuppressedErrorConstructor.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2022, David Tuin <davidot@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <LibJS/Runtime/NativeFunction.h> + +namespace JS { + +class SuppressedErrorConstructor final : public NativeFunction { + JS_OBJECT(SuppressedErrorConstructor, NativeFunction); + +public: + virtual void initialize(Realm&) override; + virtual ~SuppressedErrorConstructor() override = default; + + virtual ThrowCompletionOr<Value> call() override; + virtual ThrowCompletionOr<NonnullGCPtr<Object>> construct(FunctionObject& new_target) override; + +private: + explicit SuppressedErrorConstructor(Realm&); + virtual bool has_constructor() const override { return true; } +}; + +} diff --git a/Userland/Libraries/LibJS/Runtime/SuppressedErrorPrototype.cpp b/Userland/Libraries/LibJS/Runtime/SuppressedErrorPrototype.cpp new file mode 100644 index 0000000000..224226cd64 --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/SuppressedErrorPrototype.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2022, David Tuin <davidot@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <LibJS/Runtime/GlobalObject.h> +#include <LibJS/Runtime/PrimitiveString.h> +#include <LibJS/Runtime/SuppressedErrorPrototype.h> + +namespace JS { + +SuppressedErrorPrototype::SuppressedErrorPrototype(Realm& realm) + : Object(ConstructWithPrototypeTag::Tag, *realm.intrinsics().error_prototype()) +{ +} + +void SuppressedErrorPrototype::initialize(Realm& realm) +{ + auto& vm = this->vm(); + Object::initialize(realm); + u8 attr = Attribute::Writable | Attribute::Configurable; + define_direct_property(vm.names.name, PrimitiveString::create(vm, "SuppressedError"), attr); + define_direct_property(vm.names.message, PrimitiveString::create(vm, ""), attr); +} + +} diff --git a/Userland/Libraries/LibJS/Runtime/SuppressedErrorPrototype.h b/Userland/Libraries/LibJS/Runtime/SuppressedErrorPrototype.h new file mode 100644 index 0000000000..27791d6d7d --- /dev/null +++ b/Userland/Libraries/LibJS/Runtime/SuppressedErrorPrototype.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2022, David Tuin <davidot@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <LibJS/Runtime/Object.h> + +namespace JS { + +class SuppressedErrorPrototype final : public Object { + JS_OBJECT(SuppressedErrorPrototype, Object); + +public: + virtual void initialize(Realm&) override; + virtual ~SuppressedErrorPrototype() override = default; + +private: + explicit SuppressedErrorPrototype(Realm&); +}; + +} diff --git a/Userland/Libraries/LibJS/Tests/builtins/SuppressedError/SuppressedError.js b/Userland/Libraries/LibJS/Tests/builtins/SuppressedError/SuppressedError.js new file mode 100644 index 0000000000..ade5c1f26b --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/SuppressedError/SuppressedError.js @@ -0,0 +1,57 @@ +describe("normal behavior", () => { + test("length is 2", () => { + expect(SuppressedError).toHaveLength(3); + }); + + test("name is SuppressedError", () => { + expect(SuppressedError.name).toBe("SuppressedError"); + }); + + test("Prototype of the SuppressedError constructor is the Error constructor", () => { + expect(Object.getPrototypeOf(SuppressedError)).toBe(Error); + }); + + test("Prototype of SuppressedError.prototype is Error.prototype", () => { + expect(Object.getPrototypeOf(SuppressedError.prototype)).toBe(Error.prototype); + }); + + test("construction", () => { + expect(SuppressedError()).toBeInstanceOf(SuppressedError); + expect(SuppressedError(1)).toBeInstanceOf(SuppressedError); + expect(SuppressedError(1, 1)).toBeInstanceOf(SuppressedError); + expect(new SuppressedError()).toBeInstanceOf(SuppressedError); + expect(new SuppressedError(1)).toBeInstanceOf(SuppressedError); + expect(new SuppressedError(1, 1)).toBeInstanceOf(SuppressedError); + expect(Object.hasOwn(new SuppressedError(1, 1), "message")).toBeFalse(); + expect(new SuppressedError().toString()).toBe("SuppressedError"); + expect(new SuppressedError(1).toString()).toBe("SuppressedError"); + expect(new SuppressedError(1, 1).toString()).toBe("SuppressedError"); + expect(new SuppressedError(undefined, undefined, "Foo").toString()).toBe( + "SuppressedError: Foo" + ); + expect(new SuppressedError(1, 1, "Foo").toString()).toBe("SuppressedError: Foo"); + expect(Object.hasOwn(new SuppressedError(), "error")).toBeTrue(); + expect(Object.hasOwn(new SuppressedError(), "suppressed")).toBeTrue(); + const obj = {}; + expect(new SuppressedError(obj).error).toBe(obj); + expect(new SuppressedError(null, obj).suppressed).toBe(obj); + }); + + test("converts message to string", () => { + expect(new SuppressedError(undefined, undefined, 1)).toHaveProperty("message", "1"); + expect(new SuppressedError(undefined, undefined, {})).toHaveProperty( + "message", + "[object Object]" + ); + }); + + test("supports options object with cause", () => { + const cause = new Error(); + const error = new SuppressedError(1, 2, "test", { cause }); + expect(error.hasOwnProperty("cause")).toBeTrue(); + expect(error.cause).toBe(cause); + + const errorWithoutCase = new SuppressedError(1, 2, "test"); + expect(errorWithoutCase.hasOwnProperty("cause")).toBeFalse(); + }); +}); diff --git a/Userland/Libraries/LibJS/Tests/builtins/SuppressedError/SuppressedError.prototype.message.js b/Userland/Libraries/LibJS/Tests/builtins/SuppressedError/SuppressedError.prototype.message.js new file mode 100644 index 0000000000..ba743ddd9c --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/SuppressedError/SuppressedError.prototype.message.js @@ -0,0 +1,21 @@ +describe("normal behavior", () => { + test("initial message value is empty string", () => { + expect(SuppressedError.prototype.message).toBe(""); + }); + + test("Error gets message via prototype by default", () => { + const error = new SuppressedError(); + expect(error.hasOwnProperty("message")).toBeFalse(); + expect(error.message).toBe(""); + SuppressedError.prototype.message = "Well hello friends"; + expect(error.message).toBe("Well hello friends"); + }); + + test("Error gets message via object if given to constructor", () => { + const error = new SuppressedError(undefined, undefined, "Custom error message"); + expect(error.hasOwnProperty("message")).toBeTrue(); + expect(error.message).toBe("Custom error message"); + SuppressedError.prototype.message = "Well hello friends"; + expect(error.message).toBe("Custom error message"); + }); +}); diff --git a/Userland/Libraries/LibJS/Tests/builtins/SuppressedError/SuppressedError.prototype.name.js b/Userland/Libraries/LibJS/Tests/builtins/SuppressedError/SuppressedError.prototype.name.js new file mode 100644 index 0000000000..b30b27d257 --- /dev/null +++ b/Userland/Libraries/LibJS/Tests/builtins/SuppressedError/SuppressedError.prototype.name.js @@ -0,0 +1,13 @@ +describe("normal behavior", () => { + test("initial name value is type name", () => { + expect(SuppressedError.prototype.name).toBe("SuppressedError"); + }); + + test("Error gets name via prototype", () => { + const error = new SuppressedError([]); + expect(error.hasOwnProperty("name")).toBeFalse(); + expect(error.name).toBe("SuppressedError"); + SuppressedError.prototype.name = "Foo"; + expect(error.name).toBe("Foo"); + }); +}); |