LibJS: Make Errors fully spec compliant
The previous handling of the name and message properties specifically was breaking websites that created their own error types and relied on the error prototype working correctly - not assuming an JS::Error this object, that is. The way it works now, and it is supposed to work, is: - Error.prototype.name and Error.prototype.message just have initial string values and are no longer getters/setters - When constructing an error with a message, we create a regular property on the newly created object, so a lookup of the message property will either get it from the object directly or go though the prototype chain - Internal m_name/m_message properties are no longer needed and removed This makes printing errors slightly more complicated, as we can no longer rely on the (safe) internal properties, and cannot trust a property lookup either - get_without_side_effects() is used to solve this, it's not perfect but something we can revisit later. I did some refactoring along the way, there was some really old stuff in there - accessing vm.call_frame().arguments[0] is not something we (have to) do anymore :^) Fixes #6245.
This commit is contained in:
parent
6e9eb0a284
commit
da177c6517
Notes:
sideshowbarker
2024-07-18 20:29:39 +09:00
Author: https://github.com/linusg Commit: https://github.com/SerenityOS/serenity/commit/da177c6517a Pull-request: https://github.com/SerenityOS/serenity/pull/6254 Reviewed-by: https://github.com/awesomekling
18 changed files with 157 additions and 180 deletions
|
@ -106,16 +106,15 @@ ConsoleWidget::ConsoleWidget()
|
|||
}
|
||||
|
||||
if (m_interpreter->exception()) {
|
||||
output_html.append("Uncaught exception: ");
|
||||
auto error = m_interpreter->exception()->value();
|
||||
if (error.is_object() && is<Web::Bindings::DOMExceptionWrapper>(error.as_object())) {
|
||||
auto& dom_exception_wrapper = static_cast<Web::Bindings::DOMExceptionWrapper&>(error.as_object());
|
||||
error = JS::Error::create(m_interpreter->global_object(), dom_exception_wrapper.impl().name(), dom_exception_wrapper.impl().message());
|
||||
}
|
||||
output_html.append(JS::MarkupGenerator::html_from_value(error));
|
||||
print_html(output_html.string_view());
|
||||
|
||||
auto* exception = m_interpreter->exception();
|
||||
m_interpreter->vm().clear_exception();
|
||||
output_html.append("Uncaught exception: ");
|
||||
auto error = exception->value();
|
||||
if (error.is_object())
|
||||
output_html.append(JS::MarkupGenerator::html_from_error(error.as_object()));
|
||||
else
|
||||
output_html.append(JS::MarkupGenerator::html_from_value(error));
|
||||
print_html(output_html.string_view());
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -121,7 +121,7 @@ SheetGlobalObject::~SheetGlobalObject()
|
|||
{
|
||||
}
|
||||
|
||||
JS::Value SheetGlobalObject::get(const JS::PropertyName& name, JS::Value receiver) const
|
||||
JS::Value SheetGlobalObject::get(const JS::PropertyName& name, JS::Value receiver, bool without_side_effects) const
|
||||
{
|
||||
if (name.is_string()) {
|
||||
if (name.as_string() == "value") {
|
||||
|
@ -137,7 +137,7 @@ JS::Value SheetGlobalObject::get(const JS::PropertyName& name, JS::Value receive
|
|||
}
|
||||
}
|
||||
|
||||
return GlobalObject::get(name, receiver);
|
||||
return GlobalObject::get(name, receiver, without_side_effects);
|
||||
}
|
||||
|
||||
bool SheetGlobalObject::put(const JS::PropertyName& name, JS::Value value, JS::Value receiver)
|
||||
|
|
|
@ -46,7 +46,7 @@ public:
|
|||
|
||||
virtual ~SheetGlobalObject() override;
|
||||
|
||||
virtual JS::Value get(const JS::PropertyName&, JS::Value receiver = {}) const override;
|
||||
virtual JS::Value get(const JS::PropertyName&, JS::Value receiver = {}, bool without_side_effects = false) const override;
|
||||
virtual bool put(const JS::PropertyName&, JS::Value value, JS::Value receiver = {}) override;
|
||||
virtual void initialize_global_object() override;
|
||||
|
||||
|
|
|
@ -53,6 +53,14 @@ String MarkupGenerator::html_from_value(Value value)
|
|||
return output_html.to_string();
|
||||
}
|
||||
|
||||
String MarkupGenerator::html_from_error(Object& object)
|
||||
{
|
||||
StringBuilder output_html;
|
||||
HashTable<Object*> seen_objects;
|
||||
error_to_html(object, output_html, seen_objects);
|
||||
return output_html.to_string();
|
||||
}
|
||||
|
||||
void MarkupGenerator::value_to_html(Value value, StringBuilder& output_html, HashTable<Object*> seen_objects)
|
||||
{
|
||||
if (value.is_empty()) {
|
||||
|
@ -156,10 +164,16 @@ void MarkupGenerator::date_to_html(const Object& date, StringBuilder& html_outpu
|
|||
|
||||
void MarkupGenerator::error_to_html(const Object& object, StringBuilder& html_output, HashTable<Object*>&)
|
||||
{
|
||||
auto& error = static_cast<const Error&>(object);
|
||||
html_output.append(wrap_string_in_style(String::formatted("[{}]", error.name()), StyleType::Invalid));
|
||||
if (!error.message().is_empty()) {
|
||||
html_output.appendff(": {}", escape_html_entities(error.message()));
|
||||
auto name = object.get_without_side_effects("name").value_or(JS::js_undefined());
|
||||
auto message = object.get_without_side_effects("message").value_or(JS::js_undefined());
|
||||
if (name.is_accessor() || name.is_native_property() || message.is_accessor() || message.is_native_property()) {
|
||||
html_output.append(wrap_string_in_style(JS::Value(&object).to_string_without_side_effects(), StyleType::Invalid));
|
||||
} else {
|
||||
auto name_string = name.to_string_without_side_effects();
|
||||
auto message_string = message.to_string_without_side_effects();
|
||||
html_output.append(wrap_string_in_style(String::formatted("[{}]", name_string), StyleType::Invalid));
|
||||
if (!message_string.is_empty())
|
||||
html_output.appendff(": {}", escape_html_entities(message_string));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ class MarkupGenerator {
|
|||
public:
|
||||
static String html_from_source(const StringView&);
|
||||
static String html_from_value(Value);
|
||||
static String html_from_error(Object&);
|
||||
|
||||
private:
|
||||
enum class StyleType {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2021, Linus Groh <mail@linusgroh.de>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
@ -29,32 +30,34 @@
|
|||
|
||||
namespace JS {
|
||||
|
||||
Error* Error::create(GlobalObject& global_object, const FlyString& name, const String& message)
|
||||
Error* Error::create(GlobalObject& global_object, const String& message)
|
||||
{
|
||||
return global_object.heap().allocate<Error>(global_object, name, message, *global_object.error_prototype());
|
||||
auto& vm = global_object.vm();
|
||||
auto* error = global_object.heap().allocate<Error>(global_object, *global_object.error_prototype());
|
||||
if (!message.is_null())
|
||||
error->define_property(vm.names.message, js_string(vm, message));
|
||||
return error;
|
||||
}
|
||||
|
||||
Error::Error(const FlyString& name, const String& message, Object& prototype)
|
||||
Error::Error(Object& prototype)
|
||||
: Object(prototype)
|
||||
, m_name(name)
|
||||
, m_message(message)
|
||||
{
|
||||
}
|
||||
|
||||
Error::~Error()
|
||||
{
|
||||
}
|
||||
|
||||
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
|
||||
ClassName* ClassName::create(GlobalObject& global_object, const String& message) \
|
||||
{ \
|
||||
return global_object.heap().allocate<ClassName>(global_object, message, *global_object.snake_name##_prototype()); \
|
||||
} \
|
||||
ClassName::ClassName(const String& message, Object& prototype) \
|
||||
: Error(vm().names.ClassName, message, prototype) \
|
||||
{ \
|
||||
} \
|
||||
ClassName::~ClassName() { }
|
||||
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
|
||||
ClassName* ClassName::create(GlobalObject& global_object, const String& message) \
|
||||
{ \
|
||||
auto& vm = global_object.vm(); \
|
||||
auto* error = global_object.heap().allocate<ClassName>(global_object, *global_object.snake_name##_prototype()); \
|
||||
if (!message.is_null()) \
|
||||
error->define_property(vm.names.message, js_string(vm, message)); \
|
||||
return error; \
|
||||
} \
|
||||
\
|
||||
ClassName::ClassName(Object& prototype) \
|
||||
: Error(prototype) \
|
||||
{ \
|
||||
}
|
||||
|
||||
JS_ENUMERATE_ERROR_SUBCLASSES
|
||||
#undef __JS_ENUMERATE
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2021, Linus Groh <mail@linusgroh.de>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
@ -35,19 +36,10 @@ class Error : public Object {
|
|||
JS_OBJECT(Error, Object);
|
||||
|
||||
public:
|
||||
static Error* create(GlobalObject&, const FlyString& name, const String& message);
|
||||
static Error* create(GlobalObject&, const String& message = {});
|
||||
|
||||
Error(const FlyString& name, const String& message, Object& prototype);
|
||||
virtual ~Error() override;
|
||||
|
||||
const FlyString& name() const { return m_name; }
|
||||
const String& message() const { return m_message; }
|
||||
|
||||
void set_name(const FlyString& name) { m_name = name; }
|
||||
|
||||
private:
|
||||
FlyString m_name;
|
||||
String m_message;
|
||||
explicit Error(Object& prototype);
|
||||
virtual ~Error() override = default;
|
||||
};
|
||||
|
||||
#define DECLARE_ERROR_SUBCLASS(ClassName, snake_name, PrototypeName, ConstructorName) \
|
||||
|
@ -55,10 +47,10 @@ private:
|
|||
JS_OBJECT(ClassName, Error); \
|
||||
\
|
||||
public: \
|
||||
static ClassName* create(GlobalObject&, const String& message); \
|
||||
static ClassName* create(GlobalObject&, const String& message = {}); \
|
||||
\
|
||||
ClassName(const String& message, Object& prototype); \
|
||||
virtual ~ClassName() override; \
|
||||
explicit ClassName(Object& prototype); \
|
||||
virtual ~ClassName() override = default; \
|
||||
};
|
||||
|
||||
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
|
||||
* Copyright (c) 2020-2021, Linus Groh <mail@linusgroh.de>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
@ -43,10 +43,6 @@ void ErrorConstructor::initialize(GlobalObject& global_object)
|
|||
define_property(vm.names.length, Value(1), Attribute::Configurable);
|
||||
}
|
||||
|
||||
ErrorConstructor::~ErrorConstructor()
|
||||
{
|
||||
}
|
||||
|
||||
Value ErrorConstructor::call()
|
||||
{
|
||||
return construct(*this);
|
||||
|
@ -55,41 +51,46 @@ Value ErrorConstructor::call()
|
|||
Value ErrorConstructor::construct(Function&)
|
||||
{
|
||||
auto& vm = this->vm();
|
||||
String message = "";
|
||||
if (!vm.call_frame().arguments.is_empty() && !vm.call_frame().arguments[0].is_undefined()) {
|
||||
message = vm.call_frame().arguments[0].to_string(global_object());
|
||||
String message;
|
||||
if (!vm.argument(0).is_undefined()) {
|
||||
message = vm.argument(0).to_string(global_object());
|
||||
if (vm.exception())
|
||||
return {};
|
||||
}
|
||||
return Error::create(global_object(), vm.names.Error, message);
|
||||
return Error::create(global_object(), message);
|
||||
}
|
||||
|
||||
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
|
||||
ConstructorName::ConstructorName(GlobalObject& global_object) \
|
||||
: NativeFunction(*global_object.function_prototype()) \
|
||||
{ \
|
||||
} \
|
||||
void ConstructorName::initialize(GlobalObject& global_object) \
|
||||
{ \
|
||||
auto& vm = this->vm(); \
|
||||
NativeFunction::initialize(global_object); \
|
||||
define_property(vm.names.prototype, global_object.snake_name##_prototype(), 0); \
|
||||
define_property(vm.names.length, Value(1), Attribute::Configurable); \
|
||||
} \
|
||||
ConstructorName::~ConstructorName() { } \
|
||||
Value ConstructorName::call() \
|
||||
{ \
|
||||
return construct(*this); \
|
||||
} \
|
||||
Value ConstructorName::construct(Function&) \
|
||||
{ \
|
||||
String message = ""; \
|
||||
if (!vm().call_frame().arguments.is_empty() && !vm().call_frame().arguments[0].is_undefined()) { \
|
||||
message = vm().call_frame().arguments[0].to_string(global_object()); \
|
||||
if (vm().exception()) \
|
||||
return {}; \
|
||||
} \
|
||||
return ClassName::create(global_object(), message); \
|
||||
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
|
||||
ConstructorName::ConstructorName(GlobalObject& global_object) \
|
||||
: NativeFunction(*global_object.function_prototype()) \
|
||||
{ \
|
||||
} \
|
||||
\
|
||||
void ConstructorName::initialize(GlobalObject& global_object) \
|
||||
{ \
|
||||
auto& vm = this->vm(); \
|
||||
NativeFunction::initialize(global_object); \
|
||||
define_property(vm.names.prototype, global_object.snake_name##_prototype(), 0); \
|
||||
define_property(vm.names.length, Value(1), Attribute::Configurable); \
|
||||
} \
|
||||
\
|
||||
ConstructorName::~ConstructorName() { } \
|
||||
\
|
||||
Value ConstructorName::call() \
|
||||
{ \
|
||||
return construct(*this); \
|
||||
} \
|
||||
\
|
||||
Value ConstructorName::construct(Function&) \
|
||||
{ \
|
||||
auto& vm = this->vm(); \
|
||||
String message = ""; \
|
||||
if (!vm.argument(0).is_undefined()) { \
|
||||
message = vm.argument(0).to_string(global_object()); \
|
||||
if (vm.exception()) \
|
||||
return {}; \
|
||||
} \
|
||||
return ClassName::create(global_object(), message); \
|
||||
}
|
||||
|
||||
JS_ENUMERATE_ERROR_SUBCLASSES
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Linus Groh <mail@linusgroh.de>
|
||||
* Copyright (c) 2020-2021, Linus Groh <mail@linusgroh.de>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
@ -37,7 +37,7 @@ class ErrorConstructor final : public NativeFunction {
|
|||
public:
|
||||
explicit ErrorConstructor(GlobalObject&);
|
||||
virtual void initialize(GlobalObject&) override;
|
||||
virtual ~ErrorConstructor() override;
|
||||
virtual ~ErrorConstructor() override = default;
|
||||
|
||||
virtual Value call() override;
|
||||
virtual Value construct(Function& new_target) override;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Andreas Kling <kling@serenityos.org>
|
||||
* Copyright (c) 2021, Linus Groh <mail@linusgroh.de>
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
|
@ -25,7 +26,6 @@
|
|||
*/
|
||||
|
||||
#include <AK/Function.h>
|
||||
#include <LibJS/Heap/Heap.h>
|
||||
#include <LibJS/Runtime/Error.h>
|
||||
#include <LibJS/Runtime/ErrorPrototype.h>
|
||||
#include <LibJS/Runtime/GlobalObject.h>
|
||||
|
@ -44,85 +44,44 @@ void ErrorPrototype::initialize(GlobalObject& global_object)
|
|||
auto& vm = this->vm();
|
||||
Object::initialize(global_object);
|
||||
u8 attr = Attribute::Writable | Attribute::Configurable;
|
||||
define_native_property(vm.names.name, name_getter, name_setter, attr);
|
||||
define_native_property(vm.names.message, message_getter, {}, attr);
|
||||
define_property(vm.names.name, js_string(vm, "Error"), attr);
|
||||
define_property(vm.names.message, js_string(vm, ""), attr);
|
||||
define_native_function(vm.names.toString, to_string, 0, attr);
|
||||
}
|
||||
|
||||
ErrorPrototype::~ErrorPrototype()
|
||||
{
|
||||
}
|
||||
|
||||
JS_DEFINE_NATIVE_GETTER(ErrorPrototype::name_getter)
|
||||
{
|
||||
auto* this_object = vm.this_value(global_object).to_object(global_object);
|
||||
if (!this_object)
|
||||
return {};
|
||||
if (!is<Error>(this_object)) {
|
||||
vm.throw_exception<TypeError>(global_object, ErrorType::NotAn, "Error");
|
||||
return {};
|
||||
}
|
||||
return js_string(vm, static_cast<const Error*>(this_object)->name());
|
||||
}
|
||||
|
||||
JS_DEFINE_NATIVE_SETTER(ErrorPrototype::name_setter)
|
||||
{
|
||||
auto* this_object = vm.this_value(global_object).to_object(global_object);
|
||||
if (!this_object)
|
||||
return;
|
||||
if (!is<Error>(this_object)) {
|
||||
vm.throw_exception<TypeError>(global_object, ErrorType::NotAn, "Error");
|
||||
return;
|
||||
}
|
||||
auto name = value.to_string(global_object);
|
||||
if (vm.exception())
|
||||
return;
|
||||
static_cast<Error*>(this_object)->set_name(name);
|
||||
}
|
||||
|
||||
JS_DEFINE_NATIVE_GETTER(ErrorPrototype::message_getter)
|
||||
{
|
||||
auto* this_object = vm.this_value(global_object).to_object(global_object);
|
||||
if (!this_object)
|
||||
return {};
|
||||
if (!is<Error>(this_object)) {
|
||||
vm.throw_exception<TypeError>(global_object, ErrorType::NotAn, "Error");
|
||||
return {};
|
||||
}
|
||||
return js_string(vm, static_cast<const Error*>(this_object)->message());
|
||||
}
|
||||
|
||||
// 20.5.3.4 Error.prototype.toString, https://tc39.es/ecma262/#sec-error.prototype.tostring
|
||||
JS_DEFINE_NATIVE_FUNCTION(ErrorPrototype::to_string)
|
||||
{
|
||||
if (!vm.this_value(global_object).is_object()) {
|
||||
vm.throw_exception<TypeError>(global_object, ErrorType::NotAnObject, vm.this_value(global_object).to_string_without_side_effects());
|
||||
auto this_value = vm.this_value(global_object);
|
||||
if (!this_value.is_object()) {
|
||||
vm.throw_exception<TypeError>(global_object, ErrorType::NotAnObject, this_value.to_string_without_side_effects());
|
||||
return {};
|
||||
}
|
||||
auto& this_object = vm.this_value(global_object).as_object();
|
||||
auto& this_object = this_value.as_object();
|
||||
|
||||
String name = "Error";
|
||||
auto name_property = this_object.get(vm.names.name);
|
||||
auto name_property = this_object.get(vm.names.name).value_or(js_undefined());
|
||||
if (vm.exception())
|
||||
return {};
|
||||
if (!name_property.is_empty() && !name_property.is_undefined()) {
|
||||
if (!name_property.is_undefined()) {
|
||||
name = name_property.to_string(global_object);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
}
|
||||
|
||||
String message = "";
|
||||
auto message_property = this_object.get(vm.names.message);
|
||||
auto message_property = this_object.get(vm.names.message).value_or(js_undefined());
|
||||
if (vm.exception())
|
||||
return {};
|
||||
if (!message_property.is_empty() && !message_property.is_undefined()) {
|
||||
if (!message_property.is_undefined()) {
|
||||
message = message_property.to_string(global_object);
|
||||
if (vm.exception())
|
||||
return {};
|
||||
}
|
||||
|
||||
if (name.length() == 0)
|
||||
if (name.is_empty())
|
||||
return js_string(vm, message);
|
||||
if (message.length() == 0)
|
||||
if (message.is_empty())
|
||||
return js_string(vm, name);
|
||||
return js_string(vm, String::formatted("{}: {}", name, message));
|
||||
}
|
||||
|
@ -131,8 +90,7 @@ JS_DEFINE_NATIVE_FUNCTION(ErrorPrototype::to_string)
|
|||
PrototypeName::PrototypeName(GlobalObject& global_object) \
|
||||
: Object(*global_object.error_prototype()) \
|
||||
{ \
|
||||
} \
|
||||
PrototypeName::~PrototypeName() { }
|
||||
}
|
||||
|
||||
JS_ENUMERATE_ERROR_SUBCLASSES
|
||||
#undef __JS_ENUMERATE
|
||||
|
|
|
@ -36,15 +36,10 @@ class ErrorPrototype final : public Object {
|
|||
public:
|
||||
explicit ErrorPrototype(GlobalObject&);
|
||||
virtual void initialize(GlobalObject&) override;
|
||||
virtual ~ErrorPrototype() override;
|
||||
virtual ~ErrorPrototype() override = default;
|
||||
|
||||
private:
|
||||
JS_DECLARE_NATIVE_FUNCTION(to_string);
|
||||
|
||||
JS_DECLARE_NATIVE_GETTER(name_getter);
|
||||
JS_DECLARE_NATIVE_SETTER(name_setter);
|
||||
|
||||
JS_DECLARE_NATIVE_GETTER(message_getter);
|
||||
};
|
||||
|
||||
#define DECLARE_ERROR_SUBCLASS_PROTOTYPE(ClassName, snake_name, PrototypeName, ConstructorName) \
|
||||
|
@ -54,7 +49,7 @@ private:
|
|||
public: \
|
||||
explicit PrototypeName(GlobalObject&); \
|
||||
virtual void initialize(GlobalObject&) override { } \
|
||||
virtual ~PrototypeName() override; \
|
||||
virtual ~PrototypeName() override = default; \
|
||||
};
|
||||
|
||||
#define __JS_ENUMERATE(ClassName, snake_name, PrototypeName, ConstructorName, ArrayType) \
|
||||
|
|
|
@ -294,9 +294,16 @@ void VM::throw_exception(Exception* exception)
|
|||
{
|
||||
if (should_log_exceptions()) {
|
||||
auto value = exception->value();
|
||||
if (value.is_object() && is<Error>(value.as_object())) {
|
||||
auto& error = static_cast<Error&>(value.as_object());
|
||||
dbgln("Throwing JavaScript exception: [{}] {}", error.name(), error.message());
|
||||
if (value.is_object()) {
|
||||
auto& object = value.as_object();
|
||||
auto name = object.get_without_side_effects(names.name).value_or(js_undefined());
|
||||
auto message = object.get_without_side_effects(names.message).value_or(js_undefined());
|
||||
if (name.is_accessor() || name.is_native_property() || message.is_accessor() || message.is_native_property()) {
|
||||
// The result is not going to be useful, let's just print the value. This affects DOMExceptions, for example.
|
||||
dbgln("Throwing JavaScript exception: {}", value);
|
||||
} else {
|
||||
dbgln("Throwing JavaScript exception: [{}] {}", name, message);
|
||||
}
|
||||
} else {
|
||||
dbgln("Throwing JavaScript exception: {}", value);
|
||||
}
|
||||
|
|
|
@ -124,7 +124,7 @@ public:
|
|||
// Ensure we got some stack space left, so the next function call doesn't kill us.
|
||||
// This value is merely a guess and might need tweaking at a later point.
|
||||
if (m_stack_info.size_free() < 16 * KiB)
|
||||
throw_exception<Error>(global_object, "RuntimeError", "Call stack size limit exceeded");
|
||||
throw_exception<Error>(global_object, "Call stack size limit exceeded");
|
||||
else
|
||||
m_call_stack.append(&call_frame);
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ test("infinite recursion", () => {
|
|||
infiniteRecursion();
|
||||
} catch (e) {
|
||||
expect(e).toBeInstanceOf(Error);
|
||||
expect(e.name).toBe("RuntimeError");
|
||||
expect(e.name).toBe("Error");
|
||||
expect(e.message).toBe("Call stack size limit exceeded");
|
||||
}
|
||||
|
||||
|
|
|
@ -31,12 +31,12 @@
|
|||
|
||||
namespace Web::Bindings {
|
||||
|
||||
JS::Value CSSStyleDeclarationWrapper::get(const JS::PropertyName& name, JS::Value receiver) const
|
||||
JS::Value CSSStyleDeclarationWrapper::get(const JS::PropertyName& name, JS::Value receiver, bool without_side_effects) const
|
||||
{
|
||||
// FIXME: These should actually use camelCase versions of the property names!
|
||||
auto property_id = CSS::property_id_from_string(name.to_string());
|
||||
if (property_id == CSS::PropertyID::Invalid)
|
||||
return Base::get(name, receiver);
|
||||
return Base::get(name, receiver, without_side_effects);
|
||||
for (auto& property : impl().properties()) {
|
||||
if (property.property_id == property_id)
|
||||
return js_string(vm(), property.value->to_string());
|
||||
|
|
|
@ -761,7 +761,7 @@ public:
|
|||
|
||||
if (interface.extended_attributes.contains("CustomGet")) {
|
||||
generator.append(R"~~~(
|
||||
virtual JS::Value get(const JS::PropertyName&, JS::Value receiver = {}) const override;
|
||||
virtual JS::Value get(const JS::PropertyName&, JS::Value receiver = {}, bool without_side_effects = false) const override;
|
||||
)~~~");
|
||||
}
|
||||
if (interface.extended_attributes.contains("CustomPut")) {
|
||||
|
|
|
@ -54,16 +54,15 @@ void WebContentConsoleClient::handle_input(const String& js_source)
|
|||
}
|
||||
|
||||
if (m_interpreter->exception()) {
|
||||
output_html.append("Uncaught exception: ");
|
||||
auto error = m_interpreter->exception()->value();
|
||||
if (error.is_object() && is<Web::Bindings::DOMExceptionWrapper>(error.as_object())) {
|
||||
auto& dom_exception_wrapper = static_cast<Web::Bindings::DOMExceptionWrapper&>(error.as_object());
|
||||
error = JS::Error::create(m_interpreter->global_object(), dom_exception_wrapper.impl().name(), dom_exception_wrapper.impl().message());
|
||||
}
|
||||
output_html.append(JS::MarkupGenerator::html_from_value(error));
|
||||
print_html(output_html.string_view());
|
||||
|
||||
auto* exception = m_interpreter->exception();
|
||||
m_interpreter->vm().clear_exception();
|
||||
output_html.append("Uncaught exception: ");
|
||||
auto error = exception->value();
|
||||
if (error.is_object())
|
||||
output_html.append(JS::MarkupGenerator::html_from_error(error.as_object()));
|
||||
else
|
||||
output_html.append(JS::MarkupGenerator::html_from_value(error));
|
||||
print_html(output_html.string_view());
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -239,18 +239,25 @@ static void print_function(const JS::Object& object, HashTable<JS::Object*>&)
|
|||
out(" {}", static_cast<const JS::NativeFunction&>(object).name());
|
||||
}
|
||||
|
||||
static void print_date(const JS::Object& date, HashTable<JS::Object*>&)
|
||||
static void print_date(const JS::Object& object, HashTable<JS::Object*>&)
|
||||
{
|
||||
print_type("Date");
|
||||
out(" \033[34;1m{}\033[0m", static_cast<const JS::Date&>(date).string());
|
||||
out(" \033[34;1m{}\033[0m", static_cast<const JS::Date&>(object).string());
|
||||
}
|
||||
|
||||
static void print_error(const JS::Object& object, HashTable<JS::Object*>&)
|
||||
static void print_error(const JS::Object& object, HashTable<JS::Object*>& seen_objects)
|
||||
{
|
||||
auto& error = static_cast<const JS::Error&>(object);
|
||||
print_type(error.name());
|
||||
if (!error.message().is_empty())
|
||||
out(" \033[31;1m{}\033[0m", error.message());
|
||||
auto name = object.get_without_side_effects(vm->names.name).value_or(JS::js_undefined());
|
||||
auto message = object.get_without_side_effects(vm->names.message).value_or(JS::js_undefined());
|
||||
if (name.is_accessor() || name.is_native_property() || message.is_accessor() || message.is_native_property()) {
|
||||
print_value(&object, seen_objects);
|
||||
} else {
|
||||
auto name_string = name.to_string_without_side_effects();
|
||||
auto message_string = message.to_string_without_side_effects();
|
||||
print_type(name_string);
|
||||
if (!message_string.is_empty())
|
||||
out(" \033[31;1m{}\033[0m", message_string);
|
||||
}
|
||||
}
|
||||
|
||||
static void print_regexp_object(const JS::Object& object, HashTable<JS::Object*>&)
|
||||
|
@ -498,9 +505,11 @@ static bool parse_and_run(JS::Interpreter& interpreter, const StringView& source
|
|||
}
|
||||
|
||||
auto handle_exception = [&] {
|
||||
auto* exception = vm->exception();
|
||||
vm->clear_exception();
|
||||
out("Uncaught exception: ");
|
||||
print(vm->exception()->value());
|
||||
auto trace = vm->exception()->trace();
|
||||
print(exception->value());
|
||||
auto& trace = exception->trace();
|
||||
if (trace.size() > 1) {
|
||||
unsigned repetitions = 0;
|
||||
for (size_t i = 0; i < trace.size(); ++i) {
|
||||
|
@ -522,7 +531,6 @@ static bool parse_and_run(JS::Interpreter& interpreter, const StringView& source
|
|||
repetitions = 0;
|
||||
}
|
||||
}
|
||||
vm->clear_exception();
|
||||
};
|
||||
|
||||
if (vm->exception()) {
|
||||
|
@ -532,8 +540,8 @@ static bool parse_and_run(JS::Interpreter& interpreter, const StringView& source
|
|||
if (s_print_last_result)
|
||||
print(vm->last_value());
|
||||
if (vm->exception()) {
|
||||
return false;
|
||||
handle_exception();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -738,7 +746,7 @@ int main(int argc, char** argv)
|
|||
OwnPtr<JS::Interpreter> interpreter;
|
||||
|
||||
interrupt_interpreter = [&] {
|
||||
auto error = JS::Error::create(interpreter->global_object(), "Error", "Received SIGINT");
|
||||
auto error = JS::Error::create(interpreter->global_object(), "Received SIGINT");
|
||||
vm->throw_exception(interpreter->global_object(), error);
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue