diff --git a/Userland/Libraries/LibJS/AST.cpp b/Userland/Libraries/LibJS/AST.cpp index 27122eff6d..328af30f07 100644 --- a/Userland/Libraries/LibJS/AST.cpp +++ b/Userland/Libraries/LibJS/AST.cpp @@ -66,6 +66,11 @@ String ASTNode::class_name() const return demangle(typeid(*this).name()).substring(4); } +static void print_indent(int indent) +{ + out("{}", String::repeated(' ', indent * 2)); +} + static void update_function_name(Value value, FlyString const& name) { if (!value.is_function()) @@ -104,6 +109,110 @@ Completion ScopeNode::evaluate_statements(Interpreter& interpreter, GlobalObject return completion; } +// 14.13.4 Runtime Semantics: LabelledEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-labelledevaluation +// BreakableStatement : IterationStatement +static Completion labelled_evaluation(Interpreter& interpreter, GlobalObject& global_object, IterationStatement const& statement, Vector const& label_set) +{ + // 1. Let stmtResult be LoopEvaluation of IterationStatement with argument labelSet. + auto result = statement.loop_evaluation(interpreter, global_object, label_set); + + // 2. If stmtResult.[[Type]] is break, then + if (result.type() == Completion::Type::Break) { + // a. If stmtResult.[[Target]] is empty, then + if (!result.target().has_value()) { + // i. If stmtResult.[[Value]] is empty, set stmtResult to NormalCompletion(undefined). + // ii. Else, set stmtResult to NormalCompletion(stmtResult.[[Value]]). + result = normal_completion(result.value().value_or(js_undefined())); + } + } + + // 3. Return Completion(stmtResult). + return result; +} + +// 14.13.4 Runtime Semantics: LabelledEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-labelledevaluation +// BreakableStatement : SwitchStatement +static Completion labelled_evaluation(Interpreter& interpreter, GlobalObject& global_object, SwitchStatement const& statement, Vector const&) +{ + // 1. Let stmtResult be the result of evaluating SwitchStatement. + auto result = statement.execute_impl(interpreter, global_object); + + // 2. If stmtResult.[[Type]] is break, then + if (result.type() == Completion::Type::Break) { + // a. If stmtResult.[[Target]] is empty, then + if (!result.target().has_value()) { + // i. If stmtResult.[[Value]] is empty, set stmtResult to NormalCompletion(undefined). + // ii. Else, set stmtResult to NormalCompletion(stmtResult.[[Value]]). + result = normal_completion(result.value().value_or(js_undefined())); + } + } + + // 3. Return Completion(stmtResult). + return result; +} + +// 14.13.4 Runtime Semantics: LabelledEvaluation, https://tc39.es/ecma262/#sec-runtime-semantics-labelledevaluation +// LabelledStatement : LabelIdentifier : LabelledItem +static Completion labelled_evaluation(Interpreter& interpreter, GlobalObject& global_object, LabelledStatement const& statement, Vector const& label_set) +{ + auto const& labelled_item = *statement.labelled_item(); + + // 1. Let label be the StringValue of LabelIdentifier. + auto const& label = statement.label(); + + // 2. Let newLabelSet be the list-concatenation of labelSet and « label ». + // Optimization: Avoid vector copy if possible. + Optional> new_label_set; + if (is(labelled_item) || is(labelled_item) || is(labelled_item)) { + new_label_set = label_set; + new_label_set->append(label); + } + + // 3. Let stmtResult be LabelledEvaluation of LabelledItem with argument newLabelSet. + Completion result; + if (is(labelled_item)) + result = labelled_evaluation(interpreter, global_object, static_cast(labelled_item), *new_label_set); + else if (is(labelled_item)) + result = labelled_evaluation(interpreter, global_object, static_cast(labelled_item), *new_label_set); + else if (is(labelled_item)) + result = labelled_evaluation(interpreter, global_object, static_cast(labelled_item), *new_label_set); + else + result = labelled_item.execute(interpreter, global_object); + + // 4. If stmtResult.[[Type]] is break and SameValue(stmtResult.[[Target]], label) is true, then + if (result.type() == Completion::Type::Break && result.target() == label) { + // a. Set stmtResult to NormalCompletion(stmtResult.[[Value]]). + result = normal_completion(result.value()); + } + + // 5. Return Completion(stmtResult). + return result; +} + +// 14.13.3 Runtime Semantics: Evaluation, https://tc39.es/ecma262/#sec-labelled-statements-runtime-semantics-evaluation +Completion LabelledStatement::execute(Interpreter& interpreter, GlobalObject& global_object) const +{ + InterpreterNodeScope node_scope { interpreter, *this }; + + // 1. Let newLabelSet be a new empty List. + // 2. Return LabelledEvaluation of this LabelledStatement with argument newLabelSet. + return labelled_evaluation(interpreter, global_object, *this, {}); +} + +void LabelledStatement::dump(int indent) const +{ + ASTNode::dump(indent); + + print_indent(indent + 1); + outln("(Label)"); + print_indent(indent + 2); + outln("\"{}\"", m_label); + + print_indent(indent + 1); + outln("(Labelled item)"); + m_labelled_item->dump(indent + 2); +} + // 10.2.1.3 Runtime Semantics: EvaluateBody, https://tc39.es/ecma262/#sec-runtime-semantics-evaluatebody Completion FunctionBody::execute(Interpreter& interpreter, GlobalObject& global_object) const { @@ -1764,11 +1873,6 @@ ThrowCompletionOr ClassDeclaration::binding_class_declaration_evaluation( return value; } -static void print_indent(int indent) -{ - out("{}", String::repeated(' ', indent * 2)); -} - void ASTNode::dump(int indent) const { print_indent(indent); diff --git a/Userland/Libraries/LibJS/AST.h b/Userland/Libraries/LibJS/AST.h index c97e161269..d918ded5a9 100644 --- a/Userland/Libraries/LibJS/AST.h +++ b/Userland/Libraries/LibJS/AST.h @@ -86,6 +86,29 @@ public: } }; +// 14.13 Labelled Statements, https://tc39.es/ecma262/#sec-labelled-statements +class LabelledStatement : public Statement { +public: + LabelledStatement(SourceRange source_range, FlyString label, NonnullRefPtr labelled_item) + : Statement(source_range) + , m_label(move(label)) + , m_labelled_item(move(labelled_item)) + { + } + + virtual Completion execute(Interpreter&, GlobalObject&) const override; + virtual void dump(int indent) const override; + + FlyString const& label() const { return m_label; } + FlyString& label() { return m_label; } + NonnullRefPtr const& labelled_item() const { return m_labelled_item; } + NonnullRefPtr& labelled_item() { return m_labelled_item; } + +private: + FlyString m_label; + NonnullRefPtr m_labelled_item; +}; + class LabelableStatement : public Statement { public: using Statement::Statement;