1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 21:17:45 +00:00

LibJS: Restructure and fully implement BindingPatterns

This commit is contained in:
Matthew Olsson 2021-06-12 18:04:28 -07:00 committed by Andreas Kling
parent 10372b8118
commit ce04c2259f
9 changed files with 244 additions and 169 deletions

View file

@ -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 {

View file

@ -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() });
});
});

View file

@ -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);
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 (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;
}
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);
});
if (exception())
break;
value_to_assign = object->get(assignment_name);
}
seen_names.set(property.name->string());
if (exception())
break;
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;