8041697: CompiledScript slower when eval with binding

Reviewed-by: lagergren, attila, hannesw
This commit is contained in:
Athijegannathan Sundararajan 2014-05-07 14:07:19 +05:30
parent f96b4d2e4e
commit b705fef9d6
3 changed files with 100 additions and 18 deletions

View File

@ -525,6 +525,31 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
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 @@ public final class NashornScriptEngine extends AbstractScriptEngine implements C
}
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() {

View File

@ -489,6 +489,39 @@ public final class Context {
return compileScript(source, scope, this.errors);
}
/**
* 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}
*
@ -949,14 +982,8 @@ public final class Context {
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 @@ public final class Context {
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);

View File

@ -453,7 +453,7 @@ public class Shell {
}
} finally {
if (globalChanged) {
Context.setGlobal(global);
Context.setGlobal(oldGlobal);
}
}