mirror of
https://github.com/SerenityOS/serenity.git
synced 2025-01-24 10:22:05 -05:00
LibJS: Implement LabelledStatement & LabelledEvaluation semantics
Instead of slapping 0..N labels on a statement like the current LabelableStatement does, we need the spec's LabelledStatement for a proper implementation of control flow using only completions - it always has one label and one labelled statement - what appears to be 'multiple labels' on the same statement is actually nested LabelledStatements. Note that this is unused yet and will fully replace LabelableStatement in the next commit.
This commit is contained in:
parent
558fd5b166
commit
fc474966bc
2 changed files with 132 additions and 5 deletions
|
@ -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<FlyString> 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<FlyString> 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<FlyString> 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<Vector<FlyString>> new_label_set;
|
||||
if (is<IterationStatement>(labelled_item) || is<SwitchStatement>(labelled_item) || is<LabelledStatement>(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<IterationStatement>(labelled_item))
|
||||
result = labelled_evaluation(interpreter, global_object, static_cast<IterationStatement const&>(labelled_item), *new_label_set);
|
||||
else if (is<SwitchStatement>(labelled_item))
|
||||
result = labelled_evaluation(interpreter, global_object, static_cast<SwitchStatement const&>(labelled_item), *new_label_set);
|
||||
else if (is<LabelledStatement>(labelled_item))
|
||||
result = labelled_evaluation(interpreter, global_object, static_cast<LabelledStatement const&>(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<Value> 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);
|
||||
|
|
|
@ -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<Statement> 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<Statement> const& labelled_item() const { return m_labelled_item; }
|
||||
NonnullRefPtr<Statement>& labelled_item() { return m_labelled_item; }
|
||||
|
||||
private:
|
||||
FlyString m_label;
|
||||
NonnullRefPtr<Statement> m_labelled_item;
|
||||
};
|
||||
|
||||
class LabelableStatement : public Statement {
|
||||
public:
|
||||
using Statement::Statement;
|
||||
|
|
Loading…
Add table
Reference in a new issue