mirror of
https://github.com/RGBCube/serenity
synced 2025-05-31 07:28:11 +00:00
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.
This commit is contained in:
parent
8d3f92c844
commit
7a742b17da
8 changed files with 33 additions and 28 deletions
|
@ -24,17 +24,17 @@
|
||||||
|
|
||||||
#define EXPECT_NO_EXCEPTION(executable) \
|
#define EXPECT_NO_EXCEPTION(executable) \
|
||||||
auto executable = JS::Bytecode::Generator::generate(program); \
|
auto executable = JS::Bytecode::Generator::generate(program); \
|
||||||
auto result = bytecode_interpreter.run(executable); \
|
auto result = bytecode_interpreter.run(*executable); \
|
||||||
EXPECT(!result.is_error()); \
|
EXPECT(!result.is_error()); \
|
||||||
EXPECT(!vm->exception());
|
EXPECT(!vm->exception());
|
||||||
|
|
||||||
#define EXPECT_NO_EXCEPTION_WITH_OPTIMIZATIONS(executable) \
|
#define EXPECT_NO_EXCEPTION_WITH_OPTIMIZATIONS(executable) \
|
||||||
auto& passes = JS::Bytecode::Interpreter::optimization_pipeline(); \
|
auto& passes = JS::Bytecode::Interpreter::optimization_pipeline(); \
|
||||||
passes.perform(executable); \
|
passes.perform(*executable); \
|
||||||
\
|
\
|
||||||
auto result_with_optimizations = bytecode_interpreter.run(executable); \
|
auto result_with_optimizations = bytecode_interpreter.run(*executable); \
|
||||||
\
|
\
|
||||||
EXPECT(!result_with_optimizations.is_error()); \
|
EXPECT(!result_with_optimizations.is_error()); \
|
||||||
EXPECT(!vm->exception())
|
EXPECT(!vm->exception())
|
||||||
|
|
||||||
#define EXPECT_NO_EXCEPTION_ALL(source) \
|
#define EXPECT_NO_EXCEPTION_ALL(source) \
|
||||||
|
@ -57,7 +57,7 @@ TEST_CASE(if_statement_fail)
|
||||||
SETUP_AND_PARSE("if (true) throw new Exception('failed');");
|
SETUP_AND_PARSE("if (true) throw new Exception('failed');");
|
||||||
|
|
||||||
auto executable = JS::Bytecode::Generator::generate(program);
|
auto executable = JS::Bytecode::Generator::generate(program);
|
||||||
auto result = bytecode_interpreter.run(executable);
|
auto result = bytecode_interpreter.run(*executable);
|
||||||
EXPECT(result.is_error());
|
EXPECT(result.is_error());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -115,7 +115,7 @@ TEST_CASE(loading_multiple_files)
|
||||||
auto const& test_file_program = test_file_script->parse_node();
|
auto const& test_file_program = test_file_script->parse_node();
|
||||||
|
|
||||||
auto executable = JS::Bytecode::Generator::generate(test_file_program);
|
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(!result.is_error());
|
||||||
EXPECT(!vm->exception());
|
EXPECT(!vm->exception());
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 generator;
|
||||||
generator.switch_to_basic_block(generator.make_block());
|
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);
|
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)
|
void Generator::grow(size_t additional_size)
|
||||||
|
|
|
@ -23,7 +23,7 @@ namespace JS::Bytecode {
|
||||||
|
|
||||||
class Generator {
|
class Generator {
|
||||||
public:
|
public:
|
||||||
static Executable generate(ASTNode const&, FunctionKind = FunctionKind::Normal);
|
static NonnullOwnPtr<Executable> generate(ASTNode const&, FunctionKind = FunctionKind::Normal);
|
||||||
|
|
||||||
Register allocate_register();
|
Register allocate_register();
|
||||||
|
|
||||||
|
|
|
@ -580,10 +580,10 @@ ThrowCompletionOr<Value> perform_eval(Value x, GlobalObject& caller_realm, Calle
|
||||||
|
|
||||||
if (auto* bytecode_interpreter = Bytecode::Interpreter::current()) {
|
if (auto* bytecode_interpreter = Bytecode::Interpreter::current()) {
|
||||||
auto executable = JS::Bytecode::Generator::generate(program);
|
auto executable = JS::Bytecode::Generator::generate(program);
|
||||||
executable.name = "eval"sv;
|
executable->name = "eval"sv;
|
||||||
if (JS::Bytecode::g_dump_bytecode)
|
if (JS::Bytecode::g_dump_bytecode)
|
||||||
executable.dump();
|
executable->dump();
|
||||||
eval_result = TRY(bytecode_interpreter->run(executable));
|
eval_result = TRY(bytecode_interpreter->run(*executable));
|
||||||
// Turn potentially empty JS::Value from the bytecode interpreter into an empty Optional
|
// Turn potentially empty JS::Value from the bytecode interpreter into an empty Optional
|
||||||
if (eval_result.has_value() && eval_result->is_empty())
|
if (eval_result.has_value() && eval_result->is_empty())
|
||||||
eval_result = {};
|
eval_result = {};
|
||||||
|
|
|
@ -775,7 +775,7 @@ Completion ECMAScriptFunctionObject::ordinary_call_evaluate_body()
|
||||||
if (bytecode_interpreter) {
|
if (bytecode_interpreter) {
|
||||||
// FIXME: pass something to evaluate default arguments with
|
// FIXME: pass something to evaluate default arguments with
|
||||||
TRY(function_declaration_instantiation(nullptr));
|
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 = Bytecode::Generator::generate(m_ecmascript_code, m_kind);
|
||||||
m_bytecode_executable->name = m_name;
|
m_bytecode_executable->name = m_name;
|
||||||
auto& passes = JS::Bytecode::Interpreter::optimization_pipeline();
|
auto& passes = JS::Bytecode::Interpreter::optimization_pipeline();
|
||||||
|
|
|
@ -103,7 +103,7 @@ private:
|
||||||
ThrowCompletionOr<void> function_declaration_instantiation(Interpreter*);
|
ThrowCompletionOr<void> function_declaration_instantiation(Interpreter*);
|
||||||
|
|
||||||
FlyString m_name;
|
FlyString m_name;
|
||||||
Optional<Bytecode::Executable> m_bytecode_executable;
|
OwnPtr<Bytecode::Executable> m_bytecode_executable;
|
||||||
i32 m_function_length { 0 };
|
i32 m_function_length { 0 };
|
||||||
|
|
||||||
// Internal Slots of ECMAScript Function Objects, https://tc39.es/ecma262/#table-internal-slots-of-ecmascript-function-objects
|
// Internal Slots of ECMAScript Function Objects, https://tc39.es/ecma262/#table-internal-slots-of-ecmascript-function-objects
|
||||||
|
|
|
@ -343,11 +343,11 @@ inline JSFileResult TestRunner::run_file_test(const String& test_path)
|
||||||
|
|
||||||
if (g_run_bytecode) {
|
if (g_run_bytecode) {
|
||||||
auto executable = JS::Bytecode::Generator::generate(test_script->parse_node());
|
auto executable = JS::Bytecode::Generator::generate(test_script->parse_node());
|
||||||
executable.name = test_path;
|
executable->name = test_path;
|
||||||
if (JS::Bytecode::g_dump_bytecode)
|
if (JS::Bytecode::g_dump_bytecode)
|
||||||
executable.dump();
|
executable->dump();
|
||||||
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 {
|
||||||
g_vm->push_execution_context(global_execution_context, interpreter->global_object());
|
g_vm->push_execution_context(global_execution_context, interpreter->global_object());
|
||||||
MUST(interpreter->run(*test_script));
|
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() };
|
return { test_path, file_script.error() };
|
||||||
if (g_run_bytecode) {
|
if (g_run_bytecode) {
|
||||||
auto executable = JS::Bytecode::Generator::generate(file_script.value()->parse_node());
|
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)
|
if (JS::Bytecode::g_dump_bytecode)
|
||||||
executable.dump();
|
executable->dump();
|
||||||
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 {
|
||||||
g_vm->push_execution_context(global_execution_context, interpreter->global_object());
|
g_vm->push_execution_context(global_execution_context, interpreter->global_object());
|
||||||
(void)interpreter->run(file_script.value());
|
(void)interpreter->run(file_script.value());
|
||||||
|
|
|
@ -1028,19 +1028,19 @@ static bool parse_and_run(JS::Interpreter& interpreter, StringView source, Strin
|
||||||
|
|
||||||
if (JS::Bytecode::g_dump_bytecode || s_run_bytecode) {
|
if (JS::Bytecode::g_dump_bytecode || s_run_bytecode) {
|
||||||
auto executable = JS::Bytecode::Generator::generate(script_or_module->parse_node());
|
auto executable = JS::Bytecode::Generator::generate(script_or_module->parse_node());
|
||||||
executable.name = source_name;
|
executable->name = source_name;
|
||||||
if (s_opt_bytecode) {
|
if (s_opt_bytecode) {
|
||||||
auto& passes = JS::Bytecode::Interpreter::optimization_pipeline();
|
auto& passes = JS::Bytecode::Interpreter::optimization_pipeline();
|
||||||
passes.perform(executable);
|
passes.perform(*executable);
|
||||||
dbgln("Optimisation passes took {}us", passes.elapsed());
|
dbgln("Optimisation passes took {}us", passes.elapsed());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (JS::Bytecode::g_dump_bytecode)
|
if (JS::Bytecode::g_dump_bytecode)
|
||||||
executable.dump();
|
executable->dump();
|
||||||
|
|
||||||
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());
|
||||||
result = bytecode_interpreter.run(executable);
|
result = bytecode_interpreter.run(*executable);
|
||||||
} else {
|
} else {
|
||||||
return ReturnEarly::Yes;
|
return ReturnEarly::Yes;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue