8060688: Nashorn: Generated script class name fails --verify-code for names with special chars
authorsundar
Wed, 15 Oct 2014 19:50:03 +0530
changeset 27106 d2d7e511c05c
parent 27105 ef61cca021aa
child 27107 e973379ef2b5
8060688: Nashorn: Generated script class name fails --verify-code for names with special chars Reviewed-by: jlaskey, hannesw
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Compiler.java
nashorn/test/script/nosecurity/JDK-8060688.js
nashorn/test/src/jdk/nashorn/internal/codegen/CompilerTest.java
nashorn/test/src/jdk/nashorn/internal/runtime/TrustedScriptEngineTest.java
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Compiler.java	Wed Oct 15 16:00:21 2014 +0200
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/codegen/Compiler.java	Wed Oct 15 19:50:03 2014 +0530
@@ -410,10 +410,29 @@
             baseName = baseName + installer.getUniqueScriptId();
         }
 
-        final String mangled = NameCodec.encode(baseName);
+        // ASM's bytecode verifier does not allow JVM allowed safe escapes using '\' as escape char.
+        // While ASM accepts such escapes for method names, field names, it enforces Java identifier
+        // for class names. Workaround that ASM bug here by replacing JVM 'dangerous' chars with '_'
+        // rather than safe encoding using '\'.
+        final String mangled = env._verify_code? replaceDangerChars(baseName) : NameCodec.encode(baseName);
         return mangled != null ? mangled : baseName;
     }
 
+    private static final String DANGEROUS_CHARS   = "\\/.;:$[]<>";
+    private static String replaceDangerChars(final String name) {
+        final int len = name.length();
+        final StringBuilder buf = new StringBuilder();
+        for (int i = 0; i < len; i++) {
+            final char ch = name.charAt(i);
+            if (DANGEROUS_CHARS.indexOf(ch) != -1) {
+                buf.append('_');
+            } else {
+                buf.append(ch);
+            }
+        }
+        return buf.toString();
+    }
+
     private String firstCompileUnitName() {
         final StringBuilder sb = new StringBuilder(SCRIPTS_PACKAGE).
                 append('/').
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/nashorn/test/script/nosecurity/JDK-8060688.js	Wed Oct 15 19:50:03 2014 +0530
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2014, 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-8060688: Nashorn: Generated script class name fails --verify-code for names with special chars
+ *
+ * @test
+ * @run
+ */
+
+var NashornEngineFactory = Java.type("jdk.nashorn.api.scripting.NashornScriptEngineFactory");
+var ScriptEngine = Java.type("javax.script.ScriptEngine");
+var ScriptContext = Java.type("javax.script.ScriptContext");
+
+var factory = new NashornEngineFactory();
+
+var e = factory.getScriptEngine("--verify-code");
+
+function evalAndCheck(code) {
+    try {
+        e.eval(code);
+    } catch (exp) {
+        exp.printStackTrace();
+    }
+}
+
+// check default name
+evalAndCheck("var a = 3");
+// check few names with special chars
+var scontext = e.context;
+scontext.setAttribute(ScriptEngine.FILENAME, "<myscript>", ScriptContext.ENGINE_SCOPE);
+evalAndCheck("var h = 'hello'");
+scontext.setAttribute(ScriptEngine.FILENAME, "[myscript]", ScriptContext.ENGINE_SCOPE);
+evalAndCheck("var foo = 'world'");
+scontext.setAttribute(ScriptEngine.FILENAME, ";/\\$.", ScriptContext.ENGINE_SCOPE);
+evalAndCheck("var foo = 'helloworld'");
--- a/nashorn/test/src/jdk/nashorn/internal/codegen/CompilerTest.java	Wed Oct 15 16:00:21 2014 +0200
+++ b/nashorn/test/src/jdk/nashorn/internal/codegen/CompilerTest.java	Wed Oct 15 19:50:03 2014 +0530
@@ -72,6 +72,7 @@
         options.set("print.parse", true);
         options.set("scripting", true);
         options.set("const.as.var", true);
+        options.set("verify.code", true);
 
         final ErrorManager errors = new ErrorManager() {
             @Override
--- a/nashorn/test/src/jdk/nashorn/internal/runtime/TrustedScriptEngineTest.java	Wed Oct 15 16:00:21 2014 +0200
+++ b/nashorn/test/src/jdk/nashorn/internal/runtime/TrustedScriptEngineTest.java	Wed Oct 15 19:50:03 2014 +0530
@@ -325,4 +325,29 @@
         );
         assertEquals(ret, 10, "Parsed and executed OK");
     }
+
+    @Test
+    public void evalDefaultFileNameTest() throws ScriptException {
+        final NashornScriptEngineFactory fac = new NashornScriptEngineFactory();
+        final ScriptEngine engine = fac.getScriptEngine(new String[] { "--verify-code=true" });
+        // default FILENAME being "<eval>" make sure generated code bytecode verifies.
+        engine.eval("var a = 3;");
+    }
+
+    @Test
+    public void evalFileNameWithSpecialCharsTest() throws ScriptException {
+        final NashornScriptEngineFactory fac = new NashornScriptEngineFactory();
+        final ScriptEngine engine = fac.getScriptEngine(new String[] { "--verify-code=true" });
+        final ScriptContext ctxt = new SimpleScriptContext();
+        // use file name with "dangerous" chars.
+        ctxt.setAttribute(ScriptEngine.FILENAME, "<myscript>", ScriptContext.ENGINE_SCOPE);
+        engine.eval("var a = 3;");
+        ctxt.setAttribute(ScriptEngine.FILENAME, "[myscript]", ScriptContext.ENGINE_SCOPE);
+        engine.eval("var h = 'hello';");
+        ctxt.setAttribute(ScriptEngine.FILENAME, ";/\\$.", ScriptContext.ENGINE_SCOPE);
+        engine.eval("var foo = 'world';");
+        // name used by jjs shell tool for the interactive mode
+        ctxt.setAttribute(ScriptEngine.FILENAME, "<shell>", ScriptContext.ENGINE_SCOPE);
+        engine.eval("var foo = 'world';");
+    }
 }