8041697: CompiledScript slower when eval with binding
Reviewed-by: lagergren, attila, hannesw
--- 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);
}
}