summaryrefslogtreecommitdiff
path: root/Userland
diff options
context:
space:
mode:
Diffstat (limited to 'Userland')
-rw-r--r--Userland/Libraries/LibJS/AST.h1
-rw-r--r--Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp10
-rw-r--r--Userland/Libraries/LibJS/Bytecode/Interpreter.cpp59
-rw-r--r--Userland/Libraries/LibJS/Bytecode/Interpreter.h16
-rw-r--r--Userland/Libraries/LibJS/Bytecode/Op.cpp13
-rw-r--r--Userland/Libraries/LibJS/Bytecode/Op.h15
-rw-r--r--Userland/Libraries/LibJS/Runtime/ScriptFunction.cpp89
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()