8073707: const re-assignment should not reported as a early error
authorhannesw
Fri, 27 Feb 2015 14:33:47 +0100
changeset 29281 8cc2618a07aa
parent 29280 8a79b4040d2a
child 29282 a8523237b66c
8073707: const re-assignment should not reported as a early error Reviewed-by: sundar, attila
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptRuntime.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/Messages.properties
nashorn/test/script/basic/es6/const-reassign.js
nashorn/test/script/basic/es6/const-reassign.js.EXPECTED
nashorn/test/script/basic/es6/let_const_reuse.js.EXPECTED
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java	Fri Feb 27 18:03:18 2015 +0530
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/AssignSymbols.java	Fri Feb 27 14:33:47 2015 +0100
@@ -712,19 +712,8 @@
         return definingFn == function;
     }
 
-    private void checkConstAssignment(final IdentNode ident) {
-        // Check for reassignment of constant
-        final Symbol symbol = ident.getSymbol();
-        if (symbol.isConst()) {
-            throwParserException(ECMAErrors.getMessage("syntax.error.assign.constant", symbol.getName()), ident);
-        }
-    }
-
     @Override
     public Node leaveBinaryNode(final BinaryNode binaryNode) {
-        if (binaryNode.isAssignment() && binaryNode.lhs() instanceof IdentNode) {
-            checkConstAssignment((IdentNode) binaryNode.lhs());
-        }
         switch (binaryNode.tokenType()) {
         case ASSIGN:
             return leaveASSIGN(binaryNode);
@@ -751,9 +740,6 @@
 
     @Override
     public Node leaveUnaryNode(final UnaryNode unaryNode) {
-        if (unaryNode.isAssignment() && unaryNode.getExpression() instanceof IdentNode) {
-            checkConstAssignment((IdentNode) unaryNode.getExpression());
-        }
         switch (unaryNode.tokenType()) {
         case DELETE:
             return leaveDELETE(unaryNode);
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java	Fri Feb 27 18:03:18 2015 +0530
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/CodeGenerator.java	Fri Feb 27 14:33:47 2015 +0100
@@ -343,8 +343,14 @@
     // This is called the temporal dead zone (TDZ). See https://gist.github.com/rwaldron/f0807a758aa03bcdd58a
     private void checkTemporalDeadZone(final IdentNode identNode) {
         if (identNode.isDead()) {
-            method.load(identNode.getSymbol().getName());
-            method.invoke(ScriptRuntime.THROW_REFERENCE_ERROR);
+            method.load(identNode.getSymbol().getName()).invoke(ScriptRuntime.THROW_REFERENCE_ERROR);
+        }
+    }
+
+    // Runtime check for assignment to ES6 const
+    private void checkAssignTarget(final Expression expression) {
+        if (expression instanceof IdentNode && ((IdentNode)expression).getSymbol().isConst()) {
+            method.load(((IdentNode)expression).getSymbol().getName()).invoke(ScriptRuntime.THROW_CONST_TYPE_ERROR);
         }
     }
 
@@ -787,72 +793,84 @@
 
             @Override
             public boolean enterASSIGN(final BinaryNode binaryNode) {
+                checkAssignTarget(binaryNode.lhs());
                 loadASSIGN(binaryNode);
                 return false;
             }
 
             @Override
             public boolean enterASSIGN_ADD(final BinaryNode binaryNode) {
+                checkAssignTarget(binaryNode.lhs());
                 loadASSIGN_ADD(binaryNode);
                 return false;
             }
 
             @Override
             public boolean enterASSIGN_BIT_AND(final BinaryNode binaryNode) {
+                checkAssignTarget(binaryNode.lhs());
                 loadASSIGN_BIT_AND(binaryNode);
                 return false;
             }
 
             @Override
             public boolean enterASSIGN_BIT_OR(final BinaryNode binaryNode) {
+                checkAssignTarget(binaryNode.lhs());
                 loadASSIGN_BIT_OR(binaryNode);
                 return false;
             }
 
             @Override
             public boolean enterASSIGN_BIT_XOR(final BinaryNode binaryNode) {
+                checkAssignTarget(binaryNode.lhs());
                 loadASSIGN_BIT_XOR(binaryNode);
                 return false;
             }
 
             @Override
             public boolean enterASSIGN_DIV(final BinaryNode binaryNode) {
+                checkAssignTarget(binaryNode.lhs());
                 loadASSIGN_DIV(binaryNode);
                 return false;
             }
 
             @Override
             public boolean enterASSIGN_MOD(final BinaryNode binaryNode) {
+                checkAssignTarget(binaryNode.lhs());
                 loadASSIGN_MOD(binaryNode);
                 return false;
             }
 
             @Override
             public boolean enterASSIGN_MUL(final BinaryNode binaryNode) {
+                checkAssignTarget(binaryNode.lhs());
                 loadASSIGN_MUL(binaryNode);
                 return false;
             }
 
             @Override
             public boolean enterASSIGN_SAR(final BinaryNode binaryNode) {
+                checkAssignTarget(binaryNode.lhs());
                 loadASSIGN_SAR(binaryNode);
                 return false;
             }
 
             @Override
             public boolean enterASSIGN_SHL(final BinaryNode binaryNode) {
+                checkAssignTarget(binaryNode.lhs());
                 loadASSIGN_SHL(binaryNode);
                 return false;
             }
 
             @Override
             public boolean enterASSIGN_SHR(final BinaryNode binaryNode) {
+                checkAssignTarget(binaryNode.lhs());
                 loadASSIGN_SHR(binaryNode);
                 return false;
             }
 
             @Override
             public boolean enterASSIGN_SUB(final BinaryNode binaryNode) {
+                checkAssignTarget(binaryNode.lhs());
                 loadASSIGN_SUB(binaryNode);
                 return false;
             }
@@ -1062,6 +1080,7 @@
 
             @Override
             public boolean enterDECINC(final UnaryNode unaryNode) {
+                checkAssignTarget(unaryNode.getExpression());
                 loadDECINC(unaryNode);
                 return false;
             }
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptRuntime.java	Fri Feb 27 18:03:18 2015 +0530
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/ScriptRuntime.java	Fri Feb 27 14:33:47 2015 +0100
@@ -115,6 +115,11 @@
     public static final Call THROW_REFERENCE_ERROR = staticCall(MethodHandles.lookup(), ScriptRuntime.class, "throwReferenceError", void.class, String.class);
 
     /**
+     * Throws a reference error for an undefined variable.
+     */
+    public static final Call THROW_CONST_TYPE_ERROR = staticCall(MethodHandles.lookup(), ScriptRuntime.class, "throwConstTypeError", void.class, String.class);
+
+    /**
      * Used to invalidate builtin names, e.g "Function" mapping to all properties in Function.prototype and Function.prototype itself.
      */
     public static final Call INVALIDATE_RESERVED_BUILTIN_NAME = staticCallNoLookup(ScriptRuntime.class, "invalidateReservedBuiltinName", void.class, String.class);
@@ -403,6 +408,15 @@
     }
 
     /**
+     * Throws a type error for an assignment to a const.
+     *
+     * @param name the const name
+     */
+    public static void throwConstTypeError(final String name) {
+        throw typeError("assign.constant", name);
+    }
+
+    /**
      * Call a script function as a constructor with given args.
      *
      * @param target ScriptFunction object.
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/Messages.properties	Fri Feb 27 18:03:18 2015 +0530
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/Messages.properties	Fri Feb 27 14:33:47 2015 +0100
@@ -167,7 +167,6 @@
 syntax.error.invalid.json=Invalid JSON: {0}
 syntax.error.strict.cant.delete=cannot delete "{0}" in strict mode
 syntax.error.redeclare.variable=Variable "{0}" has already been declared
-syntax.error.assign.constant=Assignment to constant "{0}"
 syntax.error.unprotected.switch.declaration=Unsupported {0} declaration in unprotected switch statement
 
 io.error.cant.write=cannot write "{0}"
--- a/nashorn/test/script/basic/es6/const-reassign.js	Fri Feb 27 18:03:18 2015 +0530
+++ b/nashorn/test/script/basic/es6/const-reassign.js	Fri Feb 27 14:33:47 2015 +0100
@@ -31,144 +31,147 @@
 "use strict";
 
 try {
-    eval('"use strict";\n' +
-        'const x = 2;\n' +
-        'x = 1;\n');
+    const x = 2;
+    x = 1;
+    fail("const assignment didn't throw");
 } catch (e) {
-    print(e.name);
+    print(e);
 }
 
 try {
-    eval('"use strict";\n' +
-        'const x = 2;\n' +
-        'x++;\n');
+    const x = 2;
+    x++;
     fail("const assignment didn't throw");
 } catch (e) {
-    print(e.name);
+    print(e);
 }
 
 try {
-    eval('"use strict";\n' +
-        'const x = 2;\n' +
-        'x--;\n');
+    const x = 2;
+    x--;
     fail("const assignment didn't throw");
 } catch (e) {
-    print(e.name);
+    print(e);
+}
+
+try {
+    const x = 2;
+    ++x;
+    fail("const assignment didn't throw");
+} catch (e) {
+    print(e);
 }
 
 try {
-    eval('"use strict";\n' +
-        'const x = 2;\n' +
-        '++x;\n');
+    const x = 2;
+    --x;
     fail("const assignment didn't throw");
 } catch (e) {
-    print(e.name);
+    print(e);
 }
 
 try {
-    eval('"use strict";\n' +
-        'const x = 2;\n' +
-        '--x;\n');
+    const x = 2;
+    x += 1;
     fail("const assignment didn't throw");
 } catch (e) {
-    print(e.name);
+    print(e);
 }
 
 try {
-    eval('"use strict";\n' +
-        'const x = 2;\n' +
-        'x += 1;\n');
+    const x = 2;
+    x *= 1;
     fail("const assignment didn't throw");
 } catch (e) {
-    print(e.name);
+    print(e);
 }
 
 try {
-    eval('"use strict";\n' +
-        'const x = 2;\n' +
-        'x *= 1;\n');
+    const x = 2;
+    x /= 1;
     fail("const assignment didn't throw");
 } catch (e) {
-    print(e.name);
+    print(e);
 }
 
 try {
-    eval('"use strict";\n' +
-        'const x = 2;\n' +
-        'x /= 1;\n');
+    const x = 2;
+    x %= 1;
     fail("const assignment didn't throw");
 } catch (e) {
-    print(e.name);
+    print(e);
 }
 
 try {
-    eval('"use strict";\n' +
-        'const x = 2;\n' +
-        'x %= 1;\n');
+    const x = 2;
+    x |= 1;
     fail("const assignment didn't throw");
 } catch (e) {
-    print(e.name);
+    print(e);
 }
 
 try {
-    eval('"use strict";\n' +
-        'const x = 2;\n' +
-        'x |= 1;\n');
+    const x = 2;
+    x &= 1;
     fail("const assignment didn't throw");
 } catch (e) {
-    print(e.name);
+    print(e);
 }
 
 try {
-    eval('"use strict";\n' +
-        'const x = 2;\n' +
-        'x &= 1;\n');
+    const x = 2;
+    x ^= 1;
     fail("const assignment didn't throw");
 } catch (e) {
-    print(e.name);
+    print(e);
+}
+
+try {
+    const x = 2;
+    x <<= 1;
+    fail("const assignment didn't throw");
+} catch (e) {
+    print(e);
 }
 
 try {
-    eval('"use strict";\n' +
-        'const x = 2;\n' +
-        'x ^= 1;\n');
+    const x = 2;
+    x >>= 1;
     fail("const assignment didn't throw");
 } catch (e) {
-    print(e.name);
+    print(e);
 }
 
 try {
-    eval('"use strict";\n' +
-        'const x = 2;\n' +
-        'x <<= 1;\n');
+    const x = 2;
+    x >>>= 1;
     fail("const assignment didn't throw");
 } catch (e) {
-    print(e.name);
+    print(e);
 }
 
 try {
-    eval('"use strict";\n' +
-        'const x = 2;\n' +
-        'x >>= 1;\n');
+    const x = 2;
+    delete x;
     fail("const assignment didn't throw");
 } catch (e) {
-    print(e.name);
+    print(e);
 }
 
+const c = 1;
+
 try {
-    eval('"use strict";\n' +
-        'const x = 2;\n' +
-        'x >>>= 1;\n');
+    c = 2;
     fail("const assignment didn't throw");
 } catch (e) {
-    print(e.name);
+    print(e);
 }
 
-try {
-    eval('"use strict";\n' +
-        'const x = 2;\n' +
-        'delete x;\n');
-    fail("const assignment didn't throw");
-} catch (e) {
-    print(e.name);
-}
+(function() {
+    try {
+        c = 2;
+        fail("const assignment didn't throw");
+    } catch (e) {
+        print(e);
+    }
+})();
--- a/nashorn/test/script/basic/es6/const-reassign.js.EXPECTED	Fri Feb 27 18:03:18 2015 +0530
+++ b/nashorn/test/script/basic/es6/const-reassign.js.EXPECTED	Fri Feb 27 14:33:47 2015 +0100
@@ -1,16 +1,18 @@
-SyntaxError
-SyntaxError
-SyntaxError
-SyntaxError
-SyntaxError
-SyntaxError
-SyntaxError
-SyntaxError
-SyntaxError
-SyntaxError
-SyntaxError
-SyntaxError
-SyntaxError
-SyntaxError
-SyntaxError
-SyntaxError
+TypeError: Assignment to constant "x"
+TypeError: Assignment to constant "x"
+TypeError: Assignment to constant "x"
+TypeError: Assignment to constant "x"
+TypeError: Assignment to constant "x"
+TypeError: Assignment to constant "x"
+TypeError: Assignment to constant "x"
+TypeError: Assignment to constant "x"
+TypeError: Assignment to constant "x"
+TypeError: Assignment to constant "x"
+TypeError: Assignment to constant "x"
+TypeError: Assignment to constant "x"
+TypeError: Assignment to constant "x"
+TypeError: Assignment to constant "x"
+TypeError: Assignment to constant "x"
+SyntaxError: cannot delete "x" in strict mode
+TypeError: Assignment to constant "c"
+TypeError: Assignment to constant "c"
--- a/nashorn/test/script/basic/es6/let_const_reuse.js.EXPECTED	Fri Feb 27 18:03:18 2015 +0530
+++ b/nashorn/test/script/basic/es6/let_const_reuse.js.EXPECTED	Fri Feb 27 14:33:47 2015 +0100
@@ -1,8 +1,4 @@
 ReferenceError: "a" is not defined
-SyntaxError: test/script/basic/es6/let_const_reuse.js#35:9<eval>:3:8 Assignment to constant "a"
-        a--
-        ^
-SyntaxError: test/script/basic/es6/let_const_reuse.js#35:9<eval>:3:8 Assignment to constant "a"
-        a--
-        ^
+TypeError: Assignment to constant "a"
+TypeError: Assignment to constant "a"
 ReferenceError: "a" is not defined