diff --git a/Userland/Applications/Browser/ConsoleWidget.cpp b/Userland/Applications/Browser/ConsoleWidget.cpp index 1cba4760e6b..8c27df29d3f 100644 --- a/Userland/Applications/Browser/ConsoleWidget.cpp +++ b/Userland/Applications/Browser/ConsoleWidget.cpp @@ -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(error.as_object())) { - auto& dom_exception_wrapper = static_cast(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; } diff --git a/Userland/Applications/Spreadsheet/JSIntegration.cpp b/Userland/Applications/Spreadsheet/JSIntegration.cpp index b62a5985e9a..0b6e700bbc5 100644 --- a/Userland/Applications/Spreadsheet/JSIntegration.cpp +++ b/Userland/Applications/Spreadsheet/JSIntegration.cpp @@ -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) diff --git a/Userland/Applications/Spreadsheet/JSIntegration.h b/Userland/Applications/Spreadsheet/JSIntegration.h index 9cb7e0bd4cc..8c786bdc5d6 100644 --- a/Userland/Applications/Spreadsheet/JSIntegration.h +++ b/Userland/Applications/Spreadsheet/JSIntegration.h @@ -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; diff --git a/Userland/Libraries/LibJS/MarkupGenerator.cpp b/Userland/Libraries/LibJS/MarkupGenerator.cpp index 6766b1b8589..fb8e8fb6e50 100644 --- a/Userland/Libraries/LibJS/MarkupGenerator.cpp +++ b/Userland/Libraries/LibJS/MarkupGenerator.cpp @@ -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 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 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&) { - auto& error = static_cast(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)); } } diff --git a/Userland/Libraries/LibJS/MarkupGenerator.h b/Userland/Libraries/LibJS/MarkupGenerator.h index 834c094539d..467f9f2ac94 100644 --- a/Userland/Libraries/LibJS/MarkupGenerator.h +++ b/Userland/Libraries/LibJS/MarkupGenerator.h @@ -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 { diff --git a/Userland/Libraries/LibJS/Runtime/Error.cpp b/Userland/Libraries/LibJS/Runtime/Error.cpp index 7f30ab872ce..7caa6f91431 100644 --- a/Userland/Libraries/LibJS/Runtime/Error.cpp +++ b/Userland/Libraries/LibJS/Runtime/Error.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, Andreas Kling + * Copyright (c) 2021, Linus Groh * 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(global_object, name, message, *global_object.error_prototype()); + auto& vm = global_object.vm(); + auto* error = global_object.heap().allocate(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(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(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 diff --git a/Userland/Libraries/LibJS/Runtime/Error.h b/Userland/Libraries/LibJS/Runtime/Error.h index 3605825f1d9..0c7e9ac66cd 100644 --- a/Userland/Libraries/LibJS/Runtime/Error.h +++ b/Userland/Libraries/LibJS/Runtime/Error.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, Andreas Kling + * Copyright (c) 2021, Linus Groh * 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) \ diff --git a/Userland/Libraries/LibJS/Runtime/ErrorConstructor.cpp b/Userland/Libraries/LibJS/Runtime/ErrorConstructor.cpp index 549c74855c4..bc0ff5965f3 100644 --- a/Userland/Libraries/LibJS/Runtime/ErrorConstructor.cpp +++ b/Userland/Libraries/LibJS/Runtime/ErrorConstructor.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Linus Groh + * Copyright (c) 2020-2021, Linus Groh * 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 diff --git a/Userland/Libraries/LibJS/Runtime/ErrorConstructor.h b/Userland/Libraries/LibJS/Runtime/ErrorConstructor.h index 2626623aaa1..c24cca73cb8 100644 --- a/Userland/Libraries/LibJS/Runtime/ErrorConstructor.h +++ b/Userland/Libraries/LibJS/Runtime/ErrorConstructor.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020, Linus Groh + * Copyright (c) 2020-2021, Linus Groh * 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; diff --git a/Userland/Libraries/LibJS/Runtime/ErrorPrototype.cpp b/Userland/Libraries/LibJS/Runtime/ErrorPrototype.cpp index 68affb57455..9b8883eac35 100644 --- a/Userland/Libraries/LibJS/Runtime/ErrorPrototype.cpp +++ b/Userland/Libraries/LibJS/Runtime/ErrorPrototype.cpp @@ -1,5 +1,6 @@ /* * Copyright (c) 2020, Andreas Kling + * Copyright (c) 2021, Linus Groh * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -25,7 +26,6 @@ */ #include -#include #include #include #include @@ -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(this_object)) { - vm.throw_exception(global_object, ErrorType::NotAn, "Error"); - return {}; - } - return js_string(vm, static_cast(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(this_object)) { - vm.throw_exception(global_object, ErrorType::NotAn, "Error"); - return; - } - auto name = value.to_string(global_object); - if (vm.exception()) - return; - static_cast(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(this_object)) { - vm.throw_exception(global_object, ErrorType::NotAn, "Error"); - return {}; - } - return js_string(vm, static_cast(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(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(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 diff --git a/Userland/Libraries/LibJS/Runtime/ErrorPrototype.h b/Userland/Libraries/LibJS/Runtime/ErrorPrototype.h index 3b461942ab6..3a996f5db76 100644 --- a/Userland/Libraries/LibJS/Runtime/ErrorPrototype.h +++ b/Userland/Libraries/LibJS/Runtime/ErrorPrototype.h @@ -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) \ diff --git a/Userland/Libraries/LibJS/Runtime/VM.cpp b/Userland/Libraries/LibJS/Runtime/VM.cpp index 1ecf5377bd1..8b1a5c41a2e 100644 --- a/Userland/Libraries/LibJS/Runtime/VM.cpp +++ b/Userland/Libraries/LibJS/Runtime/VM.cpp @@ -294,9 +294,16 @@ void VM::throw_exception(Exception* exception) { if (should_log_exceptions()) { auto value = exception->value(); - if (value.is_object() && is(value.as_object())) { - auto& error = static_cast(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); } diff --git a/Userland/Libraries/LibJS/Runtime/VM.h b/Userland/Libraries/LibJS/Runtime/VM.h index 1988e20afa1..702ec9580af 100644 --- a/Userland/Libraries/LibJS/Runtime/VM.h +++ b/Userland/Libraries/LibJS/Runtime/VM.h @@ -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(global_object, "RuntimeError", "Call stack size limit exceeded"); + throw_exception(global_object, "Call stack size limit exceeded"); else m_call_stack.append(&call_frame); } diff --git a/Userland/Libraries/LibJS/Tests/runtime-error-call-stack-size.js b/Userland/Libraries/LibJS/Tests/runtime-error-call-stack-size.js index 177bb2d675d..c4e3d6f5f9c 100644 --- a/Userland/Libraries/LibJS/Tests/runtime-error-call-stack-size.js +++ b/Userland/Libraries/LibJS/Tests/runtime-error-call-stack-size.js @@ -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"); } diff --git a/Userland/Libraries/LibWeb/Bindings/CSSStyleDeclarationWrapperCustom.cpp b/Userland/Libraries/LibWeb/Bindings/CSSStyleDeclarationWrapperCustom.cpp index 46fd9057870..3d3817e7fb9 100644 --- a/Userland/Libraries/LibWeb/Bindings/CSSStyleDeclarationWrapperCustom.cpp +++ b/Userland/Libraries/LibWeb/Bindings/CSSStyleDeclarationWrapperCustom.cpp @@ -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()); diff --git a/Userland/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp b/Userland/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp index a9f9c8b9009..611fe08135a 100644 --- a/Userland/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp +++ b/Userland/Libraries/LibWeb/CodeGenerators/WrapperGenerator.cpp @@ -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")) { diff --git a/Userland/Services/WebContent/WebContentConsoleClient.cpp b/Userland/Services/WebContent/WebContentConsoleClient.cpp index 74d348a9659..f8916fd3b3c 100644 --- a/Userland/Services/WebContent/WebContentConsoleClient.cpp +++ b/Userland/Services/WebContent/WebContentConsoleClient.cpp @@ -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(error.as_object())) { - auto& dom_exception_wrapper = static_cast(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; } diff --git a/Userland/Utilities/js.cpp b/Userland/Utilities/js.cpp index 49e22f313d6..5cfb28e4b13 100644 --- a/Userland/Utilities/js.cpp +++ b/Userland/Utilities/js.cpp @@ -239,18 +239,25 @@ static void print_function(const JS::Object& object, HashTable&) out(" {}", static_cast(object).name()); } -static void print_date(const JS::Object& date, HashTable&) +static void print_date(const JS::Object& object, HashTable&) { print_type("Date"); - out(" \033[34;1m{}\033[0m", static_cast(date).string()); + out(" \033[34;1m{}\033[0m", static_cast(object).string()); } -static void print_error(const JS::Object& object, HashTable&) +static void print_error(const JS::Object& object, HashTable& seen_objects) { - auto& error = static_cast(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&) @@ -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 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); };