mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-09-20 09:49:15 +03: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
Notes:
sideshowbarker
2024-07-19 08:08:37 +09:00
Author: https://github.com/awesomekling Commit: https://github.com/SerenityOS/serenity/commit/faddf3a1db6
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());
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -32,6 +32,7 @@ class ASTNode;
|
||||
class Argument;
|
||||
class Cell;
|
||||
class Error;
|
||||
class Exception;
|
||||
class Expression;
|
||||
class Function;
|
||||
class HandleImpl;
|
||||
|
@ -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);
|
||||
|
@ -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 };
|
||||
};
|
||||
|
@ -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);
|
||||
|
@ -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 \
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
||||
|
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,
|
||||
SlashEquals,
|
||||
StringLiteral,
|
||||
Throw,
|
||||
Tilde,
|
||||
Try,
|
||||
Typeof,
|
||||
|
Loading…
Reference in New Issue
Block a user