Skip to content

Commit

Permalink
Implement nullish assignment operator ??=
Browse files Browse the repository at this point in the history
  • Loading branch information
andreabergia committed Oct 23, 2024
1 parent 6f29f9f commit 03a2e28
Show file tree
Hide file tree
Showing 7 changed files with 41 additions and 14 deletions.
3 changes: 3 additions & 0 deletions rhino/src/main/java/org/mozilla/javascript/IRFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -2152,6 +2152,9 @@ private Node createAssignment(int assignType, Node left, Node right) {
case Token.ASSIGN_EXP:
assignOp = Token.EXP;
break;
case Token.ASSIGN_NULLISH:
assignOp = Token.NULLISH_COALESCING;
break;
default:
throw Kit.codeBug();
}
Expand Down
2 changes: 2 additions & 0 deletions rhino/src/main/java/org/mozilla/javascript/Node.java
Original file line number Diff line number Diff line change
Expand Up @@ -944,6 +944,8 @@ public boolean hasSideEffects() {
case Token.ASSIGN_LSH:
case Token.ASSIGN_RSH:
case Token.ASSIGN_URSH:
case Token.ASSIGN_EXP:
case Token.ASSIGN_NULLISH:
case Token.ENTERWITH:
case Token.LEAVEWITH:
case Token.RETURN:
Expand Down
7 changes: 5 additions & 2 deletions rhino/src/main/java/org/mozilla/javascript/Token.java
Original file line number Diff line number Diff line change
Expand Up @@ -148,9 +148,10 @@ public static enum CommentType {
ASSIGN_MUL = ASSIGN_SUB + 1, // *=
ASSIGN_DIV = ASSIGN_MUL + 1, // /=
ASSIGN_MOD = ASSIGN_DIV + 1, // %=
ASSIGN_EXP = ASSIGN_MOD + 1; // **=
ASSIGN_EXP = ASSIGN_MOD + 1, // **=
ASSIGN_NULLISH = ASSIGN_EXP + 1; // ??=
public static final int FIRST_ASSIGN = ASSIGN,
LAST_ASSIGN = ASSIGN_EXP,
LAST_ASSIGN = ASSIGN_NULLISH,
HOOK = LAST_ASSIGN + 1, // conditional (?:)
COLON = HOOK + 1,
OR = COLON + 1, // logical or (||)
Expand Down Expand Up @@ -468,6 +469,8 @@ public static String typeToName(int token) {
return "ASSIGN_MOD";
case ASSIGN_EXP:
return "ASSIGN_EXP";
case ASSIGN_NULLISH:
return "ASSIGN_NULLISH";
case HOOK:
return "HOOK";
case COLON:
Expand Down
3 changes: 3 additions & 0 deletions rhino/src/main/java/org/mozilla/javascript/TokenStream.java
Original file line number Diff line number Diff line change
Expand Up @@ -1156,6 +1156,9 @@ && peekChar() == '!'
}
ungetChar('.');
} else if (matchChar('?')) {
if (matchChar('=')) {
return Token.ASSIGN_NULLISH;
}
return Token.NULLISH_COALESCING;
}
}
Expand Down
3 changes: 3 additions & 0 deletions rhino/src/main/java/org/mozilla/javascript/ast/AstNode.java
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ public abstract class AstNode extends Node implements Comparable<AstNode> {
operatorNames.put(Token.ASSIGN_MOD, "%=");
operatorNames.put(Token.ASSIGN_BITXOR, "^=");
operatorNames.put(Token.ASSIGN_EXP, "**=");
operatorNames.put(Token.ASSIGN_NULLISH, "??=");
operatorNames.put(Token.VOID, "void");

StringBuilder sb = new StringBuilder();
Expand Down Expand Up @@ -374,6 +375,8 @@ public boolean hasSideEffects() {
case Token.ASSIGN_RSH:
case Token.ASSIGN_SUB:
case Token.ASSIGN_URSH:
case Token.ASSIGN_EXP:
case Token.ASSIGN_NULLISH:
case Token.BLOCK:
case Token.BREAK:
case Token.CALL:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -72,4 +72,25 @@ public void testNullishCoalescingDoesNotLeakVariables() {
String script = "$0 = false; true ?? true; $0";
Utils.assertWithAllOptimizationLevelsES6(false, script);
}

@Test
public void testNullishAssignmentRequiresES6() {
Utils.runWithAllOptimizationLevels(
cx -> {
Scriptable scope = cx.initStandardObjects();
assertThrows(
EvaluatorException.class,
() ->
cx.evaluateString(
scope, "a = true; a ??= false", "test.js", 0, null));
return null;
});
}

@Test
public void testNullishAssignment() {
Utils.assertWithAllOptimizationLevelsES6(true, "a = true; a ??= false; a");
Utils.assertWithAllOptimizationLevelsES6(false, "a = undefined; a ??= false; a");
Utils.assertWithAllOptimizationLevelsES6(false, "a = null; a ??= false; a");
}
}
16 changes: 4 additions & 12 deletions tests/testsrc/test262.properties
Original file line number Diff line number Diff line change
Expand Up @@ -4891,7 +4891,7 @@ language/expressions/less-than-or-equal 0/47 (0.0%)
language/expressions/logical-and 1/18 (5.56%)
tco-right.js {unsupported: [tail-call-optimization]}

language/expressions/logical-assignment 53/78 (67.95%)
language/expressions/logical-assignment 46/78 (58.97%)
left-hand-side-private-reference-accessor-property-and.js {unsupported: [class-fields-private]}
left-hand-side-private-reference-accessor-property-nullish.js {unsupported: [class-fields-private]}
left-hand-side-private-reference-accessor-property-or.js {unsupported: [class-fields-private]}
Expand Down Expand Up @@ -4922,21 +4922,14 @@ language/expressions/logical-assignment 53/78 (67.95%)
lgcl-and-assignment-operator-non-extensible.js strict
lgcl-and-assignment-operator-non-simple-lhs.js
lgcl-and-assignment-operator-non-writeable.js strict
lgcl-nullish-assignment-operator.js
lgcl-nullish-assignment-operator-bigint.js
lgcl-nullish-arguments-strict.js strict
lgcl-nullish-assignment-operator-lhs-before-rhs.js
lgcl-nullish-assignment-operator-namedevaluation-arrow-function.js
lgcl-nullish-assignment-operator-namedevaluation-class-expression.js
lgcl-nullish-assignment-operator-namedevaluation-function.js
lgcl-nullish-assignment-operator-no-set.js strict
lgcl-nullish-assignment-operator-no-set-put.js strict
lgcl-nullish-assignment-operator-non-extensible.js strict
lgcl-nullish-assignment-operator-non-simple-lhs.js
lgcl-nullish-assignment-operator-non-writeable.js strict
lgcl-nullish-assignment-operator-non-writeable-put.js strict
lgcl-nullish-assignment-operator-unresolved-lhs.js strict
lgcl-nullish-assignment-operator-unresolved-rhs.js
lgcl-nullish-assignment-operator-unresolved-rhs-put.js
lgcl-nullish-whitespace.js
lgcl-or-arguments-strict.js strict
lgcl-or-assignment-operator-lhs-before-rhs.js
lgcl-or-assignment-operator-namedevaluation-arrow-function.js
Expand Down Expand Up @@ -5002,7 +4995,7 @@ language/expressions/new 41/59 (69.49%)

~language/expressions/new.target

language/expressions/object 790/1169 (67.58%)
language/expressions/object 789/1169 (67.49%)
dstr/async-gen-meth-ary-init-iter-close.js {unsupported: [async-iteration, async]}
dstr/async-gen-meth-ary-init-iter-get-err.js {unsupported: [async-iteration]}
dstr/async-gen-meth-ary-init-iter-get-err-array-prototype.js {unsupported: [async-iteration]}
Expand Down Expand Up @@ -5736,7 +5729,6 @@ language/expressions/object 790/1169 (67.58%)
accessor-name-literal-numeric-octal.js {strict: [-1], non-strict: [-1]}
computed-__proto__.js
concise-generator.js
cpn-obj-lit-computed-property-name-from-assignment-expression-coalesce.js
cpn-obj-lit-computed-property-name-from-async-arrow-function-expression.js
cpn-obj-lit-computed-property-name-from-await-expression.js {unsupported: [module, async]}
cpn-obj-lit-computed-property-name-from-yield-expression.js
Expand Down

0 comments on commit 03a2e28

Please sign in to comment.