8027302: Identifiers containing unicode escapes are not recognized as reserved words
authorhannesw
Fri, 13 Oct 2017 17:25:12 +0200
changeset 47337 079a87f87518
parent 47336 6694369ed3f9
child 47339 186868cadb5d
child 47626 52cf482c4d28
8027302: Identifiers containing unicode escapes are not recognized as reserved words Reviewed-by: jlaskey, sundar
src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/IdentNode.java
src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Lexer.java
src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java
src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/Messages.properties
test/nashorn/script/basic/JDK-8027302.js
test/nashorn/script/basic/es6/JDK-8027302.js
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/IdentNode.java	Fri Oct 13 15:22:02 2017 +0200
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/ir/IdentNode.java	Fri Oct 13 17:25:12 2017 +0200
@@ -477,4 +477,14 @@
     public IdentNode setIsDestructuredParameter() {
         return new IdentNode(this, name, type, flags | DESTRUCTURED_PARAMETER, programPoint, conversion);
     }
+
+    /**
+     * Checks whether the source code for this ident contains a unicode escape sequence by comparing
+     * the length of its name with its length in source code.
+     *
+     * @return true if ident source contains a unicode escape sequence
+     */
+    public boolean containsEscapes() {
+        return Token.descLength(getToken()) != name.length();
+    }
 }
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Lexer.java	Fri Oct 13 15:22:02 2017 +0200
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Lexer.java	Fri Oct 13 17:25:12 2017 +0200
@@ -786,15 +786,9 @@
             if (ch0 == '\\' && ch1 == 'u') {
                 skip(2);
                 final int ch = hexSequence(4, TokenType.IDENT);
-                if (isWhitespace((char)ch)) {
-                    return null;
-                }
-                if (ch < 0) {
-                    sb.append('\\');
-                    sb.append('u');
-                } else {
-                    sb.append((char)ch);
-                }
+                assert ! isWhitespace((char)ch);
+                assert ch >= 0;
+                sb.append((char)ch);
             } else {
                 // Add regular character.
                 sb.append(ch0);
@@ -994,9 +988,6 @@
             if (ch0 == '\\') {
                 type = ESCSTRING;
                 skip(1);
-                if (! isEscapeCharacter(ch0)) {
-                    error(Lexer.message("invalid.escape.char"), STRING, position, limit);
-                }
                 if (isEOL(ch0)) {
                     // Multiline string literal
                     skipEOL(false);
@@ -1093,9 +1084,6 @@
             } else if (ch0 == '\\') {
                 skip(1);
                 // EscapeSequence
-                if (!isEscapeCharacter(ch0)) {
-                    error(Lexer.message("invalid.escape.char"), TEMPLATE, position, limit);
-                }
                 if (isEOL(ch0)) {
                     // LineContinuation
                     skipEOL(false);
@@ -1115,16 +1103,6 @@
     }
 
     /**
-     * Is the given character a valid escape char after "\" ?
-     *
-     * @param ch character to be checked
-     * @return if the given character is valid after "\"
-     */
-    protected boolean isEscapeCharacter(final char ch) {
-        return true;
-    }
-
-    /**
      * Convert string to number.
      *
      * @param valueString  String to convert.
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java	Fri Oct 13 15:22:02 2017 +0200
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/parser/Parser.java	Fri Oct 13 17:25:12 2017 +0200
@@ -1472,12 +1472,7 @@
      */
     private void verifyIdent(final IdentNode ident, final String contextString) {
         verifyStrictIdent(ident, contextString);
-        if (isES6()) {
-            final TokenType tokenType = TokenLookup.lookupKeyword(ident.getName().toCharArray(), 0, ident.getName().length());
-            if (tokenType != IDENT && tokenType.getKind() != TokenKind.FUTURESTRICT) {
-                throw error(expectMessage(IDENT));
-            }
-        }
+        checkEscapedKeyword(ident);
     }
 
     /**
@@ -1502,6 +1497,18 @@
         }
     }
 
+    /**
+     * ES6 11.6.2: A code point in a ReservedWord cannot be expressed by a | UnicodeEscapeSequence.
+     */
+    private void checkEscapedKeyword(final IdentNode ident) {
+        if (isES6() && ident.containsEscapes()) {
+            final TokenType tokenType = TokenLookup.lookupKeyword(ident.getName().toCharArray(), 0, ident.getName().length());
+            if (tokenType != IDENT && !(tokenType.getKind() == TokenKind.FUTURESTRICT && !isStrictMode)) {
+                throw error(AbstractParser.message("keyword.escaped.character"), ident.getToken());
+            }
+        }
+    }
+
     /*
      * VariableStatement :
      *      var VariableDeclarationList ;
@@ -2646,7 +2653,7 @@
                     });
                 } else {
                     // ECMA 12.4.1 strict mode restrictions
-                    verifyStrictIdent((IdentNode) exception, "catch argument");
+                    verifyIdent((IdentNode) exception, "catch argument");
                 }
 
 
@@ -2761,6 +2768,7 @@
                 break;
             }
             detectSpecialProperty(ident);
+            checkEscapedKeyword(ident);
             return ident;
         case OCTAL_LEGACY:
             if (isStrictMode) {
@@ -3404,6 +3412,7 @@
             // Catch special functions.
             if (lhs instanceof IdentNode) {
                 detectSpecialFunction((IdentNode)lhs);
+                checkEscapedKeyword((IdentNode)lhs);
             }
 
             lhs = new CallNode(callLine, callToken, finish, lhs, arguments, false);
@@ -3779,7 +3788,7 @@
                 expect(IDENT);
             }
             name = getIdent();
-            verifyStrictIdent(name, "function name");
+            verifyIdent(name, "function name");
         } else if (isStatement) {
             // Nashorn extension: anonymous function statements.
             // Do not allow anonymous function statement if extensions
@@ -4871,7 +4880,7 @@
         final String contextString = "function parameter";
         if (param instanceof IdentNode) {
             final IdentNode ident = (IdentNode)param;
-            verifyStrictIdent(ident, contextString);
+            verifyIdent(ident, contextString);
             final ParserContextFunctionNode currentFunction = lc.getCurrentFunction();
             if (currentFunction != null) {
                 currentFunction.addParameterBinding(ident);
--- a/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/Messages.properties	Fri Oct 13 15:22:02 2017 +0200
+++ b/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/resources/Messages.properties	Fri Oct 13 17:25:12 2017 +0200
@@ -62,6 +62,7 @@
 parser.error.trailing.comma.in.json=Trailing comma is not allowed in JSON
 parser.error.missing.const.assignment=Missing assignment to constant "{0}"
 parser.error.unterminated.template.expression=Expected } after expression in template literal
+parser.error.keyword.escaped.character=Keyword must not contain escaped characters
 
 # ES6 mode error messages
 parser.error.multiple.constructors=Class contains more than one constructor
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/nashorn/script/basic/JDK-8027302.js	Fri Oct 13 17:25:12 2017 +0200
@@ -0,0 +1,160 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ * 
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ * 
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ * 
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8027302: Identifiers containing unicode escapes are not recognized as reserved words
+ *
+ * @test
+ * @run
+ */
+
+// keywords containing escapes
+
+try {
+    eval("v\\u0061r i;");
+    fail("Expected error");
+} catch (e) {
+    Assert.assertTrue(e instanceof SyntaxError);
+}
+
+try {
+    eval("\\u0069f (true) ;");
+    fail("Expected error");
+} catch (e) {
+    Assert.assertTrue(e instanceof ReferenceError); // no SyntaxError in ES5
+}
+
+try {
+    eval("if (true) ; \\u0065lse ;");
+    fail("Expected error");
+} catch (e) {
+    Assert.assertTrue(e instanceof ReferenceError); // no SyntaxError in ES5
+}
+
+try {
+    eval("f\\u0075nction x() {}");
+    fail("Expected error");
+} catch (e) {
+    Assert.assertTrue(e instanceof SyntaxError);
+}
+
+try {
+    eval("var f = f\\u0075nction() {}");
+    fail("Expected error");
+} catch (e) {
+    Assert.assertTrue(e instanceof SyntaxError);
+}
+
+try {
+    eval("var o = { f: f\\u0075nction() {}}");
+    fail("Expected error");
+} catch (e) {
+    Assert.assertTrue(e instanceof SyntaxError);
+}
+
+try {
+    eval("var a = [f\\u0075nction() {}]");
+    fail("Expected error");
+} catch (e) {
+    Assert.assertTrue(e instanceof SyntaxError);
+}
+
+// keywords as identifiers, with and without escapes
+
+try {
+    eval("function break() {}");
+    fail("Expected error");
+} catch (e) {
+    Assert.assertTrue(e instanceof SyntaxError);
+}
+
+try {
+    eval("function bre\\u0061k() {}");
+} catch (e) {
+    fail("Unexpected error");
+}
+
+try {
+    eval("function f(break) {}");
+    fail("Expected error");
+} catch (e) {
+    Assert.assertTrue(e instanceof SyntaxError);
+}
+
+try {
+    eval("function f(bre\\u0061k) {}");
+} catch (e) {
+    fail("Unexpected error");
+}
+
+try {
+    eval("var break = 3");
+    fail("Expected error");
+} catch (e) {
+    Assert.assertTrue(e instanceof SyntaxError);
+}
+
+try {
+    eval("'use strict'; var break = 3");
+    fail("Expected error");
+} catch (e) {
+    Assert.assertTrue(e instanceof SyntaxError);
+}
+
+try {
+    eval("var bre\\u0061k = 3");
+} catch (e) {
+    fail("Unexpected error");
+}
+
+try {
+    eval("'use strict'; var bre\\u0061k = 3");
+} catch (e) {
+    fail("Unexpected error");
+}
+
+try {
+    eval("var package = 3");
+} catch (e) {
+    fail("Unexpected error");
+}
+
+try {
+    eval("'use strict'; var package = 3");
+    fail("Expected error");
+} catch (e) {
+    Assert.assertTrue(e instanceof SyntaxError);
+}
+
+try {
+    eval("var p\\u0061ckage = 3");
+} catch (e) {
+    fail("Unexpected error");
+}
+
+try {
+    eval("'use strict'; var p\\u0061ckage = 3");
+} catch (e) {
+    fail("Unexpected error");
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/nashorn/script/basic/es6/JDK-8027302.js	Fri Oct 13 17:25:12 2017 +0200
@@ -0,0 +1,166 @@
+/*
+ * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ * 
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ * 
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ * 
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ * 
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * JDK-8027302: Identifiers containing unicode escapes are not recognized as reserved words
+ *
+ * @test
+ * @run
+ * @option --language=es6
+ */
+
+// keywords containing escapes
+
+try {
+    eval("v\\u0061r i;");
+    fail("Expected error");
+} catch (e) {
+    Assert.assertTrue(e instanceof SyntaxError);
+}
+
+try {
+    eval("\\u0069f (true) ;");
+    fail("Expected error");
+} catch (e) {
+    Assert.assertTrue(e instanceof SyntaxError);
+}
+
+try {
+    eval("if (true) ; \\u0065lse ;");
+    fail("Expected error");
+} catch (e) {
+    Assert.assertTrue(e instanceof SyntaxError);
+}
+
+try {
+    eval("f\\u0075nction x() {}");
+    fail("Expected error");
+} catch (e) {
+    Assert.assertTrue(e instanceof SyntaxError);
+}
+
+try {
+    eval("var f = f\\u0075nction() {}");
+    fail("Expected error");
+} catch (e) {
+    Assert.assertTrue(e instanceof SyntaxError);
+}
+
+try {
+    eval("var o = { f: f\\u0075nction() {}}");
+    fail("Expected error");
+} catch (e) {
+    Assert.assertTrue(e instanceof SyntaxError);
+}
+
+try {
+    eval("var a = [f\\u0075nction() {}]");
+    fail("Expected error");
+} catch (e) {
+    Assert.assertTrue(e instanceof SyntaxError);
+}
+
+// keywords as identifiers, with and without escapes
+
+try {
+    eval("function break() {}");
+    fail("Expected error");
+} catch (e) {
+    Assert.assertTrue(e instanceof SyntaxError);
+}
+
+try {
+    eval("function bre\\u0061k() {}");
+    fail("Expected error");
+} catch (e) {
+    Assert.assertTrue(e instanceof SyntaxError);
+}
+
+try {
+    eval("function f(break) {}");
+    fail("Expected error");
+} catch (e) {
+    Assert.assertTrue(e instanceof SyntaxError);
+}
+
+try {
+    eval("function f(bre\\u0061k) {}");
+    fail("Expected error");
+} catch (e) {
+    Assert.assertTrue(e instanceof SyntaxError);
+}
+
+try {
+    eval("var break = 3");
+    fail("Expected error");
+} catch (e) {
+    Assert.assertTrue(e instanceof SyntaxError);
+}
+
+try {
+    eval("'use strict'; var break = 3");
+    fail("Expected error");
+} catch (e) {
+    Assert.assertTrue(e instanceof SyntaxError);
+}
+
+try {
+    eval("var bre\\u0061k = 3");
+    fail("Expected error");
+} catch (e) {
+    Assert.assertTrue(e instanceof SyntaxError);
+}
+
+try {
+    eval("'use strict'; var bre\\u0061k = 3");
+    fail("Expected error");
+} catch (e) {
+    Assert.assertTrue(e instanceof SyntaxError);
+}
+
+try {
+    eval("var package = 3");
+} catch (e) {
+    fail("Unexpected error");
+}
+
+try {
+    eval("'use strict'; var package = 3");
+    fail("Expected error");
+} catch (e) {
+    Assert.assertTrue(e instanceof SyntaxError);
+}
+
+try {
+    eval("var p\\u0061ckage = 3");
+} catch (e) {
+    fail("Unexpected error");
+}
+
+try {
+    eval("'use strict'; var p\\u0061ckage = 3");
+    fail("Expected error");
+} catch (e) {
+    Assert.assertTrue(e instanceof SyntaxError);
+}
+