mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 07:48:11 +00:00
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.
This commit is contained in:
parent
db024a9cb1
commit
faddf3a1db
13 changed files with 160 additions and 7 deletions
5
Base/home/anon/js/throw.js
Normal file
5
Base/home/anon/js/throw.js
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
try {
|
||||||
|
throw 123;
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
}
|
|
@ -480,7 +480,7 @@ Value Identifier::execute(Interpreter& interpreter) const
|
||||||
{
|
{
|
||||||
auto value = interpreter.get_variable(string());
|
auto value = interpreter.get_variable(string());
|
||||||
if (value.is_undefined())
|
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;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -762,13 +762,19 @@ void CatchClause::dump(int indent) const
|
||||||
body().dump(indent + 1);
|
body().dump(indent + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ThrowStatement::dump(int indent) const
|
||||||
|
{
|
||||||
|
ASTNode::dump(indent);
|
||||||
|
argument().dump(indent + 1);
|
||||||
|
}
|
||||||
|
|
||||||
Value TryStatement::execute(Interpreter& interpreter) const
|
Value TryStatement::execute(Interpreter& interpreter) const
|
||||||
{
|
{
|
||||||
interpreter.run(block(), {}, ScopeType::Try);
|
interpreter.run(block(), {}, ScopeType::Try);
|
||||||
if (auto* exception = interpreter.exception()) {
|
if (auto* exception = interpreter.exception()) {
|
||||||
if (m_handler) {
|
if (m_handler) {
|
||||||
interpreter.clear_exception();
|
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));
|
interpreter.run(m_handler->body(), move(arguments));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -786,4 +792,10 @@ Value CatchClause::execute(Interpreter&) const
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Value ThrowStatement::execute(Interpreter& interrupt) const
|
||||||
|
{
|
||||||
|
auto value = m_argument->execute(interrupt);
|
||||||
|
return interrupt.throw_exception(value);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -675,4 +675,22 @@ private:
|
||||||
RefPtr<BlockStatement> m_finalizer;
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,6 +32,7 @@ class ASTNode;
|
||||||
class Argument;
|
class Argument;
|
||||||
class Cell;
|
class Cell;
|
||||||
class Error;
|
class Error;
|
||||||
|
class Exception;
|
||||||
class Expression;
|
class Expression;
|
||||||
class Function;
|
class Function;
|
||||||
class HandleImpl;
|
class HandleImpl;
|
||||||
|
|
|
@ -188,7 +188,7 @@ Value Interpreter::call(Function* function, Value this_value, const Vector<Value
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Value Interpreter::throw_exception(Error* exception)
|
Value Interpreter::throw_exception(Exception* exception)
|
||||||
{
|
{
|
||||||
m_exception = exception;
|
m_exception = exception;
|
||||||
unwind(ScopeType::Try);
|
unwind(ScopeType::Try);
|
||||||
|
|
|
@ -26,12 +26,13 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <AK/FlyString.h>
|
||||||
#include <AK/HashMap.h>
|
#include <AK/HashMap.h>
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
#include <AK/FlyString.h>
|
|
||||||
#include <AK/Vector.h>
|
#include <AK/Vector.h>
|
||||||
#include <LibJS/Forward.h>
|
#include <LibJS/Forward.h>
|
||||||
#include <LibJS/Heap/Heap.h>
|
#include <LibJS/Heap/Heap.h>
|
||||||
|
#include <LibJS/Runtime/Exception.h>
|
||||||
#include <LibJS/Runtime/Value.h>
|
#include <LibJS/Runtime/Value.h>
|
||||||
|
|
||||||
namespace JS {
|
namespace JS {
|
||||||
|
@ -107,9 +108,20 @@ public:
|
||||||
Object* array_prototype() { return m_array_prototype; }
|
Object* array_prototype() { return m_array_prototype; }
|
||||||
Object* error_prototype() { return m_error_prototype; }
|
Object* error_prototype() { return m_error_prototype; }
|
||||||
|
|
||||||
Error* exception() { return m_exception; }
|
Exception* exception() { return m_exception; }
|
||||||
void clear_exception() { m_exception = nullptr; }
|
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:
|
private:
|
||||||
Heap m_heap;
|
Heap m_heap;
|
||||||
|
@ -123,7 +135,7 @@ private:
|
||||||
Object* m_array_prototype { nullptr };
|
Object* m_array_prototype { nullptr };
|
||||||
Object* m_error_prototype { nullptr };
|
Object* m_error_prototype { nullptr };
|
||||||
|
|
||||||
Error* m_exception { nullptr };
|
Exception* m_exception { nullptr };
|
||||||
|
|
||||||
ScopeType m_unwind_until { ScopeType::None };
|
ScopeType m_unwind_until { ScopeType::None };
|
||||||
};
|
};
|
||||||
|
|
|
@ -62,6 +62,7 @@ Lexer::Lexer(StringView source)
|
||||||
s_keywords.set("null", TokenType::NullLiteral);
|
s_keywords.set("null", TokenType::NullLiteral);
|
||||||
s_keywords.set("undefined", TokenType::UndefinedLiteral);
|
s_keywords.set("undefined", TokenType::UndefinedLiteral);
|
||||||
s_keywords.set("return", TokenType::Return);
|
s_keywords.set("return", TokenType::Return);
|
||||||
|
s_keywords.set("throw", TokenType::Throw);
|
||||||
s_keywords.set("true", TokenType::BoolLiteral);
|
s_keywords.set("true", TokenType::BoolLiteral);
|
||||||
s_keywords.set("try", TokenType::Try);
|
s_keywords.set("try", TokenType::Try);
|
||||||
s_keywords.set("typeof", TokenType::Typeof);
|
s_keywords.set("typeof", TokenType::Typeof);
|
||||||
|
|
|
@ -12,6 +12,7 @@ OBJS = \
|
||||||
Runtime/ConsoleObject.o \
|
Runtime/ConsoleObject.o \
|
||||||
Runtime/Error.o \
|
Runtime/Error.o \
|
||||||
Runtime/ErrorPrototype.o \
|
Runtime/ErrorPrototype.o \
|
||||||
|
Runtime/Exception.o \
|
||||||
Runtime/Function.o \
|
Runtime/Function.o \
|
||||||
Runtime/GlobalObject.o \
|
Runtime/GlobalObject.o \
|
||||||
Runtime/MathObject.o \
|
Runtime/MathObject.o \
|
||||||
|
|
|
@ -197,6 +197,8 @@ NonnullRefPtr<Statement> Parser::parse_statement()
|
||||||
return parse_for_statement();
|
return parse_for_statement();
|
||||||
case TokenType::If:
|
case TokenType::If:
|
||||||
return parse_if_statement();
|
return parse_if_statement();
|
||||||
|
case TokenType::Throw:
|
||||||
|
return parse_throw_statement();
|
||||||
case TokenType::Try:
|
case TokenType::Try:
|
||||||
return parse_try_statement();
|
return parse_try_statement();
|
||||||
default:
|
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);
|
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()
|
NonnullRefPtr<TryStatement> Parser::parse_try_statement()
|
||||||
{
|
{
|
||||||
consume(TokenType::Try);
|
consume(TokenType::Try);
|
||||||
|
@ -700,6 +708,7 @@ bool Parser::match_statement() const
|
||||||
|| type == TokenType::Delete
|
|| type == TokenType::Delete
|
||||||
|| type == TokenType::Do
|
|| type == TokenType::Do
|
||||||
|| type == TokenType::If
|
|| type == TokenType::If
|
||||||
|
|| type == TokenType::Throw
|
||||||
|| type == TokenType::Try
|
|| type == TokenType::Try
|
||||||
|| type == TokenType::While
|
|| type == TokenType::While
|
||||||
|| type == TokenType::For
|
|| type == TokenType::For
|
||||||
|
|
|
@ -52,6 +52,7 @@ public:
|
||||||
NonnullRefPtr<VariableDeclaration> parse_variable_declaration();
|
NonnullRefPtr<VariableDeclaration> parse_variable_declaration();
|
||||||
NonnullRefPtr<ForStatement> parse_for_statement();
|
NonnullRefPtr<ForStatement> parse_for_statement();
|
||||||
NonnullRefPtr<IfStatement> parse_if_statement();
|
NonnullRefPtr<IfStatement> parse_if_statement();
|
||||||
|
NonnullRefPtr<ThrowStatement> parse_throw_statement();
|
||||||
NonnullRefPtr<TryStatement> parse_try_statement();
|
NonnullRefPtr<TryStatement> parse_try_statement();
|
||||||
NonnullRefPtr<CatchClause> parse_catch_clause();
|
NonnullRefPtr<CatchClause> parse_catch_clause();
|
||||||
|
|
||||||
|
|
46
Libraries/LibJS/Runtime/Exception.cpp
Normal file
46
Libraries/LibJS/Runtime/Exception.cpp
Normal file
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
46
Libraries/LibJS/Runtime/Exception.h
Normal file
46
Libraries/LibJS/Runtime/Exception.h
Normal file
|
@ -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;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
|
@ -105,6 +105,7 @@ enum class TokenType {
|
||||||
Slash,
|
Slash,
|
||||||
SlashEquals,
|
SlashEquals,
|
||||||
StringLiteral,
|
StringLiteral,
|
||||||
|
Throw,
|
||||||
Tilde,
|
Tilde,
|
||||||
Try,
|
Try,
|
||||||
Typeof,
|
Typeof,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue