diff options
Diffstat (limited to 'Userland')
-rw-r--r-- | Userland/Libraries/LibJS/AST.h | 1 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp | 10 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Bytecode/Interpreter.cpp | 59 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Bytecode/Interpreter.h | 16 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Bytecode/Op.cpp | 13 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Bytecode/Op.h | 15 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Runtime/ScriptFunction.cpp | 89 |
7 files changed, 152 insertions, 51 deletions
diff --git a/Userland/Libraries/LibJS/AST.h b/Userland/Libraries/LibJS/AST.h index d6ff389251..4e156ef655 100644 --- a/Userland/Libraries/LibJS/AST.h +++ b/Userland/Libraries/LibJS/AST.h @@ -329,6 +329,7 @@ public: virtual Value execute(Interpreter&, GlobalObject&) const override; virtual void dump(int indent) const override; + virtual Optional<Bytecode::Register> generate_bytecode(Bytecode::Generator&) const override; private: RefPtr<Expression> m_argument; diff --git a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp index 32d80310be..18d4e24700 100644 --- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp +++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp @@ -177,4 +177,14 @@ Optional<Bytecode::Register> CallExpression::generate_bytecode(Bytecode::Generat return dst_reg; } +Optional<Bytecode::Register> ReturnStatement::generate_bytecode(Bytecode::Generator& generator) const +{ + Optional<Bytecode::Register> argument_reg; + if (m_argument) + argument_reg = m_argument->generate_bytecode(generator); + + generator.emit<Bytecode::Op::Return>(argument_reg); + return argument_reg; +} + } diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp index 535acc486f..121b2ca066 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Interpreter.cpp @@ -11,32 +11,46 @@ namespace JS::Bytecode { +static Interpreter* s_current; + +Interpreter* Interpreter::current() +{ + return s_current; +} + Interpreter::Interpreter(GlobalObject& global_object) : m_vm(global_object.vm()) , m_global_object(global_object) { + VERIFY(!s_current); + s_current = this; } Interpreter::~Interpreter() { + VERIFY(s_current == this); + s_current = nullptr; } -void Interpreter::run(Bytecode::Block const& block) +Value Interpreter::run(Bytecode::Block const& block) { dbgln("Bytecode::Interpreter will run block {:p}", &block); - CallFrame global_call_frame; - global_call_frame.this_value = &global_object(); - static FlyString global_execution_context_name = "(*BC* global execution context)"; - global_call_frame.function_name = global_execution_context_name; - global_call_frame.scope = &global_object(); - VERIFY(!vm().exception()); - // FIXME: How do we know if we're in strict mode? Maybe the Bytecode::Block should know this? - // global_call_frame.is_strict_mode = ???; - vm().push_call_frame(global_call_frame, global_object()); - VERIFY(!vm().exception()); + if (vm().call_stack().is_empty()) { + CallFrame global_call_frame; + global_call_frame.this_value = &global_object(); + static FlyString global_execution_context_name = "(*BC* global execution context)"; + global_call_frame.function_name = global_execution_context_name; + global_call_frame.scope = &global_object(); + VERIFY(!vm().exception()); + // FIXME: How do we know if we're in strict mode? Maybe the Bytecode::Block should know this? + // global_call_frame.is_strict_mode = ???; + vm().push_call_frame(global_call_frame, global_object()); + VERIFY(!vm().exception()); + } - m_registers.resize(block.register_count()); + m_register_windows.append(make<RegisterWindow>()); + registers().resize(block.register_count()); size_t pc = 0; while (pc < block.instructions().size()) { @@ -46,18 +60,33 @@ void Interpreter::run(Bytecode::Block const& block) pc = m_pending_jump.release_value(); continue; } + if (!m_return_value.is_empty()) + break; ++pc; } dbgln("Bytecode::Interpreter did run block {:p}", &block); - for (size_t i = 0; i < m_registers.size(); ++i) { + for (size_t i = 0; i < registers().size(); ++i) { String value_string; - if (m_registers[i].is_empty()) + if (registers()[i].is_empty()) value_string = "(empty)"; else - value_string = m_registers[i].to_string_without_side_effects(); + value_string = registers()[i].to_string_without_side_effects(); dbgln("[{:3}] {}", i, value_string); } + + m_register_windows.take_last(); + + m_return_value = m_return_value.value_or(js_undefined()); + + // NOTE: The return value from a called function is put into $0 in the caller context. + if (!m_register_windows.is_empty()) + m_register_windows.last()[0] = m_return_value; + + if (vm().call_stack().size() == 1) + vm().pop_call_frame(); + + return m_return_value; } } diff --git a/Userland/Libraries/LibJS/Bytecode/Interpreter.h b/Userland/Libraries/LibJS/Bytecode/Interpreter.h index dcb74b7fc2..f48cce2231 100644 --- a/Userland/Libraries/LibJS/Bytecode/Interpreter.h +++ b/Userland/Libraries/LibJS/Bytecode/Interpreter.h @@ -6,6 +6,7 @@ #pragma once +#include <AK/NonnullOwnPtrVector.h> #include <LibJS/Bytecode/Label.h> #include <LibJS/Bytecode/Register.h> #include <LibJS/Forward.h> @@ -14,25 +15,34 @@ namespace JS::Bytecode { +using RegisterWindow = Vector<Value>; + class Interpreter { public: explicit Interpreter(GlobalObject&); ~Interpreter(); + // FIXME: Remove this thing once we don't need it anymore! + static Interpreter* current(); + GlobalObject& global_object() { return m_global_object; } VM& vm() { return m_vm; } - void run(Bytecode::Block const&); + Value run(Bytecode::Block const&); - Value& reg(Register const& r) { return m_registers[r.index()]; } + Value& reg(Register const& r) { return registers()[r.index()]; } void jump(Label const& label) { m_pending_jump = label.address(); } + void do_return(Value return_value) { m_return_value = return_value; } private: + RegisterWindow& registers() { return m_register_windows.last(); } + VM& m_vm; GlobalObject& m_global_object; - Vector<Value> m_registers; + NonnullOwnPtrVector<RegisterWindow> m_register_windows; Optional<size_t> m_pending_jump; + Value m_return_value; }; } diff --git a/Userland/Libraries/LibJS/Bytecode/Op.cpp b/Userland/Libraries/LibJS/Bytecode/Op.cpp index d40584067e..869dd169ce 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Op.cpp @@ -133,6 +133,12 @@ void EnterScope::execute(Bytecode::Interpreter& interpreter) const // FIXME: Whatever else JS::Interpreter::enter_scope() does. } +void Return::execute(Bytecode::Interpreter& interpreter) const +{ + auto return_value = m_argument.has_value() ? interpreter.reg(m_argument.value()) : js_undefined(); + interpreter.do_return(return_value); +} + String Load::to_string() const { return String::formatted("Load dst:{}, value:{}", m_dst, m_value.to_string_without_side_effects()); @@ -228,4 +234,11 @@ String EnterScope::to_string() const return "EnterScope"; } +String Return::to_string() const +{ + if (m_argument.has_value()) + return String::formatted("Return {}", m_argument.value()); + return "Return"; +} + } diff --git a/Userland/Libraries/LibJS/Bytecode/Op.h b/Userland/Libraries/LibJS/Bytecode/Op.h index 793abff75d..1d57fed10b 100644 --- a/Userland/Libraries/LibJS/Bytecode/Op.h +++ b/Userland/Libraries/LibJS/Bytecode/Op.h @@ -301,4 +301,19 @@ private: ScopeNode const& m_scope_node; }; +class Return final : public Instruction { +public: + explicit Return(Optional<Register> argument) + : m_argument(move(argument)) + { + } + + virtual ~Return() override { } + virtual void execute(Bytecode::Interpreter&) const override; + virtual String to_string() const override; + +private: + Optional<Register> m_argument; +}; + } diff --git a/Userland/Libraries/LibJS/Runtime/ScriptFunction.cpp b/Userland/Libraries/LibJS/Runtime/ScriptFunction.cpp index e80954b4a0..4e4cf9698b 100644 --- a/Userland/Libraries/LibJS/Runtime/ScriptFunction.cpp +++ b/Userland/Libraries/LibJS/Runtime/ScriptFunction.cpp @@ -6,6 +6,9 @@ #include <AK/Function.h> #include <LibJS/AST.h> +#include <LibJS/Bytecode/Block.h> +#include <LibJS/Bytecode/Generator.h> +#include <LibJS/Bytecode/Interpreter.h> #include <LibJS/Interpreter.h> #include <LibJS/Runtime/Array.h> #include <LibJS/Runtime/Error.h> @@ -110,45 +113,65 @@ Value ScriptFunction::execute_function_body() { auto& vm = this->vm(); - OwnPtr<Interpreter> local_interpreter; - Interpreter* interpreter = vm.interpreter_if_exists(); + Interpreter* ast_interpreter = nullptr; + auto* bytecode_interpreter = Bytecode::Interpreter::current(); + + auto prepare_arguments = [&] { + auto& call_frame_args = vm.call_frame().arguments; + for (size_t i = 0; i < m_parameters.size(); ++i) { + auto& parameter = m_parameters[i]; + parameter.binding.visit( + [&](const auto& param) { + Value argument_value; + if (parameter.is_rest) { + auto* array = Array::create(global_object()); + for (size_t rest_index = i; rest_index < call_frame_args.size(); ++rest_index) + array->indexed_properties().append(call_frame_args[rest_index]); + argument_value = move(array); + } else if (i < call_frame_args.size() && !call_frame_args[i].is_undefined()) { + argument_value = call_frame_args[i]; + } else if (parameter.default_value) { + // FIXME: Support default arguments in the bytecode world! + if (!bytecode_interpreter) + argument_value = parameter.default_value->execute(*ast_interpreter, global_object()); + if (vm.exception()) + return; + } else { + argument_value = js_undefined(); + } + + vm.assign(param, argument_value, global_object(), true, vm.current_scope()); + }); - if (!interpreter) { - local_interpreter = Interpreter::create_with_existing_global_object(global_object()); - interpreter = local_interpreter.ptr(); - } + if (vm.exception()) + return; + } + }; + + if (bytecode_interpreter) { + prepare_arguments(); + auto block = Bytecode::Generator::generate(m_body); + VERIFY(block); + dbgln("Compiled Bytecode::Block for function '{}':", m_name); + block->dump(); + return bytecode_interpreter->run(*block); + } else { + OwnPtr<Interpreter> local_interpreter; + ast_interpreter = vm.interpreter_if_exists(); + + if (!ast_interpreter) { + local_interpreter = Interpreter::create_with_existing_global_object(global_object()); + ast_interpreter = local_interpreter.ptr(); + } - VM::InterpreterExecutionScope scope(*interpreter); - - auto& call_frame_args = vm.call_frame().arguments; - for (size_t i = 0; i < m_parameters.size(); ++i) { - auto& parameter = m_parameters[i]; - parameter.binding.visit( - [&](const auto& param) { - Value argument_value; - if (parameter.is_rest) { - auto* array = Array::create(global_object()); - for (size_t rest_index = i; rest_index < call_frame_args.size(); ++rest_index) - array->indexed_properties().append(call_frame_args[rest_index]); - argument_value = move(array); - } else if (i < call_frame_args.size() && !call_frame_args[i].is_undefined()) { - argument_value = call_frame_args[i]; - } else if (parameter.default_value) { - argument_value = parameter.default_value->execute(*interpreter, global_object()); - if (vm.exception()) - return; - } else { - argument_value = js_undefined(); - } - - vm.assign(param, argument_value, global_object(), true, vm.current_scope()); - }); + VM::InterpreterExecutionScope scope(*ast_interpreter); + prepare_arguments(); if (vm.exception()) return {}; - } - return interpreter->execute_statement(global_object(), m_body, ScopeType::Function); + return ast_interpreter->execute_statement(global_object(), m_body, ScopeType::Function); + } } Value ScriptFunction::call() |