blob: 6e04247b18d8f52356ab7b8881de9571c7411c55 [file] [log] [blame]
/*
* Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package jdk.nashorn.internal.runtime;
import static jdk.nashorn.internal.codegen.CompilerConstants.RUN_SCRIPT;
import static jdk.nashorn.internal.codegen.CompilerConstants.STRICT_MODE;
import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
import static jdk.nashorn.internal.runtime.linker.Lookup.MH;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.AccessController;
import java.security.CodeSigner;
import java.security.CodeSource;
import java.security.PrivilegedAction;
import java.util.Locale;
import java.util.TimeZone;
import jdk.internal.org.objectweb.asm.ClassReader;
import jdk.internal.org.objectweb.asm.util.CheckClassAdapter;
import jdk.nashorn.internal.codegen.ClassEmitter;
import jdk.nashorn.internal.codegen.Compiler;
import jdk.nashorn.internal.codegen.Namespace;
import jdk.nashorn.internal.codegen.objects.ObjectClassGenerator;
import jdk.nashorn.internal.runtime.linker.JavaAdapterFactory;
import jdk.nashorn.internal.runtime.linker.NashornCallSiteDescriptor;
import jdk.nashorn.internal.runtime.options.KeyValueOption;
import jdk.nashorn.internal.runtime.options.Option;
import jdk.nashorn.internal.runtime.options.Options;
import sun.reflect.Reflection;
/**
* This class manages the global state of execution. Context is immutable.
*/
public final class Context {
/** Is Context global debug mode enabled ? */
public static final boolean DEBUG = Options.getBooleanProperty("nashorn.debug");
private static final ThreadLocal<ScriptObject> currentGlobal =
new ThreadLocal<ScriptObject>() {
@Override
protected ScriptObject initialValue() {
return null;
}
};
/**
* Get the current global scope
* @return the current global scope
*/
public static ScriptObject getGlobal() {
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
// skip getCallerClass and getGlobal and get to the real caller
Class<?> caller = Reflection.getCallerClass(2);
ClassLoader callerLoader = caller.getClassLoader();
// Allow this method only for nashorn's own classes, script
// generated classes and Java adapter classes. Rest should
// have the necessary security permission.
if (callerLoader != myLoader &&
!(callerLoader instanceof NashornLoader) &&
!(JavaAdapterFactory.isAdapterClass(caller))) {
sm.checkPermission(new RuntimePermission("getNashornGlobal"));
}
}
return getGlobalTrusted();
}
/**
* Set the current global scope
* @param global the global scope
*/
public static void setGlobal(final ScriptObject global) {
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("setNashornGlobal"));
}
if (global != null && !(global instanceof GlobalObject)) {
throw new IllegalArgumentException("global does not implement GlobalObject!");
}
setGlobalTrusted(global);
}
/**
* Get context of the current global
* @return current global scope's context.
*/
public static Context getContext() {
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("getNashornContext"));
}
return getContextTrusted();
}
/**
* Get current context's error writer
*
* @return error writer of the current context
*/
public static PrintWriter getCurrentErr() {
final ScriptObject global = getGlobalTrusted();
return (global != null)? global.getContext().getErr() : new PrintWriter(System.err);
}
/**
* Output text to this Context's error stream
* @param str text to write
*/
public static void err(final String str) {
err(str, true);
}
/**
* Output text to this Context's error stream, optionally with
* a newline afterwards
*
* @param str text to write
* @param crlf write a carriage return/new line after text
*/
@SuppressWarnings("resource")
public static void err(final String str, final boolean crlf) {
final PrintWriter err = Context.getCurrentErr();
if (err != null) {
if (crlf) {
err.println(str);
} else {
err.print(str);
}
}
}
/** class loader to resolve classes from script. */
private final ClassLoader appLoader;
/** Class loader to load classes from -classpath option, if set. */
private final ClassLoader classPathLoader;
/** Class loader to load classes compiled from scripts. */
private final ScriptLoader scriptLoader;
/** Top level namespace. */
private final Namespace namespace;
/** Current options. */
private final Options options;
/** Current error manager. */
private final ErrorManager errors;
/** Output writer for this context */
private final PrintWriter out;
/** Error writer for this context */
private final PrintWriter err;
/** Local for error messages */
private final Locale locale;
/** Empty map used for seed map for JO$ objects */
final PropertyMap emptyMap = PropertyMap.newEmptyMap(this);
// cache fields for "well known" options.
// see jdk.nashorn.internal.runtime.Resources
/** Always allow functions as statements */
public final boolean _anon_functions;
/** Size of the per-global Class cache size */
public final int _class_cache_size;
/** Only compile script, do not run it or generate other ScriptObjects */
public final boolean _compile_only;
/** Accumulated callsite flags that will be used when boostrapping script callsites */
public final int _callsite_flags;
/** Genereate line number table in class files */
public final boolean _debug_lines;
/** Package to which generated class files are added */
public final String _dest_dir;
/** Display stack trace upon error, default is false */
public final boolean _dump_on_error;
/** Invalid lvalue expressions should be reported as early errors */
public final boolean _early_lvalue_error;
/** Empty statements should be preserved in the AST */
public final boolean _empty_statements;
/** Show full Nashorn version */
public final boolean _fullversion;
/** Create a new class loaded for each compilation */
public final boolean _loader_per_compile;
/** Do not support non-standard syntax extensions. */
public final boolean _no_syntax_extensions;
/** Package to which generated class files are added */
public final String _package;
/** Only parse the source code, do not compile */
public final boolean _parse_only;
/** Print the AST before lowering */
public final boolean _print_ast;
/** Print the AST after lowering */
public final boolean _print_lower_ast;
/** Print resulting bytecode for script */
public final boolean _print_code;
/** Print function will no print newline characters */
public final boolean _print_no_newline;
/** Print AST in more human readable form */
public final boolean _print_parse;
/** Print AST in more human readable form after Lowering */
public final boolean _print_lower_parse;
/** print symbols and their contents for the script */
public final boolean _print_symbols;
/** is this context in scripting mode? */
public final boolean _scripting;
/** is this context in strict mode? */
public final boolean _strict;
/** print version info of Nashorn */
public final boolean _version;
/** should code verification be done of generated bytecode */
public final boolean _verify_code;
/** time zone for this context */
public final TimeZone _timezone;
private static final ClassLoader myLoader = Context.class.getClassLoader();
private static final StructureLoader sharedLoader;
static {
sharedLoader = AccessController.doPrivileged(new PrivilegedAction<StructureLoader>() {
@Override
public StructureLoader run() {
return new StructureLoader(myLoader, null);
}
});
}
/**
* ThrowErrorManager that throws ParserException upon error conditions.
*/
public static class ThrowErrorManager extends ErrorManager {
@Override
public void error(final String message) {
throw new ParserException(message);
}
@Override
public void error(final ParserException e) {
throw e;
}
}
/**
* Constructor
*
* @param options options from command line or Context creator
* @param errors error manger
* @param appLoader application class loader
*/
public Context(final Options options, final ErrorManager errors, final ClassLoader appLoader) {
this(options, errors, new PrintWriter(System.out, true), new PrintWriter(System.err, true), appLoader);
}
/**
* Constructor
*
* @param options options from command line or Context creator
* @param errors error manger
* @param out output writer for this Context
* @param err error writer for this Context
* @param appLoader application class loader
*/
public Context(final Options options, final ErrorManager errors, final PrintWriter out, final PrintWriter err, final ClassLoader appLoader) {
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("createNashornContext"));
}
this.appLoader = appLoader;
this.scriptLoader = (ScriptLoader)AccessController.doPrivileged(
new PrivilegedAction<ClassLoader>() {
@Override
public ClassLoader run() {
final ClassLoader structureLoader = new StructureLoader(sharedLoader, Context.this);
return new ScriptLoader(structureLoader, Context.this);
}
});
this.namespace = new Namespace();
this.options = options;
this.errors = errors;
this.locale = Locale.getDefault();
this.out = out;
this.err = err;
_anon_functions = options.getBoolean("anon.functions");
_class_cache_size = options.getInteger("class.cache.size");
_compile_only = options.getBoolean("compile.only");
_debug_lines = options.getBoolean("debug.lines");
_dest_dir = options.getString("d");
_dump_on_error = options.getBoolean("doe");
_early_lvalue_error = options.getBoolean("early.lvalue.error");
_empty_statements = options.getBoolean("empty.statements");
_fullversion = options.getBoolean("fullversion");
_loader_per_compile = options.getBoolean("loader.per.compile");
_no_syntax_extensions = options.getBoolean("no.syntax.extensions");
_package = options.getString("package");
_parse_only = options.getBoolean("parse.only");
_print_ast = options.getBoolean("print.ast");
_print_lower_ast = options.getBoolean("print.lower.ast");
_print_code = options.getBoolean("print.code");
_print_no_newline = options.getBoolean("print.no.newline");
_print_parse = options.getBoolean("print.parse");
_print_lower_parse = options.getBoolean("print.lower.parse");
_print_symbols = options.getBoolean("print.symbols");
_scripting = options.getBoolean("scripting");
_strict = options.getBoolean("strict");
_version = options.getBoolean("version");
_verify_code = options.getBoolean("verify.code");
int callSiteFlags = 0;
if (options.getBoolean("profile.callsites")) {
callSiteFlags |= NashornCallSiteDescriptor.CALLSITE_PROFILE;
}
if (options.get("trace.callsites") instanceof KeyValueOption) {
callSiteFlags |= NashornCallSiteDescriptor.CALLSITE_TRACE;
final KeyValueOption kv = (KeyValueOption)options.get("trace.callsites");
if (kv.hasValue("miss")) {
callSiteFlags |= NashornCallSiteDescriptor.CALLSITE_TRACE_MISSES;
}
if (kv.hasValue("enterexit") || (callSiteFlags & NashornCallSiteDescriptor.CALLSITE_TRACE_MISSES) == 0) {
callSiteFlags |= NashornCallSiteDescriptor.CALLSITE_TRACE_ENTEREXIT;
}
if (kv.hasValue("objects")) {
callSiteFlags |= NashornCallSiteDescriptor.CALLSITE_TRACE_VALUES;
}
if (kv.hasValue("scope")) {
callSiteFlags |= NashornCallSiteDescriptor.CALLSITE_TRACE_SCOPE;
}
}
this._callsite_flags = callSiteFlags;
final Option<?> option = options.get("timezone");
if (option != null) {
this._timezone = (TimeZone)option.getValue();
} else {
this._timezone = TimeZone.getDefault();
}
// if user passed -classpath option, make a class loader with that and set it as
// thread context class loader so that script can access classes from that path.
final String classPath = options.getString("classpath");
if (! _compile_only && classPath != null && !classPath.isEmpty()) {
// make sure that caller can create a class loader.
if (sm != null) {
sm.checkPermission(new RuntimePermission("createClassLoader"));
}
this.classPathLoader = NashornLoader.createClassLoader(classPath);
} else {
this.classPathLoader = null;
}
// print version info if asked.
if (_version) {
getErr().println("nashorn " + Version.version());
}
if (_fullversion) {
getErr().println("nashorn full version " + Version.fullVersion());
}
}
/**
* Get the error manager for this context
* @return error manger
*/
public ErrorManager getErrorManager() {
return errors;
}
/**
* Get the output stream for this context
* @return output print writer
*/
public PrintWriter getOut() {
return out;
}
/**
* Get the error stream for this context
* @return error print writer
*/
public PrintWriter getErr() {
return err;
}
/**
* Get the namespace for this context
* @return namespace
*/
public Namespace getNamespace() {
return namespace;
}
/**
* Get the options given to this context
* @return options
*/
public Options getOptions() {
return options;
}
/**
* Get the locale for this context
* @return locale
*/
public Locale getLocale() {
return locale;
}
/**
* Get the time zone for this context
* @return time zone
*/
public TimeZone getTimeZone() {
return _timezone;
}
/**
* Get the PropertyMap of the current global scope
* @return the property map of the current global scope
*/
public static PropertyMap getGlobalMap() {
return Context.getGlobalTrusted().getMap();
}
/**
* Compile a top level script.
*
* @param source the source
* @param scope the scope
* @param strict are we in strict mode
*
* @return top level function for script
*/
public ScriptFunction compileScript(final Source source, final ScriptObject scope, final boolean strict) {
return compileScript(source, scope, this.errors, strict);
}
/**
* Compile a top level script - no Source given, but an URL to
* load it from
*
* @param name name of script/source
* @param url URL to source
* @param scope the scope
* @param strict are we in strict mode
*
* @return top level function for the script
*
* @throws IOException if URL cannot be resolved
*/
public ScriptFunction compileScript(final String name, final URL url, final ScriptObject scope, final boolean strict) throws IOException {
return compileScript(name, url, scope, this.errors, strict);
}
/**
* Entry point for {@code eval}
*
* @param initialScope The scope of this eval call
* @param string Evaluated code as a String
* @param callThis "this" to be passed to the evaluated code
* @param location location of the eval call
* @param strict is this {@code eval} call from a strict mode code?
*
* @return the return value of the {@code eval}
*/
public Object eval(final ScriptObject initialScope, final String string, final Object callThis, final Object location, final boolean strict) {
final String file = (location == UNDEFINED || location == null) ? "<eval>" : location.toString();
final Source source = new Source(file, string);
final boolean directEval = location != UNDEFINED; // is this direct 'eval' call or indirectly invoked eval?
final ScriptObject global = Context.getGlobalTrusted();
ScriptObject scope = initialScope;
// ECMA section 10.1.1 point 2 says eval code is strict if it begins
// with "use strict" directive or eval direct call itself is made
// from from strict mode code. We are passed with caller's strict mode.
boolean strictFlag = directEval && strict;
Class<?> clazz = null;
try {
clazz = compile(source, new ThrowErrorManager(), strictFlag);
} catch (final ParserException e) {
e.throwAsEcmaException(global);
return null;
}
if (!strictFlag) {
// We need to get strict mode flag from compiled class. This is
// because eval code may start with "use strict" directive.
try {
strictFlag = clazz.getField(STRICT_MODE.tag()).getBoolean(null);
} catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
//ignored
strictFlag = false;
}
}
// In strict mode, eval does not instantiate variables and functions
// in the caller's environment. A new environment is created!
if (strictFlag) {
// Create a new scope object
final ScriptObject strictEvalScope = ((GlobalObject)global).newObject();
// bless it as a "scope"
strictEvalScope.setIsScope();
// set given scope to be it's proto so that eval can still
// access caller environment vars in the new environment.
strictEvalScope.setProto(scope);
scope = strictEvalScope;
}
ScriptFunction func = getRunScriptFunction(clazz, scope);
Object evalThis;
if (directEval) {
evalThis = (callThis instanceof ScriptObject || strictFlag) ? callThis : global;
} else {
evalThis = global;
}
return ScriptRuntime.apply(func, evalThis);
}
/**
* Implementation of {@code load} Nashorn extension. Load a script file from a source
* expression
*
* @param scope the scope
* @param source source expression for script
*
* @return return value for load call (undefined)
*
* @throws IOException if source cannot be found or loaded
*/
public Object load(final ScriptObject scope, final Object source) throws IOException {
Object src = source;
URL url = null;
String srcName = null;
if (src instanceof ConsString) {
src = src.toString();
}
if (src instanceof String) {
srcName = (String)src;
final File file = new File((String)src);
if (srcName.indexOf(':') != -1) {
try {
url = new URL((String)src);
} catch (final MalformedURLException e) {
// fallback URL - nashorn:foo.js - check under jdk/nashorn/internal/runtime/resources
final String str = (String)src;
if (str.startsWith("nashorn:")) {
final String resource = "resources/" + str.substring("nashorn:".length());
// NOTE: even sandbox scripts should be able to load scripts in nashorn: scheme
// These scripts are always available and are loaded from nashorn.jar's resources.
final Source code = AccessController.doPrivileged(
new PrivilegedAction<Source>() {
@Override
public Source run() {
try {
final URL resURL = Context.class.getResource(resource);
return (resURL != null)? new Source(str, resURL) : null;
} catch (final IOException exp) {
return null;
}
}
});
if (code == null) {
throw e;
}
return evaluateSource(code, scope, scope);
} else {
throw e;
}
}
} else if (file.isFile()) {
url = file.toURI().toURL();
}
src = url;
}
if (src instanceof File && ((File)src).isFile()) {
final File file = (File)src;
url = file.toURI().toURL();
if (srcName == null) {
srcName = file.getCanonicalPath();
}
} else if (src instanceof URL) {
url = (URL)src;
if (srcName == null) {
srcName = url.toString();
}
}
if (url != null) {
assert srcName != null : "srcName null here!";
return evaluateSource(srcName, url, scope, scope);
} else if (src instanceof ScriptObject) {
final ScriptObject sobj = (ScriptObject)src;
if (sobj.has("script") && sobj.has("name")) {
final String script = JSType.toString(sobj.get("script"));
final String name = JSType.toString(sobj.get("name"));
return evaluateSource(new Source(name, script), scope, scope);
}
}
typeError("cant.load.script", ScriptRuntime.safeToString(source));
return UNDEFINED;
}
/**
* Load or get a structure class. Structure class names are based on the number of parameter fields
* and {@link AccessorProperty} fields in them. Structure classes are used to represent ScriptObjects
*
* @see ObjectClassGenerator
* @see AccessorProperty
* @see ScriptObject
*
* @param fullName full name of class, e.g. jdk.nashorn.internal.objects.JO$2P1 contains 2 fields and 1 parameter.
*
* @return the Class<?> for this structure
*
* @throws ClassNotFoundException if structure class cannot be resolved
*/
public static Class<?> forStructureClass(final String fullName) throws ClassNotFoundException {
return Class.forName(fullName, true, sharedLoader);
}
/**
* Lookup a Java class. This is used for JSR-223 stuff linking in from
* {@link jdk.nashorn.internal.objects.NativeJava} and {@link jdk.nashorn.internal.runtime.NativeJavaPackage}
*
* @param fullName full name of class to load
*
* @return the Class<?> for the name
*
* @throws ClassNotFoundException if class cannot be resolved
*/
public Class<?> findClass(final String fullName) throws ClassNotFoundException {
// check package access as soon as possible!
final int index = fullName.lastIndexOf('.');
if (index != -1) {
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPackageAccess(fullName.substring(0, index));
}
}
// try the script -classpath loader, if that is set
if (classPathLoader != null) {
try {
return Class.forName(fullName, true, classPathLoader);
} catch (final ClassNotFoundException ignored) {
// ignore, continue search
}
}
// Try finding using the "app" loader.
return Class.forName(fullName, true, appLoader);
}
/**
* Hook to print stack trace for a {@link Throwable} that occurred during
* execution
*
* @param t throwable for which to dump stack
*/
public static void printStackTrace(final Throwable t) {
if (Context.DEBUG) {
t.printStackTrace(Context.getCurrentErr());
}
}
/**
* Verify generated bytecode before emission. This is called back from the
* {@link ClassEmitter} or the {@link Compiler}. If the "--verify-code" parameter
* hasn't been given, this is a nop
*
* Note that verification may load classes -- we don't want to do that unless
* user specified verify option. We check it here even though caller
* may have already checked that flag
*
* @param bytecode bytecode to verify
*/
public void verify(final byte[] bytecode) {
if (_verify_code) {
// No verification when security manager is around as verifier
// may load further classes - which should be avoided.
if (System.getSecurityManager() == null) {
CheckClassAdapter.verify(new ClassReader(bytecode), scriptLoader, false, new PrintWriter(System.err, true));
}
}
}
/**
* Create and initialize a new global scope object.
*
* @return the initialized global scope object.
*/
public ScriptObject createGlobal() {
return initGlobal(newGlobal());
}
/**
* Create a new uninitialized global scope object
* @return the global script object
*/
public ScriptObject newGlobal() {
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(new RuntimePermission("createNashornGlobal"));
}
return newGlobalTrusted();
}
/**
* Initialize given global scope object.
*
* @return the initialized global scope object.
*/
public ScriptObject initGlobal(final ScriptObject global) {
if (! (global instanceof GlobalObject)) {
throw new IllegalArgumentException("not a global object!");
}
// Need only minimal global object, if we are just compiling.
if (!_compile_only) {
final ScriptObject oldGlobal = Context.getGlobalTrusted();
try {
Context.setGlobalTrusted(global);
// initialize global scope with builtin global objects
((GlobalObject)global).initBuiltinObjects();
} finally {
Context.setGlobalTrusted(oldGlobal);
}
}
return global;
}
/**
* Trusted variants - package-private
*/
/**
* Return the current global scope
* @return current global scope
*/
static ScriptObject getGlobalTrusted() {
return currentGlobal.get();
}
/**
* Set the current global scope
*/
static void setGlobalTrusted(ScriptObject global) {
currentGlobal.set(global);
}
/**
* Return the current global's context
* @return current global's context
*/
static Context getContextTrusted() {
return Context.getGlobalTrusted().getContext();
}
/**
* Try to infer Context instance from the Class. If we cannot,
* then get it from the thread local variable.
*
* @param clazz the class
* @return context
*/
static Context fromClass(final Class<?> clazz) {
final ClassLoader loader = clazz.getClassLoader();
Context context = null;
if (loader instanceof NashornLoader) {
context = ((NashornLoader)loader).getContext();
}
return (context != null) ? context : Context.getContextTrusted();
}
private Object evaluateSource(final String name, final URL url, final ScriptObject scope, final ScriptObject thiz) throws IOException {
ScriptFunction script = null;
try {
script = compileScript(name, url, scope, new Context.ThrowErrorManager(), _strict);
} catch (final ParserException e) {
e.throwAsEcmaException();
}
return ScriptRuntime.apply(script, thiz);
}
private Object evaluateSource(final Source source, final ScriptObject scope, final ScriptObject thiz) {
ScriptFunction script = null;
try {
script = compileScript(source, scope, new Context.ThrowErrorManager(), _strict);
} catch (final ParserException e) {
e.throwAsEcmaException();
}
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(
MethodHandles.lookup(),
script,
RUN_SCRIPT.tag(),
MH.type(
Object.class,
Object.class,
ScriptFunction.class));
boolean strict;
try {
strict = script.getField(STRICT_MODE.tag()).getBoolean(null);
} catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
strict = false;
}
// Package as a JavaScript function and pass function back to shell.
return ((GlobalObject)Context.getGlobalTrusted()).newScriptFunction(RUN_SCRIPT.tag(), runMethodHandle, scope, strict);
}
private ScriptFunction compileScript(final String name, final URL url, final ScriptObject scope, final ErrorManager errMan, final boolean strict) throws IOException {
return getRunScriptFunction(compile(new Source(name, url), url, errMan, strict), scope);
}
private ScriptFunction compileScript(final Source source, final ScriptObject scope, final ErrorManager errMan, final boolean strict) {
return getRunScriptFunction(compile(source, null, errMan, strict), scope);
}
private Class<?> compile(final Source source, final ErrorManager errMan, final boolean strict) {
return compile(source, null, errMan, strict);
}
private synchronized Class<?> compile(final Source source, final URL url, final ErrorManager errMan, final boolean strict) {
// start with no errors, no warnings.
errMan.reset();
GlobalObject global = null;
Class<?> script;
if (_class_cache_size > 0) {
global = (GlobalObject)Context.getGlobalTrusted();
script = global.findCachedClass(source);
if (script != null) {
return script;
}
}
final Compiler compiler = Compiler.compiler(source, this, errMan, strict);
if (!compiler.compile()) {
return null;
}
final ScriptLoader loader = _loader_per_compile ? createNewLoader() : scriptLoader;
final CodeSource cs = url == null ? null : new CodeSource(url, (CodeSigner[])null);
script = compiler.install(new CodeInstaller() {
@Override
public Class<?> install(final String className, final byte[] bytecode) {
return loader.installClass(className, bytecode, cs);
}
});
if (global != null) {
global.cacheClass(source, script);
}
return script;
}
private ScriptLoader createNewLoader() {
return AccessController.doPrivileged(
new PrivilegedAction<ScriptLoader>() {
@Override
public ScriptLoader run() {
// Generated code won't refer to any class generated by context
// script loader and so parent loader can be the structure
// loader -- which is parent of the context script loader.
return new ScriptLoader(scriptLoader.getParent(), Context.this);
}
});
}
private ScriptObject newGlobalTrusted() {
try {
final Class<?> clazz = Class.forName("jdk.nashorn.internal.objects.Global", true, scriptLoader);
return (ScriptObject) clazz.newInstance();
} catch (final ClassNotFoundException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException e) {
printStackTrace(e);
throw new RuntimeException(e);
}
}
}