diff --git a/Tests/LibJS/test262-runner.cpp b/Tests/LibJS/test262-runner.cpp index 4ea77aa01d17949..b8a9f8fa951d853 100644 --- a/Tests/LibJS/test262-runner.cpp +++ b/Tests/LibJS/test262-runner.cpp @@ -215,12 +215,10 @@ static Result run_test(StringView source, StringView filepath, if (program_or_error.is_error()) return program_or_error.release_error(); - OwnPtr bytecode_interpreter = nullptr; - if (JS::Bytecode::Interpreter::enabled()) - bytecode_interpreter = make(realm); + auto* bytecode_interpreter = vm->bytecode_interpreter_if_exists(); auto run_with_interpreter = [&](ScriptOrModuleProgram& program) { - if (JS::Bytecode::Interpreter::enabled()) + if (bytecode_interpreter) return run_program(*bytecode_interpreter, program); return run_program(*ast_interpreter, program); }; diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp index 2fe1dbcda71f1dc..c7c42c61bcbef8d 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp @@ -32,26 +32,15 @@ void Interpreter::set_enabled(bool enabled) s_bytecode_interpreter_enabled = enabled; } -static Interpreter* s_current; bool g_dump_bytecode = false; -Interpreter* Interpreter::current() +Interpreter::Interpreter(VM& vm) + : m_vm(vm) { - return s_current; -} - -Interpreter::Interpreter(Realm& realm) - : m_vm(realm.vm()) - , m_realm(realm) -{ - VERIFY(!s_current); - s_current = this; } Interpreter::~Interpreter() { - VERIFY(s_current == this); - s_current = nullptr; } // 16.1.6 ScriptEvaluation ( scriptRecord ), https://tc39.es/ecma262/#sec-runtime-semantics-scriptevaluation @@ -124,7 +113,7 @@ ThrowCompletionOr Interpreter::run(Script& script_record, JS::GCPtrdump(); // a. Set result to the result of evaluating script. - auto result_or_error = run_and_return_frame(*executable, nullptr); + auto result_or_error = run_and_return_frame(script_record.realm(), *executable, nullptr); if (result_or_error.value.is_error()) result = result_or_error.value.release_error(); else @@ -189,7 +178,7 @@ void Interpreter::set_optimizations_enabled(bool enabled) m_optimizations_enabled = enabled; } -Interpreter::ValueAndFrame Interpreter::run_and_return_frame(Executable const& executable, BasicBlock const* entry_point, RegisterWindow* in_frame) +Interpreter::ValueAndFrame Interpreter::run_and_return_frame(Realm& realm, Executable const& executable, BasicBlock const* entry_point, RegisterWindow* in_frame) { dbgln_if(JS_BYTECODE_DEBUG, "Bytecode::Interpreter will run unit {:p}", &executable); @@ -201,12 +190,12 @@ Interpreter::ValueAndFrame Interpreter::run_and_return_frame(Executable const& e ExecutionContext execution_context(vm().heap()); if (vm().execution_context_stack().is_empty() || !vm().running_execution_context().lexical_environment) { // The "normal" interpreter pushes an execution context without environment so in that case we also want to push one. - execution_context.this_value = &m_realm->global_object(); + execution_context.this_value = &realm.global_object(); static DeprecatedFlyString global_execution_context_name = "(*BC* global execution context)"; execution_context.function_name = global_execution_context_name; - execution_context.lexical_environment = &m_realm->global_environment(); - execution_context.variable_environment = &m_realm->global_environment(); - execution_context.realm = m_realm; + execution_context.lexical_environment = &realm.global_environment(); + execution_context.variable_environment = &realm.global_environment(); + execution_context.realm = realm; execution_context.is_strict_mode = executable.is_strict_mode; vm().push_execution_context(execution_context); pushed_execution_context = true; @@ -395,10 +384,10 @@ ThrowCompletionOr Interpreter::continue_pending_unwind(Label const& resume return {}; } -VM::InterpreterExecutionScope Interpreter::ast_interpreter_scope() +VM::InterpreterExecutionScope Interpreter::ast_interpreter_scope(Realm& realm) { if (!m_ast_interpreter) - m_ast_interpreter = JS::Interpreter::create_with_existing_realm(m_realm); + m_ast_interpreter = JS::Interpreter::create_with_existing_realm(realm); return { *m_ast_interpreter }; } @@ -449,4 +438,29 @@ DeprecatedString Interpreter::debug_position() const return DeprecatedString::formatted("{}:{:2}:{:4x}", m_current_executable->name, m_current_block->name(), pc()); } +ThrowCompletionOr> compile(VM& vm, ASTNode const& node, FunctionKind kind, DeprecatedFlyString const& name) +{ + auto executable_result = Bytecode::Generator::generate(node, kind); + if (executable_result.is_error()) + return vm.throw_completion(ErrorType::NotImplemented, TRY_OR_THROW_OOM(vm, executable_result.error().to_string())); + + auto bytecode_executable = executable_result.release_value(); + bytecode_executable->name = name; + auto& passes = Bytecode::Interpreter::optimization_pipeline(); + passes.perform(*bytecode_executable); + if constexpr (JS_BYTECODE_DEBUG) { + dbgln("Optimisation passes took {}us", passes.elapsed()); + dbgln("Compiled Bytecode::Block for function '{}':", name); + } + if (Bytecode::g_dump_bytecode) + bytecode_executable->dump(); + + return bytecode_executable; +} + +Realm& Interpreter::realm() +{ + return *m_vm.current_realm(); +} + } diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.h b/Userland/Libraries/LibJS/Bytecode/Interpreter.h index a3b05bb966e854f..010ac71fdeedfa8 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.h +++ b/Userland/Libraries/LibJS/Bytecode/Interpreter.h @@ -11,6 +11,7 @@ #include #include #include +#include #include #include @@ -31,13 +32,13 @@ class Interpreter { [[nodiscard]] static bool enabled(); static void set_enabled(bool); - explicit Interpreter(Realm&); + explicit Interpreter(VM&); ~Interpreter(); // FIXME: Remove this thing once we don't need it anymore! static Interpreter* current(); - Realm& realm() { return m_realm; } + Realm& realm(); VM& vm() { return m_vm; } void set_optimizations_enabled(bool); @@ -45,9 +46,9 @@ class Interpreter { ThrowCompletionOr run(Script&, JS::GCPtr lexical_environment_override = nullptr); ThrowCompletionOr run(SourceTextModule&); - ThrowCompletionOr run(Bytecode::Executable const& executable, Bytecode::BasicBlock const* entry_point = nullptr) + ThrowCompletionOr run(Realm& realm, Bytecode::Executable const& executable, Bytecode::BasicBlock const* entry_point = nullptr) { - auto value_and_frame = run_and_return_frame(executable, entry_point); + auto value_and_frame = run_and_return_frame(realm, executable, entry_point); return move(value_and_frame.value); } @@ -55,7 +56,7 @@ class Interpreter { ThrowCompletionOr value; OwnPtr frame; }; - ValueAndFrame run_and_return_frame(Bytecode::Executable const&, Bytecode::BasicBlock const* entry_point, RegisterWindow* = nullptr); + ValueAndFrame run_and_return_frame(Realm&, Bytecode::Executable const&, Bytecode::BasicBlock const* entry_point, RegisterWindow* = nullptr); ALWAYS_INLINE Value& accumulator() { return reg(Register::accumulator()); } Value& reg(Register const& r) { return registers()[r.index()]; } @@ -97,7 +98,7 @@ class Interpreter { }; static Bytecode::PassManager& optimization_pipeline(OptimizationLevel = OptimizationLevel::Default); - VM::InterpreterExecutionScope ast_interpreter_scope(); + VM::InterpreterExecutionScope ast_interpreter_scope(Realm&); private: RegisterWindow& window() @@ -115,7 +116,6 @@ class Interpreter { static AK::Array, static_cast>(Interpreter::OptimizationLevel::__Count)> s_optimization_pipelines; VM& m_vm; - NonnullGCPtr m_realm; Vector, RegisterWindow*>> m_register_windows; Optional m_pending_jump; BasicBlock const* m_scheduled_jump { nullptr }; @@ -131,4 +131,6 @@ class Interpreter { extern bool g_dump_bytecode; +ThrowCompletionOr> compile(VM&, ASTNode const& no, JS::FunctionKind kind, DeprecatedFlyString const& name); + } diff --git a/Userland/Libraries/LibJS/Bytecode/Op.cpp b/Userland/Libraries/LibJS/Bytecode/Op.cpp index 06e21e994128feb..b488e1349c8537d 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Op.cpp @@ -1081,7 +1081,7 @@ ThrowCompletionOr IteratorResultValue::execute_impl(Bytecode::Interpreter& ThrowCompletionOr NewClass::execute_impl(Bytecode::Interpreter& interpreter) const { auto name = m_class_expression.name(); - auto scope = interpreter.ast_interpreter_scope(); + auto scope = interpreter.ast_interpreter_scope(interpreter.realm()); auto& ast_interpreter = scope.interpreter(); auto* class_object = TRY(m_class_expression.class_definition_evaluation(ast_interpreter, name, name.is_null() ? ""sv : name)); diff --git a/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp b/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp index cfe989cf0000037..c066396e699ff2e 100644 --- a/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp +++ b/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp @@ -701,7 +701,7 @@ ThrowCompletionOr perform_eval(VM& vm, Value x, CallerMode strict_caller, // 29. If result.[[Type]] is normal, then // a. Set result to the result of evaluating body. - if (auto* bytecode_interpreter = Bytecode::Interpreter::current()) { + if (auto* bytecode_interpreter = vm.bytecode_interpreter_if_exists()) { auto executable_result = Bytecode::Generator::generate(program); if (executable_result.is_error()) return vm.throw_completion(ErrorType::NotImplemented, TRY_OR_THROW_OOM(vm, executable_result.error().to_string())); @@ -710,7 +710,7 @@ ThrowCompletionOr perform_eval(VM& vm, Value x, CallerMode strict_caller, executable->name = "eval"sv; if (Bytecode::g_dump_bytecode) executable->dump(); - auto result_or_error = bytecode_interpreter->run_and_return_frame(*executable, nullptr); + auto result_or_error = bytecode_interpreter->run_and_return_frame(eval_realm, *executable, nullptr); if (result_or_error.value.is_error()) return result_or_error.value.release_error(); diff --git a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp index 24a24580bf5baa2..c04e90e38d3e673 100644 --- a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp +++ b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp @@ -455,8 +455,8 @@ ThrowCompletionOr ECMAScriptFunctionObject::function_declaration_instantia } else if (i < execution_context_arguments.size() && !execution_context_arguments[i].is_undefined()) { argument_value = execution_context_arguments[i]; } else if (parameter.default_value) { - if (auto* bytecode_interpreter = Bytecode::Interpreter::current()) { - auto value_and_frame = bytecode_interpreter->run_and_return_frame(*m_default_parameter_bytecode_executables[default_parameter_index - 1], nullptr); + if (auto* bytecode_interpreter = vm.bytecode_interpreter_if_exists()) { + auto value_and_frame = bytecode_interpreter->run_and_return_frame(realm, *m_default_parameter_bytecode_executables[default_parameter_index - 1], nullptr); if (value_and_frame.value.is_error()) return value_and_frame.value.release_error(); // Resulting value is in the accumulator. @@ -751,9 +751,19 @@ void async_block_start(VM& vm, NonnullRefPtr const& async_body, auto& running_context = vm.running_execution_context(); // 3. Set the code evaluation state of asyncContext such that when evaluation is resumed for that execution context the following steps will be performed: - auto execution_steps = NativeFunction::create(realm, "", [&async_body, &promise_capability, &async_context](auto& vm) -> ThrowCompletionOr { + auto execution_steps = NativeFunction::create(realm, "", [&realm, &async_body, &promise_capability, &async_context](auto& vm) -> ThrowCompletionOr { // a. Let result be the result of evaluating asyncBody. - auto result = async_body->execute(vm.interpreter()); + Completion result; + if (auto* bytecode_interpreter = vm.bytecode_interpreter_if_exists()) { + // FIXME: Cache this executable somewhere. + auto maybe_executable = Bytecode::compile(vm, async_body, FunctionKind::Async, "AsyncBlockStart"sv); + if (maybe_executable.is_error()) + result = maybe_executable.release_error(); + else + result = bytecode_interpreter->run_and_return_frame(realm, *maybe_executable.value(), nullptr).value; + } else { + result = async_body->execute(vm.interpreter()); + } // b. Assert: If we return here, the async function either threw an exception or performed an implicit or explicit return; all awaiting is done. @@ -817,7 +827,7 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body() if (m_kind == FunctionKind::AsyncGenerator) return vm.throw_completion(ErrorType::NotImplemented, "Async Generator function execution"); - auto* bytecode_interpreter = Bytecode::Interpreter::current(); + auto* bytecode_interpreter = vm.bytecode_interpreter_if_exists(); // The bytecode interpreter can execute generator functions while the AST interpreter cannot. // This simply makes it create a new bytecode interpreter when one doesn't exist when executing a generator function. @@ -826,32 +836,11 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body() // However, this does cause an awkward situation with features not supported in bytecode, where features that work outside of generators with AST // suddenly stop working inside of generators. // This is a stop gap until bytecode mode becomes the default. - OwnPtr temp_bc_interpreter; if (m_kind == FunctionKind::Generator && !bytecode_interpreter) { - temp_bc_interpreter = make(realm); - bytecode_interpreter = temp_bc_interpreter.ptr(); + bytecode_interpreter = &vm.bytecode_interpreter(); } if (bytecode_interpreter) { - auto compile = [&](auto& node, auto kind, auto name) -> ThrowCompletionOr> { - auto executable_result = Bytecode::Generator::generate(node, kind); - if (executable_result.is_error()) - return vm.throw_completion(ErrorType::NotImplemented, TRY_OR_THROW_OOM(vm, executable_result.error().to_string())); - - auto bytecode_executable = executable_result.release_value(); - bytecode_executable->name = name; - auto& passes = Bytecode::Interpreter::optimization_pipeline(); - passes.perform(*bytecode_executable); - if constexpr (JS_BYTECODE_DEBUG) { - dbgln("Optimisation passes took {}us", passes.elapsed()); - dbgln("Compiled Bytecode::Block for function '{}':", m_name); - } - if (Bytecode::g_dump_bytecode) - bytecode_executable->dump(); - - return bytecode_executable; - }; - // NOTE: There's a subtle ordering issue here: // - We have to compile the default parameter values before instantiating the function. // - We have to instantiate the function before compiling the function body. @@ -864,7 +853,7 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body() for (auto& parameter : m_formal_parameters) { if (!parameter.default_value) continue; - auto executable = TRY(compile(*parameter.default_value, FunctionKind::Normal, DeprecatedString::formatted("default parameter #{} for {}", default_parameter_index, m_name))); + auto executable = TRY(Bytecode::compile(vm, *parameter.default_value, FunctionKind::Normal, DeprecatedString::formatted("default parameter #{} for {}", default_parameter_index, m_name))); m_default_parameter_bytecode_executables.append(move(executable)); } } @@ -872,10 +861,10 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body() TRY(function_declaration_instantiation(nullptr)); if (!m_bytecode_executable) { - m_bytecode_executable = TRY(compile(*m_ecmascript_code, m_kind, m_name)); + m_bytecode_executable = TRY(Bytecode::compile(vm, *m_ecmascript_code, m_kind, m_name)); } - auto result_and_frame = bytecode_interpreter->run_and_return_frame(*m_bytecode_executable, nullptr); + auto result_and_frame = bytecode_interpreter->run_and_return_frame(realm, *m_bytecode_executable, nullptr); VERIFY(result_and_frame.frame != nullptr); if (result_and_frame.value.is_error()) diff --git a/Userland/Libraries/LibJS/Runtime/GeneratorObject.cpp b/Userland/Libraries/LibJS/Runtime/GeneratorObject.cpp index d57d58ccbb7ae9d..4ec105b18d00663 100644 --- a/Userland/Libraries/LibJS/Runtime/GeneratorObject.cpp +++ b/Userland/Libraries/LibJS/Runtime/GeneratorObject.cpp @@ -104,18 +104,7 @@ ThrowCompletionOr GeneratorObject::execute(VM& vm, Completion const& comp completion_object->define_direct_property(vm.names.type, Value(to_underlying(completion.type())), default_attributes); completion_object->define_direct_property(vm.names.value, completion.value().value(), default_attributes); - auto* bytecode_interpreter = Bytecode::Interpreter::current(); - - // If we're coming from a context which has no bytecode interpreter, e.g. from AST mode calling Generate.prototype.next, - // we need to make one to be able to continue, as generators are only supported in bytecode mode. - // See also ECMAScriptFunctionObject::ordinary_call_evaluate_body where this is done as well. - OwnPtr temp_bc_interpreter; - if (!bytecode_interpreter) { - temp_bc_interpreter = make(realm); - bytecode_interpreter = temp_bc_interpreter.ptr(); - } - - VERIFY(bytecode_interpreter); + auto& bytecode_interpreter = vm.bytecode_interpreter(); auto const* next_block = generated_continuation(m_previous_value); @@ -131,9 +120,9 @@ ThrowCompletionOr GeneratorObject::execute(VM& vm, Completion const& comp if (frame) frame->registers[0] = completion_object; else - bytecode_interpreter->accumulator() = completion_object; + bytecode_interpreter.accumulator() = completion_object; - auto next_result = bytecode_interpreter->run_and_return_frame(*m_generating_function->bytecode_executable(), next_block, frame); + auto next_result = bytecode_interpreter.run_and_return_frame(realm, *m_generating_function->bytecode_executable(), next_block, frame); vm.pop_execution_context(); diff --git a/Userland/Libraries/LibJS/Runtime/ShadowRealm.cpp b/Userland/Libraries/LibJS/Runtime/ShadowRealm.cpp index 140fc895b4a1489..5469ea33a275a97 100644 --- a/Userland/Libraries/LibJS/Runtime/ShadowRealm.cpp +++ b/Userland/Libraries/LibJS/Runtime/ShadowRealm.cpp @@ -4,6 +4,8 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include +#include #include #include #include diff --git a/Userland/Libraries/LibJS/Runtime/VM.cpp b/Userland/Libraries/LibJS/Runtime/VM.cpp index 921a28b15438edc..b1cce1d3382b757 100644 --- a/Userland/Libraries/LibJS/Runtime/VM.cpp +++ b/Userland/Libraries/LibJS/Runtime/VM.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -65,6 +66,8 @@ VM::VM(OwnPtr custom_data, ErrorMessages error_messages) , m_error_messages(move(error_messages)) , m_custom_data(move(custom_data)) { + m_bytecode_interpreter = make(*this); + m_empty_string = m_heap.allocate_without_realm(String {}); for (size_t i = 0; i < single_ascii_character_strings.size(); ++i) @@ -166,6 +169,8 @@ VM::VM(OwnPtr custom_data, ErrorMessages error_messages) }; } +VM::~VM() = default; + String const& VM::error_message(ErrorMessage type) const { VERIFY(type < ErrorMessage::__Count); @@ -196,6 +201,18 @@ Interpreter* VM::interpreter_if_exists() return m_interpreters.last(); } +Bytecode::Interpreter& VM::bytecode_interpreter() +{ + return *m_bytecode_interpreter; +} + +Bytecode::Interpreter* VM::bytecode_interpreter_if_exists() +{ + if (!Bytecode::Interpreter::enabled()) + return nullptr; + return m_bytecode_interpreter; +} + void VM::push_interpreter(Interpreter& interpreter) { m_interpreters.append(&interpreter); diff --git a/Userland/Libraries/LibJS/Runtime/VM.h b/Userland/Libraries/LibJS/Runtime/VM.h index 924734506377dbf..adeb242b4290472 100644 --- a/Userland/Libraries/LibJS/Runtime/VM.h +++ b/Userland/Libraries/LibJS/Runtime/VM.h @@ -39,11 +39,14 @@ class VM : public RefCounted { }; static ErrorOr> create(OwnPtr = {}); - ~VM() = default; + ~VM(); Heap& heap() { return m_heap; } Heap const& heap() const { return m_heap; } + Bytecode::Interpreter& bytecode_interpreter(); + Bytecode::Interpreter* bytecode_interpreter_if_exists(); + Interpreter& interpreter(); Interpreter* interpreter_if_exists(); @@ -332,6 +335,8 @@ class VM : public RefCounted { u32 m_execution_generation { 0 }; OwnPtr m_custom_data; + + OwnPtr m_bytecode_interpreter; }; ALWAYS_INLINE Heap& Cell::heap() const diff --git a/Userland/Libraries/LibTest/JavaScriptTestRunner.h b/Userland/Libraries/LibTest/JavaScriptTestRunner.h index 2bb933a387634cb..65f29c520db621d 100644 --- a/Userland/Libraries/LibTest/JavaScriptTestRunner.h +++ b/Userland/Libraries/LibTest/JavaScriptTestRunner.h @@ -126,7 +126,6 @@ static consteval size_t __testjs_last() static constexpr auto TOP_LEVEL_TEST_NAME = "__$$TOP_LEVEL$$__"; extern RefPtr g_vm; extern bool g_collect_on_every_allocation; -extern bool g_run_bytecode; extern DeprecatedString g_currently_running_test; struct FunctionWithLength { JS::ThrowCompletionOr (*function)(JS::VM&); @@ -360,10 +359,9 @@ inline JSFileResult TestRunner::run_file_test(DeprecatedString const& test_path) } auto test_script = result.release_value(); - if (g_run_bytecode) { + if (auto* bytecode_interpreter = g_vm->bytecode_interpreter_if_exists()) { g_vm->push_execution_context(global_execution_context); - JS::Bytecode::Interpreter bytecode_interpreter(interpreter->realm()); - MUST(bytecode_interpreter.run(*test_script)); + MUST(bytecode_interpreter->run(*test_script)); g_vm->pop_execution_context(); } else { g_vm->push_execution_context(global_execution_context); @@ -376,9 +374,8 @@ inline JSFileResult TestRunner::run_file_test(DeprecatedString const& test_path) if (file_script.is_error()) return { test_path, file_script.error() }; g_vm->push_execution_context(global_execution_context); - if (g_run_bytecode) { - JS::Bytecode::Interpreter bytecode_interpreter(interpreter->realm()); - top_level_result = bytecode_interpreter.run(file_script.value()); + if (auto* bytecode_interpreter = g_vm->bytecode_interpreter_if_exists()) { + top_level_result = bytecode_interpreter->run(file_script.value()); } else { top_level_result = interpreter->run(file_script.value()); } diff --git a/Userland/Libraries/LibTest/JavaScriptTestRunnerMain.cpp b/Userland/Libraries/LibTest/JavaScriptTestRunnerMain.cpp index 07854ff5e376c82..b5c83ceedc74791 100644 --- a/Userland/Libraries/LibTest/JavaScriptTestRunnerMain.cpp +++ b/Userland/Libraries/LibTest/JavaScriptTestRunnerMain.cpp @@ -20,7 +20,6 @@ namespace JS { RefPtr<::JS::VM> g_vm; bool g_collect_on_every_allocation = false; -bool g_run_bytecode = false; DeprecatedString g_currently_running_test; HashMap s_exposed_global_functions; Function g_main_hook; @@ -94,6 +93,7 @@ int main(int argc, char** argv) #endif bool print_json = false; bool per_file = false; + bool use_bytecode = false; StringView specified_test_root; DeprecatedString common_path; DeprecatedString test_glob; @@ -115,10 +115,11 @@ int main(int argc, char** argv) return true; }, }); + args_parser.add_option(print_json, "Show results as JSON", "json", 'j'); args_parser.add_option(per_file, "Show detailed per-file results as JSON (implies -j)", "per-file", 0); args_parser.add_option(g_collect_on_every_allocation, "Collect garbage after every allocation", "collect-often", 'g'); - args_parser.add_option(g_run_bytecode, "Use the bytecode interpreter", "run-bytecode", 'b'); + args_parser.add_option(use_bytecode, "Use the bytecode interpreter", "run-bytecode", 'b'); args_parser.add_option(JS::Bytecode::g_dump_bytecode, "Dump the bytecode", "dump-bytecode", 'd'); args_parser.add_option(test_glob, "Only run tests matching the given glob", "filter", 'f', "glob"); for (auto& entry : g_extra_args) @@ -136,11 +137,13 @@ int main(int argc, char** argv) AK::set_debug_enabled(false); } - if (JS::Bytecode::g_dump_bytecode && !g_run_bytecode) { + if (JS::Bytecode::g_dump_bytecode && !use_bytecode) { warnln("--dump-bytecode can only be used when --run-bytecode is specified."); return 1; } + JS::Bytecode::Interpreter::set_enabled(use_bytecode); + DeprecatedString test_root; if (!specified_test_root.is_empty()) { diff --git a/Userland/Libraries/LibWeb/HTML/Scripting/ClassicScript.cpp b/Userland/Libraries/LibWeb/HTML/Scripting/ClassicScript.cpp index c4beae208b8026a..c7794c5e6505934 100644 --- a/Userland/Libraries/LibWeb/HTML/Scripting/ClassicScript.cpp +++ b/Userland/Libraries/LibWeb/HTML/Scripting/ClassicScript.cpp @@ -95,9 +95,8 @@ JS::Completion ClassicScript::run(RethrowErrors rethrow_errors, JS::GCPtrrealm()); - evaluation_status = interpreter.run(*m_script_record, lexical_environment_override); + if (auto* bytecode_interpreter = vm().bytecode_interpreter_if_exists()) { + evaluation_status = bytecode_interpreter->run(*m_script_record, lexical_environment_override); } else { auto interpreter = JS::Interpreter::create_with_existing_realm(m_script_record->realm()); evaluation_status = interpreter->run(*m_script_record, lexical_environment_override); diff --git a/Userland/Utilities/js.cpp b/Userland/Utilities/js.cpp index 326c374e07b7be4..f26f81312364f4b 100644 --- a/Userland/Utilities/js.cpp +++ b/Userland/Utilities/js.cpp @@ -71,7 +71,6 @@ class ScriptObject final : public JS::GlobalObject { }; static bool s_dump_ast = false; -static bool s_run_bytecode = false; static bool s_opt_bytecode = false; static bool s_as_module = false; static bool s_print_last_result = false; @@ -212,10 +211,9 @@ static ErrorOr parse_and_run(JS::Interpreter& interpreter, StringView sour if (s_dump_ast) script_or_module->parse_node().dump(0); - if (s_run_bytecode) { - JS::Bytecode::Interpreter bytecode_interpreter(interpreter.realm()); - bytecode_interpreter.set_optimizations_enabled(s_opt_bytecode); - result = bytecode_interpreter.run(*script_or_module); + if (auto* bytecode_interpreter = g_vm->bytecode_interpreter_if_exists()) { + bytecode_interpreter->set_optimizations_enabled(s_opt_bytecode); + result = bytecode_interpreter->run(*script_or_module); } else { result = interpreter.run(*script_or_module); } @@ -579,12 +577,13 @@ ErrorOr serenity_main(Main::Arguments arguments) bool use_test262_global = false; StringView evaluate_script; Vector script_paths; + bool use_bytecode = false; Core::ArgsParser args_parser; args_parser.set_general_help("This is a JavaScript interpreter."); args_parser.add_option(s_dump_ast, "Dump the AST", "dump-ast", 'A'); args_parser.add_option(JS::Bytecode::g_dump_bytecode, "Dump the bytecode", "dump-bytecode", 'd'); - args_parser.add_option(s_run_bytecode, "Run the bytecode", "run-bytecode", 'b'); + args_parser.add_option(use_bytecode, "Run the bytecode", "run-bytecode", 'b'); args_parser.add_option(s_opt_bytecode, "Optimize the bytecode", "optimize-bytecode", 'p'); args_parser.add_option(s_as_module, "Treat as module", "as-module", 'm'); args_parser.add_option(s_print_last_result, "Print last result", "print-last-result", 'l'); @@ -598,6 +597,8 @@ ErrorOr serenity_main(Main::Arguments arguments) args_parser.add_positional_argument(script_paths, "Path to script files", "scripts", Core::ArgsParser::Required::No); args_parser.parse(arguments); + JS::Bytecode::Interpreter::set_enabled(use_bytecode); + bool syntax_highlight = !disable_syntax_highlight; AK::set_debug_enabled(!disable_debug_printing);