use crate::bytecompiler::{Access, BindingAccessOpcode, ByteCompiler, Register, ToJsString};
use boa_ast::{
expression::{
access::{PropertyAccess, PropertyAccessField},
operator::{Update, update::UpdateOp},
},
scope::BindingLocatorError,
};
impl ByteCompiler<'_> {
pub(crate) fn compile_update(&mut self, update: &Update, dst: &Register) {
let mut compiler = self.position_guard(update);
let increment = matches!(
update.op(),
UpdateOp::IncrementPost | UpdateOp::IncrementPre
);
let post = matches!(
update.op(),
UpdateOp::IncrementPost | UpdateOp::DecrementPost
);
match Access::from_update_target(update.target()) {
Access::Variable { name } => {
let name = name.to_js_string(compiler.interner());
let binding = compiler
.lexical_scope
.get_identifier_reference(name.clone());
let is_lexical = binding.is_lexical();
let index = compiler.get_binding(&binding);
if is_lexical {
compiler.emit_binding_access(BindingAccessOpcode::GetName, &index, dst);
} else {
compiler.emit_binding_access(
BindingAccessOpcode::GetNameAndLocator,
&index,
dst,
);
}
let value = compiler.register_allocator.alloc();
if increment {
compiler.bytecode.emit_inc(value.variable(), dst.variable());
} else {
compiler.bytecode.emit_dec(value.variable(), dst.variable());
}
if is_lexical {
match compiler.lexical_scope.set_mutable_binding(name.clone()) {
Ok(binding) => {
let index = compiler.insert_binding(binding);
compiler.emit_binding_access(
BindingAccessOpcode::SetName,
&index,
&value,
);
}
Err(BindingLocatorError::MutateImmutable) => {
let index = compiler.get_or_insert_string(name);
compiler.bytecode.emit_throw_mutate_immutable(index.into());
}
Err(BindingLocatorError::Silent) => {}
}
} else {
compiler.emit_binding_access(
BindingAccessOpcode::SetNameByLocator,
&index,
&value,
);
}
if !post {
compiler
.bytecode
.emit_move(dst.variable(), value.variable());
}
compiler.register_allocator.dealloc(value);
}
Access::Property { access } => match access {
PropertyAccess::Simple(access) => {
let object = compiler.register_allocator.alloc();
compiler.compile_expr(access.target(), &object);
match access.field() {
PropertyAccessField::Const(ident) => {
compiler.emit_get_property_by_name(dst, &object, &object, ident.sym());
let value = compiler.register_allocator.alloc();
if increment {
compiler.bytecode.emit_inc(value.variable(), dst.variable());
} else {
compiler.bytecode.emit_dec(value.variable(), dst.variable());
}
compiler.emit_set_property_by_name(
&value,
&object,
&object,
ident.sym(),
);
if !post {
compiler
.bytecode
.emit_move(dst.variable(), value.variable());
}
compiler.register_allocator.dealloc(object);
compiler.register_allocator.dealloc(value);
}
PropertyAccessField::Expr(expr) => {
let key = compiler.register_allocator.alloc();
compiler.compile_expr(expr, &key);
compiler.bytecode.emit_get_property_by_value_push(
dst.variable(),
key.variable(),
object.variable(),
object.variable(),
);
let value = compiler.register_allocator.alloc();
if increment {
compiler.bytecode.emit_inc(value.variable(), dst.variable());
} else {
compiler.bytecode.emit_dec(value.variable(), dst.variable());
}
compiler.bytecode.emit_set_property_by_value(
value.variable(),
key.variable(),
object.variable(),
object.variable(),
);
if !post {
compiler
.bytecode
.emit_move(dst.variable(), value.variable());
}
compiler.register_allocator.dealloc(key);
compiler.register_allocator.dealloc(object);
compiler.register_allocator.dealloc(value);
}
}
}
PropertyAccess::Private(access) => {
let index = compiler.get_or_insert_private_name(access.field());
let object = compiler.register_allocator.alloc();
compiler.compile_expr(access.target(), &object);
compiler.bytecode.emit_get_private_field(
dst.variable(),
object.variable(),
index.into(),
);
let value = compiler.register_allocator.alloc();
if increment {
compiler.bytecode.emit_inc(value.variable(), dst.variable());
} else {
compiler.bytecode.emit_dec(value.variable(), dst.variable());
}
compiler.bytecode.emit_set_private_field(
value.variable(),
object.variable(),
index.into(),
);
if !post {
compiler
.bytecode
.emit_move(dst.variable(), value.variable());
}
compiler.register_allocator.dealloc(value);
compiler.register_allocator.dealloc(object);
}
PropertyAccess::Super(access) => match access.field() {
PropertyAccessField::Const(ident) => {
let object = compiler.register_allocator.alloc();
let receiver = compiler.register_allocator.alloc();
compiler.bytecode.emit_super(object.variable());
compiler.bytecode.emit_this(receiver.variable());
compiler.emit_get_property_by_name(dst, &receiver, &object, ident.sym());
let value = compiler.register_allocator.alloc();
if increment {
compiler.bytecode.emit_inc(value.variable(), dst.variable());
} else {
compiler.bytecode.emit_dec(value.variable(), dst.variable());
}
compiler.emit_set_property_by_name(&value, &receiver, &object, ident.sym());
if !post {
compiler
.bytecode
.emit_move(dst.variable(), value.variable());
}
compiler.register_allocator.dealloc(receiver);
compiler.register_allocator.dealloc(object);
compiler.register_allocator.dealloc(value);
}
PropertyAccessField::Expr(expr) => {
let object = compiler.register_allocator.alloc();
let receiver = compiler.register_allocator.alloc();
compiler.bytecode.emit_super(object.variable());
compiler.bytecode.emit_this(receiver.variable());
let key = compiler.register_allocator.alloc();
compiler.compile_expr(expr, &key);
compiler.bytecode.emit_get_property_by_value(
dst.variable(),
key.variable(),
receiver.variable(),
object.variable(),
);
if increment {
compiler.bytecode.emit_inc(dst.variable(), dst.variable());
} else {
compiler.bytecode.emit_dec(dst.variable(), dst.variable());
}
compiler.bytecode.emit_set_property_by_value(
dst.variable(),
key.variable(),
receiver.variable(),
object.variable(),
);
compiler.register_allocator.dealloc(receiver);
compiler.register_allocator.dealloc(object);
compiler.register_allocator.dealloc(key);
}
},
},
Access::This => unreachable!(),
}
}
}