mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 16:17:45 +00:00
LibJS: Implement the Error Cause proposal
Currently stage 3. https://github.com/tc39/proposal-error-cause
This commit is contained in:
parent
8d77a3297a
commit
862ba64037
11 changed files with 130 additions and 42 deletions
|
@ -10,15 +10,9 @@
|
|||
|
||||
namespace JS {
|
||||
|
||||
AggregateError* AggregateError::create(GlobalObject& global_object, String const& message, Vector<Value> const& errors)
|
||||
AggregateError* AggregateError::create(GlobalObject& global_object)
|
||||
{
|
||||
auto& vm = global_object.vm();
|
||||
auto* error = global_object.heap().allocate<AggregateError>(global_object, *global_object.aggregate_error_prototype());
|
||||
u8 attr = Attribute::Writable | Attribute::Configurable;
|
||||
if (!message.is_null())
|
||||
error->define_property(vm.names.message, js_string(vm, message), attr);
|
||||
error->define_property(vm.names.errors, Array::create_from(global_object, errors), attr);
|
||||
return error;
|
||||
return global_object.heap().allocate<AggregateError>(global_object, *global_object.aggregate_error_prototype());
|
||||
}
|
||||
|
||||
AggregateError::AggregateError(Object& prototype)
|
||||
|
|
|
@ -14,7 +14,7 @@ class AggregateError : public Object {
|
|||
JS_OBJECT(Error, Object);
|
||||
|
||||
public:
|
||||
static AggregateError* create(GlobalObject&, String const& message, Vector<Value> const& errors);
|
||||
static AggregateError* create(GlobalObject&);
|
||||
|
||||
explicit AggregateError(Object& prototype);
|
||||
virtual ~AggregateError() override = default;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#include <LibJS/Runtime/AggregateError.h>
|
||||
#include <LibJS/Runtime/AggregateErrorConstructor.h>
|
||||
#include <LibJS/Runtime/Array.h>
|
||||
#include <LibJS/Runtime/ErrorConstructor.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/IteratorOperations.h>
|
||||
|
@ -34,17 +35,29 @@ Value AggregateErrorConstructor::call()
|
|||
Value AggregateErrorConstructor::construct(Function&)
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
String message;
|
||||
// FIXME: Use OrdinaryCreateFromConstructor(newTarget, "%AggregateError.prototype%")
|
||||
auto* aggregate_error = AggregateError::create(global_object());
|
||||
|
||||
u8 attr = Attribute::Writable | Attribute::Configurable;
|
||||
|
||||
if (!vm.argument(1).is_undefined()) {
|
||||
message = vm.argument(1).to_string(global_object());
|
||||
auto message = vm.argument(1).to_string(global_object());
|
||||
if (vm.exception())
|
||||
return {};
|
||||
aggregate_error->define_property(vm.names.message, js_string(vm, message), attr);
|
||||
}
|
||||
|
||||
aggregate_error->install_error_cause(vm.argument(2));
|
||||
if (vm.exception())
|
||||
return {};
|
||||
|
||||
auto errors_list = iterable_to_list(global_object(), vm.argument(0));
|
||||
if (vm.exception())
|
||||
return {};
|
||||
// FIXME: 2. Let O be ? OrdinaryCreateFromConstructor(newTarget, "%AggregateError.prototype%", « [[ErrorData]] »).
|
||||
return AggregateError::create(global_object(), message, errors_list);
|
||||
|
||||
aggregate_error->define_property(vm.names.errors, Array::create_from(global_object(), errors_list), attr);
|
||||
|
||||
return aggregate_error;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -69,6 +69,7 @@ namespace JS {
|
|||
P(byteOffset) \
|
||||
P(call) \
|
||||
P(callee) \
|
||||
P(cause) \
|
||||
P(cbrt) \
|
||||
P(ceil) \
|
||||
P(charAt) \
|
||||
|
|
|
@ -10,14 +10,17 @@
|
|||
|
||||
namespace JS {
|
||||
|
||||
Error* Error::create(GlobalObject& global_object, const String& message)
|
||||
Error* Error::create(GlobalObject& global_object)
|
||||
{
|
||||
return global_object.heap().allocate<Error>(global_object, *global_object.error_prototype());
|
||||
}
|
||||
|
||||
Error* Error::create(GlobalObject& global_object, String const& message)
|
||||
{
|
||||
auto& vm = global_object.vm();
|
||||
auto* error = global_object.heap().allocate<Error>(global_object, *global_object.error_prototype());
|
||||
if (!message.is_null()) {
|
||||
auto* error = Error::create(global_object);
|
||||
u8 attr = Attribute::Writable | Attribute::Configurable;
|
||||
error->define_property(vm.names.message, js_string(vm, message), attr);
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
|
@ -27,14 +30,17 @@ Error::Error(Object& prototype)
|
|||
}
|
||||
|
||||
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
|
||||
ClassName* ClassName::create(GlobalObject& global_object, const String& message) \
|
||||
ClassName* ClassName::create(GlobalObject& global_object) \
|
||||
{ \
|
||||
return global_object.heap().allocate<ClassName>(global_object, *global_object.snake_name##_prototype()); \
|
||||
} \
|
||||
\
|
||||
ClassName* ClassName::create(GlobalObject& global_object, String const& message) \
|
||||
{ \
|
||||
auto& vm = global_object.vm(); \
|
||||
auto* error = global_object.heap().allocate<ClassName>(global_object, *global_object.snake_name##_prototype()); \
|
||||
if (!message.is_null()) { \
|
||||
auto* error = ClassName::create(global_object); \
|
||||
u8 attr = Attribute::Writable | Attribute::Configurable; \
|
||||
error->define_property(vm.names.message, js_string(vm, message), attr); \
|
||||
} \
|
||||
return error; \
|
||||
} \
|
||||
\
|
||||
|
|
|
@ -16,7 +16,8 @@ class Error : public Object {
|
|||
JS_OBJECT(Error, Object);
|
||||
|
||||
public:
|
||||
static Error* create(GlobalObject&, const String& message = {});
|
||||
static Error* create(GlobalObject&);
|
||||
static Error* create(GlobalObject&, String const& message);
|
||||
|
||||
explicit Error(Object& prototype);
|
||||
virtual ~Error() override = default;
|
||||
|
@ -30,7 +31,8 @@ public:
|
|||
JS_OBJECT(ClassName, Error); \
|
||||
\
|
||||
public: \
|
||||
static ClassName* create(GlobalObject&, const String& message = {}); \
|
||||
static ClassName* create(GlobalObject&); \
|
||||
static ClassName* create(GlobalObject&, String const& message); \
|
||||
\
|
||||
explicit ClassName(Object& prototype); \
|
||||
virtual ~ClassName() override = default; \
|
||||
|
|
|
@ -31,13 +31,23 @@ Value ErrorConstructor::call()
|
|||
Value ErrorConstructor::construct(Function&)
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
String message;
|
||||
// FIXME: Use OrdinaryCreateFromConstructor(newTarget, "%Error.prototype%")
|
||||
auto* error = Error::create(global_object());
|
||||
|
||||
u8 attr = Attribute::Writable | Attribute::Configurable;
|
||||
|
||||
if (!vm.argument(0).is_undefined()) {
|
||||
message = vm.argument(0).to_string(global_object());
|
||||
auto message = vm.argument(0).to_string(global_object());
|
||||
if (vm.exception())
|
||||
return {};
|
||||
error->define_property(vm.names.message, js_string(vm, message), attr);
|
||||
}
|
||||
return Error::create(global_object(), message);
|
||||
|
||||
error->install_error_cause(vm.argument(1));
|
||||
if (vm.exception())
|
||||
return {};
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
|
||||
|
@ -64,13 +74,24 @@ Value ErrorConstructor::construct(Function&)
|
|||
Value ConstructorName::construct(Function&) \
|
||||
{ \
|
||||
auto& vm = this->vm(); \
|
||||
String message = ""; \
|
||||
/* FIXME: Use OrdinaryCreateFromConstructor( \
|
||||
* FIXME: newTarget, "%NativeError.prototype%"). */ \
|
||||
auto* error = ClassName::create(global_object()); \
|
||||
\
|
||||
u8 attr = Attribute::Writable | Attribute::Configurable; \
|
||||
\
|
||||
if (!vm.argument(0).is_undefined()) { \
|
||||
message = vm.argument(0).to_string(global_object()); \
|
||||
auto message = vm.argument(0).to_string(global_object()); \
|
||||
if (vm.exception()) \
|
||||
return {}; \
|
||||
error->define_property(vm.names.message, js_string(vm, message), attr); \
|
||||
} \
|
||||
return ClassName::create(global_object(), message); \
|
||||
\
|
||||
error->install_error_cause(vm.argument(1)); \
|
||||
if (vm.exception()) \
|
||||
return {}; \
|
||||
\
|
||||
return error; \
|
||||
}
|
||||
|
||||
JS_ENUMERATE_NATIVE_ERRORS
|
||||
|
|
|
@ -1015,6 +1015,21 @@ Value Object::ordinary_to_primitive(Value::PreferredType preferred_type) const
|
|||
return {};
|
||||
}
|
||||
|
||||
// 20.5.8.1 InstallErrorCause, https://tc39.es/proposal-error-cause/#sec-errorobjects-install-error-cause
|
||||
void Object::install_error_cause(Value options)
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
if (!options.is_object())
|
||||
return;
|
||||
auto& options_object = options.as_object();
|
||||
if (!options_object.has_property(vm.names.cause))
|
||||
return;
|
||||
auto cause = options_object.get(vm.names.cause).value_or(js_undefined());
|
||||
if (vm.exception())
|
||||
return;
|
||||
define_property(vm.names.cause, cause, Attribute::Writable | Attribute::Configurable);
|
||||
}
|
||||
|
||||
Value Object::invoke_internal(const StringOrSymbol& property_name, Optional<MarkedValueList> arguments)
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
|
|
|
@ -129,6 +129,8 @@ public:
|
|||
IndexedProperties& indexed_properties() { return m_indexed_properties; }
|
||||
void set_indexed_property_elements(Vector<Value>&& values) { m_indexed_properties = IndexedProperties(move(values)); }
|
||||
|
||||
void install_error_cause(Value options);
|
||||
|
||||
[[nodiscard]] Value invoke_internal(const StringOrSymbol& property_name, Optional<MarkedValueList> arguments);
|
||||
|
||||
template<typename... Args>
|
||||
|
|
|
@ -56,4 +56,11 @@ describe("normal behavior", () => {
|
|||
}).errors
|
||||
).toEqual(errors);
|
||||
});
|
||||
|
||||
test("supports options object with cause", () => {
|
||||
const cause = new Error();
|
||||
const error = new AggregateError([], "test", { cause });
|
||||
expect(error.hasOwnProperty("cause")).toBeTrue();
|
||||
expect(error.cause).toBe(cause);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -31,4 +31,31 @@ describe("normal behavior", () => {
|
|||
expect(TypeError()).toBeInstanceOf(TypeError);
|
||||
expect(new TypeError()).toBeInstanceOf(TypeError);
|
||||
});
|
||||
|
||||
test("supports options object with cause", () => {
|
||||
const errors = [Error, EvalError, RangeError, ReferenceError, SyntaxError, TypeError];
|
||||
const cause = new Error();
|
||||
errors.forEach(T => {
|
||||
const error = new T("test", { cause });
|
||||
expect(error.hasOwnProperty("cause")).toBeTrue();
|
||||
expect(error.cause).toBe(cause);
|
||||
});
|
||||
});
|
||||
|
||||
test("supports options object with cause (chained)", () => {
|
||||
let error;
|
||||
try {
|
||||
try {
|
||||
throw new Error("foo");
|
||||
} catch (e) {
|
||||
throw new Error("bar", { cause: e });
|
||||
}
|
||||
} catch (e) {
|
||||
error = new Error("baz", { cause: e });
|
||||
}
|
||||
expect(error.message).toBe("baz");
|
||||
expect(error.cause.message).toBe("bar");
|
||||
expect(error.cause.cause.message).toBe("foo");
|
||||
expect(error.cause.cause.cause).toBe(undefined);
|
||||
});
|
||||
});
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue