Wed, 07 May 2014 14:07:19 +0530
8041697: CompiledScript slower when eval with binding
Reviewed-by: lagergren, attila, hannesw
1.1 --- a/src/jdk/nashorn/api/scripting/NashornScriptEngine.java Tue Feb 11 12:05:22 2014 +0100 1.2 +++ b/src/jdk/nashorn/api/scripting/NashornScriptEngine.java Wed May 07 14:07:19 2014 +0530 1.3 @@ -525,6 +525,31 @@ 1.4 return evalImpl(script, ctxt, getNashornGlobalFrom(ctxt)); 1.5 } 1.6 1.7 + private Object evalImpl(final Context.MultiGlobalCompiledScript mgcs, final ScriptContext ctxt, final Global ctxtGlobal) throws ScriptException { 1.8 + final Global oldGlobal = Context.getGlobal(); 1.9 + final boolean globalChanged = (oldGlobal != ctxtGlobal); 1.10 + try { 1.11 + if (globalChanged) { 1.12 + Context.setGlobal(ctxtGlobal); 1.13 + } 1.14 + 1.15 + final ScriptFunction script = mgcs.getFunction(ctxtGlobal); 1.16 + 1.17 + // set ScriptContext variables if ctxt is non-null 1.18 + if (ctxt != null) { 1.19 + setContextVariables(ctxtGlobal, ctxt); 1.20 + } 1.21 + return ScriptObjectMirror.translateUndefined(ScriptObjectMirror.wrap(ScriptRuntime.apply(script, ctxtGlobal), ctxtGlobal)); 1.22 + } catch (final Exception e) { 1.23 + throwAsScriptException(e, ctxtGlobal); 1.24 + throw new AssertionError("should not reach here"); 1.25 + } finally { 1.26 + if (globalChanged) { 1.27 + Context.setGlobal(oldGlobal); 1.28 + } 1.29 + } 1.30 + } 1.31 + 1.32 private Object evalImpl(final ScriptFunction script, final ScriptContext ctxt, final Global ctxtGlobal) throws ScriptException { 1.33 if (script == null) { 1.34 return null; 1.35 @@ -571,18 +596,38 @@ 1.36 } 1.37 1.38 private CompiledScript asCompiledScript(final Source source) throws ScriptException { 1.39 - final ScriptFunction func = compileImpl(source, context); 1.40 + final Context.MultiGlobalCompiledScript mgcs; 1.41 + final ScriptFunction func; 1.42 + final Global oldGlobal = Context.getGlobal(); 1.43 + final Global newGlobal = getNashornGlobalFrom(context); 1.44 + final boolean globalChanged = (oldGlobal != newGlobal); 1.45 + try { 1.46 + if (globalChanged) { 1.47 + Context.setGlobal(newGlobal); 1.48 + } 1.49 + 1.50 + mgcs = nashornContext.compileScript(source); 1.51 + func = mgcs.getFunction(newGlobal); 1.52 + } catch (final Exception e) { 1.53 + throwAsScriptException(e, newGlobal); 1.54 + throw new AssertionError("should not reach here"); 1.55 + } finally { 1.56 + if (globalChanged) { 1.57 + Context.setGlobal(oldGlobal); 1.58 + } 1.59 + } 1.60 + 1.61 return new CompiledScript() { 1.62 @Override 1.63 public Object eval(final ScriptContext ctxt) throws ScriptException { 1.64 final Global globalObject = getNashornGlobalFrom(ctxt); 1.65 - // Are we running the script in the correct global? 1.66 + // Are we running the script in the same global in which it was compiled? 1.67 if (func.getScope() == globalObject) { 1.68 return evalImpl(func, ctxt, globalObject); 1.69 } 1.70 - // ScriptContext with a different global. Compile again! 1.71 - // Note that we may still hit per-global compilation cache. 1.72 - return evalImpl(compileImpl(source, ctxt), ctxt, globalObject); 1.73 + 1.74 + // different global 1.75 + return evalImpl(mgcs, ctxt, globalObject); 1.76 } 1.77 @Override 1.78 public ScriptEngine getEngine() {
2.1 --- a/src/jdk/nashorn/internal/runtime/Context.java Tue Feb 11 12:05:22 2014 +0100 2.2 +++ b/src/jdk/nashorn/internal/runtime/Context.java Wed May 07 14:07:19 2014 +0530 2.3 @@ -491,6 +491,39 @@ 2.4 } 2.5 2.6 /** 2.7 + * Interface to represent compiled code that can be re-used across many 2.8 + * global scope instances 2.9 + */ 2.10 + public static interface MultiGlobalCompiledScript { 2.11 + /** 2.12 + * Obtain script function object for a specific global scope object. 2.13 + * 2.14 + * @param newGlobal global scope for which function object is obtained 2.15 + * @return script function for script level expressions 2.16 + */ 2.17 + public ScriptFunction getFunction(final Global newGlobal); 2.18 + } 2.19 + 2.20 + /** 2.21 + * Compile a top level script. 2.22 + * 2.23 + * @param source the script source 2.24 + * @return reusable compiled script across many global scopes. 2.25 + */ 2.26 + public MultiGlobalCompiledScript compileScript(final Source source) { 2.27 + final Class<?> clazz = compile(source, this.errors, this._strict); 2.28 + final MethodHandle runMethodHandle = getRunScriptHandle(clazz); 2.29 + final boolean strict = isStrict(clazz); 2.30 + 2.31 + return new MultiGlobalCompiledScript() { 2.32 + @Override 2.33 + public ScriptFunction getFunction(final Global newGlobal) { 2.34 + return Context.getGlobal().newScriptFunction(RUN_SCRIPT.symbolName(), runMethodHandle, newGlobal, strict); 2.35 + } 2.36 + }; 2.37 + } 2.38 + 2.39 + /** 2.40 * Entry point for {@code eval} 2.41 * 2.42 * @param initialScope The scope of this eval call 2.43 @@ -950,14 +983,8 @@ 2.44 return ScriptRuntime.apply(script, thiz); 2.45 } 2.46 2.47 - private static ScriptFunction getRunScriptFunction(final Class<?> script, final ScriptObject scope) { 2.48 - if (script == null) { 2.49 - return null; 2.50 - } 2.51 - 2.52 - // Get run method - the entry point to the script 2.53 - final MethodHandle runMethodHandle = 2.54 - MH.findStatic( 2.55 + private static MethodHandle getRunScriptHandle(final Class<?> script) { 2.56 + return MH.findStatic( 2.57 MethodHandles.lookup(), 2.58 script, 2.59 RUN_SCRIPT.symbolName(), 2.60 @@ -965,15 +992,25 @@ 2.61 Object.class, 2.62 ScriptFunction.class, 2.63 Object.class)); 2.64 + } 2.65 2.66 - boolean strict; 2.67 + private static boolean isStrict(final Class<?> script) { 2.68 + try { 2.69 + return script.getField(STRICT_MODE.symbolName()).getBoolean(null); 2.70 + } catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { 2.71 + return false; 2.72 + } 2.73 + } 2.74 2.75 - try { 2.76 - strict = script.getField(STRICT_MODE.symbolName()).getBoolean(null); 2.77 - } catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { 2.78 - strict = false; 2.79 + private static ScriptFunction getRunScriptFunction(final Class<?> script, final ScriptObject scope) { 2.80 + if (script == null) { 2.81 + return null; 2.82 } 2.83 2.84 + // Get run method - the entry point to the script 2.85 + final MethodHandle runMethodHandle = getRunScriptHandle(script); 2.86 + boolean strict = isStrict(script); 2.87 + 2.88 // Package as a JavaScript function and pass function back to shell. 2.89 return Context.getGlobal().newScriptFunction(RUN_SCRIPT.symbolName(), runMethodHandle, scope, strict); 2.90 }
3.1 --- a/src/jdk/nashorn/tools/Shell.java Tue Feb 11 12:05:22 2014 +0100 3.2 +++ b/src/jdk/nashorn/tools/Shell.java Wed May 07 14:07:19 2014 +0530 3.3 @@ -453,7 +453,7 @@ 3.4 } 3.5 } finally { 3.6 if (globalChanged) { 3.7 - Context.setGlobal(global); 3.8 + Context.setGlobal(oldGlobal); 3.9 } 3.10 } 3.11