Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - Add field accessors to destructing assignment #2213

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 103 additions & 8 deletions boa_engine/src/bytecompiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -989,7 +989,7 @@ impl<'b> ByteCompiler<'b> {
PropertyDefinition::SpreadObject(expr) => {
self.compile_expr(expr, true)?;
self.emit_opcode(Opcode::Swap);
self.emit(Opcode::CopyDataProperties, &[0]);
self.emit(Opcode::CopyDataProperties, &[0, 0]);
self.emit_opcode(Opcode::Pop);
}
PropertyDefinition::CoverInitializedName(_, _) => {
Expand Down Expand Up @@ -2195,9 +2195,13 @@ impl<'b> ByteCompiler<'b> {

self.emit_opcode(Opcode::RequireObjectCoercible);

let mut additional_excluded_keys_count = 0;
let rest_exits = pattern.has_rest();

for binding in pattern.bindings() {
use BindingPatternTypeObject::{
BindingPattern, Empty, RestGetConstField, RestProperty, SingleName,
AssignmentGetConstField, AssignmentGetField, AssignmentRestProperty,
BindingPattern, Empty, RestProperty, SingleName,
};

match binding {
Expand All @@ -2218,7 +2222,11 @@ impl<'b> ByteCompiler<'b> {
PropertyName::Computed(node) => {
self.compile_expr(node, true)?;
self.emit_opcode(Opcode::Swap);
self.emit_opcode(Opcode::GetPropertyByValue);
if rest_exits {
self.emit_opcode(Opcode::GetPropertyByValuePush);
} else {
self.emit_opcode(Opcode::GetPropertyByValue);
}
}
}

Expand All @@ -2229,13 +2237,17 @@ impl<'b> ByteCompiler<'b> {
self.patch_jump(skip);
}
self.emit_binding(def, *ident);

if rest_exits && property_name.computed().is_some() {
self.emit_opcode(Opcode::Swap);
additional_excluded_keys_count += 1;
}
}
// BindingRestProperty : ... BindingIdentifier
RestProperty {
ident,
excluded_keys,
} => {
self.emit_opcode(Opcode::Dup);
self.emit_opcode(Opcode::PushEmptyObject);

for key in excluded_keys {
Expand All @@ -2244,10 +2256,13 @@ impl<'b> ByteCompiler<'b> {
));
}

self.emit(Opcode::CopyDataProperties, &[excluded_keys.len() as u32]);
self.emit(
Opcode::CopyDataProperties,
&[excluded_keys.len() as u32, additional_excluded_keys_count],
);
self.emit_binding(def, *ident);
}
RestGetConstField {
AssignmentRestProperty {
get_const_field,
excluded_keys,
} => {
Expand All @@ -2258,14 +2273,92 @@ impl<'b> ByteCompiler<'b> {
self.interner().resolve_expect(*key).into(),
));
}
self.emit(Opcode::CopyDataProperties, &[excluded_keys.len() as u32]);
self.emit(Opcode::CopyDataProperties, &[excluded_keys.len() as u32, 0]);
self.access_set(
Access::ByName {
node: get_const_field,
},
None,
false,
)?;
}
AssignmentGetConstField {
property_name,
get_const_field,
default_init,
} => {
self.emit_opcode(Opcode::Dup);
match property_name {
PropertyName::Literal(name) => {
let index = self.get_or_insert_name(*name);
self.emit(Opcode::GetPropertyByName, &[index]);
}
PropertyName::Computed(node) => {
self.compile_expr(node, true)?;
self.emit_opcode(Opcode::Swap);
if rest_exits {
self.emit_opcode(Opcode::GetPropertyByValuePush);
} else {
self.emit_opcode(Opcode::GetPropertyByValue);
}
}
}

if let Some(init) = default_init {
let skip =
self.emit_opcode_with_operand(Opcode::JumpIfNotUndefined);
self.compile_expr(init, true)?;
self.patch_jump(skip);
}

self.access_set(
Access::ByName {
node: get_const_field,
},
None,
false,
)?;

if rest_exits && property_name.computed().is_some() {
self.emit_opcode(Opcode::Swap);
additional_excluded_keys_count += 1;
}
}
AssignmentGetField {
property_name,
get_field,
default_init,
} => {
self.emit_opcode(Opcode::Dup);
match property_name {
PropertyName::Literal(name) => {
let index = self.get_or_insert_name(*name);
self.emit(Opcode::GetPropertyByName, &[index]);
}
PropertyName::Computed(node) => {
self.compile_expr(node, true)?;
self.emit_opcode(Opcode::Swap);
if rest_exits {
self.emit_opcode(Opcode::GetPropertyByValuePush);
} else {
self.emit_opcode(Opcode::GetPropertyByValue);
}
}
}

if let Some(init) = default_init {
let skip =
self.emit_opcode_with_operand(Opcode::JumpIfNotUndefined);
self.compile_expr(init, true)?;
self.patch_jump(skip);
}

self.access_set(Access::ByValue { node: get_field }, None, false)?;

if rest_exits && property_name.computed().is_some() {
self.emit_opcode(Opcode::Swap);
additional_excluded_keys_count += 1;
}
}
BindingPattern {
ident,
Expand Down Expand Up @@ -2297,7 +2390,9 @@ impl<'b> ByteCompiler<'b> {
}
}

self.emit_opcode(Opcode::Pop);
if !rest_exits {
self.emit_opcode(Opcode::Pop);
}
}
DeclarationPattern::Array(pattern) => {
let skip_init = self.emit_opcode_with_operand(Opcode::JumpIfNotUndefined);
Expand Down
121 changes: 110 additions & 11 deletions boa_engine/src/syntax/ast/node/declaration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -333,8 +333,9 @@ impl DeclarationPattern {
}
}
}
BindingPatternTypeObject::RestGetConstField {
get_const_field, ..
BindingPatternTypeObject::AssignmentRestProperty {
get_const_field,
..
} => {
if get_const_field.obj().contains_arguments() {
return true;
Expand Down Expand Up @@ -432,8 +433,9 @@ impl DeclarationPattern {
return true;
}
}
BindingPatternTypeObject::RestGetConstField {
get_const_field, ..
BindingPatternTypeObject::AssignmentRestProperty {
get_const_field,
..
} => {
if get_const_field.obj().contains(symbol) {
return true;
Expand Down Expand Up @@ -560,18 +562,27 @@ impl DeclarationPatternObject {
&self.bindings
}

// Returns if the object binding pattern has a rest element.
#[inline]
pub(crate) fn has_rest(&self) -> bool {
matches!(
self.bindings.last(),
Some(BindingPatternTypeObject::RestProperty { .. })
)
}

/// Gets the list of identifiers declared by the object binding pattern.
#[inline]
pub(crate) fn idents(&self) -> Vec<Sym> {
let mut idents = Vec::new();

for binding in &self.bindings {
use BindingPatternTypeObject::{
BindingPattern, Empty, RestGetConstField, RestProperty, SingleName,
AssignmentRestProperty, BindingPattern, Empty, RestProperty, SingleName,
};

match binding {
Empty | RestGetConstField { .. } => {}
Empty | AssignmentRestProperty { .. } => {}
SingleName {
ident,
property_name: _,
Expand All @@ -585,6 +596,12 @@ impl DeclarationPatternObject {
} => {
idents.push(*property_name);
}
BindingPatternTypeObject::AssignmentGetConstField { property_name, .. }
| BindingPatternTypeObject::AssignmentGetField { property_name, .. } => {
if let Some(name) = property_name.literal() {
idents.push(name);
}
}
BindingPattern {
ident: _,
pattern,
Expand Down Expand Up @@ -737,20 +754,50 @@ pub enum BindingPatternTypeObject {
/// [spec1]: https://tc39.es/ecma262/#prod-BindingRestProperty
RestProperty { ident: Sym, excluded_keys: Vec<Sym> },

/// RestGetConstField represents a rest property (spread operator) with a property accessor.
/// AssignmentRestProperty represents a rest property with a DestructuringAssignmentTarget.
///
/// Note: According to the spec this is not part of an ObjectBindingPattern.
/// This is only used when a object literal is used as the left-hand-side of an assignment expression.
/// This is only used when a object literal is used to cover an AssignmentPattern.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentExpression
RestGetConstField {
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentRestProperty
AssignmentRestProperty {
get_const_field: GetConstField,
excluded_keys: Vec<Sym>,
},

/// AssignmentGetConstField represents an AssignmentProperty with a cost field member expression AssignmentElement.
///
/// Note: According to the spec this is not part of an ObjectBindingPattern.
/// This is only used when a object literal is used to cover an AssignmentPattern.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentProperty
AssignmentGetConstField {
property_name: PropertyName,
get_const_field: GetConstField,
default_init: Option<Node>,
},

/// AssignmentGetField represents an AssignmentProperty with an expression field member expression AssignmentElement.
///
/// Note: According to the spec this is not part of an ObjectBindingPattern.
/// This is only used when a object literal is used to cover an AssignmentPattern.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#prod-AssignmentProperty
AssignmentGetField {
property_name: PropertyName,
get_field: GetField,
default_init: Option<Node>,
},

/// BindingPattern represents a `BindingProperty` with a `BindingPattern` as the `BindingElement`.
///
/// Additionally to the identifier of the new property and the nested binding pattern,
Expand Down Expand Up @@ -806,11 +853,63 @@ impl ToInternedString for BindingPatternTypeObject {
} => {
format!(" ... {}", interner.resolve_expect(*property_name))
}
Self::RestGetConstField {
Self::AssignmentRestProperty {
get_const_field, ..
} => {
format!(" ... {}", get_const_field.to_interned_string(interner))
}
Self::AssignmentGetConstField {
property_name,
get_const_field,
default_init,
} => {
let mut buf = match property_name {
PropertyName::Literal(name) => {
format!(
" {} : {}",
interner.resolve_expect(*name),
get_const_field.to_interned_string(interner)
)
}
PropertyName::Computed(node) => {
format!(
" [{}] : {}",
node.to_interned_string(interner),
get_const_field.to_interned_string(interner)
)
}
};
if let Some(init) = &default_init {
buf.push_str(&format!(" = {}", init.to_interned_string(interner)));
}
buf
}
Self::AssignmentGetField {
property_name,
get_field,
default_init,
} => {
let mut buf = match property_name {
PropertyName::Literal(name) => {
format!(
" {} : {}",
interner.resolve_expect(*name),
get_field.to_interned_string(interner)
)
}
PropertyName::Computed(node) => {
format!(
" [{}] : {}",
node.to_interned_string(interner),
get_field.to_interned_string(interner)
)
}
};
if let Some(init) = &default_init {
buf.push_str(&format!(" = {}", init.to_interned_string(interner)));
}
buf
}
Self::BindingPattern {
ident: property_name,
pattern,
Expand Down
4 changes: 2 additions & 2 deletions boa_engine/src/syntax/ast/node/object/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -390,7 +390,7 @@ pub enum PropertyName {

impl PropertyName {
/// Returns the literal property name if it exists.
pub(in crate::syntax) fn literal(&self) -> Option<Sym> {
pub(crate) fn literal(&self) -> Option<Sym> {
if let Self::Literal(sym) = self {
Some(*sym)
} else {
Expand All @@ -399,7 +399,7 @@ impl PropertyName {
}

/// Returns the expression node if the property name is computed.
pub(in crate::syntax) fn computed(&self) -> Option<&Node> {
pub(crate) fn computed(&self) -> Option<&Node> {
if let Self::Computed(node) = self {
Some(node)
} else {
Expand Down
Loading