summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Olsson <matthewcolsson@gmail.com>2021-06-12 18:04:28 -0700
committerAndreas Kling <kling@serenityos.org>2021-06-19 09:38:26 +0200
commitce04c2259f78341667a5a94d1e5b9725167047e2 (patch)
tree50abde87bd1eb1fa68dc783f5bdda7a758749f94
parent10372b81184b00aea37d497efd13345232170cef (diff)
downloadserenity-ce04c2259f78341667a5a94d1e5b9725167047e2.zip
LibJS: Restructure and fully implement BindingPatterns
-rw-r--r--Userland/Libraries/LibJS/AST.cpp44
-rw-r--r--Userland/Libraries/LibJS/AST.h31
-rw-r--r--Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp4
-rw-r--r--Userland/Libraries/LibJS/Interpreter.cpp4
-rw-r--r--Userland/Libraries/LibJS/Parser.cpp162
-rw-r--r--Userland/Libraries/LibJS/Parser.h1
-rw-r--r--Userland/Libraries/LibJS/Runtime/PropertyName.h31
-rw-r--r--Userland/Libraries/LibJS/Runtime/ScriptFunction.cpp4
-rw-r--r--Userland/Libraries/LibJS/Runtime/VM.cpp118
9 files changed, 237 insertions, 162 deletions
diff --git a/Userland/Libraries/LibJS/AST.cpp b/Userland/Libraries/LibJS/AST.cpp
index e109a698ec..5ed141449c 100644
--- a/Userland/Libraries/LibJS/AST.cpp
+++ b/Userland/Libraries/LibJS/AST.cpp
@@ -1133,29 +1133,41 @@ void BindingPattern::dump(int indent) const
{
print_indent(indent);
outln("BindingPattern {}", kind == Kind::Array ? "Array" : "Object");
- print_indent(++indent);
- outln("(Properties)");
- for (auto& property : properties) {
+
+ for (auto& entry : entries) {
print_indent(indent + 1);
- outln("(Identifier)");
- if (property.name) {
- property.name->dump(indent + 2);
- } else {
+ outln("(Property)");
+
+ if (kind == Kind::Object) {
print_indent(indent + 2);
- outln("(None)");
+ outln("(Identifier)");
+ if (entry.name.has<NonnullRefPtr<Identifier>>()) {
+ entry.name.get<NonnullRefPtr<Identifier>>()->dump(indent + 3);
+ } else {
+ entry.name.get<NonnullRefPtr<Expression>>()->dump(indent + 3);
+ }
+ } else if (entry.is_elision()) {
+ print_indent(indent + 2);
+ outln("(Elision)");
+ continue;
}
- print_indent(indent + 1);
- outln("(Pattern)");
- if (property.pattern) {
- property.pattern->dump(indent + 2);
+ print_indent(indent + 2);
+ outln("(Pattern{})", entry.is_rest ? " rest=true" : "");
+ if (entry.alias.has<NonnullRefPtr<Identifier>>()) {
+ entry.alias.get<NonnullRefPtr<Identifier>>()->dump(indent + 3);
+ } else if (entry.alias.has<NonnullRefPtr<BindingPattern>>()) {
+ entry.alias.get<NonnullRefPtr<BindingPattern>>()->dump(indent + 3);
} else {
- print_indent(indent + 2);
- outln("(None)");
+ print_indent(indent + 3);
+ outln("<empty>");
}
- print_indent(indent + 1);
- outln("(Is Rest = {})", property.is_rest);
+ if (entry.initializer) {
+ print_indent(indent + 2);
+ outln("(Initializer)");
+ entry.initializer->dump(indent + 3);
+ }
}
}
diff --git a/Userland/Libraries/LibJS/AST.h b/Userland/Libraries/LibJS/AST.h
index 27c891620a..c75152a9e4 100644
--- a/Userland/Libraries/LibJS/AST.h
+++ b/Userland/Libraries/LibJS/AST.h
@@ -215,12 +215,15 @@ public:
};
struct BindingPattern : RefCounted<BindingPattern> {
- struct BindingProperty {
- RefPtr<Identifier> name;
- RefPtr<Identifier> alias;
- RefPtr<BindingPattern> pattern;
- RefPtr<Expression> initializer;
+ // This covers both BindingProperty and BindingElement, hence the more generic name
+ struct BindingEntry {
+ // If this entry represents a BindingElement, then name will be Empty
+ Variant<NonnullRefPtr<Identifier>, NonnullRefPtr<Expression>, Empty> name { Empty {} };
+ Variant<NonnullRefPtr<Identifier>, NonnullRefPtr<BindingPattern>, Empty> alias { Empty {} };
+ RefPtr<Expression> initializer {};
bool is_rest { false };
+
+ bool is_elision() const { return name.has<Empty>() && alias.has<Empty>(); }
};
enum class Kind {
@@ -229,10 +232,11 @@ struct BindingPattern : RefCounted<BindingPattern> {
};
void dump(int indent) const;
+
template<typename C>
- void for_each_assigned_name(C&& callback) const;
+ void for_each_bound_name(C&& callback) const;
- Vector<BindingProperty> properties;
+ Vector<BindingEntry> entries;
Kind kind { Kind::Object };
};
@@ -1398,14 +1402,15 @@ public:
};
template<typename C>
-void BindingPattern::for_each_assigned_name(C&& callback) const
+void BindingPattern::for_each_bound_name(C&& callback) const
{
- for (auto& property : properties) {
- if (property.name) {
- callback(property.name->string());
- continue;
+ for (auto& entry : entries) {
+ auto& alias = entry.alias;
+ if (alias.has<NonnullRefPtr<Identifier>>()) {
+ callback(alias.get<NonnullRefPtr<Identifier>>()->string());
+ } else if (alias.has<NonnullRefPtr<BindingPattern>>()) {
+ alias.get<NonnullRefPtr<BindingPattern>>()->for_each_bound_name(forward<C>(callback));
}
- property.pattern->template for_each_assigned_name(forward<C>(callback));
}
}
diff --git a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp
index 9fcdcb1fbd..022f11ea87 100644
--- a/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp
+++ b/Userland/Libraries/LibJS/Bytecode/ASTCodegen.cpp
@@ -43,7 +43,7 @@ void ScopeNode::generate_bytecode(Bytecode::Generator& generator) const
generator.emit<Bytecode::Op::PutById>(Bytecode::Register::global_object(), generator.intern_string(id->string()));
},
[&](const NonnullRefPtr<BindingPattern>& binding) {
- binding->for_each_assigned_name([&](const auto& name) {
+ binding->for_each_bound_name([&](const auto& name) {
generator.emit<Bytecode::Op::LoadImmediate>(js_undefined());
generator.emit<Bytecode::Op::PutById>(Bytecode::Register::global_object(), generator.intern_string(name));
});
@@ -54,7 +54,7 @@ void ScopeNode::generate_bytecode(Bytecode::Generator& generator) const
scope_variables_with_declaration_kind.set((size_t)generator.intern_string(id->string()).value(), { js_undefined(), declaration.declaration_kind() });
},
[&](const NonnullRefPtr<BindingPattern>& binding) {
- binding->for_each_assigned_name([&](const auto& name) {
+ binding->for_each_bound_name([&](const auto& name) {
scope_variables_with_declaration_kind.set((size_t)generator.intern_string(name).value(), { js_undefined(), declaration.declaration_kind() });
});
});
diff --git a/Userland/Libraries/LibJS/Interpreter.cpp b/Userland/Libraries/LibJS/Interpreter.cpp
index db50839e22..06e096e59f 100644
--- a/Userland/Libraries/LibJS/Interpreter.cpp
+++ b/Userland/Libraries/LibJS/Interpreter.cpp
@@ -108,7 +108,7 @@ void Interpreter::enter_scope(const ScopeNode& scope_node, ScopeType scope_type,
global_object.put(id->string(), js_undefined());
},
[&](const NonnullRefPtr<BindingPattern>& binding) {
- binding->for_each_assigned_name([&](const auto& name) {
+ binding->for_each_bound_name([&](const auto& name) {
global_object.put(name, js_undefined());
});
});
@@ -120,7 +120,7 @@ void Interpreter::enter_scope(const ScopeNode& scope_node, ScopeType scope_type,
scope_variables_with_declaration_kind.set(id->string(), { js_undefined(), declaration.declaration_kind() });
},
[&](const NonnullRefPtr<BindingPattern>& binding) {
- binding->for_each_assigned_name([&](const auto& name) {
+ binding->for_each_bound_name([&](const auto& name) {
scope_variables_with_declaration_kind.set(name, { js_undefined(), declaration.declaration_kind() });
});
});
diff --git a/Userland/Libraries/LibJS/Parser.cpp b/Userland/Libraries/LibJS/Parser.cpp
index 48d286c102..36a23e12d6 100644
--- a/Userland/Libraries/LibJS/Parser.cpp
+++ b/Userland/Libraries/LibJS/Parser.cpp
@@ -1248,6 +1248,15 @@ NonnullRefPtr<AssignmentExpression> Parser::parse_assignment_expression(Assignme
return create_ast_node<AssignmentExpression>({ m_parser_state.m_current_token.filename(), rule_start.position(), position() }, assignment_op, move(lhs), move(rhs));
}
+NonnullRefPtr<Identifier> Parser::parse_identifier()
+{
+ auto identifier_start = position();
+ auto token = consume(TokenType::Identifier);
+ return create_ast_node<Identifier>(
+ { m_parser_state.m_current_token.filename(), identifier_start, position() },
+ token.value());
+}
+
NonnullRefPtr<CallExpression> Parser::parse_call_expression(NonnullRefPtr<Expression> lhs)
{
auto rule_start = push_start();
@@ -1522,36 +1531,28 @@ RefPtr<BindingPattern> Parser::parse_binding_pattern()
{
auto rule_start = push_start();
- auto pattern_ptr = adopt_ref(*new BindingPattern);
- auto& pattern = *pattern_ptr;
TokenType closing_token;
- auto allow_named_property = false;
- auto elide_extra_commas = false;
- auto allow_nested_pattern = false;
+ bool is_object = true;
if (match(TokenType::BracketOpen)) {
consume();
- pattern.kind = BindingPattern::Kind::Array;
closing_token = TokenType::BracketClose;
- elide_extra_commas = true;
- allow_nested_pattern = true;
+ is_object = false;
} else if (match(TokenType::CurlyOpen)) {
consume();
- pattern.kind = BindingPattern::Kind::Object;
closing_token = TokenType::CurlyClose;
- allow_named_property = true;
} else {
return {};
}
+ Vector<BindingPattern::BindingEntry> entries;
+
while (!match(closing_token)) {
- if (elide_extra_commas && match(TokenType::Comma))
+ if (!is_object && match(TokenType::Comma)) {
consume();
-
- ScopeGuard consume_commas { [&] {
- if (match(TokenType::Comma))
- consume();
- } };
+ entries.append(BindingPattern::BindingEntry {});
+ continue;
+ }
auto is_rest = false;
@@ -1560,89 +1561,88 @@ RefPtr<BindingPattern> Parser::parse_binding_pattern()
is_rest = true;
}
- if (match(TokenType::Identifier)) {
- auto identifier_start = position();
- auto token = consume(TokenType::Identifier);
- auto name = create_ast_node<Identifier>(
- { m_parser_state.m_current_token.filename(), identifier_start, position() },
- token.value());
+ decltype(BindingPattern::BindingEntry::name) name = Empty {};
+ decltype(BindingPattern::BindingEntry::alias) alias = Empty {};
+ RefPtr<Expression> initializer = {};
- if (!is_rest && allow_named_property && match(TokenType::Colon)) {
+ if (is_object) {
+ if (match(TokenType::Identifier)) {
+ name = parse_identifier();
+ } else if (match(TokenType::BracketOpen)) {
consume();
- if (!match(TokenType::Identifier)) {
- syntax_error("Expected a binding pattern as the value of a named element in destructuring object");
- break;
- } else {
- auto identifier_start = position();
- auto token = consume(TokenType::Identifier);
- auto alias_name = create_ast_node<Identifier>(
- { m_parser_state.m_current_token.filename(), identifier_start, position() },
- token.value());
- pattern.properties.append(BindingPattern::BindingProperty {
- .name = move(name),
- .alias = move(alias_name),
- .pattern = nullptr,
- .initializer = nullptr,
- .is_rest = false,
- });
- }
- continue;
+ name = parse_expression(0);
+ consume(TokenType::BracketOpen);
+ } else {
+ syntax_error("Expected identifier or computed property name");
+ return {};
}
- RefPtr<Expression> initializer;
- if (match(TokenType::Equals)) {
+ if (!is_rest && match(TokenType::Colon)) {
consume();
- initializer = parse_expression(2);
+ if (match(TokenType::CurlyOpen) || match(TokenType::BracketOpen)) {
+ auto binding_pattern = parse_binding_pattern();
+ if (!binding_pattern)
+ return {};
+ alias = binding_pattern.release_nonnull();
+ } else if (match_identifier_name()) {
+ alias = parse_identifier();
+ } else {
+ syntax_error("Expected identifier or binding pattern");
+ return {};
+ }
+ }
+ } else {
+ if (match(TokenType::Identifier)) {
+ // BindingElement must always have an Empty name field
+ alias = parse_identifier();
+ } else if (match(TokenType::BracketOpen) || match(TokenType::CurlyOpen)) {
+ auto pattern = parse_binding_pattern();
+ if (!pattern) {
+ syntax_error("Expected binding pattern");
+ return {};
+ }
+ alias = pattern.release_nonnull();
+ } else {
+ syntax_error("Expected identifier or binding pattern");
+ return {};
}
- pattern.properties.append(BindingPattern::BindingProperty {
- .name = move(name),
- .alias = nullptr,
- .pattern = nullptr,
- .initializer = move(initializer),
- .is_rest = is_rest,
- });
- if (is_rest)
- break;
- continue;
}
- if (allow_nested_pattern) {
- auto binding_pattern = parse_binding_pattern();
- if (!binding_pattern) {
- if (is_rest)
- syntax_error("Expected a binding pattern after ... in destructuring list");
- else
- syntax_error("Expected a binding pattern or identifier in destructuring list");
- break;
- } else {
- RefPtr<Expression> initializer;
- if (match(TokenType::Equals)) {
- consume();
- initializer = parse_expression(2);
- }
- pattern.properties.append(BindingPattern::BindingProperty {
- .name = nullptr,
- .alias = nullptr,
- .pattern = move(binding_pattern),
- .initializer = move(initializer),
- .is_rest = is_rest,
- });
- if (is_rest)
- break;
- continue;
+ if (match(TokenType::Equals)) {
+ if (is_rest) {
+ syntax_error("Unexpected initializer after rest element");
+ return {};
}
- continue;
+ consume();
+
+ initializer = parse_expression(2);
+ if (!initializer) {
+ syntax_error("Expected initialization expression");
+ return {};
+ }
}
- break;
+ entries.append(BindingPattern::BindingEntry { move(name), move(alias), move(initializer), is_rest });
+
+ if (match(TokenType::Comma)) {
+ if (is_rest) {
+ syntax_error("Rest element may not be followed by a comma");
+ return {};
+ }
+ consume();
+ }
}
- while (elide_extra_commas && match(TokenType::Comma))
+ while (!is_object && match(TokenType::Comma))
consume();
consume(closing_token);
+ auto kind = is_object ? BindingPattern::Kind::Object : BindingPattern::Kind::Array;
+ auto pattern = adopt_ref(*new BindingPattern);
+ pattern->entries = move(entries);
+ pattern->kind = kind;
return pattern;
}
diff --git a/Userland/Libraries/LibJS/Parser.h b/Userland/Libraries/LibJS/Parser.h
index 6b63d8c985..490dcff09a 100644
--- a/Userland/Libraries/LibJS/Parser.h
+++ b/Userland/Libraries/LibJS/Parser.h
@@ -86,6 +86,7 @@ public:
NonnullRefPtr<YieldExpression> parse_yield_expression();
NonnullRefPtr<Expression> parse_property_key();
NonnullRefPtr<AssignmentExpression> parse_assignment_expression(AssignmentOp, NonnullRefPtr<Expression> lhs, int min_precedence, Associativity);
+ NonnullRefPtr<Identifier> parse_identifier();
RefPtr<FunctionExpression> try_parse_arrow_function_expression(bool expect_parens);
RefPtr<Statement> try_parse_labelled_statement();
diff --git a/Userland/Libraries/LibJS/Runtime/PropertyName.h b/Userland/Libraries/LibJS/Runtime/PropertyName.h
index 162059948d..fd6f3696fd 100644
--- a/Userland/Libraries/LibJS/Runtime/PropertyName.h
+++ b/Userland/Libraries/LibJS/Runtime/PropertyName.h
@@ -87,6 +87,8 @@ public:
}
}
+ ALWAYS_INLINE Type type() const { return m_type; }
+
bool is_valid() const { return m_type != Type::Invalid; }
bool is_number() const
{
@@ -176,6 +178,35 @@ private:
u32 m_number { 0 };
};
+struct PropertyNameTraits : public Traits<PropertyName> {
+ static unsigned hash(PropertyName const& name)
+ {
+ VERIFY(name.is_valid());
+ if (name.is_string())
+ return name.as_string().hash();
+ if (name.is_number())
+ return int_hash(name.as_number());
+ return ptr_hash(name.as_symbol());
+ }
+
+ static bool equals(PropertyName const& a, PropertyName const& b)
+ {
+ if (a.type() != b.type())
+ return false;
+
+ switch (a.type()) {
+ case PropertyName::Type::Number:
+ return a.as_number() == b.as_number();
+ case PropertyName::Type::String:
+ return a.as_string() == b.as_string();
+ case PropertyName::Type::Symbol:
+ return a.as_symbol() == b.as_symbol();
+ default:
+ VERIFY_NOT_REACHED();
+ }
+ }
+};
+
}
namespace AK {
diff --git a/Userland/Libraries/LibJS/Runtime/ScriptFunction.cpp b/Userland/Libraries/LibJS/Runtime/ScriptFunction.cpp
index 6ac8ac8b75..6bcb0dfe0d 100644
--- a/Userland/Libraries/LibJS/Runtime/ScriptFunction.cpp
+++ b/Userland/Libraries/LibJS/Runtime/ScriptFunction.cpp
@@ -100,7 +100,7 @@ LexicalEnvironment* ScriptFunction::create_environment()
parameter.binding.visit(
[&](const FlyString& name) { variables.set(name, { js_undefined(), DeclarationKind::Var }); },
[&](const NonnullRefPtr<BindingPattern>& binding) {
- binding->for_each_assigned_name([&](const auto& name) {
+ binding->for_each_bound_name([&](const auto& name) {
variables.set(name, { js_undefined(), DeclarationKind::Var });
});
});
@@ -114,7 +114,7 @@ LexicalEnvironment* ScriptFunction::create_environment()
variables.set(id->string(), { js_undefined(), declaration.declaration_kind() });
},
[&](const NonnullRefPtr<BindingPattern>& binding) {
- binding->for_each_assigned_name([&](const auto& name) {
+ binding->for_each_bound_name([&](const auto& name) {
variables.set(name, { js_undefined(), declaration.declaration_kind() });
});
});
diff --git a/Userland/Libraries/LibJS/Runtime/VM.cpp b/Userland/Libraries/LibJS/Runtime/VM.cpp
index 3c776eaf88..2efebbe12d 100644
--- a/Userland/Libraries/LibJS/Runtime/VM.cpp
+++ b/Userland/Libraries/LibJS/Runtime/VM.cpp
@@ -208,18 +208,15 @@ void VM::assign(const NonnullRefPtr<BindingPattern>& target, Value value, Global
if (!iterator)
return;
- size_t index = 0;
- while (true) {
+ for (size_t i = 0; i < binding.entries.size(); i++) {
if (exception())
return;
- if (index >= binding.properties.size())
- break;
+ auto& entry = binding.entries[i];
- auto pattern_property = binding.properties[index];
- ++index;
+ if (entry.is_rest) {
+ VERIFY(i == binding.entries.size() - 1);
- if (pattern_property.is_rest) {
auto* array = Array::create(global_object);
for (;;) {
auto next_object = iterator_next(*iterator);
@@ -240,7 +237,7 @@ void VM::assign(const NonnullRefPtr<BindingPattern>& target, Value value, Global
array->indexed_properties().append(next_value);
}
value = array;
- } else {
+ } else if (iterator) {
auto next_object = iterator_next(*iterator);
if (!next_object)
return;
@@ -249,65 +246,83 @@ void VM::assign(const NonnullRefPtr<BindingPattern>& target, Value value, Global
if (exception())
return;
- if (!done_property.is_empty() && done_property.to_boolean())
- break;
+ if (!done_property.is_empty() && done_property.to_boolean()) {
+ iterator = nullptr;
+ value = js_undefined();
+ } else {
+ value = next_object->get(names.value);
+ if (exception())
+ return;
+ }
+ } else {
+ value = js_undefined();
+ }
- value = next_object->get(names.value);
+ if (value.is_undefined() && entry.initializer) {
+ value = entry.initializer->execute(interpreter(), global_object);
if (exception())
return;
}
- if (value.is_undefined() && pattern_property.initializer)
- value = pattern_property.initializer->execute(interpreter(), global_object);
-
- if (exception())
- return;
-
- if (pattern_property.name) {
- set_variable(pattern_property.name->string(), value, global_object, first_assignment, specific_scope);
- if (pattern_property.is_rest)
- break;
- continue;
- }
+ entry.alias.visit(
+ [&](Empty) {},
+ [&](NonnullRefPtr<Identifier> const& identifier) {
+ set_variable(identifier->string(), value, global_object, first_assignment, specific_scope);
+ },
+ [&](NonnullRefPtr<BindingPattern> const& pattern) {
+ assign(pattern, value, global_object, first_assignment, specific_scope);
+ });
- if (pattern_property.pattern) {
- assign(NonnullRefPtr(*pattern_property.pattern), value, global_object, first_assignment, specific_scope);
- if (pattern_property.is_rest)
- break;
- continue;
- }
+ if (entry.is_rest)
+ break;
}
+
break;
}
case BindingPattern::Kind::Object: {
auto object = value.to_object(global_object);
- HashTable<FlyString> seen_names;
- for (auto& property : binding.properties) {
- VERIFY(!property.pattern);
+ HashTable<PropertyName, PropertyNameTraits> seen_names;
+ for (auto& property : binding.entries) {
+ VERIFY(!property.is_elision());
+
+ PropertyName assignment_name;
JS::Value value_to_assign;
if (property.is_rest) {
- auto* rest_object = Object::create(global_object, nullptr);
- for (auto& property : object->shape().property_table()) {
- if (!property.value.attributes.has_enumerable())
+ VERIFY(property.name.has<NonnullRefPtr<Identifier>>());
+ assignment_name = property.name.get<NonnullRefPtr<Identifier>>()->string();
+
+ auto* rest_object = Object::create(global_object, global_object.object_prototype());
+ for (auto& object_property : object->shape().property_table()) {
+ if (!object_property.value.attributes.has_enumerable())
continue;
- if (seen_names.contains(property.key.to_display_string()))
+ if (seen_names.contains(object_property.key.to_display_string()))
continue;
- rest_object->put(property.key, object->get(property.key));
+ rest_object->put(object_property.key, object->get(object_property.key));
if (exception())
return;
}
+
value_to_assign = rest_object;
} else {
- value_to_assign = object->get(property.name->string());
- }
+ property.name.visit(
+ [&](Empty) { VERIFY_NOT_REACHED(); },
+ [&](NonnullRefPtr<Identifier> const& identifier) {
+ assignment_name = identifier->string();
+ },
+ [&](NonnullRefPtr<Expression> const& expression) {
+ auto result = expression->execute(interpreter(), global_object);
+ if (exception())
+ return;
+ assignment_name = result.to_property_key(global_object);
+ });
- seen_names.set(property.name->string());
- if (exception())
- break;
+ if (exception())
+ break;
+
+ value_to_assign = object->get(assignment_name);
+ }
- auto assignment_name = property.name->string();
- if (property.alias)
- assignment_name = property.alias->string();
+ seen_names.set(assignment_name);
if (value_to_assign.is_empty())
value_to_assign = js_undefined();
@@ -318,7 +333,18 @@ void VM::assign(const NonnullRefPtr<BindingPattern>& target, Value value, Global
if (exception())
break;
- set_variable(assignment_name, value_to_assign, global_object, first_assignment, specific_scope);
+ property.alias.visit(
+ [&](Empty) {
+ set_variable(assignment_name.to_string(), value_to_assign, global_object, first_assignment, specific_scope);
+ },
+ [&](NonnullRefPtr<Identifier> const& identifier) {
+ VERIFY(!property.is_rest);
+ set_variable(identifier->string(), value_to_assign, global_object, first_assignment, specific_scope);
+ },
+ [&](NonnullRefPtr<BindingPattern> const& pattern) {
+ VERIFY(!property.is_rest);
+ assign(pattern, value_to_assign, global_object, first_assignment, specific_scope);
+ });
if (property.is_rest)
break;