Просмотр исходного кода

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.
Andreas Kling 3 лет назад
Родитель
Сommit
7a742b17da

+ 10 - 10
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());
     }

+ 7 - 2
Userland/Libraries/LibJS/Bytecode/Generator.cpp

@@ -23,7 +23,7 @@ Generator::~Generator()
 {
 }
 
-Executable Generator::generate(ASTNode const& node, FunctionKind enclosing_function_kind)
+NonnullOwnPtr<Executable> 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<Bytecode::Op::Yield>(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)

+ 1 - 1
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<Executable> generate(ASTNode const&, FunctionKind = FunctionKind::Normal);
 
     Register allocate_register();
 

+ 3 - 3
Userland/Libraries/LibJS/Runtime/AbstractOperations.cpp

@@ -580,10 +580,10 @@ ThrowCompletionOr<Value> 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 = {};

+ 1 - 1
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();

+ 1 - 1
Userland/Libraries/LibJS/Runtime/ECMAScriptFunctionObject.h

@@ -103,7 +103,7 @@ private:
     ThrowCompletionOr<void> function_declaration_instantiation(Interpreter*);
 
     FlyString m_name;
-    Optional<Bytecode::Executable> m_bytecode_executable;
+    OwnPtr<Bytecode::Executable> 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

+ 6 - 6
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());

+ 4 - 4
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;
             }