in src/main/java/org/apache/sling/scripting/javascript/internal/RhinoJavaScriptEngine.java [235:353]
public Object eval(ScriptContext scriptContext) throws ScriptException {
Bindings bindings = scriptContext.getBindings(ScriptContext.ENGINE_SCOPE);
// container for replaced properties
Map<String, Object> replacedProperties = null;
Scriptable scope = null;
boolean isTopLevelCall = false;
// create a rhino Context and execute the script
try {
final Context rhinoContext = Context.enter();
rhinoContext.setLanguageVersion(((RhinoJavaScriptEngineFactory) getFactory()).rhinoLanguageVersion());
rhinoContext.setOptimizationLevel(optimizationLevel());
if (ScriptRuntime.hasTopCall(rhinoContext)) {
// reuse the top scope if we are included
scope = ScriptRuntime.getTopCallScope(rhinoContext);
} else {
// create the request top scope, use the ImporterToplevel here
// to support the importPackage and importClasses functions
scope = new ImporterTopLevel();
// Set the global scope to be our prototype
scope.setPrototype(rootScope);
// We want "scope" to be a new top-level scope, so set its
// parent scope to null. This means that any variables created
// by assignments will be properties of "scope".
scope.setParentScope(null);
// setup the context for use
WrapFactory wrapFactory = ((RhinoJavaScriptEngineFactory) getFactory()).getWrapFactory();
rhinoContext.setWrapFactory(wrapFactory);
// this is the top level call
isTopLevelCall = true;
}
// add initial properties to the scope
replacedProperties = setBoundProperties(scope, bindings);
Object result = script.exec(rhinoContext, scope);
if (result instanceof Wrapper) {
result = ((Wrapper) result).unwrap();
}
return (result instanceof Undefined) ? null : result;
} catch (JavaScriptException t) {
// prevent variables to be pushed back in case of errors
isTopLevelCall = false;
final ScriptException se = new ScriptException(t.details(), t.sourceName(), t.lineNumber());
// log the script stack trace
((Logger) bindings.get(SlingBindings.LOG)).error(t.getScriptStackTrace());
// set the exception cause
Object value = t.getValue();
if (value != null) {
if (value instanceof Wrapper) {
value = ((Wrapper) value).unwrap();
}
if (value instanceof Throwable) {
se.initCause((Throwable) value);
}
}
// if the cause could not be set, overwrite the stack trace
if (se.getCause() == null) {
se.setStackTrace(t.getStackTrace());
}
throw se;
} catch (Throwable t) {
// prevent variables to be pushed back in case of errors
isTopLevelCall = false;
String scriptName = getScriptName(scriptContext);
final ScriptException se =
new ScriptException("Failure running script " + scriptName + ": " + t.getMessage());
se.initCause(t);
throw se;
} finally {
// if we are the top call (the Context is now null) we have to
// play back any properties from the scope back to the bindings
if (isTopLevelCall) {
getBoundProperties(scope, bindings);
}
// if properties have been replaced, reset them
resetBoundProperties(scope, replacedProperties);
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if (classLoader instanceof DynamicClassLoader) {
DynamicClassLoader dynamicClassLoader = (DynamicClassLoader) classLoader;
if (!dynamicClassLoader.isLive()) {
/**
* if the class loader on this thread is a dynamic class loader and it's dirty we should clear Rhino's class cache
* to avoid class loader leaks
*/
if (scope != null) {
ClassCache classCache = ClassCache.get(scope);
classCache.clearCaches();
LOGGER.info(
"Detected dirty class loader on thread {}. Emptying Rhino's class cache.",
Thread.currentThread().getName());
}
}
}
Context.exit();
}
}