Skip to content

feat(ecmascript): array and object assignment pattern #592

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
merged 14 commits into from
Mar 15, 2025
155 changes: 1 addition & 154 deletions nova_vm/src/engine/bytecode/bytecode_compiler.rs
Original file line number Diff line number Diff line change
@@ -2,6 +2,7 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

mod assignment;
mod block_declaration_instantiation;
mod class_definition_evaluation;
mod for_in_of_statement;
@@ -770,160 +771,6 @@ impl CompileEvaluation for ast::LogicalExpression<'_> {
}
}

impl CompileEvaluation for ast::AssignmentExpression<'_> {
fn compile(&self, ctx: &mut CompileContext) {
// 1. Let lref be ? Evaluation of LeftHandSideExpression.
let is_identifier_ref = match &self.left {
ast::AssignmentTarget::ArrayAssignmentTarget(_) => todo!(),
ast::AssignmentTarget::AssignmentTargetIdentifier(identifier) => {
identifier.compile(ctx);
true
}
ast::AssignmentTarget::ComputedMemberExpression(expression) => {
expression.compile(ctx);
false
}
ast::AssignmentTarget::ObjectAssignmentTarget(_) => todo!(),
ast::AssignmentTarget::PrivateFieldExpression(_) => todo!(),
ast::AssignmentTarget::StaticMemberExpression(expression) => {
expression.compile(ctx);
false
}
ast::AssignmentTarget::TSAsExpression(_)
| ast::AssignmentTarget::TSSatisfiesExpression(_)
| ast::AssignmentTarget::TSNonNullExpression(_)
| ast::AssignmentTarget::TSTypeAssertion(_)
| ast::AssignmentTarget::TSInstantiationExpression(_) => unreachable!(),
};

if self.operator == oxc_syntax::operator::AssignmentOperator::Assign {
ctx.add_instruction(Instruction::PushReference);
self.right.compile(ctx);

if is_reference(&self.right) {
ctx.add_instruction(Instruction::GetValue);
}

ctx.add_instruction(Instruction::LoadCopy);
ctx.add_instruction(Instruction::PopReference);
ctx.add_instruction(Instruction::PutValue);

// ... Return rval.
ctx.add_instruction(Instruction::Store);
} else if matches!(
self.operator,
oxc_syntax::operator::AssignmentOperator::LogicalAnd
| oxc_syntax::operator::AssignmentOperator::LogicalNullish
| oxc_syntax::operator::AssignmentOperator::LogicalOr
) {
// 2. Let lval be ? GetValue(lref).
ctx.add_instruction(Instruction::GetValueKeepReference);
ctx.add_instruction(Instruction::PushReference);
// We store the left value on the stack, because we'll need to
// restore it later.
ctx.add_instruction(Instruction::LoadCopy);

match self.operator {
oxc_syntax::operator::AssignmentOperator::LogicalAnd => {
// 3. Let lbool be ToBoolean(lval).
// Note: We do not directly call ToBoolean: JumpIfNot does.
// 4. If lbool is false, return lval.
}
oxc_syntax::operator::AssignmentOperator::LogicalOr => {
// 3. Let lbool be ToBoolean(lval).
// Note: We do not directly call ToBoolean: JumpIfNot does.
// 4. If lbool is true, return lval.
ctx.add_instruction(Instruction::LogicalNot);
}
oxc_syntax::operator::AssignmentOperator::LogicalNullish => {
// 3. If lval is neither undefined nor null, return lval.
ctx.add_instruction(Instruction::IsNullOrUndefined);
}
_ => unreachable!(),
}

let jump_to_end = ctx.add_instruction_with_jump_slot(Instruction::JumpIfNot);

// We're returning the right expression, so we discard the left
// value at the top of the stack.
ctx.add_instruction(Instruction::Store);

// 5. If IsAnonymousFunctionDefinition(AssignmentExpression)
// is true and IsIdentifierRef of LeftHandSideExpression is true,
// then
if is_identifier_ref && is_anonymous_function_definition(&self.right) {
// a. Let lhs be the StringValue of LeftHandSideExpression.
// b. Let rval be ? NamedEvaluation of AssignmentExpression with argument lhs.
ctx.name_identifier = Some(NamedEvaluationParameter::ReferenceStack);
self.right.compile(ctx);
} else {
// 6. Else
// a. Let rref be ? Evaluation of AssignmentExpression.
self.right.compile(ctx);
// b. Let rval be ? GetValue(rref).
if is_reference(&self.right) {
ctx.add_instruction(Instruction::GetValue);
}
}

// 7. Perform ? PutValue(lref, rval).
ctx.add_instruction(Instruction::LoadCopy);
ctx.add_instruction(Instruction::PopReference);
ctx.add_instruction(Instruction::PutValue);

// 4. ... return lval.
ctx.set_jump_target_here(jump_to_end);
ctx.add_instruction(Instruction::Store);
} else {
// 2. let lval be ? GetValue(lref).
ctx.add_instruction(Instruction::GetValueKeepReference);
ctx.add_instruction(Instruction::Load);
ctx.add_instruction(Instruction::PushReference);
// 3. Let rref be ? Evaluation of AssignmentExpression.
self.right.compile(ctx);

// 4. Let rval be ? GetValue(rref).
if is_reference(&self.right) {
ctx.add_instruction(Instruction::GetValue);
}

// 5. Let assignmentOpText be the source text matched by AssignmentOperator.
// 6. Let opText be the sequence of Unicode code points associated with assignmentOpText in the following table:
let op_text = match self.operator {
oxc_syntax::operator::AssignmentOperator::Addition => BinaryOperator::Addition,
oxc_syntax::operator::AssignmentOperator::Subtraction => {
BinaryOperator::Subtraction
}
oxc_syntax::operator::AssignmentOperator::Multiplication => {
BinaryOperator::Multiplication
}
oxc_syntax::operator::AssignmentOperator::Division => BinaryOperator::Division,
oxc_syntax::operator::AssignmentOperator::Remainder => BinaryOperator::Remainder,
oxc_syntax::operator::AssignmentOperator::ShiftLeft => BinaryOperator::ShiftLeft,
oxc_syntax::operator::AssignmentOperator::ShiftRight => BinaryOperator::ShiftRight,
oxc_syntax::operator::AssignmentOperator::ShiftRightZeroFill => {
BinaryOperator::ShiftRightZeroFill
}
oxc_syntax::operator::AssignmentOperator::BitwiseOR => BinaryOperator::BitwiseOR,
oxc_syntax::operator::AssignmentOperator::BitwiseXOR => BinaryOperator::BitwiseXOR,
oxc_syntax::operator::AssignmentOperator::BitwiseAnd => BinaryOperator::BitwiseAnd,
oxc_syntax::operator::AssignmentOperator::Exponential => {
BinaryOperator::Exponential
}
_ => unreachable!(),
};
// 7. Let r be ? ApplyStringOrNumericBinaryOperator(lval, opText, rval).
ctx.add_instruction(Instruction::ApplyStringOrNumericBinaryOperator(op_text));
ctx.add_instruction(Instruction::LoadCopy);
// 8. Perform ? PutValue(lref, r).
ctx.add_instruction(Instruction::PopReference);
ctx.add_instruction(Instruction::PutValue);
// 9. Return r.
ctx.add_instruction(Instruction::Store);
}
}
}

impl CompileEvaluation for ast::ParenthesizedExpression<'_> {
fn compile(&self, ctx: &mut CompileContext) {
self.expression.compile(ctx);
Loading