diff options
author | Andreas Kling <kling@serenityos.org> | 2020-03-24 22:03:50 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2020-03-24 22:21:58 +0100 |
commit | faddf3a1db6d9c8bb4106eb2259f91fe8e58b817 (patch) | |
tree | ad93acf4f78d132e27259fc4ef5109e3266f9efe /Libraries | |
parent | db024a9cb147731cfb14ef60a862a3e392eedc9b (diff) | |
download | serenity-faddf3a1db6d9c8bb4106eb2259f91fe8e58b817.zip |
LibJS: Implement "throw"
You can now throw an expression to the nearest catcher! :^)
To support throwing arbitrary values, I added an Exception class that
sits as a wrapper around whatever is thrown. In the future it will be
a logical place to store a call stack.
Diffstat (limited to 'Libraries')
-rw-r--r-- | Libraries/LibJS/AST.cpp | 16 | ||||
-rw-r--r-- | Libraries/LibJS/AST.h | 18 | ||||
-rw-r--r-- | Libraries/LibJS/Forward.h | 1 | ||||
-rw-r--r-- | Libraries/LibJS/Interpreter.cpp | 2 | ||||
-rw-r--r-- | Libraries/LibJS/Interpreter.h | 20 | ||||
-rw-r--r-- | Libraries/LibJS/Lexer.cpp | 1 | ||||
-rw-r--r-- | Libraries/LibJS/Makefile | 1 | ||||
-rw-r--r-- | Libraries/LibJS/Parser.cpp | 9 | ||||
-rw-r--r-- | Libraries/LibJS/Parser.h | 1 | ||||
-rw-r--r-- | Libraries/LibJS/Runtime/Exception.cpp | 46 | ||||
-rw-r--r-- | Libraries/LibJS/Runtime/Exception.h | 46 | ||||
-rw-r--r-- | Libraries/LibJS/Token.h | 1 |
12 files changed, 155 insertions, 7 deletions
diff --git a/Libraries/LibJS/AST.cpp b/Libraries/LibJS/AST.cpp index b6d48ad82f..4528d4a4bd 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<Error>("ReferenceError", String::format("'%s' not known", string().characters()))); + return interpreter.throw_exception<Error>("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<Argument> arguments { { m_handler->parameter(), Value(exception) } }; + Vector<Argument> 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 6a8e3f9f9c..34a3fd6b4c 100644 --- a/Libraries/LibJS/AST.h +++ b/Libraries/LibJS/AST.h @@ -675,4 +675,22 @@ private: RefPtr<BlockStatement> m_finalizer; }; +class ThrowStatement final : public Statement { +public: + explicit ThrowStatement(NonnullRefPtr<Expression> 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<Expression> m_argument; +}; + } diff --git a/Libraries/LibJS/Forward.h b/Libraries/LibJS/Forward.h index aa4079bc61..47ee9a13f9 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 6a5bee1938..1dd5cc46db 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<Value return result; } -Value Interpreter::throw_exception(Error* exception) +Value Interpreter::throw_exception(Exception* exception) { m_exception = exception; unwind(ScopeType::Try); diff --git a/Libraries/LibJS/Interpreter.h b/Libraries/LibJS/Interpreter.h index 6c2beecd9e..fe806f9a4f 100644 --- a/Libraries/LibJS/Interpreter.h +++ b/Libraries/LibJS/Interpreter.h @@ -26,12 +26,13 @@ #pragma once +#include <AK/FlyString.h> #include <AK/HashMap.h> #include <AK/String.h> -#include <AK/FlyString.h> #include <AK/Vector.h> #include <LibJS/Forward.h> #include <LibJS/Heap/Heap.h> +#include <LibJS/Runtime/Exception.h> #include <LibJS/Runtime/Value.h> 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<typename T, typename... Args> + Value throw_exception(Args&&... args) + { + return throw_exception(heap().allocate<T>(forward<Args>(args)...)); + } + + Value throw_exception(Exception*); + Value throw_exception(Value value) + { + return throw_exception(heap().allocate<Exception>(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 15aa4c8026..97ee8ec901 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 fd0b255115..97d35be52d 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 77e892e553..7b1769ac4a 100644 --- a/Libraries/LibJS/Parser.cpp +++ b/Libraries/LibJS/Parser.cpp @@ -197,6 +197,8 @@ NonnullRefPtr<Statement> 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<VariableDeclaration> Parser::parse_variable_declaration() return create_ast_node<VariableDeclaration>(create_ast_node<Identifier>(name), move(initializer), declaration_type); } +NonnullRefPtr<ThrowStatement> Parser::parse_throw_statement() +{ + consume(TokenType::Throw); + return create_ast_node<ThrowStatement>(parse_expression(0)); +} + NonnullRefPtr<TryStatement> 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 7d131347b7..7cbd614b32 100644 --- a/Libraries/LibJS/Parser.h +++ b/Libraries/LibJS/Parser.h @@ -52,6 +52,7 @@ public: NonnullRefPtr<VariableDeclaration> parse_variable_declaration(); NonnullRefPtr<ForStatement> parse_for_statement(); NonnullRefPtr<IfStatement> parse_if_statement(); + NonnullRefPtr<ThrowStatement> parse_throw_statement(); NonnullRefPtr<TryStatement> parse_try_statement(); NonnullRefPtr<CatchClause> parse_catch_clause(); diff --git a/Libraries/LibJS/Runtime/Exception.cpp b/Libraries/LibJS/Runtime/Exception.cpp new file mode 100644 index 0000000000..a11116a602 --- /dev/null +++ b/Libraries/LibJS/Runtime/Exception.cpp @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> + * 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 <LibJS/Runtime/Exception.h> + +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 0000000000..14082c310f --- /dev/null +++ b/Libraries/LibJS/Runtime/Exception.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020, Andreas Kling <kling@serenityos.org> + * 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 <LibJS/Runtime/Cell.h> +#include <LibJS/Runtime/Value.h> + +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 d278a6f4e5..880834d301 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, |