mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 15:28:11 +00:00
LibJS: Convert Interpreter::run() to ThrowCompletionOr<Value>
Instead of making it a void function, checking for an exception, and then receiving the relevant result via VM::last_value(), we can consolidate all of this by using completions. This allows us to remove more uses of VM::exception(), and all uses of VM::last_value().
This commit is contained in:
parent
f73afbb5ae
commit
eb60d16549
13 changed files with 62 additions and 56 deletions
|
@ -21,7 +21,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size)
|
||||||
if (!parser.has_errors()) {
|
if (!parser.has_errors()) {
|
||||||
auto vm = JS::VM::create();
|
auto vm = JS::VM::create();
|
||||||
auto interpreter = JS::Interpreter::create<JS::GlobalObject>(*vm);
|
auto interpreter = JS::Interpreter::create<JS::GlobalObject>(*vm);
|
||||||
interpreter->run(interpreter->global_object(), *program);
|
(void)interpreter->run(interpreter->global_object(), *program);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -213,8 +213,8 @@ int main(int, char**)
|
||||||
if (parser.has_errors()) {
|
if (parser.has_errors()) {
|
||||||
result = 1;
|
result = 1;
|
||||||
} else {
|
} else {
|
||||||
interpreter->run(interpreter->global_object(), *program);
|
auto completion = interpreter->run(interpreter->global_object(), *program);
|
||||||
if (interpreter->exception()) {
|
if (completion.is_error()) {
|
||||||
result = 1;
|
result = 1;
|
||||||
vm->clear_exception();
|
vm->clear_exception();
|
||||||
}
|
}
|
||||||
|
|
|
@ -98,11 +98,11 @@ void CalculatorProvider::query(String const& query, Function<void(NonnullRefPtrV
|
||||||
if (parser.has_errors())
|
if (parser.has_errors())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
interpreter->run(interpreter->global_object(), *program);
|
auto completion = interpreter->run(interpreter->global_object(), *program);
|
||||||
if (interpreter->exception())
|
if (completion.is_error())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto result = interpreter->vm().last_value();
|
auto result = completion.release_value();
|
||||||
String calculation;
|
String calculation;
|
||||||
if (!result.is_number()) {
|
if (!result.is_number()) {
|
||||||
calculation = "0";
|
calculation = "0";
|
||||||
|
|
|
@ -55,7 +55,7 @@ Sheet::Sheet(Workbook& workbook)
|
||||||
warnln("Spreadsheet: Failed to parse runtime code");
|
warnln("Spreadsheet: Failed to parse runtime code");
|
||||||
parser.print_errors();
|
parser.print_errors();
|
||||||
} else {
|
} else {
|
||||||
interpreter().run(global_object(), parser.parse_program());
|
(void)interpreter().run(global_object(), parser.parse_program());
|
||||||
if (auto* exception = interpreter().exception()) {
|
if (auto* exception = interpreter().exception()) {
|
||||||
warnln("Spreadsheet: Failed to run runtime code:");
|
warnln("Spreadsheet: Failed to run runtime code:");
|
||||||
for (auto& traceback_frame : exception->traceback()) {
|
for (auto& traceback_frame : exception->traceback()) {
|
||||||
|
@ -164,16 +164,12 @@ Sheet::ValueAndException Sheet::evaluate(StringView source, Cell* on_behalf_of)
|
||||||
if (parser.has_errors() || interpreter().exception())
|
if (parser.has_errors() || interpreter().exception())
|
||||||
return { JS::js_undefined(), interpreter().exception() };
|
return { JS::js_undefined(), interpreter().exception() };
|
||||||
|
|
||||||
interpreter().run(global_object(), program);
|
auto result = interpreter().run(global_object(), program);
|
||||||
if (interpreter().exception()) {
|
if (result.is_error()) {
|
||||||
auto exc = interpreter().exception();
|
auto exc = interpreter().exception();
|
||||||
return { JS::js_undefined(), exc };
|
return { JS::js_undefined(), exc };
|
||||||
}
|
}
|
||||||
|
return { result.value(), {} };
|
||||||
auto value = interpreter().vm().last_value();
|
|
||||||
if (value.is_empty())
|
|
||||||
return { JS::js_undefined(), {} };
|
|
||||||
return { value, {} };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Cell* Sheet::at(StringView name)
|
Cell* Sheet::at(StringView name)
|
||||||
|
|
|
@ -112,21 +112,21 @@ void EvaluateExpressionDialog::handle_evaluation(const String& expression)
|
||||||
auto program = parser.parse_program();
|
auto program = parser.parse_program();
|
||||||
|
|
||||||
StringBuilder output_html;
|
StringBuilder output_html;
|
||||||
|
auto result = JS::ThrowCompletionOr<JS::Value> { JS::js_undefined() };
|
||||||
if (parser.has_errors()) {
|
if (parser.has_errors()) {
|
||||||
auto error = parser.errors()[0];
|
auto error = parser.errors()[0];
|
||||||
auto hint = error.source_location_hint(expression);
|
auto hint = error.source_location_hint(expression);
|
||||||
if (!hint.is_empty())
|
if (!hint.is_empty())
|
||||||
output_html.append(String::formatted("<pre>{}</pre>", escape_html_entities(hint)));
|
output_html.append(String::formatted("<pre>{}</pre>", escape_html_entities(hint)));
|
||||||
m_interpreter->vm().throw_exception<JS::SyntaxError>(m_interpreter->global_object(), error.to_string());
|
result = m_interpreter->vm().throw_completion<JS::SyntaxError>(m_interpreter->global_object(), error.to_string());
|
||||||
} else {
|
} else {
|
||||||
m_interpreter->run(m_interpreter->global_object(), *program);
|
result = m_interpreter->run(m_interpreter->global_object(), *program);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_interpreter->exception()) {
|
if (result.is_error()) {
|
||||||
auto* exception = m_interpreter->exception();
|
|
||||||
m_interpreter->vm().clear_exception();
|
m_interpreter->vm().clear_exception();
|
||||||
output_html.append("Uncaught exception: ");
|
output_html.append("Uncaught exception: ");
|
||||||
auto error = exception->value();
|
auto error = *result.throw_completion().value();
|
||||||
if (error.is_object())
|
if (error.is_object())
|
||||||
output_html.append(JS::MarkupGenerator::html_from_error(error.as_object()));
|
output_html.append(JS::MarkupGenerator::html_from_error(error.as_object()));
|
||||||
else
|
else
|
||||||
|
@ -135,7 +135,7 @@ void EvaluateExpressionDialog::handle_evaluation(const String& expression)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
set_output(JS::MarkupGenerator::html_from_value(m_interpreter->vm().last_value()));
|
set_output(JS::MarkupGenerator::html_from_value(result.value()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EvaluateExpressionDialog::set_output(StringView html)
|
void EvaluateExpressionDialog::set_output(StringView html)
|
||||||
|
|
|
@ -38,7 +38,7 @@ Interpreter::~Interpreter()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interpreter::run(GlobalObject& global_object, const Program& program)
|
ThrowCompletionOr<Value> Interpreter::run(GlobalObject& global_object, const Program& program)
|
||||||
{
|
{
|
||||||
// FIXME: Why does this receive a GlobalObject? Interpreter has one already, and this might not be in sync with the Realm's GlobalObject.
|
// FIXME: Why does this receive a GlobalObject? Interpreter has one already, and this might not be in sync with the Realm's GlobalObject.
|
||||||
|
|
||||||
|
@ -71,6 +71,12 @@ void Interpreter::run(GlobalObject& global_object, const Program& program)
|
||||||
vm.pop_execution_context();
|
vm.pop_execution_context();
|
||||||
|
|
||||||
vm.finish_execution_generation();
|
vm.finish_execution_generation();
|
||||||
|
|
||||||
|
if (completion.is_abrupt()) {
|
||||||
|
VERIFY(completion.type() == Completion::Type::Throw);
|
||||||
|
return completion.release_error();
|
||||||
|
}
|
||||||
|
return completion.value().value_or(js_undefined());
|
||||||
}
|
}
|
||||||
|
|
||||||
GlobalObject& Interpreter::global_object()
|
GlobalObject& Interpreter::global_object()
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include <LibJS/Forward.h>
|
#include <LibJS/Forward.h>
|
||||||
#include <LibJS/Heap/DeferGC.h>
|
#include <LibJS/Heap/DeferGC.h>
|
||||||
#include <LibJS/Heap/Heap.h>
|
#include <LibJS/Heap/Heap.h>
|
||||||
|
#include <LibJS/Runtime/Completion.h>
|
||||||
#include <LibJS/Runtime/DeclarativeEnvironment.h>
|
#include <LibJS/Runtime/DeclarativeEnvironment.h>
|
||||||
#include <LibJS/Runtime/ErrorTypes.h>
|
#include <LibJS/Runtime/ErrorTypes.h>
|
||||||
#include <LibJS/Runtime/Exception.h>
|
#include <LibJS/Runtime/Exception.h>
|
||||||
|
@ -52,7 +53,7 @@ public:
|
||||||
|
|
||||||
~Interpreter();
|
~Interpreter();
|
||||||
|
|
||||||
void run(GlobalObject&, const Program&);
|
ThrowCompletionOr<Value> run(GlobalObject&, const Program&);
|
||||||
|
|
||||||
GlobalObject& global_object();
|
GlobalObject& global_object();
|
||||||
const GlobalObject& global_object() const;
|
const GlobalObject& global_object() const;
|
||||||
|
|
|
@ -342,11 +342,9 @@ inline JSFileResult TestRunner::run_file_test(const String& test_path)
|
||||||
JS::Bytecode::Interpreter bytecode_interpreter(interpreter->global_object(), interpreter->realm());
|
JS::Bytecode::Interpreter bytecode_interpreter(interpreter->global_object(), interpreter->realm());
|
||||||
MUST(bytecode_interpreter.run(executable));
|
MUST(bytecode_interpreter.run(executable));
|
||||||
} else {
|
} else {
|
||||||
interpreter->run(interpreter->global_object(), m_test_script->parse_node());
|
MUST(interpreter->run(interpreter->global_object(), m_test_script->parse_node()));
|
||||||
}
|
}
|
||||||
|
|
||||||
VERIFY(!g_vm->exception());
|
|
||||||
|
|
||||||
auto file_script = parse_script(test_path, interpreter->realm());
|
auto file_script = parse_script(test_path, interpreter->realm());
|
||||||
if (file_script.is_error())
|
if (file_script.is_error())
|
||||||
return { test_path, file_script.error() };
|
return { test_path, file_script.error() };
|
||||||
|
@ -358,7 +356,7 @@ inline JSFileResult TestRunner::run_file_test(const String& test_path)
|
||||||
JS::Bytecode::Interpreter bytecode_interpreter(interpreter->global_object(), interpreter->realm());
|
JS::Bytecode::Interpreter bytecode_interpreter(interpreter->global_object(), interpreter->realm());
|
||||||
(void)bytecode_interpreter.run(executable);
|
(void)bytecode_interpreter.run(executable);
|
||||||
} else {
|
} else {
|
||||||
interpreter->run(interpreter->global_object(), file_script.value()->parse_node());
|
(void)interpreter->run(interpreter->global_object(), file_script.value()->parse_node());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_vm->exception())
|
if (g_vm->exception())
|
||||||
|
|
|
@ -687,10 +687,13 @@ JS::Value Document::run_javascript(StringView source, StringView filename)
|
||||||
}
|
}
|
||||||
auto& interpreter = document().interpreter();
|
auto& interpreter = document().interpreter();
|
||||||
auto& vm = interpreter.vm();
|
auto& vm = interpreter.vm();
|
||||||
interpreter.run(interpreter.global_object(), *program);
|
auto result = interpreter.run(interpreter.global_object(), *program);
|
||||||
if (vm.exception())
|
if (result.is_error()) {
|
||||||
|
// FIXME: I'm sure the spec could tell us something about error propagation here!
|
||||||
vm.clear_exception();
|
vm.clear_exception();
|
||||||
return vm.last_value();
|
return {};
|
||||||
|
}
|
||||||
|
return result.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://dom.spec.whatwg.org/#dom-document-createelement
|
// https://dom.spec.whatwg.org/#dom-document-createelement
|
||||||
|
|
|
@ -70,12 +70,15 @@ JS::Value ClassicScript::run(RethrowErrors rethrow_errors)
|
||||||
|
|
||||||
auto timer = Core::ElapsedTimer::start_new();
|
auto timer = Core::ElapsedTimer::start_new();
|
||||||
auto interpreter = JS::Interpreter::create_with_existing_realm(m_script_record->realm());
|
auto interpreter = JS::Interpreter::create_with_existing_realm(m_script_record->realm());
|
||||||
interpreter->run(interpreter->global_object(), m_script_record->parse_node());
|
|
||||||
auto& vm = interpreter->vm();
|
auto result = interpreter->run(interpreter->global_object(), m_script_record->parse_node());
|
||||||
if (vm.exception())
|
|
||||||
vm.clear_exception();
|
|
||||||
dbgln("ClassicScript: Finished running script {}, Duration: {}ms", filename(), timer.elapsed());
|
dbgln("ClassicScript: Finished running script {}, Duration: {}ms", filename(), timer.elapsed());
|
||||||
return vm.last_value();
|
if (result.is_error()) {
|
||||||
|
// FIXME: Propagate error according to the spec.
|
||||||
|
interpreter->vm().clear_exception();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return result.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
ClassicScript::ClassicScript(AK::URL base_url, String filename)
|
ClassicScript::ClassicScript(AK::URL base_url, String filename)
|
||||||
|
|
|
@ -331,9 +331,9 @@ void ClientConnection::run_javascript(String const& js_source)
|
||||||
|
|
||||||
auto parser = JS::Parser(JS::Lexer(js_source));
|
auto parser = JS::Parser(JS::Lexer(js_source));
|
||||||
auto program = parser.parse_program();
|
auto program = parser.parse_program();
|
||||||
interpreter.run(interpreter.global_object(), *program);
|
auto result = interpreter.run(interpreter.global_object(), *program);
|
||||||
|
|
||||||
if (interpreter.vm().exception()) {
|
if (result.is_error()) {
|
||||||
dbgln("Exception :(");
|
dbgln("Exception :(");
|
||||||
interpreter.vm().clear_exception();
|
interpreter.vm().clear_exception();
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,12 +32,13 @@ void WebContentConsoleClient::handle_input(String const& js_source)
|
||||||
auto program = parser.parse_program();
|
auto program = parser.parse_program();
|
||||||
|
|
||||||
StringBuilder output_html;
|
StringBuilder output_html;
|
||||||
|
auto result = JS::ThrowCompletionOr<JS::Value> { JS::js_undefined() };
|
||||||
if (parser.has_errors()) {
|
if (parser.has_errors()) {
|
||||||
auto error = parser.errors()[0];
|
auto error = parser.errors()[0];
|
||||||
auto hint = error.source_location_hint(js_source);
|
auto hint = error.source_location_hint(js_source);
|
||||||
if (!hint.is_empty())
|
if (!hint.is_empty())
|
||||||
output_html.append(String::formatted("<pre>{}</pre>", escape_html_entities(hint)));
|
output_html.append(String::formatted("<pre>{}</pre>", escape_html_entities(hint)));
|
||||||
m_interpreter->vm().throw_exception<JS::SyntaxError>(*m_console_global_object.cell(), error.to_string());
|
result = m_interpreter->vm().throw_completion<JS::SyntaxError>(*m_console_global_object.cell(), error.to_string());
|
||||||
} else {
|
} else {
|
||||||
// FIXME: This is not the correct way to do this, we probably want to have
|
// FIXME: This is not the correct way to do this, we probably want to have
|
||||||
// multiple execution contexts we switch between.
|
// multiple execution contexts we switch between.
|
||||||
|
@ -46,16 +47,15 @@ void WebContentConsoleClient::handle_input(String const& js_source)
|
||||||
auto& this_value_before = m_interpreter->realm().global_environment().global_this_value();
|
auto& this_value_before = m_interpreter->realm().global_environment().global_this_value();
|
||||||
m_interpreter->realm().set_global_object(*m_console_global_object.cell(), &global_object_before);
|
m_interpreter->realm().set_global_object(*m_console_global_object.cell(), &global_object_before);
|
||||||
|
|
||||||
m_interpreter->run(*m_console_global_object.cell(), *program);
|
result = m_interpreter->run(*m_console_global_object.cell(), *program);
|
||||||
|
|
||||||
m_interpreter->realm().set_global_object(global_object_before, &this_value_before);
|
m_interpreter->realm().set_global_object(global_object_before, &this_value_before);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_interpreter->exception()) {
|
if (result.is_error()) {
|
||||||
auto* exception = m_interpreter->exception();
|
|
||||||
m_interpreter->vm().clear_exception();
|
m_interpreter->vm().clear_exception();
|
||||||
output_html.append("Uncaught exception: ");
|
output_html.append("Uncaught exception: ");
|
||||||
auto error = exception->value();
|
auto error = *result.throw_completion().value();
|
||||||
if (error.is_object())
|
if (error.is_object())
|
||||||
output_html.append(JS::MarkupGenerator::html_from_error(error.as_object()));
|
output_html.append(JS::MarkupGenerator::html_from_error(error.as_object()));
|
||||||
else
|
else
|
||||||
|
@ -64,7 +64,7 @@ void WebContentConsoleClient::handle_input(String const& js_source)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
print_html(JS::MarkupGenerator::html_from_value(m_interpreter->vm().last_value()));
|
print_html(JS::MarkupGenerator::html_from_value(result.value()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebContentConsoleClient::print_html(String const& line)
|
void WebContentConsoleClient::print_html(String const& line)
|
||||||
|
|
|
@ -915,6 +915,8 @@ static bool parse_and_run(JS::Interpreter& interpreter, StringView source, Strin
|
||||||
if (s_dump_ast)
|
if (s_dump_ast)
|
||||||
program->dump(0);
|
program->dump(0);
|
||||||
|
|
||||||
|
auto result = JS::ThrowCompletionOr<JS::Value> { JS::js_undefined() };
|
||||||
|
|
||||||
if (parser.has_errors()) {
|
if (parser.has_errors()) {
|
||||||
auto error = parser.errors()[0];
|
auto error = parser.errors()[0];
|
||||||
if (!s_disable_source_location_hints) {
|
if (!s_disable_source_location_hints) {
|
||||||
|
@ -922,7 +924,7 @@ static bool parse_and_run(JS::Interpreter& interpreter, StringView source, Strin
|
||||||
if (!hint.is_empty())
|
if (!hint.is_empty())
|
||||||
js_outln("{}", hint);
|
js_outln("{}", hint);
|
||||||
}
|
}
|
||||||
vm->throw_exception<JS::SyntaxError>(interpreter.global_object(), error.to_string());
|
result = vm->throw_completion<JS::SyntaxError>(interpreter.global_object(), error.to_string());
|
||||||
} else {
|
} else {
|
||||||
if (JS::Bytecode::g_dump_bytecode || s_run_bytecode) {
|
if (JS::Bytecode::g_dump_bytecode || s_run_bytecode) {
|
||||||
auto executable = JS::Bytecode::Generator::generate(*program);
|
auto executable = JS::Bytecode::Generator::generate(*program);
|
||||||
|
@ -938,15 +940,12 @@ static bool parse_and_run(JS::Interpreter& interpreter, StringView source, Strin
|
||||||
|
|
||||||
if (s_run_bytecode) {
|
if (s_run_bytecode) {
|
||||||
JS::Bytecode::Interpreter bytecode_interpreter(interpreter.global_object(), interpreter.realm());
|
JS::Bytecode::Interpreter bytecode_interpreter(interpreter.global_object(), interpreter.realm());
|
||||||
auto result = bytecode_interpreter.run(executable);
|
result = bytecode_interpreter.run(executable);
|
||||||
// Since all the error handling code uses vm.exception() we just rethrow any exception we got here.
|
|
||||||
if (result.is_error())
|
|
||||||
vm->throw_exception(interpreter.global_object(), *result.throw_completion().value());
|
|
||||||
} else {
|
} else {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
interpreter.run(interpreter.global_object(), *program);
|
result = interpreter.run(interpreter.global_object(), *program);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -982,15 +981,15 @@ static bool parse_and_run(JS::Interpreter& interpreter, StringView source, Strin
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (vm->exception()) {
|
if (result.is_error()) {
|
||||||
handle_exception();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (s_print_last_result)
|
|
||||||
print(vm->last_value());
|
|
||||||
if (vm->exception()) {
|
|
||||||
handle_exception();
|
handle_exception();
|
||||||
return false;
|
return false;
|
||||||
|
} else if (s_print_last_result) {
|
||||||
|
print(result.value());
|
||||||
|
if (vm->exception()) {
|
||||||
|
handle_exception();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1010,7 +1009,7 @@ static JS::ThrowCompletionOr<JS::Value> load_file_impl(JS::VM& vm, JS::GlobalObj
|
||||||
return vm.throw_completion<JS::SyntaxError>(global_object, error.to_string());
|
return vm.throw_completion<JS::SyntaxError>(global_object, error.to_string());
|
||||||
}
|
}
|
||||||
// FIXME: Use eval()-like semantics and execute in current scope?
|
// FIXME: Use eval()-like semantics and execute in current scope?
|
||||||
vm.interpreter().run(global_object, *program);
|
TRY(vm.interpreter().run(global_object, *program));
|
||||||
return JS::js_undefined();
|
return JS::js_undefined();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue