mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-25 17:10:23 +00:00
LibJS: Implement AggregateError
This commit is contained in:
parent
2f03eb8628
commit
cbd7437d40
Notes:
sideshowbarker
2024-07-18 12:25:26 +09:00
Author: https://github.com/linusg Commit: https://github.com/SerenityOS/serenity/commit/cbd7437d40c Pull-request: https://github.com/SerenityOS/serenity/pull/7999
13 changed files with 301 additions and 15 deletions
|
@ -17,6 +17,9 @@ set(SOURCES
|
|||
Lexer.cpp
|
||||
MarkupGenerator.cpp
|
||||
Parser.cpp
|
||||
Runtime/AggregateError.cpp
|
||||
Runtime/AggregateErrorConstructor.cpp
|
||||
Runtime/AggregateErrorPrototype.cpp
|
||||
Runtime/Array.cpp
|
||||
Runtime/ArrayBuffer.cpp
|
||||
Runtime/ArrayBufferConstructor.cpp
|
||||
|
|
|
@ -25,21 +25,22 @@
|
|||
void name([[maybe_unused]] JS::VM& vm, [[maybe_unused]] JS::GlobalObject& global_object, [[maybe_unused]] JS::Value value)
|
||||
|
||||
// NOTE: Proxy is not included here as it doesn't have a prototype - m_proxy_constructor is initialized separately.
|
||||
#define JS_ENUMERATE_NATIVE_OBJECTS_EXCLUDING_TEMPLATES \
|
||||
__JS_ENUMERATE(Array, array, ArrayPrototype, ArrayConstructor, void) \
|
||||
__JS_ENUMERATE(ArrayBuffer, array_buffer, ArrayBufferPrototype, ArrayBufferConstructor, void) \
|
||||
__JS_ENUMERATE(BigIntObject, bigint, BigIntPrototype, BigIntConstructor, void) \
|
||||
__JS_ENUMERATE(BooleanObject, boolean, BooleanPrototype, BooleanConstructor, void) \
|
||||
__JS_ENUMERATE(Date, date, DatePrototype, DateConstructor, void) \
|
||||
__JS_ENUMERATE(Error, error, ErrorPrototype, ErrorConstructor, void) \
|
||||
__JS_ENUMERATE(Function, function, FunctionPrototype, FunctionConstructor, void) \
|
||||
__JS_ENUMERATE(NumberObject, number, NumberPrototype, NumberConstructor, void) \
|
||||
__JS_ENUMERATE(Object, object, ObjectPrototype, ObjectConstructor, void) \
|
||||
__JS_ENUMERATE(Promise, promise, PromisePrototype, PromiseConstructor, void) \
|
||||
__JS_ENUMERATE(RegExpObject, regexp, RegExpPrototype, RegExpConstructor, void) \
|
||||
__JS_ENUMERATE(Set, set, SetPrototype, SetConstructor, void) \
|
||||
__JS_ENUMERATE(StringObject, string, StringPrototype, StringConstructor, void) \
|
||||
__JS_ENUMERATE(SymbolObject, symbol, SymbolPrototype, SymbolConstructor, void) \
|
||||
#define JS_ENUMERATE_NATIVE_OBJECTS_EXCLUDING_TEMPLATES \
|
||||
__JS_ENUMERATE(AggregateError, aggregate_error, AggregateErrorPrototype, AggregateErrorConstructor, void) \
|
||||
__JS_ENUMERATE(Array, array, ArrayPrototype, ArrayConstructor, void) \
|
||||
__JS_ENUMERATE(ArrayBuffer, array_buffer, ArrayBufferPrototype, ArrayBufferConstructor, void) \
|
||||
__JS_ENUMERATE(BigIntObject, bigint, BigIntPrototype, BigIntConstructor, void) \
|
||||
__JS_ENUMERATE(BooleanObject, boolean, BooleanPrototype, BooleanConstructor, void) \
|
||||
__JS_ENUMERATE(Date, date, DatePrototype, DateConstructor, void) \
|
||||
__JS_ENUMERATE(Error, error, ErrorPrototype, ErrorConstructor, void) \
|
||||
__JS_ENUMERATE(Function, function, FunctionPrototype, FunctionConstructor, void) \
|
||||
__JS_ENUMERATE(NumberObject, number, NumberPrototype, NumberConstructor, void) \
|
||||
__JS_ENUMERATE(Object, object, ObjectPrototype, ObjectConstructor, void) \
|
||||
__JS_ENUMERATE(Promise, promise, PromisePrototype, PromiseConstructor, void) \
|
||||
__JS_ENUMERATE(RegExpObject, regexp, RegExpPrototype, RegExpConstructor, void) \
|
||||
__JS_ENUMERATE(Set, set, SetPrototype, SetConstructor, void) \
|
||||
__JS_ENUMERATE(StringObject, string, StringPrototype, StringConstructor, void) \
|
||||
__JS_ENUMERATE(SymbolObject, symbol, SymbolPrototype, SymbolConstructor, void) \
|
||||
__JS_ENUMERATE(WeakSet, weak_set, WeakSetPrototype, WeakSetConstructor, void)
|
||||
|
||||
#define JS_ENUMERATE_NATIVE_OBJECTS \
|
||||
|
|
29
Userland/Libraries/LibJS/Runtime/AggregateError.cpp
Normal file
29
Userland/Libraries/LibJS/Runtime/AggregateError.cpp
Normal file
|
@ -0,0 +1,29 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Runtime/AggregateError.h>
|
||||
#include <LibJS/Runtime/Array.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
AggregateError* AggregateError::create(GlobalObject& global_object, String const& message, Vector<Value> const& errors)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
AggregateError::AggregateError(Object& prototype)
|
||||
: Object(prototype)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
23
Userland/Libraries/LibJS/Runtime/AggregateError.h
Normal file
23
Userland/Libraries/LibJS/Runtime/AggregateError.h
Normal file
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Runtime/Object.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
class AggregateError : public Object {
|
||||
JS_OBJECT(Error, Object);
|
||||
|
||||
public:
|
||||
static AggregateError* create(GlobalObject&, String const& message, Vector<Value> const& errors);
|
||||
|
||||
explicit AggregateError(Object& prototype);
|
||||
virtual ~AggregateError() override = default;
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Runtime/AggregateError.h>
|
||||
#include <LibJS/Runtime/AggregateErrorConstructor.h>
|
||||
#include <LibJS/Runtime/ErrorConstructor.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/IteratorOperations.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
AggregateErrorConstructor::AggregateErrorConstructor(GlobalObject& global_object)
|
||||
: NativeFunction(*static_cast<Object*>(global_object.error_constructor()))
|
||||
{
|
||||
}
|
||||
|
||||
void AggregateErrorConstructor::initialize(GlobalObject& global_object)
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
NativeFunction::initialize(global_object);
|
||||
define_property(vm.names.prototype, global_object.aggregate_error_prototype(), 0);
|
||||
define_property(vm.names.length, Value(2), Attribute::Configurable);
|
||||
}
|
||||
|
||||
Value AggregateErrorConstructor::call()
|
||||
{
|
||||
return construct(*this);
|
||||
}
|
||||
|
||||
// 20.5.7.1 The AggregateError Constructor, https://tc39.es/ecma262/#sec-aggregate-error-constructor
|
||||
Value AggregateErrorConstructor::construct(Function&)
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
String message;
|
||||
if (!vm.argument(1).is_undefined()) {
|
||||
message = vm.argument(1).to_string(global_object());
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
28
Userland/Libraries/LibJS/Runtime/AggregateErrorConstructor.h
Normal file
28
Userland/Libraries/LibJS/Runtime/AggregateErrorConstructor.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Runtime/NativeFunction.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
class AggregateErrorConstructor final : public NativeFunction {
|
||||
JS_OBJECT(AggregateErrorConstructor, NativeFunction);
|
||||
|
||||
public:
|
||||
explicit AggregateErrorConstructor(GlobalObject&);
|
||||
virtual void initialize(GlobalObject&) override;
|
||||
virtual ~AggregateErrorConstructor() override = default;
|
||||
|
||||
virtual Value call() override;
|
||||
virtual Value construct(Function& new_target) override;
|
||||
|
||||
private:
|
||||
virtual bool has_constructor() const override { return true; }
|
||||
};
|
||||
|
||||
}
|
27
Userland/Libraries/LibJS/Runtime/AggregateErrorPrototype.cpp
Normal file
27
Userland/Libraries/LibJS/Runtime/AggregateErrorPrototype.cpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibJS/Runtime/AggregateErrorPrototype.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
#include <LibJS/Runtime/PrimitiveString.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
AggregateErrorPrototype::AggregateErrorPrototype(GlobalObject& global_object)
|
||||
: Object(*global_object.error_prototype())
|
||||
{
|
||||
}
|
||||
|
||||
void AggregateErrorPrototype::initialize(GlobalObject& global_object)
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
Object::initialize(global_object);
|
||||
u8 attr = Attribute::Writable | Attribute::Configurable;
|
||||
define_property(vm.names.name, js_string(vm, "AggregateError"), attr);
|
||||
define_property(vm.names.message, js_string(vm, ""), attr);
|
||||
}
|
||||
|
||||
}
|
22
Userland/Libraries/LibJS/Runtime/AggregateErrorPrototype.h
Normal file
22
Userland/Libraries/LibJS/Runtime/AggregateErrorPrototype.h
Normal file
|
@ -0,0 +1,22 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Linus Groh <linusg@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibJS/Runtime/Object.h>
|
||||
|
||||
namespace JS {
|
||||
|
||||
class AggregateErrorPrototype final : public Object {
|
||||
JS_OBJECT(AggregateErrorPrototype, Object);
|
||||
|
||||
public:
|
||||
explicit AggregateErrorPrototype(GlobalObject&);
|
||||
virtual void initialize(GlobalObject&) override;
|
||||
virtual ~AggregateErrorPrototype() override = default;
|
||||
};
|
||||
|
||||
}
|
|
@ -100,6 +100,7 @@ namespace JS {
|
|||
P(entries) \
|
||||
P(enumerable) \
|
||||
P(error) \
|
||||
P(errors) \
|
||||
P(escape) \
|
||||
P(eval) \
|
||||
P(every) \
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
#include <LibJS/Interpreter.h>
|
||||
#include <LibJS/Lexer.h>
|
||||
#include <LibJS/Parser.h>
|
||||
#include <LibJS/Runtime/AggregateErrorConstructor.h>
|
||||
#include <LibJS/Runtime/AggregateErrorPrototype.h>
|
||||
#include <LibJS/Runtime/ArrayBufferConstructor.h>
|
||||
#include <LibJS/Runtime/ArrayBufferPrototype.h>
|
||||
#include <LibJS/Runtime/ArrayConstructor.h>
|
||||
|
@ -94,6 +96,9 @@ void GlobalObject::initialize_global_object()
|
|||
|
||||
Object::set_prototype(m_object_prototype);
|
||||
|
||||
// This must be initialized before allocating AggregateErrorPrototype, which uses ErrorPrototype as its prototype.
|
||||
m_error_prototype = heap().allocate<ErrorPrototype>(*this, *this);
|
||||
|
||||
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
|
||||
if (!m_##snake_name##_prototype) \
|
||||
m_##snake_name##_prototype = heap().allocate<PrototypeName>(*this, *this);
|
||||
|
@ -130,6 +135,10 @@ void GlobalObject::initialize_global_object()
|
|||
define_property(vm.names.JSON, heap().allocate<JSONObject>(*this, *this), attr);
|
||||
define_property(vm.names.Reflect, heap().allocate<ReflectObject>(*this, *this), attr);
|
||||
|
||||
// This must be initialized before allocating AggregateErrorConstructor, which uses ErrorConstructor as its prototype.
|
||||
initialize_constructor(vm.names.Error, m_error_constructor, m_error_prototype);
|
||||
|
||||
add_constructor(vm.names.AggregateError, m_aggregate_error_constructor, m_aggregate_error_prototype);
|
||||
add_constructor(vm.names.Array, m_array_constructor, m_array_prototype);
|
||||
add_constructor(vm.names.ArrayBuffer, m_array_buffer_constructor, m_array_buffer_prototype);
|
||||
add_constructor(vm.names.BigInt, m_bigint_constructor, m_bigint_prototype);
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
describe("errors", () => {
|
||||
test("first argument must be coercible to object", () => {
|
||||
expect(() => {
|
||||
new AggregateError();
|
||||
}).toThrowWithMessage(TypeError, "ToObject on null or undefined");
|
||||
});
|
||||
|
||||
test("@@iterator throws", () => {
|
||||
expect(() => {
|
||||
new AggregateError({
|
||||
[Symbol.iterator]() {
|
||||
throw Error("oops!");
|
||||
},
|
||||
});
|
||||
}).toThrowWithMessage(Error, "oops!");
|
||||
});
|
||||
});
|
||||
|
||||
describe("normal behavior", () => {
|
||||
test("length is 2", () => {
|
||||
expect(AggregateError).toHaveLength(2);
|
||||
});
|
||||
|
||||
test("name is AggregateError", () => {
|
||||
expect(AggregateError.name).toBe("AggregateError");
|
||||
});
|
||||
|
||||
test("Prototype of the AggregateError constructor is the Error constructor", () => {
|
||||
expect(Object.getPrototypeOf(AggregateError)).toBe(Error);
|
||||
});
|
||||
|
||||
test("Prototype of AggregateError.prototype is Error.prototype", () => {
|
||||
expect(Object.getPrototypeOf(AggregateError.prototype)).toBe(Error.prototype);
|
||||
});
|
||||
|
||||
test("basic functionality", () => {
|
||||
expect(AggregateError([])).toBeInstanceOf(AggregateError);
|
||||
expect(new AggregateError([])).toBeInstanceOf(AggregateError);
|
||||
expect(new AggregateError([]).toString()).toBe("AggregateError");
|
||||
expect(new AggregateError([], "Foo").toString()).toBe("AggregateError: Foo");
|
||||
expect(new AggregateError([]).hasOwnProperty("errors")).toBeTrue();
|
||||
const errors = [1, 2, 3];
|
||||
expect(new AggregateError(errors).errors).toEqual(errors);
|
||||
expect(new AggregateError(errors).errors).not.toBe(errors);
|
||||
expect(
|
||||
new AggregateError({
|
||||
[Symbol.iterator]: (i = 0) => ({
|
||||
next() {
|
||||
if (i < 3) {
|
||||
i++;
|
||||
return { value: i };
|
||||
}
|
||||
return { value: null, done: true };
|
||||
},
|
||||
}),
|
||||
}).errors
|
||||
).toEqual(errors);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
describe("normal behavior", () => {
|
||||
test("initial message value is empty string", () => {
|
||||
expect(AggregateError.prototype.message).toBe("");
|
||||
});
|
||||
|
||||
test("Error gets message via prototype by default", () => {
|
||||
const error = new AggregateError([]);
|
||||
expect(error.hasOwnProperty("message")).toBeFalse();
|
||||
expect(error.message).toBe("");
|
||||
AggregateError.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 AggregateError([], "Custom error message");
|
||||
expect(error.hasOwnProperty("message")).toBeTrue();
|
||||
expect(error.message).toBe("Custom error message");
|
||||
AggregateError.prototype.message = "Well hello friends";
|
||||
expect(error.message).toBe("Custom error message");
|
||||
});
|
||||
});
|
|
@ -0,0 +1,13 @@
|
|||
describe("normal behavior", () => {
|
||||
test("initial name value is type name", () => {
|
||||
expect(AggregateError.prototype.name).toBe("AggregateError");
|
||||
});
|
||||
|
||||
test("Error gets name via prototype", () => {
|
||||
const error = new AggregateError([]);
|
||||
expect(error.hasOwnProperty("name")).toBeFalse();
|
||||
expect(error.name).toBe("AggregateError");
|
||||
AggregateError.prototype.name = "Foo";
|
||||
expect(error.name).toBe("Foo");
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue