From 7a742b17da1cc7e53ea272e733a5244c168afc10 Mon Sep 17 00:00:00 2001 From: Andreas Kling Date: Mon, 31 Jan 2022 13:25:39 +0100 Subject: [PATCH] LibJS: Store ECMAScriptFunctionObject bytecode in an OwnPtr Using an Optional was extremely wasteful for function objects that don't even have a bytecode executable. This allows ECMAScriptFunctionObject to fit in a smaller size class. --- Tests/LibJS/test-bytecode-js.cpp | 20 +++++++++---------- .../Libraries/LibJS/Bytecode/Generator.cpp | 9 +++++++-- Userland/Libraries/LibJS/Bytecode/Generator.h | 2 +- .../LibJS/Runtime/AbstractOperations.cpp | 6 +++--- .../Runtime/ECMAScriptFunctionObject.cpp | 2 +- .../LibJS/Runtime/ECMAScriptFunctionObject.h | 2 +- .../Libraries/LibTest/JavaScriptTestRunner.h | 12 +++++------ Userland/Utilities/js.cpp | 8 ++++---- 8 files changed, 33 insertions(+), 28 deletions(-) diff --git a/Tests/LibJS/test-bytecode-js.cpp b/Tests/LibJS/test-bytecode-js.cpp index e2dad31beb..267d2a7e62 100644 --- a/Tests/LibJS/test-bytecode-js.cpp +++ b/Tests/LibJS/test-bytecode-js.cpp @@ -24,17 +24,17 @@ #define EXPECT_NO_EXCEPTION(executable) \ auto executable = JS::Bytecode::Generator::generate(program); \ - auto result = bytecode_interpreter.run(executable); \ + auto result = bytecode_interpreter.run(*executable); \ EXPECT(!result.is_error()); \ EXPECT(!vm->exception()); -#define EXPECT_NO_EXCEPTION_WITH_OPTIMIZATIONS(executable) \ - auto& passes = JS::Bytecode::Interpreter::optimization_pipeline(); \ - passes.perform(executable); \ - \ - auto result_with_optimizations = bytecode_interpreter.run(executable); \ - \ - EXPECT(!result_with_optimizations.is_error()); \ +#define EXPECT_NO_EXCEPTION_WITH_OPTIMIZATIONS(executable) \ + auto& passes = JS::Bytecode::Interpreter::optimization_pipeline(); \ + passes.perform(*executable); \ + \ + auto result_with_optimizations = bytecode_interpreter.run(*executable); \ + \ + EXPECT(!result_with_optimizations.is_error()); \ EXPECT(!vm->exception()) #define EXPECT_NO_EXCEPTION_ALL(source) \ @@ -57,7 +57,7 @@ TEST_CASE(if_statement_fail) SETUP_AND_PARSE("if (true) throw new Exception('failed');"); auto executable = JS::Bytecode::Generator::generate(program); - auto result = bytecode_interpreter.run(executable); + auto result = bytecode_interpreter.run(*executable); EXPECT(result.is_error()); } @@ -115,7 +115,7 @@ TEST_CASE(loading_multiple_files) auto const& test_file_program = test_file_script->parse_node(); auto executable = JS::Bytecode::Generator::generate(test_file_program); - auto result = bytecode_interpreter.run(executable); + auto result = bytecode_interpreter.run(*executable); EXPECT(!result.is_error()); EXPECT(!vm->exception()); } diff --git a/Userland/Libraries/LibJS/Bytecode/Generator.cpp b/Userland/Libraries/LibJS/Bytecode/Generator.cpp index adea224ac9..da1d837a4e 100644 --- a/Userland/Libraries/LibJS/Bytecode/Generator.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Generator.cpp @@ -23,7 +23,7 @@ Generator::~Generator() { } -Executable Generator::generate(ASTNode const& node, FunctionKind enclosing_function_kind) +NonnullOwnPtr Generator::generate(ASTNode const& node, FunctionKind enclosing_function_kind) { Generator generator; generator.switch_to_basic_block(generator.make_block()); @@ -45,7 +45,12 @@ Executable Generator::generate(ASTNode const& node, FunctionKind enclosing_funct generator.emit(nullptr); } } - return { {}, move(generator.m_root_basic_blocks), move(generator.m_string_table), move(generator.m_identifier_table), generator.m_next_register }; + return adopt_own(*new Executable { + .name = {}, + .basic_blocks = move(generator.m_root_basic_blocks), + .string_table = move(generator.m_string_table), + .identifier_table = move(generator.m_identifier_table), + .number_of_registers = generator.m_next_register }); } void Generator::grow(size_t additional_size) diff --git a/Userland/Libraries/LibJS/Bytecode/Generator.h b/Userland/Libraries/LibJS/Bytecode/Generator.h index 066fae8a18..1f5c542771 100644 --- a/Userland/Libraries/LibJS/Bytecode/Generator.h +++ b/Userland/Libraries/LibJS/Bytecode/Generator.h @@ -23,7 +23,7 @@ namespace JS::Bytecode { class Generator { public: - static Executable generate(ASTNode const&, FunctionKind = FunctionKind::Normal); + static NonnullOwnPtr generate(ASTNode const&, FunctionKind = FunctionKind::Normal); Register allocate_register(); diff --git a/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp b/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp index cc11b874fe..0b709cd774 100644 --- a/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp +++ b/Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp @@ -580,10 +580,10 @@ ThrowCompletionOr perform_eval(Value x, GlobalObject& caller_realm, Calle if (auto* bytecode_interpreter = Bytecode::Interpreter::current()) { auto executable = JS::Bytecode::Generator::generate(program); - executable.name = "eval"sv; + executable->name = "eval"sv; if (JS::Bytecode::g_dump_bytecode) - executable.dump(); - eval_result = TRY(bytecode_interpreter->run(executable)); + executable->dump(); + eval_result = TRY(bytecode_interpreter->run(*executable)); // Turn potentially empty JS::Value from the bytecode interpreter into an empty Optional if (eval_result.has_value() && eval_result->is_empty()) eval_result = {}; diff --git a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp index 80abaa5a5a..65fe372efd 100644 --- a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp +++ b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.cpp @@ -775,7 +775,7 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body() if (bytecode_interpreter) { // FIXME: pass something to evaluate default arguments with TRY(function_declaration_instantiation(nullptr)); - if (!m_bytecode_executable.has_value()) { + if (!m_bytecode_executable) { m_bytecode_executable = Bytecode::Generator::generate(m_ecmascript_code, m_kind); m_bytecode_executable->name = m_name; auto& passes = JS::Bytecode::Interpreter::optimization_pipeline(); diff --git a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h index 3ac350500a..e54fe6e944 100644 --- a/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h +++ b/Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h @@ -103,7 +103,7 @@ private: ThrowCompletionOr function_declaration_instantiation(Interpreter*); FlyString m_name; - Optional m_bytecode_executable; + OwnPtr m_bytecode_executable; i32 m_function_length { 0 }; // Internal Slots of ECMAScript Function Objects, https://tc39.es/ecma262/#table-internal-slots-of-ecmascript-function-objects diff --git a/Userland/Libraries/LibTest/JavaScriptTestRunner.h b/Userland/Libraries/LibTest/JavaScriptTestRunner.h index b24f7b9b53..6dff4cb26d 100644 --- a/Userland/Libraries/LibTest/JavaScriptTestRunner.h +++ b/Userland/Libraries/LibTest/JavaScriptTestRunner.h @@ -343,11 +343,11 @@ inline JSFileResult TestRunner::run_file_test(const String& test_path) if (g_run_bytecode) { auto executable = JS::Bytecode::Generator::generate(test_script->parse_node()); - executable.name = test_path; + executable->name = test_path; if (JS::Bytecode::g_dump_bytecode) - executable.dump(); + executable->dump(); JS::Bytecode::Interpreter bytecode_interpreter(interpreter->global_object(), interpreter->realm()); - MUST(bytecode_interpreter.run(executable)); + MUST(bytecode_interpreter.run(*executable)); } else { g_vm->push_execution_context(global_execution_context, interpreter->global_object()); MUST(interpreter->run(*test_script)); @@ -359,11 +359,11 @@ inline JSFileResult TestRunner::run_file_test(const String& test_path) return { test_path, file_script.error() }; if (g_run_bytecode) { auto executable = JS::Bytecode::Generator::generate(file_script.value()->parse_node()); - executable.name = test_path; + executable->name = test_path; if (JS::Bytecode::g_dump_bytecode) - executable.dump(); + executable->dump(); JS::Bytecode::Interpreter bytecode_interpreter(interpreter->global_object(), interpreter->realm()); - (void)bytecode_interpreter.run(executable); + (void)bytecode_interpreter.run(*executable); } else { g_vm->push_execution_context(global_execution_context, interpreter->global_object()); (void)interpreter->run(file_script.value()); diff --git a/Userland/Utilities/js.cpp b/Userland/Utilities/js.cpp index 7822fdd36f..f2d99ac71b 100644 --- a/Userland/Utilities/js.cpp +++ b/Userland/Utilities/js.cpp @@ -1028,19 +1028,19 @@ static bool parse_and_run(JS::Interpreter& interpreter, StringView source, Strin if (JS::Bytecode::g_dump_bytecode || s_run_bytecode) { auto executable = JS::Bytecode::Generator::generate(script_or_module->parse_node()); - executable.name = source_name; + executable->name = source_name; if (s_opt_bytecode) { auto& passes = JS::Bytecode::Interpreter::optimization_pipeline(); - passes.perform(executable); + passes.perform(*executable); dbgln("Optimisation passes took {}us", passes.elapsed()); } if (JS::Bytecode::g_dump_bytecode) - executable.dump(); + executable->dump(); if (s_run_bytecode) { JS::Bytecode::Interpreter bytecode_interpreter(interpreter.global_object(), interpreter.realm()); - result = bytecode_interpreter.run(executable); + result = bytecode_interpreter.run(*executable); } else { return ReturnEarly::Yes; }