From 96007eec2c0c380a48fc50c33f46269aac4c96ff Mon Sep 17 00:00:00 2001 From: "satish.srinivasan" Date: Thu, 12 Sep 2024 19:47:50 +0530 Subject: [PATCH] FIX: Evaluation of ?. --- .../org/mozilla/javascript/IRFactory.java | 52 +++++++++++-------- .../tests/NullishCoalescingOpTest.java | 19 +++++++ 2 files changed, 49 insertions(+), 22 deletions(-) diff --git a/rhino/src/main/java/org/mozilla/javascript/IRFactory.java b/rhino/src/main/java/org/mozilla/javascript/IRFactory.java index 25faea2892..9733a7e205 100644 --- a/rhino/src/main/java/org/mozilla/javascript/IRFactory.java +++ b/rhino/src/main/java/org/mozilla/javascript/IRFactory.java @@ -758,7 +758,36 @@ private Node transformIf(IfStatement n) { private Node transformInfix(InfixExpression node) { Node left = transform(node.getLeft()); Node right = transform(node.getRight()); - return createBinary(node.getType(), left, right); + if (node.getType() == Token.NULLISH_COALESCING) { + return transformNullishCoalescing(left, right, node); + } else { + return createBinary(node.getType(), left, right); + } + } + + private Node transformNullishCoalescing(Node left, Node right, Node parent) { + String tempName = parser.currentScriptOrFn.getNextTempName(); + + Node nullNode = new Node(Token.NULL); + Node undefinedNode = new Name(0, "undefined"); + + Node conditional = + new Node( + Token.OR, + new Node(Token.SHEQ, nullNode, parser.createName(tempName)), + new Node(Token.SHEQ, undefinedNode, parser.createName(tempName))); + + Node hookNode = + new Node( + Token.HOOK, + /* left= */ conditional, + /* mid= */ right, + /* right= */ parser.createName(tempName)); + + return createBinary( + Token.COMMA, + createAssignment(Token.ASSIGN, parser.createName(tempName), left), + hookNode); } private Node transformLabeledStatement(LabeledStatement ls) { @@ -2038,27 +2067,6 @@ private static Node createBinary(int nodeType, Node left, Node right) { } break; } - - case Token.NULLISH_COALESCING: - { - // foo ?? default => - // (foo == undefined || foo == null) ? foo (left) : default (right) - - Node undefinedNode = new Name(0, "undefined"); - Node nullNode = new Node(Token.NULL); - - Node conditional = - new Node( - Token.OR, - new Node(Token.SHEQ, nullNode, left), - new Node(Token.SHEQ, undefinedNode, left)); - - return new Node( - Token.HOOK, - /* left= */ conditional, - /* mid= */ right, - /* right= */ left); - } } return new Node(nodeType, left, right); diff --git a/tests/src/test/java/org/mozilla/javascript/tests/NullishCoalescingOpTest.java b/tests/src/test/java/org/mozilla/javascript/tests/NullishCoalescingOpTest.java index 861a5d9364..8447f5af62 100644 --- a/tests/src/test/java/org/mozilla/javascript/tests/NullishCoalescingOpTest.java +++ b/tests/src/test/java/org/mozilla/javascript/tests/NullishCoalescingOpTest.java @@ -56,4 +56,23 @@ public void testNullishColascingPrecedence() { return null; }); } + + @Test + public void testNullishColascingEvalOnce() { + Utils.runWithAllOptimizationLevels( + cx -> { + Scriptable scope = cx.initStandardObjects(); + cx.setLanguageVersion(Context.VERSION_ES6); + + String script1 = + "var runce = 0; \n" + + "function f() { runce++; return 3; } \n" + + "var eval1 = f() ?? 42; \n" + + "runce"; + Assert.assertEquals( + 1, + cx.evaluateString(scope, script1, "nullish coalescing basic", 0, null)); + return null; + }); + } }