diff --git a/Userland/Libraries/LibJS/AST.cpp b/Userland/Libraries/LibJS/AST.cpp index 203bc880eb..f56a5c33bb 100644 --- a/Userland/Libraries/LibJS/AST.cpp +++ b/Userland/Libraries/LibJS/AST.cpp @@ -259,6 +259,12 @@ Value CallExpression::execute(Interpreter& interpreter, GlobalObject& global_obj return result; } +Value YieldExpression::execute(Interpreter&, GlobalObject&) const +{ + // This should be transformed to a return. + VERIFY_NOT_REACHED(); +} + Value ReturnStatement::execute(Interpreter& interpreter, GlobalObject& global_object) const { InterpreterNodeScope node_scope { interpreter, *this }; @@ -1151,7 +1157,7 @@ void BindingPattern::dump(int indent) const void FunctionNode::dump(int indent, const String& class_name) const { print_indent(indent); - outln("{} '{}'", class_name, name()); + outln("{}{} '{}'", class_name, m_is_generator ? "*" : "", name()); if (!m_parameters.is_empty()) { print_indent(indent + 1); outln("(Parameters)"); @@ -1193,6 +1199,13 @@ void FunctionExpression::dump(int indent) const FunctionNode::dump(indent, class_name()); } +void YieldExpression::dump(int indent) const +{ + ASTNode::dump(indent); + if (argument()) + argument()->dump(indent + 1); +} + void ReturnStatement::dump(int indent) const { ASTNode::dump(indent); diff --git a/Userland/Libraries/LibJS/AST.h b/Userland/Libraries/LibJS/AST.h index 946a60feaa..1f32544e71 100644 --- a/Userland/Libraries/LibJS/AST.h +++ b/Userland/Libraries/LibJS/AST.h @@ -227,14 +227,16 @@ public: const Vector& parameters() const { return m_parameters; }; i32 function_length() const { return m_function_length; } bool is_strict_mode() const { return m_is_strict_mode; } + bool is_generator() const { return m_is_generator; } protected: - FunctionNode(const FlyString& name, NonnullRefPtr body, Vector parameters, i32 function_length, NonnullRefPtrVector variables, bool is_strict_mode) + FunctionNode(const FlyString& name, NonnullRefPtr body, Vector parameters, i32 function_length, NonnullRefPtrVector variables, bool is_generator, bool is_strict_mode) : m_name(name) , m_body(move(body)) , m_parameters(move(parameters)) , m_variables(move(variables)) , m_function_length(function_length) + , m_is_generator(is_generator) , m_is_strict_mode(is_strict_mode) { } @@ -256,6 +258,7 @@ private: const Vector m_parameters; NonnullRefPtrVector m_variables; const i32 m_function_length; + bool m_is_generator; bool m_is_strict_mode; }; @@ -265,9 +268,9 @@ class FunctionDeclaration final public: static bool must_have_name() { return true; } - FunctionDeclaration(SourceRange source_range, const FlyString& name, NonnullRefPtr body, Vector parameters, i32 function_length, NonnullRefPtrVector variables, bool is_strict_mode = false) + FunctionDeclaration(SourceRange source_range, const FlyString& name, NonnullRefPtr body, Vector parameters, i32 function_length, NonnullRefPtrVector variables, bool is_generator, bool is_strict_mode = false) : Declaration(move(source_range)) - , FunctionNode(name, move(body), move(parameters), function_length, move(variables), is_strict_mode) + , FunctionNode(name, move(body), move(parameters), function_length, move(variables), is_generator, is_strict_mode) { } @@ -282,9 +285,9 @@ class FunctionExpression final public: static bool must_have_name() { return false; } - FunctionExpression(SourceRange source_range, const FlyString& name, NonnullRefPtr body, Vector parameters, i32 function_length, NonnullRefPtrVector variables, bool is_strict_mode, bool is_arrow_function = false) + FunctionExpression(SourceRange source_range, const FlyString& name, NonnullRefPtr body, Vector parameters, i32 function_length, NonnullRefPtrVector variables, bool is_generator, bool is_strict_mode, bool is_arrow_function = false) : Expression(source_range) - , FunctionNode(name, move(body), move(parameters), function_length, move(variables), is_strict_mode) + , FunctionNode(name, move(body), move(parameters), function_length, move(variables), is_generator, is_strict_mode) , m_is_arrow_function(is_arrow_function) { } @@ -318,6 +321,24 @@ public: Value execute(Interpreter&, GlobalObject&) const override { return {}; } }; +class YieldExpression final : public Expression { +public: + explicit YieldExpression(SourceRange source_range, RefPtr argument) + : Expression(move(source_range)) + , m_argument(move(argument)) + { + } + + const Expression* argument() const { return m_argument; } + + virtual Value execute(Interpreter&, GlobalObject&) const override; + virtual void dump(int indent) const override; + virtual void generate_bytecode(Bytecode::Generator&) const override; + +private: + RefPtr m_argument; +}; + class ReturnStatement final : public Statement { public: explicit ReturnStatement(SourceRange source_range, RefPtr argument) diff --git a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp index 6397ca2e8c..e5e3704bbe 100644 --- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp +++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp @@ -668,7 +668,23 @@ void ReturnStatement::generate_bytecode(Bytecode::Generator& generator) const { if (m_argument) m_argument->generate_bytecode(generator); - generator.emit(); + + if (generator.is_in_generator_function()) + generator.emit(nullptr); + else + generator.emit(); +} + +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::Label { continuation_block }); + generator.switch_to_basic_block(continuation_block); } void IfStatement::generate_bytecode(Bytecode::Generator& generator) const diff --git a/Userland/Libraries/LibJS/Bytecode/Generator.cpp b/Userland/Libraries/LibJS/Bytecode/Generator.cpp index c7e394ef2f..7d356588f3 100644 --- a/Userland/Libraries/LibJS/Bytecode/Generator.cpp +++ b/Userland/Libraries/LibJS/Bytecode/Generator.cpp @@ -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(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(js_undefined()); + generator.emit(nullptr); + } + } return { move(generator.m_root_basic_blocks), move(generator.m_string_table), generator.m_next_register }; } diff --git a/Userland/Libraries/LibJS/Bytecode/Generator.h b/Userland/Libraries/LibJS/Bytecode/Generator.h index 1ae3e0ab94..49b34e138a 100644 --- a/Userland/Libraries/LibJS/Bytecode/Generator.h +++ b/Userland/Libraries/LibJS/Bytecode/Generator.h @@ -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