Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion parser-Java-Js/src/frontend/javascript/parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,22 @@ const visitor = (opts: any) => ({
return seq;
}

return UAST.assignmentExpression(visit(node.left, opts) as UAST.LVal, visit(node.right, opts) as Expression, node.operator as UAST.AssignmentExpression['operator'], false);
const left = visit(node.left, opts) as UAST.LVal;
const right = visit(node.right, opts) as Expression;

// handle ??=, ||=, &&=
// a ??= b to a = a ?? b, similar for others
if (node.operator === '??=') {
return UAST.assignmentExpression(left, UAST.binaryExpression('??', left, right), '=', false);
}
if (node.operator === '||=') {
return UAST.assignmentExpression(left, UAST.binaryExpression('||', left, right), '=', false);
}
if (node.operator === '&&=') {
return UAST.assignmentExpression(left, UAST.binaryExpression('&&', left, right), '=', false);
}
Comment on lines +345 to +353

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The current implementation of logical assignment operators (??=, ||=, &&=) has two significant issues:

  1. Semantic Correctness (Side Effects & Short-circuiting): Desugaring a ??= b to a = a ?? b is not equivalent in JavaScript. It causes the left-hand side to be evaluated twice (e.g., func().prop ??= 1 calls func() twice), which is incorrect if the expression has side effects. Furthermore, logical assignment operators in JS are short-circuiting; a ||= b only performs an assignment if a is falsy. The desugared a = a || b always performs an assignment, which can trigger setters or proxy traps unnecessarily.
  2. UAST Node Reuse: The left node object is reused as both the assignment target and an operand in the binary expression. In most AST/UAST implementations, node objects must be unique instances within the tree. Reusing the same object instance can lead to broken parent pointers and issues during tree traversal or serialization.

If the UAST specification supports these operators, it is highly recommended to pass them directly to UAST.assignmentExpression. If not, consider updating the UAST spec to support them, as correct desugaring is complex and typically requires temporary variables to ensure single evaluation of the LHS.


return UAST.assignmentExpression(left, right, node.operator as UAST.AssignmentExpression['operator'], false);
},
LogicalExpression(node: AST.LogicalExpression): ParseResult<UAST.BinaryExpression> {
return UAST.binaryExpression(node.operator as UAST.BinaryExpression['operator'], visit(node.left, opts) as Expression, visit(node.right, opts) as Expression);
Expand Down