1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 16:57:35 +00:00

LibJS: Implement generator functions (only in bytecode mode)

This commit is contained in:
Ali Mohammad Pur 2021-06-11 01:38:30 +04:30 committed by Andreas Kling
parent c53a86a3fe
commit 3234697eca
21 changed files with 407 additions and 34 deletions

View file

@ -668,7 +668,23 @@ void ReturnStatement::generate_bytecode(Bytecode::Generator& generator) const
{
if (m_argument)
m_argument->generate_bytecode(generator);
generator.emit<Bytecode::Op::Return>();
if (generator.is_in_generator_function())
generator.emit<Bytecode::Op::Yield>(nullptr);
else
generator.emit<Bytecode::Op::Return>();
}
void YieldExpression::generate_bytecode(Bytecode::Generator& generator) const
{
VERIFY(generator.is_in_generator_function());
if (m_argument)
m_argument->generate_bytecode(generator);
auto& continuation_block = generator.make_block();
generator.emit<Bytecode::Op::Yield>(Bytecode::Label { continuation_block });
generator.switch_to_basic_block(continuation_block);
}
void IfStatement::generate_bytecode(Bytecode::Generator& generator) const

View file

@ -23,11 +23,28 @@ Generator::~Generator()
{
}
Executable Generator::generate(ASTNode const& node)
Executable Generator::generate(ASTNode const& node, bool is_in_generator_function)
{
Generator generator;
generator.switch_to_basic_block(generator.make_block());
if (is_in_generator_function) {
generator.enter_generator_context();
// Immediately yield with no value.
auto& start_block = generator.make_block();
generator.emit<Bytecode::Op::Yield>(Label { start_block });
generator.switch_to_basic_block(start_block);
}
node.generate_bytecode(generator);
if (is_in_generator_function) {
// Terminate all unterminated blocks with yield return
for (auto& block : generator.m_root_basic_blocks) {
if (block.is_terminated())
continue;
generator.switch_to_basic_block(block);
generator.emit<Bytecode::Op::LoadImmediate>(js_undefined());
generator.emit<Bytecode::Op::Yield>(nullptr);
}
}
return { move(generator.m_root_basic_blocks), move(generator.m_string_table), generator.m_next_register };
}

View file

@ -28,7 +28,7 @@ struct Executable {
class Generator {
public:
static Executable generate(ASTNode const&);
static Executable generate(ASTNode const&, bool is_in_generator_function = false);
Register allocate_register();
@ -109,6 +109,10 @@ public:
return m_string_table->insert(string);
}
bool is_in_generator_function() const { return m_is_in_generator_function; }
void enter_generator_context() { m_is_in_generator_function = true; }
void leave_generator_context() { m_is_in_generator_function = false; }
private:
Generator();
~Generator();
@ -122,6 +126,7 @@ private:
u32 m_next_register { 2 };
u32 m_next_block { 1 };
bool m_is_in_generator_function { false };
Vector<Label> m_continuable_scopes;
Vector<Label> m_breakable_scopes;
};

View file

@ -61,7 +61,8 @@
O(PushLexicalEnvironment) \
O(EnterUnwindContext) \
O(LeaveUnwindContext) \
O(ContinuePendingUnwind)
O(ContinuePendingUnwind) \
O(Yield)
namespace JS::Bytecode {

View file

@ -35,7 +35,7 @@ Interpreter::~Interpreter()
s_current = nullptr;
}
Value Interpreter::run(Executable const& executable)
Value Interpreter::run(Executable const& executable, BasicBlock const* entry_point)
{
dbgln_if(JS_BYTECODE_DEBUG, "Bytecode::Interpreter will run unit {:p}", &executable);
@ -56,10 +56,14 @@ Value Interpreter::run(Executable const& executable)
VERIFY(!vm().exception());
}
auto block = &executable.basic_blocks.first();
m_register_windows.append(make<RegisterWindow>());
registers().resize(executable.number_of_registers);
registers()[Register::global_object_index] = Value(&global_object());
auto block = entry_point ?: &executable.basic_blocks.first();
if (m_manually_entered_frames) {
VERIFY(registers().size() >= executable.number_of_registers);
} else {
m_register_windows.append(make<RegisterWindow>());
registers().resize(executable.number_of_registers);
registers()[Register::global_object_index] = Value(&global_object());
}
for (;;) {
Bytecode::InstructionStreamIterator pc(block->instruction_stream());
@ -124,7 +128,8 @@ Value Interpreter::run(Executable const& executable)
vm().set_last_value(Badge<Interpreter> {}, accumulator());
m_register_windows.take_last();
if (!m_manually_entered_frames)
m_register_windows.take_last();
auto return_value = m_return_value.value_or(js_undefined());
m_return_value = {};

View file

@ -30,10 +30,23 @@ public:
GlobalObject& global_object() { return m_global_object; }
VM& vm() { return m_vm; }
Value run(Bytecode::Executable const&);
Value run(Bytecode::Executable const&, Bytecode::BasicBlock const* entry_point = nullptr);
ALWAYS_INLINE Value& accumulator() { return reg(Register::accumulator()); }
Value& reg(Register const& r) { return registers()[r.index()]; }
[[nodiscard]] RegisterWindow snapshot_frame() const { return m_register_windows.last(); }
void enter_frame(RegisterWindow const& frame)
{
++m_manually_entered_frames;
m_register_windows.append(make<RegisterWindow>(frame));
}
void leave_frame()
{
VERIFY(m_manually_entered_frames);
--m_manually_entered_frames;
m_register_windows.take_last();
}
void jump(Label const& label)
{
@ -55,6 +68,7 @@ private:
NonnullOwnPtrVector<RegisterWindow> m_register_windows;
Optional<BasicBlock const*> m_pending_jump;
Value m_return_value;
size_t m_manually_entered_frames { 0 };
Executable const* m_current_executable { nullptr };
Vector<UnwindInfo> m_unwind_contexts;
Handle<Exception> m_saved_exception;

View file

@ -218,8 +218,7 @@ void Call::execute(Bytecode::Interpreter& interpreter) const
void NewFunction::execute(Bytecode::Interpreter& interpreter) const
{
auto& vm = interpreter.vm();
auto& global_object = interpreter.global_object();
interpreter.accumulator() = ScriptFunction::create(global_object, m_function_node.name(), m_function_node.body(), m_function_node.parameters(), m_function_node.function_length(), vm.current_scope(), m_function_node.is_strict_mode());
interpreter.accumulator() = ScriptFunction::create(interpreter.global_object(), m_function_node.name(), m_function_node.body(), m_function_node.parameters(), m_function_node.function_length(), vm.current_scope(), m_function_node.is_generator(), m_function_node.is_strict_mode());
}
void Return::execute(Bytecode::Interpreter& interpreter) const
@ -280,6 +279,18 @@ void PushLexicalEnvironment::execute(Bytecode::Interpreter& interpreter) const
interpreter.vm().call_frame().scope = block_lexical_environment;
}
void Yield::execute(Bytecode::Interpreter& interpreter) const
{
auto yielded_value = interpreter.accumulator().value_or(js_undefined());
auto object = JS::Object::create_empty(interpreter.global_object());
object->put("result", yielded_value);
if (m_continuation_label.has_value())
object->put("continuation", Value(static_cast<double>(reinterpret_cast<u64>(&m_continuation_label->block()))));
else
object->put("continuation", Value(0));
interpreter.do_return(object);
}
String Load::to_string(Bytecode::Executable const&) const
{
return String::formatted("Load {}", m_src);
@ -445,4 +456,11 @@ String PushLexicalEnvironment::to_string(const Bytecode::Executable& executable)
return builder.to_string();
}
String Yield::to_string(Bytecode::Executable const&) const
{
if (m_continuation_label.has_value())
return String::formatted("Yield continuation:@{}", m_continuation_label->block().name());
return String::formatted("Yield return");
}
}

View file

@ -184,8 +184,6 @@ public:
void execute(Bytecode::Interpreter&) const;
String to_string(Bytecode::Executable const&) const;
size_t length() const { return sizeof(*this) + sizeof(Register) * m_element_count; }
private:
size_t m_element_count { 0 };
Register m_elements[];
@ -462,6 +460,28 @@ private:
Label m_resume_target;
};
class Yield final : public Instruction {
public:
constexpr static bool IsTerminator = true;
explicit Yield(Label continuation_label)
: Instruction(Type::Yield)
, m_continuation_label(continuation_label)
{
}
explicit Yield(std::nullptr_t)
: Instruction(Type::Yield)
{
}
void execute(Bytecode::Interpreter&) const;
String to_string(Bytecode::Executable const&) const;
private:
Optional<Label> m_continuation_label;
};
class PushLexicalEnvironment final : public Instruction {
public:
PushLexicalEnvironment(HashMap<u32, Variable> variables)
@ -472,6 +492,7 @@ public:
void execute(Bytecode::Interpreter&) const;
String to_string(Bytecode::Executable const&) const;
private:
HashMap<u32, Variable> m_variables;
};
}