8041697: CompiledScript slower when eval with binding
authorsundar
Wed, 07 May 2014 14:07:19 +0530
changeset 24282 2e3bd98c5664
parent 24281 58ed42a1ebc6
child 24283 bda887c0088a
8041697: CompiledScript slower when eval with binding Reviewed-by: lagergren, attila, hannesw
nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngine.java
nashorn/src/jdk/nashorn/internal/runtime/Context.java
nashorn/src/jdk/nashorn/tools/Shell.java
--- a/nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngine.java	Tue May 06 17:54:15 2014 +0530
+++ b/nashorn/src/jdk/nashorn/api/scripting/NashornScriptEngine.java	Wed May 07 14:07:19 2014 +0530
@@ -525,6 +525,31 @@
         return evalImpl(script, ctxt, getNashornGlobalFrom(ctxt));
     }
 
+    private Object evalImpl(final Context.MultiGlobalCompiledScript mgcs, final ScriptContext ctxt, final Global ctxtGlobal) throws ScriptException {
+        final Global oldGlobal = Context.getGlobal();
+        final boolean globalChanged = (oldGlobal != ctxtGlobal);
+        try {
+            if (globalChanged) {
+                Context.setGlobal(ctxtGlobal);
+            }
+
+            final ScriptFunction script = mgcs.getFunction(ctxtGlobal);
+
+            // set ScriptContext variables if ctxt is non-null
+            if (ctxt != null) {
+                setContextVariables(ctxtGlobal, ctxt);
+            }
+            return ScriptObjectMirror.translateUndefined(ScriptObjectMirror.wrap(ScriptRuntime.apply(script, ctxtGlobal), ctxtGlobal));
+        } catch (final Exception e) {
+            throwAsScriptException(e, ctxtGlobal);
+            throw new AssertionError("should not reach here");
+        } finally {
+            if (globalChanged) {
+                Context.setGlobal(oldGlobal);
+            }
+        }
+    }
+
     private Object evalImpl(final ScriptFunction script, final ScriptContext ctxt, final Global ctxtGlobal) throws ScriptException {
         if (script == null) {
             return null;
@@ -571,18 +596,38 @@
     }
 
     private CompiledScript asCompiledScript(final Source source) throws ScriptException {
-        final ScriptFunction func = compileImpl(source, context);
+        final Context.MultiGlobalCompiledScript mgcs;
+        final ScriptFunction func;
+        final Global oldGlobal = Context.getGlobal();
+        final Global newGlobal = getNashornGlobalFrom(context);
+        final boolean globalChanged = (oldGlobal != newGlobal);
+        try {
+            if (globalChanged) {
+                Context.setGlobal(newGlobal);
+            }
+
+            mgcs = nashornContext.compileScript(source);
+            func = mgcs.getFunction(newGlobal);
+        } catch (final Exception e) {
+            throwAsScriptException(e, newGlobal);
+            throw new AssertionError("should not reach here");
+        } finally {
+            if (globalChanged) {
+                Context.setGlobal(oldGlobal);
+            }
+        }
+
         return new CompiledScript() {
             @Override
             public Object eval(final ScriptContext ctxt) throws ScriptException {
                 final Global globalObject = getNashornGlobalFrom(ctxt);
-                // Are we running the script in the correct global?
+                // Are we running the script in the same global in which it was compiled?
                 if (func.getScope() == globalObject) {
                     return evalImpl(func, ctxt, globalObject);
                 }
-                // ScriptContext with a different global. Compile again!
-                // Note that we may still hit per-global compilation cache.
-                return evalImpl(compileImpl(source, ctxt), ctxt, globalObject);
+
+                // different global
+                return evalImpl(mgcs, ctxt, globalObject);
             }
             @Override
             public ScriptEngine getEngine() {
--- a/nashorn/src/jdk/nashorn/internal/runtime/Context.java	Tue May 06 17:54:15 2014 +0530
+++ b/nashorn/src/jdk/nashorn/internal/runtime/Context.java	Wed May 07 14:07:19 2014 +0530
@@ -490,6 +490,39 @@
     }
 
     /**
+     * Interface to represent compiled code that can be re-used across many
+     * global scope instances
+     */
+    public static interface MultiGlobalCompiledScript {
+        /**
+         * Obtain script function object for a specific global scope object.
+         *
+         * @param newGlobal global scope for which function object is obtained
+         * @return script function for script level expressions
+         */
+        public ScriptFunction getFunction(final Global newGlobal);
+    }
+
+    /**
+     * Compile a top level script.
+     *
+     * @param source the script source
+     * @return reusable compiled script across many global scopes.
+     */
+    public MultiGlobalCompiledScript compileScript(final Source source) {
+        final Class<?> clazz = compile(source, this.errors, this._strict);
+        final MethodHandle runMethodHandle = getRunScriptHandle(clazz);
+        final boolean strict = isStrict(clazz);
+
+        return new MultiGlobalCompiledScript() {
+            @Override
+            public ScriptFunction getFunction(final Global newGlobal) {
+                return Context.getGlobal().newScriptFunction(RUN_SCRIPT.symbolName(), runMethodHandle, newGlobal, strict);
+            }
+        };
+    }
+
+    /**
      * Entry point for {@code eval}
      *
      * @param initialScope The scope of this eval call
@@ -949,14 +982,8 @@
         return ScriptRuntime.apply(script, thiz);
     }
 
-    private static ScriptFunction getRunScriptFunction(final Class<?> script, final ScriptObject scope) {
-        if (script == null) {
-            return null;
-        }
-
-        // Get run method - the entry point to the script
-        final MethodHandle runMethodHandle =
-                MH.findStatic(
+    private static MethodHandle getRunScriptHandle(final Class<?> script) {
+        return MH.findStatic(
                     MethodHandles.lookup(),
                     script,
                     RUN_SCRIPT.symbolName(),
@@ -964,14 +991,24 @@
                         Object.class,
                         ScriptFunction.class,
                         Object.class));
+    }
 
-        boolean strict;
-
+    private static boolean isStrict(final Class<?> script) {
         try {
-            strict = script.getField(STRICT_MODE.symbolName()).getBoolean(null);
+            return script.getField(STRICT_MODE.symbolName()).getBoolean(null);
         } catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
-            strict = false;
+            return false;
         }
+    }
+
+    private static ScriptFunction getRunScriptFunction(final Class<?> script, final ScriptObject scope) {
+        if (script == null) {
+            return null;
+        }
+
+        // Get run method - the entry point to the script
+        final MethodHandle runMethodHandle = getRunScriptHandle(script);
+        boolean strict = isStrict(script);
 
         // Package as a JavaScript function and pass function back to shell.
         return Context.getGlobal().newScriptFunction(RUN_SCRIPT.symbolName(), runMethodHandle, scope, strict);
--- a/nashorn/src/jdk/nashorn/tools/Shell.java	Tue May 06 17:54:15 2014 +0530
+++ b/nashorn/src/jdk/nashorn/tools/Shell.java	Wed May 07 14:07:19 2014 +0530
@@ -453,7 +453,7 @@
             }
         } finally {
             if (globalChanged) {
-                Context.setGlobal(global);
+                Context.setGlobal(oldGlobal);
             }
         }