Skip to content

Commit

Permalink
[GR-58286] Fix const binding increment, decrement, and compound assig…
Browse files Browse the repository at this point in the history
…nment.
  • Loading branch information
woess committed Sep 20, 2024
1 parent 2b1fd14 commit 9ed5034
Show file tree
Hide file tree
Showing 2 changed files with 50 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2885,15 +2885,19 @@ private JavaScriptNode transformAssignmentIdent(IdentNode identNode, JavaScriptN

// if scopeVar is const, the assignment will never succeed and is only there to perform
// the temporal dead zone check and throw a ReferenceError instead of a TypeError
if (!initializationAssignment && scopeVar.isConst()) {
rhs = checkMutableBinding(rhs, scopeVar.getName());
}
boolean constAssignment = !initializationAssignment && scopeVar.isConst();

if (binaryOp == null) {
if (constAssignment) {
rhs = checkMutableBinding(rhs, scopeVar.getName());
}
return scopeVar.createWriteNode(rhs);
} else {
if (isLogicalOp(binaryOp)) {
assert !convertLHSToNumeric && !returnOldValue;
if (constAssignment) {
rhs = checkMutableBinding(rhs, scopeVar.getName());
}
JavaScriptNode readNode = tagExpression(scopeVar.createReadNode(), identNode);
JavaScriptNode writeNode = scopeVar.createWriteNode(rhs);
return factory.createBinary(context, binaryOp, readNode, writeNode);
Expand All @@ -2913,6 +2917,9 @@ private JavaScriptNode transformAssignmentIdent(IdentNode identNode, JavaScriptN
readNode = prevValueTemp.createWriteNode(readNode);
}
JavaScriptNode binOpNode = tagExpression(factory.createBinary(context, binaryOp, readNode, rhs), identNode);
if (constAssignment) {
binOpNode = checkMutableBinding(binOpNode, scopeVar.getName());
}
JavaScriptNode writeNode = pair.getSecond().apply(binOpNode);
if (returnOldValue) {
return factory.createDual(context, writeNode, prevValueTemp.createReadNode());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl.
*/

/**
* Tests correct immutable binding reassignment handling for `const` variables in the global scope
* via increment (++), decrement (--), and compound assignment operators.
*/

const i = 4; try { i++; fail(); } catch (e) { if (!(e instanceof TypeError)) throw e; }
const j = 4; try { ++j; fail(); } catch (e) { if (!(e instanceof TypeError)) throw e; }
const k = 4; try { k--; fail(); } catch (e) { if (!(e instanceof TypeError)) throw e; }
const l = 4; try { --l; fail(); } catch (e) { if (!(e instanceof TypeError)) throw e; }

// If binding is immutable, compound assignment should fail only after the binary operator (including both its operands) has been executed.
let side_effects = 0;
const m = {toString() { side_effects += 1; return "a"; }};
try {
m += {toString() { side_effects += 2; return "b"; }};
fail();
} catch (e) {
if (!(e instanceof TypeError)) throw e;
}
if (side_effects !== 3) throw new Error(`${side_effects}`);

// TypeError due to mixing BigInt and Number is thrown first.
const n = 1n;
try {
n += 1;
fail();
} catch (e) {
if (!(e instanceof TypeError && e.message.includes("BigInt"))) throw e;
}

function fail() {
throw new Error("Expected a TypeError, but no error was thrown.");
}

0 comments on commit 9ed5034

Please sign in to comment.