mirror of
https://github.com/RGBCube/serenity
synced 2025-05-22 17:05:10 +00:00
LibJS: Evaluate AssignmentExpression LHS before RHS according to the spec
Fixes #3689.
This commit is contained in:
parent
7fd4646acb
commit
2d4cd5b49b
2 changed files with 63 additions and 39 deletions
|
@ -1198,84 +1198,67 @@ void ThisExpression::dump(int indent) const
|
||||||
|
|
||||||
Value AssignmentExpression::execute(Interpreter& interpreter, GlobalObject& global_object) const
|
Value AssignmentExpression::execute(Interpreter& interpreter, GlobalObject& global_object) const
|
||||||
{
|
{
|
||||||
auto rhs_result = m_rhs->execute(interpreter, global_object);
|
#define EXECUTE_LHS_AND_RHS() \
|
||||||
if (interpreter.exception())
|
do { \
|
||||||
return {};
|
lhs_result = m_lhs->execute(interpreter, global_object); \
|
||||||
|
if (interpreter.exception()) \
|
||||||
|
return {}; \
|
||||||
|
rhs_result = m_rhs->execute(interpreter, global_object); \
|
||||||
|
if (interpreter.exception()) \
|
||||||
|
return {}; \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
Value lhs_result;
|
Value lhs_result;
|
||||||
|
Value rhs_result;
|
||||||
switch (m_op) {
|
switch (m_op) {
|
||||||
case AssignmentOp::Assignment:
|
case AssignmentOp::Assignment:
|
||||||
break;
|
break;
|
||||||
case AssignmentOp::AdditionAssignment:
|
case AssignmentOp::AdditionAssignment:
|
||||||
lhs_result = m_lhs->execute(interpreter, global_object);
|
EXECUTE_LHS_AND_RHS();
|
||||||
if (interpreter.exception())
|
|
||||||
return {};
|
|
||||||
rhs_result = add(global_object, lhs_result, rhs_result);
|
rhs_result = add(global_object, lhs_result, rhs_result);
|
||||||
break;
|
break;
|
||||||
case AssignmentOp::SubtractionAssignment:
|
case AssignmentOp::SubtractionAssignment:
|
||||||
lhs_result = m_lhs->execute(interpreter, global_object);
|
EXECUTE_LHS_AND_RHS();
|
||||||
if (interpreter.exception())
|
|
||||||
return {};
|
|
||||||
rhs_result = sub(global_object, lhs_result, rhs_result);
|
rhs_result = sub(global_object, lhs_result, rhs_result);
|
||||||
break;
|
break;
|
||||||
case AssignmentOp::MultiplicationAssignment:
|
case AssignmentOp::MultiplicationAssignment:
|
||||||
lhs_result = m_lhs->execute(interpreter, global_object);
|
EXECUTE_LHS_AND_RHS();
|
||||||
if (interpreter.exception())
|
|
||||||
return {};
|
|
||||||
rhs_result = mul(global_object, lhs_result, rhs_result);
|
rhs_result = mul(global_object, lhs_result, rhs_result);
|
||||||
break;
|
break;
|
||||||
case AssignmentOp::DivisionAssignment:
|
case AssignmentOp::DivisionAssignment:
|
||||||
lhs_result = m_lhs->execute(interpreter, global_object);
|
EXECUTE_LHS_AND_RHS();
|
||||||
if (interpreter.exception())
|
|
||||||
return {};
|
|
||||||
rhs_result = div(global_object, lhs_result, rhs_result);
|
rhs_result = div(global_object, lhs_result, rhs_result);
|
||||||
break;
|
break;
|
||||||
case AssignmentOp::ModuloAssignment:
|
case AssignmentOp::ModuloAssignment:
|
||||||
lhs_result = m_lhs->execute(interpreter, global_object);
|
EXECUTE_LHS_AND_RHS();
|
||||||
if (interpreter.exception())
|
|
||||||
return {};
|
|
||||||
rhs_result = mod(global_object, lhs_result, rhs_result);
|
rhs_result = mod(global_object, lhs_result, rhs_result);
|
||||||
break;
|
break;
|
||||||
case AssignmentOp::ExponentiationAssignment:
|
case AssignmentOp::ExponentiationAssignment:
|
||||||
lhs_result = m_lhs->execute(interpreter, global_object);
|
EXECUTE_LHS_AND_RHS();
|
||||||
if (interpreter.exception())
|
|
||||||
return {};
|
|
||||||
rhs_result = exp(global_object, lhs_result, rhs_result);
|
rhs_result = exp(global_object, lhs_result, rhs_result);
|
||||||
break;
|
break;
|
||||||
case AssignmentOp::BitwiseAndAssignment:
|
case AssignmentOp::BitwiseAndAssignment:
|
||||||
lhs_result = m_lhs->execute(interpreter, global_object);
|
EXECUTE_LHS_AND_RHS();
|
||||||
if (interpreter.exception())
|
|
||||||
return {};
|
|
||||||
rhs_result = bitwise_and(global_object, lhs_result, rhs_result);
|
rhs_result = bitwise_and(global_object, lhs_result, rhs_result);
|
||||||
break;
|
break;
|
||||||
case AssignmentOp::BitwiseOrAssignment:
|
case AssignmentOp::BitwiseOrAssignment:
|
||||||
lhs_result = m_lhs->execute(interpreter, global_object);
|
EXECUTE_LHS_AND_RHS();
|
||||||
if (interpreter.exception())
|
|
||||||
return {};
|
|
||||||
rhs_result = bitwise_or(global_object, lhs_result, rhs_result);
|
rhs_result = bitwise_or(global_object, lhs_result, rhs_result);
|
||||||
break;
|
break;
|
||||||
case AssignmentOp::BitwiseXorAssignment:
|
case AssignmentOp::BitwiseXorAssignment:
|
||||||
lhs_result = m_lhs->execute(interpreter, global_object);
|
EXECUTE_LHS_AND_RHS();
|
||||||
if (interpreter.exception())
|
|
||||||
return {};
|
|
||||||
rhs_result = bitwise_xor(global_object, lhs_result, rhs_result);
|
rhs_result = bitwise_xor(global_object, lhs_result, rhs_result);
|
||||||
break;
|
break;
|
||||||
case AssignmentOp::LeftShiftAssignment:
|
case AssignmentOp::LeftShiftAssignment:
|
||||||
lhs_result = m_lhs->execute(interpreter, global_object);
|
EXECUTE_LHS_AND_RHS();
|
||||||
if (interpreter.exception())
|
|
||||||
return {};
|
|
||||||
rhs_result = left_shift(global_object, lhs_result, rhs_result);
|
rhs_result = left_shift(global_object, lhs_result, rhs_result);
|
||||||
break;
|
break;
|
||||||
case AssignmentOp::RightShiftAssignment:
|
case AssignmentOp::RightShiftAssignment:
|
||||||
lhs_result = m_lhs->execute(interpreter, global_object);
|
EXECUTE_LHS_AND_RHS();
|
||||||
if (interpreter.exception())
|
|
||||||
return {};
|
|
||||||
rhs_result = right_shift(global_object, lhs_result, rhs_result);
|
rhs_result = right_shift(global_object, lhs_result, rhs_result);
|
||||||
break;
|
break;
|
||||||
case AssignmentOp::UnsignedRightShiftAssignment:
|
case AssignmentOp::UnsignedRightShiftAssignment:
|
||||||
lhs_result = m_lhs->execute(interpreter, global_object);
|
EXECUTE_LHS_AND_RHS();
|
||||||
if (interpreter.exception())
|
|
||||||
return {};
|
|
||||||
rhs_result = unsigned_right_shift(global_object, lhs_result, rhs_result);
|
rhs_result = unsigned_right_shift(global_object, lhs_result, rhs_result);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -1286,6 +1269,12 @@ Value AssignmentExpression::execute(Interpreter& interpreter, GlobalObject& glob
|
||||||
if (interpreter.exception())
|
if (interpreter.exception())
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
|
if (m_op == AssignmentOp::Assignment) {
|
||||||
|
rhs_result = m_rhs->execute(interpreter, global_object);
|
||||||
|
if (interpreter.exception())
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
if (reference.is_unresolvable()) {
|
if (reference.is_unresolvable()) {
|
||||||
interpreter.vm().throw_exception<ReferenceError>(global_object, ErrorType::InvalidLeftHandAssignment);
|
interpreter.vm().throw_exception<ReferenceError>(global_object, ErrorType::InvalidLeftHandAssignment);
|
||||||
return {};
|
return {};
|
||||||
|
|
|
@ -53,3 +53,38 @@ test("basic functionality", () => {
|
||||||
expect((x >>>= 2)).toBe(2);
|
expect((x >>>= 2)).toBe(2);
|
||||||
expect(x).toBe(2);
|
expect(x).toBe(2);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("evaluation order", () => {
|
||||||
|
for (const op of [
|
||||||
|
"=",
|
||||||
|
"+=",
|
||||||
|
"-=",
|
||||||
|
"*=",
|
||||||
|
"/=",
|
||||||
|
"%=",
|
||||||
|
"**=",
|
||||||
|
"&=",
|
||||||
|
"|=",
|
||||||
|
"^=",
|
||||||
|
"<<=",
|
||||||
|
">>=",
|
||||||
|
">>>=",
|
||||||
|
]) {
|
||||||
|
var a = [];
|
||||||
|
function b() {
|
||||||
|
b.hasBeenCalled = true;
|
||||||
|
throw Error();
|
||||||
|
}
|
||||||
|
function c() {
|
||||||
|
c.hasBeenCalled = true;
|
||||||
|
throw Error();
|
||||||
|
}
|
||||||
|
b.hasBeenCalled = false;
|
||||||
|
c.hasBeenCalled = false;
|
||||||
|
expect(() => {
|
||||||
|
new Function(`a[b()] ${op} c()`)();
|
||||||
|
}).toThrow(Error);
|
||||||
|
expect(b.hasBeenCalled).toBeTrue();
|
||||||
|
expect(c.hasBeenCalled).toBeFalse();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue