diff --git a/Base/home/anon/js/throw.js b/Base/home/anon/js/throw.js new file mode 100644 index 00000000000..67c891f571e --- /dev/null +++ b/Base/home/anon/js/throw.js @@ -0,0 +1,5 @@ +try { + throw 123; +} catch (e) { + console.log(e); +} diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index b6d48ad82f2..4528d4a4bd6 100644 --- a/Libraries/LibJS/AST.cpp +++ b/Libraries/LibJS/AST.cpp @@ -480,7 +480,7 @@ Value Identifier::execute(Interpreter& interpreter) const { 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 interpreter.throw_exception("ReferenceError", String::format("'%s' not known", string().characters())); return value; } @@ -762,13 +762,19 @@ void CatchClause::dump(int indent) const body().dump(indent + 1); } +void ThrowStatement::dump(int indent) const +{ + ASTNode::dump(indent); + argument().dump(indent + 1); +} + 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) } }; + Vector arguments { { m_handler->parameter(), exception->value() } }; interpreter.run(m_handler->body(), move(arguments)); } } @@ -786,4 +792,10 @@ Value CatchClause::execute(Interpreter&) const return {}; } +Value ThrowStatement::execute(Interpreter& interrupt) const +{ + auto value = m_argument->execute(interrupt); + return interrupt.throw_exception(value); +} + } diff --git a/Libraries/LibJS/AST.h b/Libraries/LibJS/AST.h index 6a8e3f9f9c9..34a3fd6b4ca 100644 --- a/Libraries/LibJS/AST.h +++ b/Libraries/LibJS/AST.h @@ -675,4 +675,22 @@ private: RefPtr m_finalizer; }; +class ThrowStatement final : public Statement { +public: + explicit ThrowStatement(NonnullRefPtr argument) + : m_argument(move(argument)) + { + } + + const Expression& argument() const { return m_argument; } + + virtual void dump(int indent) const override; + virtual Value execute(Interpreter&) const override; + +private: + virtual const char* class_name() const override { return "ThrowStatement"; } + + NonnullRefPtr m_argument; +}; + } diff --git a/Libraries/LibJS/Forward.h b/Libraries/LibJS/Forward.h index aa4079bc618..47ee9a13f9c 100644 --- a/Libraries/LibJS/Forward.h +++ b/Libraries/LibJS/Forward.h @@ -32,6 +32,7 @@ class ASTNode; class Argument; class Cell; class Error; +class Exception; class Expression; class Function; class HandleImpl; diff --git a/Libraries/LibJS/Interpreter.cpp b/Libraries/LibJS/Interpreter.cpp index 6a5bee19384..1dd5cc46db3 100644 --- a/Libraries/LibJS/Interpreter.cpp +++ b/Libraries/LibJS/Interpreter.cpp @@ -188,7 +188,7 @@ Value Interpreter::call(Function* function, Value this_value, const Vector #include #include -#include #include #include #include +#include #include namespace JS { @@ -107,9 +108,20 @@ public: Object* array_prototype() { return m_array_prototype; } Object* error_prototype() { return m_error_prototype; } - Error* exception() { return m_exception; } + Exception* exception() { return m_exception; } void clear_exception() { m_exception = nullptr; } - Value throw_exception(Error*); + + template + Value throw_exception(Args&&... args) + { + return throw_exception(heap().allocate(forward(args)...)); + } + + Value throw_exception(Exception*); + Value throw_exception(Value value) + { + return throw_exception(heap().allocate(value)); + } private: Heap m_heap; @@ -123,7 +135,7 @@ private: Object* m_array_prototype { nullptr }; Object* m_error_prototype { nullptr }; - Error* m_exception { nullptr }; + Exception* m_exception { nullptr }; ScopeType m_unwind_until { ScopeType::None }; }; diff --git a/Libraries/LibJS/Lexer.cpp b/Libraries/LibJS/Lexer.cpp index 15aa4c80266..97ee8ec901e 100644 --- a/Libraries/LibJS/Lexer.cpp +++ b/Libraries/LibJS/Lexer.cpp @@ -62,6 +62,7 @@ Lexer::Lexer(StringView source) s_keywords.set("null", TokenType::NullLiteral); s_keywords.set("undefined", TokenType::UndefinedLiteral); s_keywords.set("return", TokenType::Return); + s_keywords.set("throw", TokenType::Throw); s_keywords.set("true", TokenType::BoolLiteral); s_keywords.set("try", TokenType::Try); s_keywords.set("typeof", TokenType::Typeof); diff --git a/Libraries/LibJS/Makefile b/Libraries/LibJS/Makefile index fd0b2551155..97d35be52d4 100644 --- a/Libraries/LibJS/Makefile +++ b/Libraries/LibJS/Makefile @@ -12,6 +12,7 @@ OBJS = \ Runtime/ConsoleObject.o \ Runtime/Error.o \ Runtime/ErrorPrototype.o \ + Runtime/Exception.o \ Runtime/Function.o \ Runtime/GlobalObject.o \ Runtime/MathObject.o \ diff --git a/Libraries/LibJS/Parser.cpp b/Libraries/LibJS/Parser.cpp index 77e892e553d..7b1769ac4a9 100644 --- a/Libraries/LibJS/Parser.cpp +++ b/Libraries/LibJS/Parser.cpp @@ -197,6 +197,8 @@ NonnullRefPtr Parser::parse_statement() return parse_for_statement(); case TokenType::If: return parse_if_statement(); + case TokenType::Throw: + return parse_throw_statement(); case TokenType::Try: return parse_try_statement(); default: @@ -520,6 +522,12 @@ NonnullRefPtr Parser::parse_variable_declaration() return create_ast_node(create_ast_node(name), move(initializer), declaration_type); } +NonnullRefPtr Parser::parse_throw_statement() +{ + consume(TokenType::Throw); + return create_ast_node(parse_expression(0)); +} + NonnullRefPtr Parser::parse_try_statement() { consume(TokenType::Try); @@ -700,6 +708,7 @@ bool Parser::match_statement() const || type == TokenType::Delete || type == TokenType::Do || type == TokenType::If + || type == TokenType::Throw || type == TokenType::Try || type == TokenType::While || type == TokenType::For diff --git a/Libraries/LibJS/Parser.h b/Libraries/LibJS/Parser.h index 7d131347b7a..7cbd614b32e 100644 --- a/Libraries/LibJS/Parser.h +++ b/Libraries/LibJS/Parser.h @@ -52,6 +52,7 @@ public: NonnullRefPtr parse_variable_declaration(); NonnullRefPtr parse_for_statement(); NonnullRefPtr parse_if_statement(); + NonnullRefPtr parse_throw_statement(); NonnullRefPtr parse_try_statement(); NonnullRefPtr parse_catch_clause(); diff --git a/Libraries/LibJS/Runtime/Exception.cpp b/Libraries/LibJS/Runtime/Exception.cpp new file mode 100644 index 00000000000..a11116a602e --- /dev/null +++ b/Libraries/LibJS/Runtime/Exception.cpp @@ -0,0 +1,46 @@ +/* + * 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 + +namespace JS { + +Exception::Exception(Value value) + : m_value(value) +{ +} + +Exception::~Exception() +{ +} + +void Exception::visit_children(Visitor& visitor) +{ + Cell::visit_children(visitor); + visitor.visit(m_value); +} + +} diff --git a/Libraries/LibJS/Runtime/Exception.h b/Libraries/LibJS/Runtime/Exception.h new file mode 100644 index 00000000000..14082c310fd --- /dev/null +++ b/Libraries/LibJS/Runtime/Exception.h @@ -0,0 +1,46 @@ +/* + * 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 Exception : public Cell { +public: + explicit Exception(Value); + virtual ~Exception() override; + + Value value() const { return m_value; } + +private: + virtual const char* class_name() const override { return "Exception"; } + virtual void visit_children(Visitor&) override; + + Value m_value; +}; + +} diff --git a/Libraries/LibJS/Token.h b/Libraries/LibJS/Token.h index d278a6f4e5d..880834d3015 100644 --- a/Libraries/LibJS/Token.h +++ b/Libraries/LibJS/Token.h @@ -105,6 +105,7 @@ enum class TokenType { Slash, SlashEquals, StringLiteral, + Throw, Tilde, Try, Typeof,