mirror of
https://github.com/RGBCube/serenity
synced 2025-07-24 20:47:43 +00:00
LibJS: Restructure and fully implement BindingPatterns
This commit is contained in:
parent
10372b8118
commit
ce04c2259f
9 changed files with 244 additions and 169 deletions
|
@ -1133,29 +1133,41 @@ void BindingPattern::dump(int indent) const
|
||||||
{
|
{
|
||||||
print_indent(indent);
|
print_indent(indent);
|
||||||
outln("BindingPattern {}", kind == Kind::Array ? "Array" : "Object");
|
outln("BindingPattern {}", kind == Kind::Array ? "Array" : "Object");
|
||||||
print_indent(++indent);
|
|
||||||
outln("(Properties)");
|
for (auto& entry : entries) {
|
||||||
for (auto& property : properties) {
|
|
||||||
print_indent(indent + 1);
|
print_indent(indent + 1);
|
||||||
outln("(Identifier)");
|
outln("(Property)");
|
||||||
if (property.name) {
|
|
||||||
property.name->dump(indent + 2);
|
if (kind == Kind::Object) {
|
||||||
} else {
|
|
||||||
print_indent(indent + 2);
|
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);
|
print_indent(indent + 2);
|
||||||
outln("(Pattern)");
|
outln("(Pattern{})", entry.is_rest ? " rest=true" : "");
|
||||||
if (property.pattern) {
|
if (entry.alias.has<NonnullRefPtr<Identifier>>()) {
|
||||||
property.pattern->dump(indent + 2);
|
entry.alias.get<NonnullRefPtr<Identifier>>()->dump(indent + 3);
|
||||||
|
} else if (entry.alias.has<NonnullRefPtr<BindingPattern>>()) {
|
||||||
|
entry.alias.get<NonnullRefPtr<BindingPattern>>()->dump(indent + 3);
|
||||||
} else {
|
} else {
|
||||||
print_indent(indent + 2);
|
print_indent(indent + 3);
|
||||||
outln("(None)");
|
outln("<empty>");
|
||||||
}
|
}
|
||||||
|
|
||||||
print_indent(indent + 1);
|
if (entry.initializer) {
|
||||||
outln("(Is Rest = {})", property.is_rest);
|
print_indent(indent + 2);
|
||||||
|
outln("(Initializer)");
|
||||||
|
entry.initializer->dump(indent + 3);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -215,12 +215,15 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BindingPattern : RefCounted<BindingPattern> {
|
struct BindingPattern : RefCounted<BindingPattern> {
|
||||||
struct BindingProperty {
|
// This covers both BindingProperty and BindingElement, hence the more generic name
|
||||||
RefPtr<Identifier> name;
|
struct BindingEntry {
|
||||||
RefPtr<Identifier> alias;
|
// If this entry represents a BindingElement, then name will be Empty
|
||||||
RefPtr<BindingPattern> pattern;
|
Variant<NonnullRefPtr<Identifier>, NonnullRefPtr<Expression>, Empty> name { Empty {} };
|
||||||
RefPtr<Expression> initializer;
|
Variant<NonnullRefPtr<Identifier>, NonnullRefPtr<BindingPattern>, Empty> alias { Empty {} };
|
||||||
|
RefPtr<Expression> initializer {};
|
||||||
bool is_rest { false };
|
bool is_rest { false };
|
||||||
|
|
||||||
|
bool is_elision() const { return name.has<Empty>() && alias.has<Empty>(); }
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class Kind {
|
enum class Kind {
|
||||||
|
@ -229,10 +232,11 @@ struct BindingPattern : RefCounted<BindingPattern> {
|
||||||
};
|
};
|
||||||
|
|
||||||
void dump(int indent) const;
|
void dump(int indent) const;
|
||||||
template<typename C>
|
|
||||||
void for_each_assigned_name(C&& callback) const;
|
|
||||||
|
|
||||||
Vector<BindingProperty> properties;
|
template<typename C>
|
||||||
|
void for_each_bound_name(C&& callback) const;
|
||||||
|
|
||||||
|
Vector<BindingEntry> entries;
|
||||||
Kind kind { Kind::Object };
|
Kind kind { Kind::Object };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1398,14 +1402,15 @@ public:
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename C>
|
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) {
|
for (auto& entry : entries) {
|
||||||
if (property.name) {
|
auto& alias = entry.alias;
|
||||||
callback(property.name->string());
|
if (alias.has<NonnullRefPtr<Identifier>>()) {
|
||||||
continue;
|
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));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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()));
|
generator.emit<Bytecode::Op::PutById>(Bytecode::Register::global_object(), generator.intern_string(id->string()));
|
||||||
},
|
},
|
||||||
[&](const NonnullRefPtr<BindingPattern>& binding) {
|
[&](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::LoadImmediate>(js_undefined());
|
||||||
generator.emit<Bytecode::Op::PutById>(Bytecode::Register::global_object(), generator.intern_string(name));
|
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() });
|
scope_variables_with_declaration_kind.set((size_t)generator.intern_string(id->string()).value(), { js_undefined(), declaration.declaration_kind() });
|
||||||
},
|
},
|
||||||
[&](const NonnullRefPtr<BindingPattern>& binding) {
|
[&](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() });
|
scope_variables_with_declaration_kind.set((size_t)generator.intern_string(name).value(), { js_undefined(), declaration.declaration_kind() });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -108,7 +108,7 @@ void Interpreter::enter_scope(const ScopeNode& scope_node, ScopeType scope_type,
|
||||||
global_object.put(id->string(), js_undefined());
|
global_object.put(id->string(), js_undefined());
|
||||||
},
|
},
|
||||||
[&](const NonnullRefPtr<BindingPattern>& binding) {
|
[&](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());
|
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() });
|
scope_variables_with_declaration_kind.set(id->string(), { js_undefined(), declaration.declaration_kind() });
|
||||||
},
|
},
|
||||||
[&](const NonnullRefPtr<BindingPattern>& binding) {
|
[&](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() });
|
scope_variables_with_declaration_kind.set(name, { js_undefined(), declaration.declaration_kind() });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -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));
|
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)
|
NonnullRefPtr<CallExpression> Parser::parse_call_expression(NonnullRefPtr<Expression> lhs)
|
||||||
{
|
{
|
||||||
auto rule_start = push_start();
|
auto rule_start = push_start();
|
||||||
|
@ -1522,36 +1531,28 @@ RefPtr<BindingPattern> Parser::parse_binding_pattern()
|
||||||
{
|
{
|
||||||
auto rule_start = push_start();
|
auto rule_start = push_start();
|
||||||
|
|
||||||
auto pattern_ptr = adopt_ref(*new BindingPattern);
|
|
||||||
auto& pattern = *pattern_ptr;
|
|
||||||
TokenType closing_token;
|
TokenType closing_token;
|
||||||
auto allow_named_property = false;
|
bool is_object = true;
|
||||||
auto elide_extra_commas = false;
|
|
||||||
auto allow_nested_pattern = false;
|
|
||||||
|
|
||||||
if (match(TokenType::BracketOpen)) {
|
if (match(TokenType::BracketOpen)) {
|
||||||
consume();
|
consume();
|
||||||
pattern.kind = BindingPattern::Kind::Array;
|
|
||||||
closing_token = TokenType::BracketClose;
|
closing_token = TokenType::BracketClose;
|
||||||
elide_extra_commas = true;
|
is_object = false;
|
||||||
allow_nested_pattern = true;
|
|
||||||
} else if (match(TokenType::CurlyOpen)) {
|
} else if (match(TokenType::CurlyOpen)) {
|
||||||
consume();
|
consume();
|
||||||
pattern.kind = BindingPattern::Kind::Object;
|
|
||||||
closing_token = TokenType::CurlyClose;
|
closing_token = TokenType::CurlyClose;
|
||||||
allow_named_property = true;
|
|
||||||
} else {
|
} else {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
while (!match(closing_token)) {
|
Vector<BindingPattern::BindingEntry> entries;
|
||||||
if (elide_extra_commas && match(TokenType::Comma))
|
|
||||||
consume();
|
|
||||||
|
|
||||||
ScopeGuard consume_commas { [&] {
|
while (!match(closing_token)) {
|
||||||
if (match(TokenType::Comma))
|
if (!is_object && match(TokenType::Comma)) {
|
||||||
consume();
|
consume();
|
||||||
} };
|
entries.append(BindingPattern::BindingEntry {});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
auto is_rest = false;
|
auto is_rest = false;
|
||||||
|
|
||||||
|
@ -1560,89 +1561,88 @@ RefPtr<BindingPattern> Parser::parse_binding_pattern()
|
||||||
is_rest = true;
|
is_rest = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (match(TokenType::Identifier)) {
|
decltype(BindingPattern::BindingEntry::name) name = Empty {};
|
||||||
auto identifier_start = position();
|
decltype(BindingPattern::BindingEntry::alias) alias = Empty {};
|
||||||
auto token = consume(TokenType::Identifier);
|
RefPtr<Expression> initializer = {};
|
||||||
auto name = create_ast_node<Identifier>(
|
|
||||||
{ m_parser_state.m_current_token.filename(), identifier_start, position() },
|
|
||||||
token.value());
|
|
||||||
|
|
||||||
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();
|
consume();
|
||||||
if (!match(TokenType::Identifier)) {
|
name = parse_expression(0);
|
||||||
syntax_error("Expected a binding pattern as the value of a named element in destructuring object");
|
consume(TokenType::BracketOpen);
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
RefPtr<Expression> initializer;
|
|
||||||
if (match(TokenType::Equals)) {
|
|
||||||
consume();
|
|
||||||
initializer = parse_expression(2);
|
|
||||||
}
|
|
||||||
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 {
|
} else {
|
||||||
RefPtr<Expression> initializer;
|
syntax_error("Expected identifier or computed property name");
|
||||||
if (match(TokenType::Equals)) {
|
return {};
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
continue;
|
if (!is_rest && match(TokenType::Colon)) {
|
||||||
|
consume();
|
||||||
|
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 {};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
if (match(TokenType::Equals)) {
|
||||||
|
if (is_rest) {
|
||||||
|
syntax_error("Unexpected initializer after rest element");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
consume();
|
||||||
|
|
||||||
|
initializer = parse_expression(2);
|
||||||
|
if (!initializer) {
|
||||||
|
syntax_error("Expected initialization expression");
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
consume(closing_token);
|
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;
|
return pattern;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -86,6 +86,7 @@ public:
|
||||||
NonnullRefPtr<YieldExpression> parse_yield_expression();
|
NonnullRefPtr<YieldExpression> parse_yield_expression();
|
||||||
NonnullRefPtr<Expression> parse_property_key();
|
NonnullRefPtr<Expression> parse_property_key();
|
||||||
NonnullRefPtr<AssignmentExpression> parse_assignment_expression(AssignmentOp, NonnullRefPtr<Expression> lhs, int min_precedence, Associativity);
|
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<FunctionExpression> try_parse_arrow_function_expression(bool expect_parens);
|
||||||
RefPtr<Statement> try_parse_labelled_statement();
|
RefPtr<Statement> try_parse_labelled_statement();
|
||||||
|
|
|
@ -87,6 +87,8 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE Type type() const { return m_type; }
|
||||||
|
|
||||||
bool is_valid() const { return m_type != Type::Invalid; }
|
bool is_valid() const { return m_type != Type::Invalid; }
|
||||||
bool is_number() const
|
bool is_number() const
|
||||||
{
|
{
|
||||||
|
@ -176,6 +178,35 @@ private:
|
||||||
u32 m_number { 0 };
|
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 {
|
namespace AK {
|
||||||
|
|
|
@ -100,7 +100,7 @@ LexicalEnvironment* ScriptFunction::create_environment()
|
||||||
parameter.binding.visit(
|
parameter.binding.visit(
|
||||||
[&](const FlyString& name) { variables.set(name, { js_undefined(), DeclarationKind::Var }); },
|
[&](const FlyString& name) { variables.set(name, { js_undefined(), DeclarationKind::Var }); },
|
||||||
[&](const NonnullRefPtr<BindingPattern>& binding) {
|
[&](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 });
|
variables.set(name, { js_undefined(), DeclarationKind::Var });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -114,7 +114,7 @@ LexicalEnvironment* ScriptFunction::create_environment()
|
||||||
variables.set(id->string(), { js_undefined(), declaration.declaration_kind() });
|
variables.set(id->string(), { js_undefined(), declaration.declaration_kind() });
|
||||||
},
|
},
|
||||||
[&](const NonnullRefPtr<BindingPattern>& binding) {
|
[&](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() });
|
variables.set(name, { js_undefined(), declaration.declaration_kind() });
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -208,18 +208,15 @@ void VM::assign(const NonnullRefPtr<BindingPattern>& target, Value value, Global
|
||||||
if (!iterator)
|
if (!iterator)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
size_t index = 0;
|
for (size_t i = 0; i < binding.entries.size(); i++) {
|
||||||
while (true) {
|
|
||||||
if (exception())
|
if (exception())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (index >= binding.properties.size())
|
auto& entry = binding.entries[i];
|
||||||
break;
|
|
||||||
|
|
||||||
auto pattern_property = binding.properties[index];
|
if (entry.is_rest) {
|
||||||
++index;
|
VERIFY(i == binding.entries.size() - 1);
|
||||||
|
|
||||||
if (pattern_property.is_rest) {
|
|
||||||
auto* array = Array::create(global_object);
|
auto* array = Array::create(global_object);
|
||||||
for (;;) {
|
for (;;) {
|
||||||
auto next_object = iterator_next(*iterator);
|
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);
|
array->indexed_properties().append(next_value);
|
||||||
}
|
}
|
||||||
value = array;
|
value = array;
|
||||||
} else {
|
} else if (iterator) {
|
||||||
auto next_object = iterator_next(*iterator);
|
auto next_object = iterator_next(*iterator);
|
||||||
if (!next_object)
|
if (!next_object)
|
||||||
return;
|
return;
|
||||||
|
@ -249,65 +246,83 @@ void VM::assign(const NonnullRefPtr<BindingPattern>& target, Value value, Global
|
||||||
if (exception())
|
if (exception())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (!done_property.is_empty() && done_property.to_boolean())
|
if (!done_property.is_empty() && done_property.to_boolean()) {
|
||||||
break;
|
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())
|
if (exception())
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value.is_undefined() && pattern_property.initializer)
|
entry.alias.visit(
|
||||||
value = pattern_property.initializer->execute(interpreter(), global_object);
|
[&](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 (exception())
|
if (entry.is_rest)
|
||||||
return;
|
break;
|
||||||
|
|
||||||
if (pattern_property.name) {
|
|
||||||
set_variable(pattern_property.name->string(), value, global_object, first_assignment, specific_scope);
|
|
||||||
if (pattern_property.is_rest)
|
|
||||||
break;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pattern_property.pattern) {
|
|
||||||
assign(NonnullRefPtr(*pattern_property.pattern), value, global_object, first_assignment, specific_scope);
|
|
||||||
if (pattern_property.is_rest)
|
|
||||||
break;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case BindingPattern::Kind::Object: {
|
case BindingPattern::Kind::Object: {
|
||||||
auto object = value.to_object(global_object);
|
auto object = value.to_object(global_object);
|
||||||
HashTable<FlyString> seen_names;
|
HashTable<PropertyName, PropertyNameTraits> seen_names;
|
||||||
for (auto& property : binding.properties) {
|
for (auto& property : binding.entries) {
|
||||||
VERIFY(!property.pattern);
|
VERIFY(!property.is_elision());
|
||||||
|
|
||||||
|
PropertyName assignment_name;
|
||||||
JS::Value value_to_assign;
|
JS::Value value_to_assign;
|
||||||
if (property.is_rest) {
|
if (property.is_rest) {
|
||||||
auto* rest_object = Object::create(global_object, nullptr);
|
VERIFY(property.name.has<NonnullRefPtr<Identifier>>());
|
||||||
for (auto& property : object->shape().property_table()) {
|
assignment_name = property.name.get<NonnullRefPtr<Identifier>>()->string();
|
||||||
if (!property.value.attributes.has_enumerable())
|
|
||||||
|
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;
|
continue;
|
||||||
if (seen_names.contains(property.key.to_display_string()))
|
if (seen_names.contains(object_property.key.to_display_string()))
|
||||||
continue;
|
continue;
|
||||||
rest_object->put(property.key, object->get(property.key));
|
rest_object->put(object_property.key, object->get(object_property.key));
|
||||||
if (exception())
|
if (exception())
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
value_to_assign = rest_object;
|
value_to_assign = rest_object;
|
||||||
} else {
|
} 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);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (exception())
|
||||||
|
break;
|
||||||
|
|
||||||
|
value_to_assign = object->get(assignment_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
seen_names.set(property.name->string());
|
seen_names.set(assignment_name);
|
||||||
if (exception())
|
|
||||||
break;
|
|
||||||
|
|
||||||
auto assignment_name = property.name->string();
|
|
||||||
if (property.alias)
|
|
||||||
assignment_name = property.alias->string();
|
|
||||||
|
|
||||||
if (value_to_assign.is_empty())
|
if (value_to_assign.is_empty())
|
||||||
value_to_assign = js_undefined();
|
value_to_assign = js_undefined();
|
||||||
|
@ -318,7 +333,18 @@ void VM::assign(const NonnullRefPtr<BindingPattern>& target, Value value, Global
|
||||||
if (exception())
|
if (exception())
|
||||||
break;
|
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)
|
if (property.is_rest)
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue