mirror of
https://github.com/RGBCube/serenity
synced 2025-05-22 19:45:08 +00:00
LibJS/Bytecode: Always evaluate LHS first in assignment expressions
This fixes an issue where expressions like `a[i] = a[++i]` could evaluate `++i` before `a[i]`.
This commit is contained in:
parent
1986693edc
commit
0f8c6dc9ad
3 changed files with 27 additions and 2 deletions
|
@ -435,7 +435,9 @@ Bytecode::CodeGenerationErrorOr<Optional<Bytecode::Operand>> AssignmentExpressio
|
||||||
}
|
}
|
||||||
|
|
||||||
if (expression.is_computed()) {
|
if (expression.is_computed()) {
|
||||||
computed_property = TRY(expression.property().generate_bytecode(generator)).value();
|
auto property = TRY(expression.property().generate_bytecode(generator)).value();
|
||||||
|
computed_property = Bytecode::Operand(generator.allocate_register());
|
||||||
|
generator.emit<Bytecode::Op::Mov>(*computed_property, property);
|
||||||
|
|
||||||
// To be continued later with PutByValue.
|
// To be continued later with PutByValue.
|
||||||
} else if (expression.property().is_identifier()) {
|
} else if (expression.property().is_identifier()) {
|
||||||
|
|
|
@ -256,11 +256,13 @@ CodeGenerationErrorOr<Generator::ReferenceOperands> Generator::emit_load_from_re
|
||||||
auto base = TRY(expression.object().generate_bytecode(*this)).value();
|
auto base = TRY(expression.object().generate_bytecode(*this)).value();
|
||||||
if (expression.is_computed()) {
|
if (expression.is_computed()) {
|
||||||
auto property = TRY(expression.property().generate_bytecode(*this)).value();
|
auto property = TRY(expression.property().generate_bytecode(*this)).value();
|
||||||
|
auto saved_property = Operand(allocate_register());
|
||||||
|
emit<Bytecode::Op::Mov>(saved_property, property);
|
||||||
auto dst = preferred_dst.has_value() ? preferred_dst.value() : Operand(allocate_register());
|
auto dst = preferred_dst.has_value() ? preferred_dst.value() : Operand(allocate_register());
|
||||||
emit<Bytecode::Op::GetByValue>(dst, base, property);
|
emit<Bytecode::Op::GetByValue>(dst, base, property);
|
||||||
return ReferenceOperands {
|
return ReferenceOperands {
|
||||||
.base = base,
|
.base = base,
|
||||||
.referenced_name = property,
|
.referenced_name = saved_property,
|
||||||
.this_value = base,
|
.this_value = base,
|
||||||
.loaded_value = dst,
|
.loaded_value = dst,
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
test("Assignment should always evaluate LHS first", () => {
|
||||||
|
function go(a) {
|
||||||
|
let i = 0;
|
||||||
|
a[i] = a[++i];
|
||||||
|
}
|
||||||
|
|
||||||
|
let a = [1, 2, 3];
|
||||||
|
go(a);
|
||||||
|
expect(a).toEqual([2, 2, 3]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Binary assignment should always evaluate LHS first", () => {
|
||||||
|
function go(a) {
|
||||||
|
let i = 0;
|
||||||
|
a[i] |= a[++i];
|
||||||
|
}
|
||||||
|
|
||||||
|
let a = [1, 2];
|
||||||
|
go(a);
|
||||||
|
expect(a).toEqual([3, 2]);
|
||||||
|
});
|
Loading…
Add table
Add a link
Reference in a new issue