diff --git a/Base/home/anon/js/try.js b/Base/home/anon/js/try.js index 26e950aa06..7b4f892ca4 100644 --- a/Base/home/anon/js/try.js +++ b/Base/home/anon/js/try.js @@ -4,6 +4,7 @@ try { console.log("not me"); } catch (e) { console.log("catch"); + console.log(e.name); } finally { console.log("finally"); } diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index 936f48d18c..b6d48ad82f 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -30,6 +30,7 @@ #include #include #include +#include #include #include #include @@ -62,16 +63,29 @@ Value ExpressionStatement::execute(Interpreter& interpreter) const Value CallExpression::execute(Interpreter& interpreter) const { auto callee = m_callee->execute(interpreter); + if (interpreter.exception()) + return {}; + ASSERT(callee.is_object()); ASSERT(callee.as_object()->is_function()); auto* function = static_cast(callee.as_object()); auto& call_frame = interpreter.push_call_frame(); - for (size_t i = 0; i < m_arguments.size(); ++i) + for (size_t i = 0; i < m_arguments.size(); ++i) { call_frame.arguments.append(m_arguments[i].execute(interpreter)); + if (interpreter.exception()) + return {}; + } - if (m_callee->is_member_expression()) - call_frame.this_value = static_cast(*m_callee).object().execute(interpreter).to_object(interpreter.heap()); + if (m_callee->is_member_expression()) { + auto object_value = static_cast(*m_callee).object().execute(interpreter); + if (interpreter.exception()) + return {}; + auto this_value = object_value.to_object(interpreter.heap()); + if (interpreter.exception()) + return {}; + call_frame.this_value = this_value; + } auto result = function->call(interpreter, call_frame.arguments); interpreter.pop_call_frame(); @@ -464,7 +478,10 @@ void ForStatement::dump(int indent) const Value Identifier::execute(Interpreter& interpreter) const { - return interpreter.get_variable(string()); + auto value = interpreter.get_variable(string()); + if (value.is_undefined()) + return interpreter.throw_exception(interpreter.heap().allocate("ReferenceError", String::format("'%s' not known", string().characters()))); + return value; } void Identifier::dump(int indent) const @@ -745,13 +762,27 @@ void CatchClause::dump(int indent) const body().dump(indent + 1); } -Value TryStatement::execute(Interpreter&) const +Value TryStatement::execute(Interpreter& interpreter) const { + interpreter.run(block(), {}, ScopeType::Try); + if (auto* exception = interpreter.exception()) { + if (m_handler) { + interpreter.clear_exception(); + Vector arguments { { m_handler->parameter(), Value(exception) } }; + interpreter.run(m_handler->body(), move(arguments)); + } + } + + if (m_finalizer) + m_finalizer->execute(interpreter); + return {}; } Value CatchClause::execute(Interpreter&) const { + // NOTE: CatchClause execution is handled by TryStatement. + ASSERT_NOT_REACHED(); return {}; } diff --git a/Libraries/LibJS/Forward.h b/Libraries/LibJS/Forward.h index 359d6f6ab4..aa4079bc61 100644 --- a/Libraries/LibJS/Forward.h +++ b/Libraries/LibJS/Forward.h @@ -31,6 +31,7 @@ namespace JS { class ASTNode; class Argument; class Cell; +class Error; class Expression; class Function; class HandleImpl; diff --git a/Libraries/LibJS/Interpreter.cpp b/Libraries/LibJS/Interpreter.cpp index f602336bc4..6a5bee1938 100644 --- a/Libraries/LibJS/Interpreter.cpp +++ b/Libraries/LibJS/Interpreter.cpp @@ -28,6 +28,8 @@ #include #include #include +#include +#include #include #include #include @@ -44,6 +46,7 @@ Interpreter::Interpreter() m_object_prototype = heap().allocate(); m_string_prototype = heap().allocate(); m_array_prototype = heap().allocate(); + m_error_prototype = heap().allocate(); } Interpreter::~Interpreter() @@ -154,6 +157,9 @@ void Interpreter::gather_roots(Badge, HashTable& roots) roots.set(m_string_prototype); roots.set(m_object_prototype); roots.set(m_array_prototype); + roots.set(m_error_prototype); + + roots.set(m_exception); for (auto& scope : m_scope_stack) { for (auto& it : scope.variables) { @@ -182,4 +188,11 @@ Value Interpreter::call(Function* function, Value this_value, const Vector + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +namespace JS { + +Error::Error(const FlyString& name, const String& message) + : m_name(name) + , m_message(message) +{ + set_prototype(interpreter().error_prototype()); +} + +Error::~Error() +{ +} + +} diff --git a/Libraries/LibJS/Runtime/Error.h b/Libraries/LibJS/Runtime/Error.h new file mode 100644 index 0000000000..411454217e --- /dev/null +++ b/Libraries/LibJS/Runtime/Error.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2020, Andreas Kling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +namespace JS { + +class Error : public Object { +public: + Error(const FlyString& name, const String& message); + virtual ~Error() override; + + const FlyString& name() const { return m_name; } + const String& message() const { return m_message; } + +private: + virtual bool is_error() const final { return true; } + virtual const char* class_name() const override { return "Error"; } + + FlyString m_name; + String m_message; +}; + +} diff --git a/Libraries/LibJS/Runtime/ErrorPrototype.cpp b/Libraries/LibJS/Runtime/ErrorPrototype.cpp new file mode 100644 index 0000000000..27b396f211 --- /dev/null +++ b/Libraries/LibJS/Runtime/ErrorPrototype.cpp @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2020, Andreas Kling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include + +namespace JS { + +ErrorPrototype::ErrorPrototype() +{ + put_native_property( + "name", [](Object* this_object) { + dbg() << "Error.prototype.name on " << this_object; + ASSERT(this_object); + ASSERT(this_object->is_error()); + return js_string(this_object->heap(), static_cast(this_object)->name()); + }, + nullptr); + + put_native_property( + "message", [](Object* this_object) { + ASSERT(this_object); + ASSERT(this_object->is_error()); + return js_string(this_object->heap(), static_cast(this_object)->message()); + }, + nullptr); +} + +ErrorPrototype::~ErrorPrototype() +{ +} + +} diff --git a/Libraries/LibJS/Runtime/ErrorPrototype.h b/Libraries/LibJS/Runtime/ErrorPrototype.h new file mode 100644 index 0000000000..8775b6aa51 --- /dev/null +++ b/Libraries/LibJS/Runtime/ErrorPrototype.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2020, Andreas Kling + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this + * list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#pragma once + +#include + +namespace JS { + +class ErrorPrototype final : public Object { +public: + ErrorPrototype(); + virtual ~ErrorPrototype() override; + +private: + virtual const char* class_name() const override { return "ErrorPrototype"; } +}; + +} diff --git a/Libraries/LibJS/Runtime/Object.h b/Libraries/LibJS/Runtime/Object.h index fd98b0e44f..af11e35af5 100644 --- a/Libraries/LibJS/Runtime/Object.h +++ b/Libraries/LibJS/Runtime/Object.h @@ -49,6 +49,7 @@ public: void put_native_function(const FlyString& property_name, AK::Function)>); void put_native_property(const FlyString& property_name, AK::Function getter, AK::Function setter); + virtual bool is_error() const { return false; } virtual bool is_array() const { return false; } virtual bool is_function() const { return false; } virtual bool is_native_function() const { return false; }