8027302: Identifiers containing unicode escapes are not recognized as reserved words
Reviewed-by: jlaskey, sundar
--- 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);
+}
+