Context.java revision 1040:cc3000241e57
1181053Srwatson/* 2187215Srwatson * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3155192Srwatson * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4155192Srwatson * 5155192Srwatson * This code is free software; you can redistribute it and/or modify it 6155192Srwatson * under the terms of the GNU General Public License version 2 only, as 7155192Srwatson * published by the Free Software Foundation. Oracle designates this 8155192Srwatson * particular file as subject to the "Classpath" exception as provided 9155192Srwatson * by Oracle in the LICENSE file that accompanied this code. 10155192Srwatson * 11155192Srwatson * This code is distributed in the hope that it will be useful, but WITHOUT 12155192Srwatson * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13155192Srwatson * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14155192Srwatson * version 2 for more details (a copy is included in the LICENSE file that 15155192Srwatson * accompanied this code). 16155192Srwatson * 17180701Srwatson * You should have received a copy of the GNU General Public License version 18155192Srwatson * 2 along with this work; if not, write to the Free Software Foundation, 19155192Srwatson * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20155192Srwatson * 21155192Srwatson * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22155192Srwatson * or visit www.oracle.com if you need additional information or have any 23155192Srwatson * questions. 24155192Srwatson */ 25155192Srwatson 26155192Srwatsonpackage jdk.nashorn.internal.runtime; 27155192Srwatson 28155192Srwatsonimport static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS; 29155192Srwatsonimport static jdk.nashorn.internal.codegen.CompilerConstants.CREATE_PROGRAM_FUNCTION; 30155192Srwatsonimport static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE; 31155192Srwatsonimport static jdk.nashorn.internal.codegen.CompilerConstants.STRICT_MODE; 32155192Srwatsonimport static jdk.nashorn.internal.runtime.CodeStore.newCodeStore; 33155192Srwatsonimport static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 34178186Srwatsonimport static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; 35178186Srwatsonimport static jdk.nashorn.internal.runtime.Source.sourceFor; 36178186Srwatsonimport java.io.File; 37196971Sphkimport java.io.IOException; 38155192Srwatsonimport java.io.PrintWriter; 39155192Srwatsonimport java.lang.invoke.MethodHandle; 40159259Srwatsonimport java.lang.invoke.MethodHandles; 41155192Srwatsonimport java.lang.invoke.MethodType; 42155192Srwatsonimport java.lang.invoke.SwitchPoint; 43155192Srwatsonimport java.lang.ref.ReferenceQueue; 44155192Srwatsonimport java.lang.ref.SoftReference; 45155192Srwatsonimport java.lang.reflect.Field; 46155192Srwatsonimport java.lang.reflect.Modifier; 47155192Srwatsonimport java.net.MalformedURLException; 48155192Srwatsonimport java.net.URL; 49155192Srwatsonimport java.security.AccessControlContext; 50155192Srwatsonimport java.security.AccessController; 51155192Srwatsonimport java.security.CodeSigner; 52155192Srwatsonimport java.security.CodeSource; 53155192Srwatsonimport java.security.Permissions; 54155192Srwatsonimport java.security.PrivilegedAction; 55155192Srwatsonimport java.security.PrivilegedActionException; 56155192Srwatsonimport java.security.PrivilegedExceptionAction; 57155192Srwatsonimport java.security.ProtectionDomain; 58155192Srwatsonimport java.util.Collection; 59155192Srwatsonimport java.util.HashMap; 60155192Srwatsonimport java.util.LinkedHashMap; 61155192Srwatsonimport java.util.Map; 62155192Srwatsonimport java.util.concurrent.atomic.AtomicLong; 63155192Srwatsonimport java.util.function.Consumer; 64155192Srwatsonimport java.util.function.Supplier; 65155192Srwatsonimport java.util.logging.Level; 66155192Srwatsonimport javax.script.ScriptEngine; 67155192Srwatsonimport jdk.internal.org.objectweb.asm.ClassReader; 68155192Srwatsonimport jdk.internal.org.objectweb.asm.util.CheckClassAdapter; 69243751Srwatsonimport jdk.nashorn.api.scripting.ClassFilter; 70243751Srwatsonimport jdk.nashorn.api.scripting.ScriptObjectMirror; 71243751Srwatsonimport jdk.nashorn.internal.codegen.Compiler; 72243751Srwatsonimport jdk.nashorn.internal.codegen.Compiler.CompilationPhases; 73243751Srwatsonimport jdk.nashorn.internal.codegen.ObjectClassGenerator; 74243751Srwatsonimport jdk.nashorn.internal.ir.FunctionNode; 75243751Srwatsonimport jdk.nashorn.internal.ir.debug.ASTWriter; 76243751Srwatsonimport jdk.nashorn.internal.ir.debug.PrintVisitor; 77243751Srwatsonimport jdk.nashorn.internal.lookup.MethodHandleFactory; 78243751Srwatsonimport jdk.nashorn.internal.objects.Global; 79243751Srwatsonimport jdk.nashorn.internal.parser.Parser; 80243751Srwatsonimport jdk.nashorn.internal.runtime.events.RuntimeEvent; 81243751Srwatsonimport jdk.nashorn.internal.runtime.logging.DebugLogger; 82243751Srwatsonimport jdk.nashorn.internal.runtime.logging.Loggable; 83243751Srwatsonimport jdk.nashorn.internal.runtime.logging.Logger; 84243751Srwatsonimport jdk.nashorn.internal.runtime.options.LoggingOption.LoggerInfo; 85243751Srwatsonimport jdk.nashorn.internal.runtime.options.Options; 86243751Srwatson 87243751Srwatson/** 88243751Srwatson * This class manages the global state of execution. Context is immutable. 89243751Srwatson */ 90243751Srwatsonpublic final class Context { 91243751Srwatson // nashorn specific security runtime access permission names 92243751Srwatson /** 93243751Srwatson * Permission needed to pass arbitrary nashorn command line options when creating Context. 94243751Srwatson */ 95243751Srwatson public static final String NASHORN_SET_CONFIG = "nashorn.setConfig"; 96243751Srwatson 97243751Srwatson /** 98243751Srwatson * Permission needed to create Nashorn Context instance. 99243751Srwatson */ 100243751Srwatson public static final String NASHORN_CREATE_CONTEXT = "nashorn.createContext"; 101243751Srwatson 102243751Srwatson /** 103243751Srwatson * Permission needed to create Nashorn Global instance. 104243751Srwatson */ 105243751Srwatson public static final String NASHORN_CREATE_GLOBAL = "nashorn.createGlobal"; 106243751Srwatson 107243751Srwatson /** 108243751Srwatson * Permission to get current Nashorn Context from thread local storage. 109243751Srwatson */ 110243751Srwatson public static final String NASHORN_GET_CONTEXT = "nashorn.getContext"; 111243751Srwatson 112243751Srwatson /** 113243751Srwatson * Permission to use Java reflection/jsr292 from script code. 114243751Srwatson */ 115243751Srwatson public static final String NASHORN_JAVA_REFLECTION = "nashorn.JavaReflection"; 116243751Srwatson 117243751Srwatson /** 118243751Srwatson * Permission to enable nashorn debug mode. 119243751Srwatson */ 120155192Srwatson public static final String NASHORN_DEBUG_MODE = "nashorn.debugMode"; 121155192Srwatson 122155192Srwatson // nashorn load psuedo URL prefixes 123155192Srwatson private static final String LOAD_CLASSPATH = "classpath:"; 124155192Srwatson private static final String LOAD_FX = "fx:"; 125155192Srwatson private static final String LOAD_NASHORN = "nashorn:"; 126185573Srwatson 127155192Srwatson private static MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); 128155192Srwatson private static MethodType CREATE_PROGRAM_FUNCTION_TYPE = MethodType.methodType(ScriptFunction.class, ScriptObject.class); 129155192Srwatson 130155192Srwatson /** 131155192Srwatson * Keeps track of which builtin prototypes and properties have been relinked 132155192Srwatson * Currently we are conservative and associate the name of a builtin class with all 133155192Srwatson * its properties, so it's enough to invalidate a property to break all assumptions 134155192Srwatson * about a prototype. This can be changed to a more fine grained approach, but no one 135155192Srwatson * ever needs this, given the very rare occurance of swapping out only parts of 136155192Srwatson * a builtin v.s. the entire builtin object 137155192Srwatson */ 138155192Srwatson private final Map<String, SwitchPoint> builtinSwitchPoints = new HashMap<>(); 139155192Srwatson 140155192Srwatson /* Force DebuggerSupport to be loaded. */ 141155192Srwatson static { 142155192Srwatson DebuggerSupport.FORCELOAD = true; 143155192Srwatson } 144155192Srwatson 145155192Srwatson /** 146155192Srwatson * ContextCodeInstaller that has the privilege of installing classes in the Context. 147155192Srwatson * Can only be instantiated from inside the context and is opaque to other classes 148185573Srwatson */ 149155192Srwatson public static class ContextCodeInstaller implements CodeInstaller<ScriptEnvironment> { 150155192Srwatson private final Context context; 151155192Srwatson private final ScriptLoader loader; 152155192Srwatson private final CodeSource codeSource; 153155192Srwatson 154155192Srwatson private ContextCodeInstaller(final Context context, final ScriptLoader loader, final CodeSource codeSource) { 155155192Srwatson this.context = context; 156155192Srwatson this.loader = loader; 157155192Srwatson this.codeSource = codeSource; 158155192Srwatson } 159155192Srwatson 160155192Srwatson /** 161155192Srwatson * Return the context for this installer 162155192Srwatson * @return ScriptEnvironment 163155192Srwatson */ 164155192Srwatson @Override 165155192Srwatson public ScriptEnvironment getOwner() { 166155192Srwatson return context.env; 167155192Srwatson } 168155192Srwatson 169155192Srwatson @Override 170185573Srwatson public Class<?> install(final String className, final byte[] bytecode) { 171155192Srwatson final String binaryName = Compiler.binaryName(className); 172155192Srwatson return loader.installClass(binaryName, bytecode, codeSource); 173155192Srwatson } 174155192Srwatson 175155192Srwatson @Override 176155192Srwatson public void initialize(final Collection<Class<?>> classes, final Source source, final Object[] constants) { 177155192Srwatson try { 178155192Srwatson AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() { 179155192Srwatson @Override 180155192Srwatson public Void run() throws Exception { 181155192Srwatson for (final Class<?> clazz : classes) { 182155192Srwatson //use reflection to write source and constants table to installed classes 183155192Srwatson final Field sourceField = clazz.getDeclaredField(SOURCE.symbolName()); 184155192Srwatson sourceField.setAccessible(true); 185155192Srwatson sourceField.set(null, source); 186155192Srwatson 187155192Srwatson final Field constantsField = clazz.getDeclaredField(CONSTANTS.symbolName()); 188155192Srwatson constantsField.setAccessible(true); 189155192Srwatson constantsField.set(null, constants); 190155192Srwatson } 191155192Srwatson return null; 192189279Srwatson } 193155192Srwatson }); 194155192Srwatson } catch (final PrivilegedActionException e) { 195155192Srwatson throw new RuntimeException(e); 196155192Srwatson } 197155192Srwatson } 198155192Srwatson 199155192Srwatson @Override 200186647Srwatson public void verify(final byte[] code) { 201186647Srwatson context.verify(code); 202186647Srwatson } 203186647Srwatson 204186647Srwatson @Override 205186647Srwatson public long getUniqueScriptId() { 206155192Srwatson return context.getUniqueScriptId(); 207155192Srwatson } 208155192Srwatson 209155192Srwatson @Override 210155192Srwatson public void storeScript(final String cacheKey, final Source source, final String mainClassName, 211155192Srwatson final Map<String,byte[]> classBytes, final Map<Integer, FunctionInitializer> initializers, 212155192Srwatson final Object[] constants, final int compilationId) { 213155192Srwatson if (context.codeStore != null) { 214155192Srwatson context.codeStore.store(cacheKey, source, mainClassName, classBytes, initializers, constants, compilationId); 215181053Srwatson } 216155192Srwatson } 217155192Srwatson 218155192Srwatson @Override 219155192Srwatson public StoredScript loadScript(final Source source, final String functionKey) { 220155192Srwatson if (context.codeStore != null) { 221155192Srwatson return context.codeStore.load(source, functionKey); 222155192Srwatson } 223155192Srwatson return null; 224155192Srwatson } 225155192Srwatson } 226155192Srwatson 227155192Srwatson /** Is Context global debug mode enabled ? */ 228155192Srwatson public static final boolean DEBUG = Options.getBooleanProperty("nashorn.debug"); 229155192Srwatson 230155192Srwatson private static final ThreadLocal<Global> currentGlobal = new ThreadLocal<>(); 231155192Srwatson 232155192Srwatson // in-memory cache for loaded classes 233155192Srwatson private ClassCache classCache; 234155192Srwatson 235155192Srwatson // persistent code store 236168783Srwatson private CodeStore codeStore; 237168783Srwatson 238168783Srwatson /** 239189279Srwatson * Get the current global scope 240155192Srwatson * @return the current global scope 241168783Srwatson */ 242168783Srwatson public static Global getGlobal() { 243168783Srwatson // This class in a package.access protected package. 244168783Srwatson // Trusted code only can call this method. 245168783Srwatson return currentGlobal.get(); 246168783Srwatson } 247186647Srwatson 248186647Srwatson /** 249186647Srwatson * Set the current global scope 250186647Srwatson * @param global the global scope 251186647Srwatson */ 252186647Srwatson public static void setGlobal(final ScriptObject global) { 253168783Srwatson if (global != null && !(global instanceof Global)) { 254168783Srwatson throw new IllegalArgumentException("not a global!"); 255168783Srwatson } 256168783Srwatson setGlobal((Global)global); 257168783Srwatson } 258168783Srwatson 259168783Srwatson /** 260168783Srwatson * Set the current global scope 261168783Srwatson * @param global the global scope 262168783Srwatson */ 263168783Srwatson public static void setGlobal(final Global global) { 264168783Srwatson // This class in a package.access protected package. 265168783Srwatson // Trusted code only can call this method. 266168783Srwatson assert getGlobal() != global; 267168783Srwatson //same code can be cached between globals, then we need to invalidate method handle constants 268168783Srwatson if (global != null) { 269168783Srwatson Global.getConstants().invalidateAll(); 270168783Srwatson } 271168783Srwatson currentGlobal.set(global); 272168783Srwatson } 273168783Srwatson 274168783Srwatson /** 275168783Srwatson * Get context of the current global 276168783Srwatson * @return current global scope's context. 277168783Srwatson */ 278155192Srwatson public static Context getContext() { 279155192Srwatson final SecurityManager sm = System.getSecurityManager(); 280155192Srwatson if (sm != null) { 281155192Srwatson sm.checkPermission(new RuntimePermission(NASHORN_GET_CONTEXT)); 282155192Srwatson } 283155192Srwatson return getContextTrusted(); 284155192Srwatson } 285155192Srwatson 286155192Srwatson /** 287155192Srwatson * Get current context's error writer 288155192Srwatson * 289155192Srwatson * @return error writer of the current context 290155192Srwatson */ 291155192Srwatson public static PrintWriter getCurrentErr() { 292155192Srwatson final ScriptObject global = getGlobal(); 293155192Srwatson return (global != null)? global.getContext().getErr() : new PrintWriter(System.err); 294155192Srwatson } 295155192Srwatson 296185573Srwatson /** 297155192Srwatson * Output text to this Context's error stream 298155192Srwatson * @param str text to write 299155192Srwatson */ 300155192Srwatson public static void err(final String str) { 301155192Srwatson err(str, true); 302155192Srwatson } 303155192Srwatson 304155192Srwatson /** 305159259Srwatson * Output text to this Context's error stream, optionally with 306155192Srwatson * a newline afterwards 307155192Srwatson * 308155192Srwatson * @param str text to write 309155192Srwatson * @param crlf write a carriage return/new line after text 310155192Srwatson */ 311155192Srwatson public static void err(final String str, final boolean crlf) { 312155192Srwatson final PrintWriter err = Context.getCurrentErr(); 313159259Srwatson if (err != null) { 314159259Srwatson if (crlf) { 315159259Srwatson err.println(str); 316155192Srwatson } else { 317155192Srwatson err.print(str); 318159259Srwatson } 319159259Srwatson } 320159259Srwatson } 321159259Srwatson 322155192Srwatson /** Current environment. */ 323180709Srwatson private final ScriptEnvironment env; 324155192Srwatson 325155192Srwatson /** is this context in strict mode? Cached from env. as this is used heavily. */ 326155192Srwatson final boolean _strict; 327155192Srwatson 328159259Srwatson /** class loader to resolve classes from script. */ 329155192Srwatson private final ClassLoader appLoader; 330186647Srwatson 331186647Srwatson /** Class loader to load classes from -classpath option, if set. */ 332186647Srwatson private final ClassLoader classPathLoader; 333186647Srwatson 334155192Srwatson /** Class loader to load classes compiled from scripts. */ 335155192Srwatson private final ScriptLoader scriptLoader; 336155192Srwatson 337155192Srwatson /** Current error manager. */ 338155192Srwatson private final ErrorManager errors; 339155192Srwatson 340155192Srwatson /** Unique id for script. Used only when --loader-per-compile=false */ 341155192Srwatson private final AtomicLong uniqueScriptId; 342155192Srwatson 343155192Srwatson /** Optional class filter to use for Java classes. Can be null. */ 344155192Srwatson private final ClassFilter classFilter; 345155192Srwatson 346155192Srwatson private static final ClassLoader myLoader = Context.class.getClassLoader(); 347155192Srwatson private static final StructureLoader sharedLoader; 348155192Srwatson 349155192Srwatson /*package-private*/ @SuppressWarnings("static-method") 350155192Srwatson ClassLoader getSharedLoader() { 351155192Srwatson return sharedLoader; 352155192Srwatson } 353155192Srwatson 354155192Srwatson private static AccessControlContext createNoPermAccCtxt() { 355155192Srwatson return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, new Permissions()) }); 356155192Srwatson } 357155192Srwatson 358155192Srwatson private static AccessControlContext createPermAccCtxt(final String permName) { 359155192Srwatson final Permissions perms = new Permissions(); 360155192Srwatson perms.add(new RuntimePermission(permName)); 361155192Srwatson return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, perms) }); 362155192Srwatson } 363155192Srwatson 364155192Srwatson private static final AccessControlContext NO_PERMISSIONS_ACC_CTXT = createNoPermAccCtxt(); 365155192Srwatson private static final AccessControlContext CREATE_LOADER_ACC_CTXT = createPermAccCtxt("createClassLoader"); 366155192Srwatson private static final AccessControlContext CREATE_GLOBAL_ACC_CTXT = createPermAccCtxt(NASHORN_CREATE_GLOBAL); 367155192Srwatson 368155192Srwatson static { 369155192Srwatson sharedLoader = AccessController.doPrivileged(new PrivilegedAction<StructureLoader>() { 370185573Srwatson @Override 371155192Srwatson public StructureLoader run() { 372155192Srwatson return new StructureLoader(myLoader); 373155192Srwatson } 374155192Srwatson }, CREATE_LOADER_ACC_CTXT); 375155192Srwatson } 376155192Srwatson 377155192Srwatson /** 378155192Srwatson * ThrowErrorManager that throws ParserException upon error conditions. 379155192Srwatson */ 380155192Srwatson public static class ThrowErrorManager extends ErrorManager { 381155192Srwatson @Override 382155192Srwatson public void error(final String message) { 383155192Srwatson throw new ParserException(message); 384155192Srwatson } 385155192Srwatson 386155192Srwatson @Override 387155192Srwatson public void error(final ParserException e) { 388155192Srwatson throw e; 389155192Srwatson } 390155192Srwatson } 391155192Srwatson 392155192Srwatson /** 393155192Srwatson * Constructor 394155192Srwatson * 395155192Srwatson * @param options options from command line or Context creator 396155192Srwatson * @param errors error manger 397155192Srwatson * @param appLoader application class loader 398155192Srwatson */ 399155192Srwatson public Context(final Options options, final ErrorManager errors, final ClassLoader appLoader) { 400155192Srwatson this(options, errors, appLoader, (ClassFilter)null); 401155192Srwatson } 402155192Srwatson 403155192Srwatson /** 404155192Srwatson * Constructor 405155192Srwatson * 406159259Srwatson * @param options options from command line or Context creator 407155192Srwatson * @param errors error manger 408155192Srwatson * @param appLoader application class loader 409159259Srwatson * @param classFilter class filter to use 410155192Srwatson */ 411155192Srwatson public Context(final Options options, final ErrorManager errors, final ClassLoader appLoader, final ClassFilter classFilter) { 412155192Srwatson this(options, errors, new PrintWriter(System.out, true), new PrintWriter(System.err, true), appLoader, classFilter); 413155192Srwatson } 414155192Srwatson 415155192Srwatson /** 416155192Srwatson * Constructor 417185573Srwatson * 418155192Srwatson * @param options options from command line or Context creator 419155192Srwatson * @param errors error manger 420155192Srwatson * @param out output writer for this Context 421155192Srwatson * @param err error writer for this Context 422155192Srwatson * @param appLoader application class loader 423155192Srwatson */ 424171066Scsjp public Context(final Options options, final ErrorManager errors, final PrintWriter out, final PrintWriter err, final ClassLoader appLoader) { 425155192Srwatson this(options, errors, out, err, appLoader, (ClassFilter)null); 426159259Srwatson } 427155192Srwatson 428155192Srwatson /** 429155192Srwatson * Constructor 430171066Scsjp * 431155192Srwatson * @param options options from command line or Context creator 432155192Srwatson * @param errors error manger 433155192Srwatson * @param out output writer for this Context 434155192Srwatson * @param err error writer for this Context 435155192Srwatson * @param appLoader application class loader 436155192Srwatson * @param classFilter class filter to use 437155192Srwatson */ 438165604Srwatson public Context(final Options options, final ErrorManager errors, final PrintWriter out, final PrintWriter err, final ClassLoader appLoader, final ClassFilter classFilter) { 439165604Srwatson final SecurityManager sm = System.getSecurityManager(); 440155192Srwatson if (sm != null) { 441155192Srwatson sm.checkPermission(new RuntimePermission(NASHORN_CREATE_CONTEXT)); 442155192Srwatson } 443155192Srwatson 444155192Srwatson this.classFilter = classFilter; 445155192Srwatson this.env = new ScriptEnvironment(options, out, err); 446155192Srwatson this._strict = env._strict; 447155192Srwatson this.appLoader = appLoader; 448155192Srwatson if (env._loader_per_compile) { 449155192Srwatson this.scriptLoader = null; 450155192Srwatson this.uniqueScriptId = null; 451155192Srwatson } else { 452155192Srwatson this.scriptLoader = createNewLoader(); 453155192Srwatson this.uniqueScriptId = new AtomicLong(); 454155192Srwatson } 455155192Srwatson this.errors = errors; 456155192Srwatson 457155192Srwatson // if user passed -classpath option, make a class loader with that and set it as 458155192Srwatson // thread context class loader so that script can access classes from that path. 459155192Srwatson final String classPath = options.getString("classpath"); 460155192Srwatson if (!env._compile_only && classPath != null && !classPath.isEmpty()) { 461155192Srwatson // make sure that caller can create a class loader. 462155192Srwatson if (sm != null) { 463155192Srwatson sm.checkPermission(new RuntimePermission("createClassLoader")); 464155192Srwatson } 465155192Srwatson this.classPathLoader = NashornLoader.createClassLoader(classPath); 466155192Srwatson } else { 467155192Srwatson this.classPathLoader = null; 468155192Srwatson } 469155192Srwatson 470155192Srwatson final int cacheSize = env._class_cache_size; 471155192Srwatson if (cacheSize > 0) { 472155192Srwatson classCache = new ClassCache(cacheSize); 473155192Srwatson } 474155192Srwatson 475155192Srwatson if (env._persistent_cache) { 476155192Srwatson try { 477155192Srwatson codeStore = newCodeStore(this); 478155192Srwatson } catch (final IOException e) { 479155192Srwatson throw new RuntimeException("Error initializing code cache", e); 480155192Srwatson } 481155192Srwatson } 482155192Srwatson 483155192Srwatson // print version info if asked. 484155192Srwatson if (env._version) { 485155192Srwatson getErr().println("nashorn " + Version.version()); 486155192Srwatson } 487155192Srwatson 488155192Srwatson if (env._fullversion) { 489155192Srwatson getErr().println("nashorn full version " + Version.fullVersion()); 490155192Srwatson } 491155192Srwatson 492189279Srwatson initLoggers(); 493189279Srwatson } 494155192Srwatson 495155192Srwatson 496155192Srwatson /** 497155192Srwatson * Get the class filter for this context 498186647Srwatson * @return class filter 499186647Srwatson */ 500186647Srwatson public ClassFilter getClassFilter() { 501186647Srwatson return classFilter; 502186647Srwatson } 503186647Srwatson 504186647Srwatson /** 505186647Srwatson * Get the error manager for this context 506155192Srwatson * @return error manger 507186647Srwatson */ 508186647Srwatson public ErrorManager getErrorManager() { 509186647Srwatson return errors; 510186647Srwatson } 511186647Srwatson 512186647Srwatson /** 513186647Srwatson * Get the script environment for this context 514186647Srwatson * @return script environment 515186647Srwatson */ 516186647Srwatson public ScriptEnvironment getEnv() { 517186647Srwatson return env; 518186647Srwatson } 519186647Srwatson 520186647Srwatson /** 521186647Srwatson * Get the output stream for this context 522155192Srwatson * @return output print writer 523155192Srwatson */ 524186647Srwatson public PrintWriter getOut() { 525155192Srwatson return env.getOut(); 526155192Srwatson } 527155192Srwatson 528155192Srwatson /** 529155192Srwatson * Get the error stream for this context 530155192Srwatson * @return error print writer 531155192Srwatson */ 532155192Srwatson public PrintWriter getErr() { 533155192Srwatson return env.getErr(); 534155192Srwatson } 535155192Srwatson 536155192Srwatson /** 537155192Srwatson * Get the PropertyMap of the current global scope 538155192Srwatson * @return the property map of the current global scope 539155192Srwatson */ 540155192Srwatson public static PropertyMap getGlobalMap() { 541155192Srwatson return Context.getGlobal().getMap(); 542155192Srwatson } 543155192Srwatson 544155192Srwatson /** 545155192Srwatson * Compile a top level script. 546155192Srwatson * 547155192Srwatson * @param source the source 548155192Srwatson * @param scope the scope 549155192Srwatson * 550155192Srwatson * @return top level function for script 551155192Srwatson */ 552155192Srwatson public ScriptFunction compileScript(final Source source, final ScriptObject scope) { 553155192Srwatson return compileScript(source, scope, this.errors); 554155192Srwatson } 555155192Srwatson 556155192Srwatson /** 557155192Srwatson * Interface to represent compiled code that can be re-used across many 558155192Srwatson * global scope instances 559185573Srwatson */ 560155192Srwatson public static interface MultiGlobalCompiledScript { 561155192Srwatson /** 562155192Srwatson * Obtain script function object for a specific global scope object. 563155192Srwatson * 564155192Srwatson * @param newGlobal global scope for which function object is obtained 565155192Srwatson * @return script function for script level expressions 566155192Srwatson */ 567155192Srwatson public ScriptFunction getFunction(final Global newGlobal); 568155192Srwatson } 569155192Srwatson 570155192Srwatson /** 571155192Srwatson * Compile a top level script. 572155192Srwatson * 573155192Srwatson * @param source the script source 574155192Srwatson * @return reusable compiled script across many global scopes. 575155192Srwatson */ 576155192Srwatson public MultiGlobalCompiledScript compileScript(final Source source) { 577155192Srwatson final Class<?> clazz = compile(source, this.errors, this._strict); 578155192Srwatson final MethodHandle createProgramFunctionHandle = getCreateProgramFunctionHandle(clazz); 579155192Srwatson 580155192Srwatson return new MultiGlobalCompiledScript() { 581185573Srwatson @Override 582155192Srwatson public ScriptFunction getFunction(final Global newGlobal) { 583155192Srwatson return invokeCreateProgramFunctionHandle(createProgramFunctionHandle, newGlobal); 584155192Srwatson } 585155192Srwatson }; 586155192Srwatson } 587155192Srwatson 588155192Srwatson /** 589155192Srwatson * Entry point for {@code eval} 590155192Srwatson * 591155192Srwatson * @param initialScope The scope of this eval call 592155192Srwatson * @param string Evaluated code as a String 593155192Srwatson * @param callThis "this" to be passed to the evaluated code 594155192Srwatson * @param location location of the eval call 595155192Srwatson * @param strict is this {@code eval} call from a strict mode code? 596155192Srwatson * @return the return value of the {@code eval} 597155192Srwatson */ 598155192Srwatson public Object eval(final ScriptObject initialScope, final String string, 599155192Srwatson final Object callThis, final Object location, final boolean strict) { 600155192Srwatson return eval(initialScope, string, callThis, location, strict, false); 601155192Srwatson } 602155192Srwatson 603155192Srwatson /** 604155192Srwatson * Entry point for {@code eval} 605155192Srwatson * 606155192Srwatson * @param initialScope The scope of this eval call 607155192Srwatson * @param string Evaluated code as a String 608155192Srwatson * @param callThis "this" to be passed to the evaluated code 609155192Srwatson * @param location location of the eval call 610155192Srwatson * @param strict is this {@code eval} call from a strict mode code? 611185573Srwatson * @param evalCall is this called from "eval" builtin? 612155192Srwatson * 613155192Srwatson * @return the return value of the {@code eval} 614155192Srwatson */ 615155192Srwatson public Object eval(final ScriptObject initialScope, final String string, 616155192Srwatson final Object callThis, final Object location, final boolean strict, final boolean evalCall) { 617155192Srwatson final String file = location == UNDEFINED || location == null ? "<eval>" : location.toString(); 618155192Srwatson final Source source = sourceFor(file, string, evalCall); 619155192Srwatson final boolean directEval = location != UNDEFINED; // is this direct 'eval' call or indirectly invoked eval? 620186647Srwatson final Global global = Context.getGlobal(); 621186647Srwatson ScriptObject scope = initialScope; 622155192Srwatson 623155192Srwatson // ECMA section 10.1.1 point 2 says eval code is strict if it begins 624155192Srwatson // with "use strict" directive or eval direct call itself is made 625155192Srwatson // from from strict mode code. We are passed with caller's strict mode. 626155192Srwatson boolean strictFlag = directEval && strict; 627155192Srwatson 628155192Srwatson Class<?> clazz = null; 629155192Srwatson try { 630155192Srwatson clazz = compile(source, new ThrowErrorManager(), strictFlag); 631155192Srwatson } catch (final ParserException e) { 632155192Srwatson e.throwAsEcmaException(global); 633155192Srwatson return null; 634155192Srwatson } 635155192Srwatson 636155192Srwatson if (!strictFlag) { 637185573Srwatson // We need to get strict mode flag from compiled class. This is 638155192Srwatson // because eval code may start with "use strict" directive. 639155192Srwatson try { 640155192Srwatson strictFlag = clazz.getField(STRICT_MODE.symbolName()).getBoolean(null); 641155192Srwatson } catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { 642155192Srwatson //ignored 643155192Srwatson strictFlag = false; 644155192Srwatson } 645155192Srwatson } 646155192Srwatson 647155192Srwatson // In strict mode, eval does not instantiate variables and functions 648155192Srwatson // in the caller's environment. A new environment is created! 649155192Srwatson if (strictFlag) { 650155192Srwatson // Create a new scope object 651155192Srwatson final ScriptObject strictEvalScope = global.newObject(); 652155192Srwatson 653155192Srwatson // bless it as a "scope" 654155192Srwatson strictEvalScope.setIsScope(); 655155192Srwatson 656155192Srwatson // set given scope to be it's proto so that eval can still 657155192Srwatson // access caller environment vars in the new environment. 658155192Srwatson strictEvalScope.setProto(scope); 659155192Srwatson scope = strictEvalScope; 660155192Srwatson } 661155192Srwatson 662155192Srwatson final ScriptFunction func = getProgramFunction(clazz, scope); 663155192Srwatson Object evalThis; 664155192Srwatson if (directEval) { 665155192Srwatson evalThis = callThis instanceof ScriptObject || strictFlag ? callThis : global; 666155192Srwatson } else { 667155192Srwatson evalThis = global; 668155192Srwatson } 669155192Srwatson 670155192Srwatson return ScriptRuntime.apply(func, evalThis); 671155192Srwatson } 672155192Srwatson 673155192Srwatson private static Source loadInternal(final String srcStr, final String prefix, final String resourcePath) { 674155192Srwatson if (srcStr.startsWith(prefix)) { 675155192Srwatson final String resource = resourcePath + srcStr.substring(prefix.length()); 676155192Srwatson // NOTE: even sandbox scripts should be able to load scripts in nashorn: scheme 677155192Srwatson // These scripts are always available and are loaded from nashorn.jar's resources. 678155192Srwatson return AccessController.doPrivileged( 679155192Srwatson new PrivilegedAction<Source>() { 680155192Srwatson @Override 681155192Srwatson public Source run() { 682155192Srwatson try { 683155192Srwatson final URL resURL = Context.class.getResource(resource); 684155192Srwatson return resURL != null ? sourceFor(srcStr, resURL) : null; 685155192Srwatson } catch (final IOException exp) { 686186647Srwatson return null; 687186647Srwatson } 688186647Srwatson } 689186647Srwatson }); 690186647Srwatson } 691186647Srwatson 692186647Srwatson return null; 693159259Srwatson } 694155192Srwatson 695155192Srwatson /** 696155192Srwatson * Implementation of {@code load} Nashorn extension. Load a script file from a source 697155192Srwatson * expression 698155192Srwatson * 699168783Srwatson * @param scope the scope 700168783Srwatson * @param from source expression for script 701155192Srwatson * 702168783Srwatson * @return return value for load call (undefined) 703168783Srwatson * 704155192Srwatson * @throws IOException if source cannot be found or loaded 705168783Srwatson */ 706168783Srwatson public Object load(final ScriptObject scope, final Object from) throws IOException { 707168783Srwatson final Object src = from instanceof ConsString ? from.toString() : from; 708168783Srwatson Source source = null; 709168783Srwatson 710168783Srwatson // load accepts a String (which could be a URL or a file name), a File, a URL 711168783Srwatson // or a ScriptObject that has "name" and "source" (string valued) properties. 712168783Srwatson if (src instanceof String) { 713168783Srwatson final String srcStr = (String)src; 714168783Srwatson if (srcStr.startsWith(LOAD_CLASSPATH)) { 715168783Srwatson final URL url = getResourceURL(srcStr.substring(LOAD_CLASSPATH.length())); 716168783Srwatson source = url != null ? sourceFor(url.toString(), url) : null; 717186647Srwatson } else { 718186647Srwatson final File file = new File(srcStr); 719186647Srwatson if (srcStr.indexOf(':') != -1) { 720186647Srwatson if ((source = loadInternal(srcStr, LOAD_NASHORN, "resources/")) == null && 721186647Srwatson (source = loadInternal(srcStr, LOAD_FX, "resources/fx/")) == null) { 722186647Srwatson URL url; 723186647Srwatson try { 724168783Srwatson //check for malformed url. if malformed, it may still be a valid file 725168783Srwatson url = new URL(srcStr); 726168783Srwatson } catch (final MalformedURLException e) { 727155192Srwatson url = file.toURI().toURL(); 728155192Srwatson } 729155192Srwatson source = sourceFor(url.toString(), url); 730168783Srwatson } 731168783Srwatson } else if (file.isFile()) { 732155192Srwatson source = sourceFor(srcStr, file); 733155192Srwatson } 734155192Srwatson } 735155192Srwatson } else if (src instanceof File && ((File)src).isFile()) { 736155192Srwatson final File file = (File)src; 737155192Srwatson source = sourceFor(file.getName(), file); 738155192Srwatson } else if (src instanceof URL) { 739155192Srwatson final URL url = (URL)src; 740155192Srwatson source = sourceFor(url.toString(), url); 741155192Srwatson } else if (src instanceof ScriptObject) { 742155192Srwatson final ScriptObject sobj = (ScriptObject)src; 743155192Srwatson if (sobj.has("script") && sobj.has("name")) { 744155192Srwatson final String script = JSType.toString(sobj.get("script")); 745155192Srwatson final String name = JSType.toString(sobj.get("name")); 746155192Srwatson source = sourceFor(name, script); 747155192Srwatson } 748155192Srwatson } else if (src instanceof Map) { 749155192Srwatson final Map<?,?> map = (Map<?,?>)src; 750185573Srwatson if (map.containsKey("script") && map.containsKey("name")) { 751155192Srwatson final String script = JSType.toString(map.get("script")); 752155192Srwatson final String name = JSType.toString(map.get("name")); 753155192Srwatson source = sourceFor(name, script); 754155192Srwatson } 755155192Srwatson } 756155192Srwatson 757155192Srwatson if (source != null) { 758155192Srwatson return evaluateSource(source, scope, scope); 759168688Scsjp } 760168688Scsjp 761185573Srwatson throw typeError("cant.load.script", ScriptRuntime.safeToString(from)); 762185573Srwatson } 763185573Srwatson 764159686Swsalamon /** 765185573Srwatson * Implementation of {@code loadWithNewGlobal} Nashorn extension. Load a script file from a source 766185573Srwatson * expression, after creating a new global scope. 767155192Srwatson * 768155192Srwatson * @param from source expression for script 769155192Srwatson * @param args (optional) arguments to be passed to the loaded script 770155192Srwatson * 771155192Srwatson * @return return value for load call (undefined) 772155192Srwatson * 773155192Srwatson * @throws IOException if source cannot be found or loaded 774155192Srwatson */ 775155192Srwatson public Object loadWithNewGlobal(final Object from, final Object...args) throws IOException { 776155192Srwatson final Global oldGlobal = getGlobal(); 777155192Srwatson final Global newGlobal = AccessController.doPrivileged(new PrivilegedAction<Global>() { 778185573Srwatson @Override 779185573Srwatson public Global run() { 780185573Srwatson try { 781185573Srwatson return newGlobal(); 782185573Srwatson } catch (final RuntimeException e) { 783185573Srwatson if (Context.DEBUG) { 784170131Srwatson e.printStackTrace(); 785155192Srwatson } 786155192Srwatson throw e; 787155192Srwatson } 788155192Srwatson } 789155192Srwatson }, CREATE_GLOBAL_ACC_CTXT); 790155192Srwatson // initialize newly created Global instance 791155192Srwatson initGlobal(newGlobal); 792168783Srwatson setGlobal(newGlobal); 793168783Srwatson 794155192Srwatson final Object[] wrapped = args == null? ScriptRuntime.EMPTY_ARRAY : ScriptObjectMirror.wrapArray(args, oldGlobal); 795168783Srwatson newGlobal.put("arguments", newGlobal.wrapAsObject(wrapped), env._strict); 796168783Srwatson 797168783Srwatson try { 798168783Srwatson // wrap objects from newGlobal's world as mirrors - but if result 799168783Srwatson // is from oldGlobal's world, unwrap it! 800168783Srwatson return ScriptObjectMirror.unwrap(ScriptObjectMirror.wrap(load(newGlobal, from), newGlobal), oldGlobal); 801168783Srwatson } finally { 802168783Srwatson setGlobal(oldGlobal); 803168783Srwatson } 804168783Srwatson } 805168783Srwatson 806168783Srwatson /** 807168783Srwatson * Load or get a structure class. Structure class names are based on the number of parameter fields 808168783Srwatson * and {@link AccessorProperty} fields in them. Structure classes are used to represent ScriptObjects 809168783Srwatson * 810168783Srwatson * @see ObjectClassGenerator 811168783Srwatson * @see AccessorProperty 812168783Srwatson * @see ScriptObject 813168783Srwatson * 814168783Srwatson * @param fullName full name of class, e.g. jdk.nashorn.internal.objects.JO2P1 contains 2 fields and 1 parameter. 815168783Srwatson * 816168783Srwatson * @return the {@code Class<?>} for this structure 817168783Srwatson * 818168783Srwatson * @throws ClassNotFoundException if structure class cannot be resolved 819168783Srwatson */ 820168783Srwatson @SuppressWarnings("unchecked") 821168783Srwatson public static Class<? extends ScriptObject> forStructureClass(final String fullName) throws ClassNotFoundException { 822168783Srwatson if (System.getSecurityManager() != null && !StructureLoader.isStructureClass(fullName)) { 823168783Srwatson throw new ClassNotFoundException(fullName); 824168783Srwatson } 825155192Srwatson return (Class<? extends ScriptObject>)Class.forName(fullName, true, sharedLoader); 826155192Srwatson } 827155192Srwatson 828155192Srwatson /** 829155192Srwatson * Checks that the given Class can be accessed from no permissions context. 830155192Srwatson * 831155192Srwatson * @param clazz Class object 832155192Srwatson * @throws SecurityException if not accessible 833155192Srwatson */ 834155192Srwatson public static void checkPackageAccess(final Class<?> clazz) { 835155192Srwatson final SecurityManager sm = System.getSecurityManager(); 836255219Spjd if (sm != null) { 837255219Spjd Class<?> bottomClazz = clazz; 838255219Spjd while (bottomClazz.isArray()) { 839255219Spjd bottomClazz = bottomClazz.getComponentType(); 840255219Spjd } 841255219Spjd checkPackageAccess(sm, bottomClazz.getName()); 842255219Spjd } 843255219Spjd } 844255219Spjd 845255219Spjd /** 846255219Spjd * Checks that the given package name can be accessed from no permissions context. 847255219Spjd * 848255219Spjd * @param pkgName package name 849255219Spjd * @throws SecurityException if not accessible 850255219Spjd */ 851255219Spjd public static void checkPackageAccess(final String pkgName) { 852155192Srwatson final SecurityManager sm = System.getSecurityManager(); 853155192Srwatson if (sm != null) { 854155192Srwatson checkPackageAccess(sm, pkgName.endsWith(".") ? pkgName : pkgName + "."); 855155192Srwatson } 856155192Srwatson } 857155192Srwatson 858155192Srwatson /** 859155192Srwatson * Checks that the given package can be accessed from no permissions context. 860155192Srwatson * 861155192Srwatson * @param sm current security manager instance 862155192Srwatson * @param fullName fully qualified package name 863155192Srwatson * @throw SecurityException if not accessible 864155192Srwatson */ 865155192Srwatson private static void checkPackageAccess(final SecurityManager sm, final String fullName) { 866155192Srwatson sm.getClass(); // null check 867155192Srwatson final int index = fullName.lastIndexOf('.'); 868155192Srwatson if (index != -1) { 869155192Srwatson final String pkgName = fullName.substring(0, index); 870155192Srwatson AccessController.doPrivileged(new PrivilegedAction<Void>() { 871155192Srwatson @Override 872155192Srwatson public Void run() { 873155192Srwatson sm.checkPackageAccess(pkgName); 874155192Srwatson return null; 875155192Srwatson } 876155192Srwatson }, NO_PERMISSIONS_ACC_CTXT); 877155192Srwatson } 878155192Srwatson } 879155192Srwatson 880155192Srwatson /** 881155192Srwatson * Checks that the given Class can be accessed from no permissions context. 882155192Srwatson * 883155192Srwatson * @param clazz Class object 884155192Srwatson * @return true if package is accessible, false otherwise 885155192Srwatson */ 886155192Srwatson private static boolean isAccessiblePackage(final Class<?> clazz) { 887155192Srwatson try { 888155192Srwatson checkPackageAccess(clazz); 889155192Srwatson return true; 890155192Srwatson } catch (final SecurityException se) { 891155192Srwatson return false; 892155192Srwatson } 893155192Srwatson } 894155192Srwatson 895155192Srwatson /** 896155192Srwatson * Checks that the given Class is public and it can be accessed from no permissions context. 897155192Srwatson * 898155192Srwatson * @param clazz Class object to check 899155192Srwatson * @return true if Class is accessible, false otherwise 900155192Srwatson */ 901155192Srwatson public static boolean isAccessibleClass(final Class<?> clazz) { 902155192Srwatson return Modifier.isPublic(clazz.getModifiers()) && Context.isAccessiblePackage(clazz); 903155192Srwatson } 904155192Srwatson 905155192Srwatson /** 906155192Srwatson * Lookup a Java class. This is used for JSR-223 stuff linking in from 907155192Srwatson * {@code jdk.nashorn.internal.objects.NativeJava} and {@code jdk.nashorn.internal.runtime.NativeJavaPackage} 908155192Srwatson * 909155192Srwatson * @param fullName full name of class to load 910155192Srwatson * 911155192Srwatson * @return the {@code Class<?>} for the name 912155192Srwatson * 913155192Srwatson * @throws ClassNotFoundException if class cannot be resolved 914186647Srwatson */ 915155192Srwatson public Class<?> findClass(final String fullName) throws ClassNotFoundException { 916186647Srwatson if (fullName.indexOf('[') != -1 || fullName.indexOf('/') != -1) { 917155192Srwatson // don't allow array class names or internal names. 918186647Srwatson throw new ClassNotFoundException(fullName); 919155192Srwatson } 920186647Srwatson 921187214Srwatson // give chance to ClassFilter to filter out, if present 922187214Srwatson if (classFilter != null && !classFilter.exposeToScripts(fullName)) { 923187214Srwatson throw new ClassNotFoundException(fullName); 924155192Srwatson } 925155192Srwatson 926186647Srwatson // check package access as soon as possible! 927186647Srwatson final SecurityManager sm = System.getSecurityManager(); 928155192Srwatson if (sm != null) { 929186647Srwatson checkPackageAccess(sm, fullName); 930186647Srwatson } 931186647Srwatson 932186647Srwatson // try the script -classpath loader, if that is set 933155192Srwatson if (classPathLoader != null) { 934186647Srwatson try { 935186647Srwatson return Class.forName(fullName, true, classPathLoader); 936186647Srwatson } catch (final ClassNotFoundException ignored) { 937186647Srwatson // ignore, continue search 938186647Srwatson } 939189279Srwatson } 940186647Srwatson 941186647Srwatson // Try finding using the "app" loader. 942186647Srwatson return Class.forName(fullName, true, appLoader); 943186647Srwatson } 944189279Srwatson 945189279Srwatson /** 946186647Srwatson * Hook to print stack trace for a {@link Throwable} that occurred during 947186647Srwatson * execution 948186647Srwatson * 949186647Srwatson * @param t throwable for which to dump stack 950186647Srwatson */ 951186647Srwatson public static void printStackTrace(final Throwable t) { 952186647Srwatson if (Context.DEBUG) { 953186647Srwatson t.printStackTrace(Context.getCurrentErr()); 954186647Srwatson } 955186647Srwatson } 956186647Srwatson 957186647Srwatson /** 958186647Srwatson * Verify generated bytecode before emission. This is called back from the 959186647Srwatson * {@link ObjectClassGenerator} or the {@link Compiler}. If the "--verify-code" parameter 960186647Srwatson * hasn't been given, this is a nop 961186647Srwatson * 962186647Srwatson * Note that verification may load classes -- we don't want to do that unless 963186647Srwatson * user specified verify option. We check it here even though caller 964186647Srwatson * may have already checked that flag 965155192Srwatson * 966155192Srwatson * @param bytecode bytecode to verify 967155192Srwatson */ 968155192Srwatson public void verify(final byte[] bytecode) { 969186647Srwatson if (env._verify_code) { 970186647Srwatson // No verification when security manager is around as verifier 971155192Srwatson // may load further classes - which should be avoided. 972155192Srwatson if (System.getSecurityManager() == null) { 973155192Srwatson CheckClassAdapter.verify(new ClassReader(bytecode), sharedLoader, false, new PrintWriter(System.err, true)); 974155192Srwatson } 975155192Srwatson } 976155192Srwatson } 977155192Srwatson 978155192Srwatson /** 979155192Srwatson * Create and initialize a new global scope object. 980155192Srwatson * 981155192Srwatson * @return the initialized global scope object. 982186647Srwatson */ 983185573Srwatson public Global createGlobal() { 984155192Srwatson return initGlobal(newGlobal()); 985155192Srwatson } 986155192Srwatson 987155192Srwatson /** 988155192Srwatson * Create a new uninitialized global scope object 989155192Srwatson * @return the global script object 990155192Srwatson */ 991155192Srwatson public Global newGlobal() { 992155192Srwatson return new Global(this); 993155192Srwatson } 994155192Srwatson 995155192Srwatson /** 996155192Srwatson * Initialize given global scope object. 997155192Srwatson * 998155192Srwatson * @param global the global 999195740Srwatson * @param engine the associated ScriptEngine instance, can be null 1000155192Srwatson * @return the initialized global scope object. 1001155192Srwatson */ 1002155192Srwatson public Global initGlobal(final Global global, final ScriptEngine engine) { 1003155192Srwatson // Need only minimal global object, if we are just compiling. 1004155192Srwatson if (!env._compile_only) { 1005155192Srwatson final Global oldGlobal = Context.getGlobal(); 1006155192Srwatson try { 1007155192Srwatson Context.setGlobal(global); 1008155192Srwatson // initialize global scope with builtin global objects 1009185573Srwatson global.initBuiltinObjects(engine); 1010155192Srwatson } finally { 1011155192Srwatson Context.setGlobal(oldGlobal); 1012155192Srwatson } 1013155192Srwatson } 1014155192Srwatson 1015155192Srwatson return global; 1016155192Srwatson } 1017155192Srwatson 1018155192Srwatson /** 1019155192Srwatson * Initialize given global scope object. 1020155192Srwatson * 1021155192Srwatson * @param global the global 1022155192Srwatson * @return the initialized global scope object. 1023155192Srwatson */ 1024155192Srwatson public Global initGlobal(final Global global) { 1025155192Srwatson return initGlobal(global, null); 1026155192Srwatson } 1027155192Srwatson 1028155192Srwatson /** 1029159259Srwatson * Return the current global's context 1030155192Srwatson * @return current global's context 1031159259Srwatson */ 1032159259Srwatson static Context getContextTrusted() { 1033155192Srwatson return ((ScriptObject)Context.getGlobal()).getContext(); 1034155192Srwatson } 1035155192Srwatson 1036159259Srwatson static Context getContextTrustedOrNull() { 1037159259Srwatson final Global global = Context.getGlobal(); 1038159259Srwatson return global == null ? null : ((ScriptObject)global).getContext(); 1039159259Srwatson } 1040159259Srwatson 1041159259Srwatson /** 1042159259Srwatson * Try to infer Context instance from the Class. If we cannot, 1043159259Srwatson * then get it from the thread local variable. 1044180709Srwatson * 1045159259Srwatson * @param clazz the class 1046159259Srwatson * @return context 1047159259Srwatson */ 1048159259Srwatson static Context fromClass(final Class<?> clazz) { 1049155192Srwatson final ClassLoader loader = clazz.getClassLoader(); 1050155192Srwatson 1051155192Srwatson if (loader instanceof ScriptLoader) { 1052155192Srwatson return ((ScriptLoader)loader).getContext(); 1053155192Srwatson } 1054155192Srwatson 1055155192Srwatson return Context.getContextTrusted(); 1056155192Srwatson } 1057155192Srwatson 1058155192Srwatson private URL getResourceURL(final String resName) { 1059155192Srwatson // try the classPathLoader if we have and then 1060155192Srwatson // try the appLoader if non-null. 1061155192Srwatson if (classPathLoader != null) { 1062155192Srwatson return classPathLoader.getResource(resName); 1063155192Srwatson } else if (appLoader != null) { 1064186647Srwatson return appLoader.getResource(resName); 1065186647Srwatson } 1066186647Srwatson 1067180709Srwatson return null; 1068155192Srwatson } 1069155192Srwatson 1070155192Srwatson private Object evaluateSource(final Source source, final ScriptObject scope, final ScriptObject thiz) { 1071155192Srwatson ScriptFunction script = null; 1072159259Srwatson 1073155192Srwatson try { 1074155192Srwatson script = compileScript(source, scope, new Context.ThrowErrorManager()); 1075155192Srwatson } catch (final ParserException e) { 1076155192Srwatson e.throwAsEcmaException(); 1077155192Srwatson } 1078155192Srwatson 1079155192Srwatson return ScriptRuntime.apply(script, thiz); 1080155192Srwatson } 1081155192Srwatson 1082155192Srwatson private static ScriptFunction getProgramFunction(final Class<?> script, final ScriptObject scope) { 1083155192Srwatson if (script == null) { 1084155192Srwatson return null; 1085155192Srwatson } 1086155192Srwatson return invokeCreateProgramFunctionHandle(getCreateProgramFunctionHandle(script), scope); 1087155192Srwatson } 1088155192Srwatson 1089155192Srwatson private static MethodHandle getCreateProgramFunctionHandle(final Class<?> script) { 1090155192Srwatson try { 1091155192Srwatson return LOOKUP.findStatic(script, CREATE_PROGRAM_FUNCTION.symbolName(), CREATE_PROGRAM_FUNCTION_TYPE); 1092155192Srwatson } catch (NoSuchMethodException | IllegalAccessException e) { 1093155192Srwatson throw new AssertionError("Failed to retrieve a handle for the program function for " + script.getName(), e); 1094155192Srwatson } 1095155192Srwatson } 1096155192Srwatson 1097155192Srwatson private static ScriptFunction invokeCreateProgramFunctionHandle(final MethodHandle createProgramFunctionHandle, final ScriptObject scope) { 1098155192Srwatson try { 1099155192Srwatson return (ScriptFunction)createProgramFunctionHandle.invokeExact(scope); 1100155192Srwatson } catch (final RuntimeException|Error e) { 1101155192Srwatson throw e; 1102155192Srwatson } catch (final Throwable t) { 1103155192Srwatson throw new AssertionError("Failed to create a program function", t); 1104155192Srwatson } 1105155192Srwatson } 1106155192Srwatson 1107155192Srwatson private ScriptFunction compileScript(final Source source, final ScriptObject scope, final ErrorManager errMan) { 1108155192Srwatson return getProgramFunction(compile(source, errMan, this._strict), scope); 1109155192Srwatson } 1110155192Srwatson 1111155192Srwatson private synchronized Class<?> compile(final Source source, final ErrorManager errMan, final boolean strict) { 1112155192Srwatson // start with no errors, no warnings. 1113155192Srwatson errMan.reset(); 1114155192Srwatson 1115159259Srwatson Class<?> script = findCachedClass(source); 1116155192Srwatson if (script != null) { 1117155192Srwatson final DebugLogger log = getLogger(Compiler.class); 1118155192Srwatson if (log.isEnabled()) { 1119155192Srwatson log.fine(new RuntimeEvent<>(Level.INFO, source), "Code cache hit for ", source, " avoiding recompile."); 1120155192Srwatson } 1121155192Srwatson return script; 1122155192Srwatson } 1123155192Srwatson 1124168783Srwatson StoredScript storedScript = null; 1125168783Srwatson FunctionNode functionNode = null; 1126155192Srwatson final boolean useCodeStore = env._persistent_cache && !env._parse_only && !env._optimistic_types; 1127168783Srwatson final String cacheKey = useCodeStore ? CodeStore.getCacheKey(0, null) : null; 1128168783Srwatson 1129168783Srwatson if (useCodeStore) { 1130168783Srwatson storedScript = codeStore.load(source, cacheKey); 1131168783Srwatson } 1132168783Srwatson 1133168783Srwatson if (storedScript == null) { 1134168783Srwatson functionNode = new Parser(env, source, errMan, strict, getLogger(Parser.class)).parse(); 1135168783Srwatson 1136168783Srwatson if (errMan.hasErrors()) { 1137168783Srwatson return null; 1138168783Srwatson } 1139168783Srwatson 1140168783Srwatson if (env._print_ast || functionNode.getFlag(FunctionNode.IS_PRINT_AST)) { 1141168783Srwatson getErr().println(new ASTWriter(functionNode)); 1142155192Srwatson } 1143155192Srwatson 1144155192Srwatson if (env._print_parse || functionNode.getFlag(FunctionNode.IS_PRINT_PARSE)) { 1145155192Srwatson getErr().println(new PrintVisitor(functionNode, true, false)); 1146155192Srwatson } 1147155192Srwatson } 1148155192Srwatson 1149155192Srwatson if (env._parse_only) { 1150155192Srwatson return null; 1151155192Srwatson } 1152155192Srwatson 1153155192Srwatson final URL url = source.getURL(); 1154155192Srwatson final ScriptLoader loader = env._loader_per_compile ? createNewLoader() : scriptLoader; 1155155192Srwatson final CodeSource cs = new CodeSource(url, (CodeSigner[])null); 1156155192Srwatson final CodeInstaller<ScriptEnvironment> installer = new ContextCodeInstaller(this, loader, cs); 1157155192Srwatson 1158155192Srwatson if (storedScript == null) { 1159155192Srwatson final CompilationPhases phases = Compiler.CompilationPhases.COMPILE_ALL; 1160155192Srwatson 1161155192Srwatson final Compiler compiler = new Compiler( 1162155192Srwatson this, 1163155192Srwatson env, 1164155192Srwatson installer, 1165185573Srwatson source, 1166155192Srwatson errMan, 1167155192Srwatson strict | functionNode.isStrict()); 1168155192Srwatson 1169155192Srwatson final FunctionNode compiledFunction = compiler.compile(functionNode, phases); 1170155192Srwatson if (errMan.hasErrors()) { 1171155192Srwatson return null; 1172155192Srwatson } 1173155192Srwatson script = compiledFunction.getRootClass(); 1174168688Scsjp compiler.persistClassInfo(cacheKey, compiledFunction); 1175168688Scsjp } else { 1176185573Srwatson Compiler.updateCompilationId(storedScript.getCompilationId()); 1177185573Srwatson script = install(storedScript, source, installer); 1178185573Srwatson } 1179159686Swsalamon 1180159686Swsalamon cacheClass(source, script); 1181185573Srwatson return script; 1182159686Swsalamon } 1183155192Srwatson 1184155192Srwatson private ScriptLoader createNewLoader() { 1185155192Srwatson return AccessController.doPrivileged( 1186155192Srwatson new PrivilegedAction<ScriptLoader>() { 1187155192Srwatson @Override 1188155192Srwatson public ScriptLoader run() { 1189155192Srwatson return new ScriptLoader(appLoader, Context.this); 1190155192Srwatson } 1191155192Srwatson }, CREATE_LOADER_ACC_CTXT); 1192155192Srwatson } 1193155192Srwatson 1194185573Srwatson private long getUniqueScriptId() { 1195168688Scsjp return uniqueScriptId.getAndIncrement(); 1196185573Srwatson } 1197168688Scsjp 1198170131Srwatson /** 1199155192Srwatson * Install a previously compiled class from the code cache. 1200155192Srwatson * 1201155192Srwatson * @param storedScript cached script containing class bytes and constants 1202155192Srwatson * @return main script class 1203155192Srwatson */ 1204155192Srwatson private static Class<?> install(final StoredScript storedScript, final Source source, final CodeInstaller<ScriptEnvironment> installer) { 1205155192Srwatson 1206168783Srwatson final Map<String, Class<?>> installedClasses = new HashMap<>(); 1207168783Srwatson final Map<String, byte[]> classBytes = storedScript.getClassBytes(); 1208155192Srwatson final Object[] constants = storedScript.getConstants(); 1209185573Srwatson final String mainClassName = storedScript.getMainClassName(); 1210185573Srwatson final byte[] mainClassBytes = classBytes.get(mainClassName); 1211185573Srwatson final Class<?> mainClass = installer.install(mainClassName, mainClassBytes); 1212168783Srwatson final Map<Integer, FunctionInitializer> initialzers = storedScript.getInitializers(); 1213168783Srwatson 1214168783Srwatson installedClasses.put(mainClassName, mainClass); 1215168783Srwatson 1216185573Srwatson for (final Map.Entry<String, byte[]> entry : classBytes.entrySet()) { 1217168783Srwatson final String className = entry.getKey(); 1218168783Srwatson if (className.equals(mainClassName)) { 1219168783Srwatson continue; 1220168783Srwatson } 1221168783Srwatson final byte[] code = entry.getValue(); 1222168783Srwatson 1223168783Srwatson installedClasses.put(className, installer.install(className, code)); 1224168783Srwatson } 1225168783Srwatson 1226168783Srwatson installer.initialize(installedClasses.values(), source, constants); 1227168783Srwatson 1228168783Srwatson for (final Object constant : constants) { 1229168783Srwatson if (constant instanceof RecompilableScriptFunctionData) { 1230168783Srwatson final RecompilableScriptFunctionData data = (RecompilableScriptFunctionData) constant; 1231168783Srwatson data.initTransients(source, installer); 1232168783Srwatson if (initialzers != null) { 1233168783Srwatson final FunctionInitializer initializer = initialzers.get(data.getFunctionNodeId()); 1234168783Srwatson initializer.setCode(installedClasses.get(initializer.getClassName())); 1235168783Srwatson data.initializeCode(initializer); 1236168783Srwatson } 1237155192Srwatson } 1238155192Srwatson } 1239155192Srwatson 1240155192Srwatson return mainClass; 1241155192Srwatson } 1242155192Srwatson 1243155192Srwatson /** 1244155192Srwatson * Cache for compiled script classes. 1245155192Srwatson */ 1246155192Srwatson @SuppressWarnings("serial") 1247155192Srwatson private static class ClassCache extends LinkedHashMap<Source, ClassReference> { 1248156291Srwatson private final int size; 1249155192Srwatson private final ReferenceQueue<Class<?>> queue; 1250185573Srwatson 1251185573Srwatson ClassCache(final int size) { 1252155192Srwatson super(size, 0.75f, true); 1253155192Srwatson this.size = size; 1254155192Srwatson this.queue = new ReferenceQueue<>(); 1255155192Srwatson } 1256155192Srwatson 1257195740Srwatson void cache(final Source source, final Class<?> clazz) { 1258155192Srwatson put(source, new ClassReference(clazz, queue, source)); 1259195740Srwatson } 1260195740Srwatson 1261195740Srwatson @Override 1262195740Srwatson protected boolean removeEldestEntry(final Map.Entry<Source, ClassReference> eldest) { 1263195740Srwatson return size() > size; 1264195740Srwatson } 1265195740Srwatson 1266195740Srwatson @Override 1267195740Srwatson public ClassReference get(final Object key) { 1268195740Srwatson for (ClassReference ref; (ref = (ClassReference)queue.poll()) != null; ) { 1269195740Srwatson remove(ref.source); 1270195740Srwatson } 1271195740Srwatson return super.get(key); 1272243751Srwatson } 1273195740Srwatson 1274243751Srwatson } 1275155192Srwatson 1276195740Srwatson private static class ClassReference extends SoftReference<Class<?>> { 1277195740Srwatson private final Source source; 1278155192Srwatson 1279155192Srwatson ClassReference(final Class<?> clazz, final ReferenceQueue<Class<?>> queue, final Source source) { 1280155192Srwatson super(clazz, queue); 1281161813Swsalamon this.source = source; 1282161813Swsalamon } 1283161813Swsalamon } 1284161813Swsalamon 1285161813Swsalamon // Class cache management 1286161813Swsalamon private Class<?> findCachedClass(final Source source) { 1287161813Swsalamon final ClassReference ref = classCache == null ? null : classCache.get(source); 1288161813Swsalamon return ref != null ? ref.get() : null; 1289161813Swsalamon } 1290161813Swsalamon 1291161813Swsalamon private void cacheClass(final Source source, final Class<?> clazz) { 1292161813Swsalamon if (classCache != null) { 1293161813Swsalamon classCache.cache(source, clazz); 1294161813Swsalamon } 1295161813Swsalamon } 1296161813Swsalamon 1297161813Swsalamon // logging 1298161813Swsalamon private final Map<String, DebugLogger> loggers = new HashMap<>(); 1299161813Swsalamon 1300161813Swsalamon private void initLoggers() { 1301161813Swsalamon ((Loggable)MethodHandleFactory.getFunctionality()).initLogger(this); 1302161813Swsalamon } 1303161813Swsalamon 1304161813Swsalamon /** 1305161813Swsalamon * Get a logger, given a loggable class 1306155192Srwatson * @param clazz a Loggable class 1307155192Srwatson * @return debuglogger associated with that class 1308155192Srwatson */ 1309155192Srwatson public DebugLogger getLogger(final Class<? extends Loggable> clazz) { 1310155192Srwatson return getLogger(clazz, null); 1311155192Srwatson } 1312161813Swsalamon 1313155192Srwatson /** 1314162465Srwatson * Get a logger, given a loggable class 1315161813Swsalamon * @param clazz a Loggable class 1316161813Swsalamon * @param initHook an init hook - if this is the first time the logger is created in the context, run the init hook 1317161813Swsalamon * @return debuglogger associated with that class 1318161813Swsalamon */ 1319161813Swsalamon public DebugLogger getLogger(final Class<? extends Loggable> clazz, final Consumer<DebugLogger> initHook) { 1320161813Swsalamon final String name = getLoggerName(clazz); 1321161813Swsalamon DebugLogger logger = loggers.get(name); 1322161813Swsalamon if (logger == null) { 1323161813Swsalamon if (!env.hasLogger(name)) { 1324161813Swsalamon return DebugLogger.DISABLED_LOGGER; 1325161813Swsalamon } 1326162465Srwatson final LoggerInfo info = env._loggers.get(name); 1327161813Swsalamon logger = new DebugLogger(name, info.getLevel(), info.isQuiet()); 1328161813Swsalamon if (initHook != null) { 1329161813Swsalamon initHook.accept(logger); 1330161813Swsalamon } 1331161813Swsalamon loggers.put(name, logger); 1332161813Swsalamon } 1333161813Swsalamon return logger; 1334161813Swsalamon } 1335161813Swsalamon 1336161813Swsalamon /** 1337161813Swsalamon * Given a Loggable class, weave debug info info a method handle for that logger. 1338155192Srwatson * Level.INFO is used 1339155192Srwatson * 1340155192Srwatson * @param clazz loggable 1341155192Srwatson * @param mh method handle 1342155192Srwatson * @param text debug printout to add 1343155192Srwatson * 1344161813Swsalamon * @return instrumented method handle, or null if logger not enabled 1345155192Srwatson */ 1346155192Srwatson public MethodHandle addLoggingToHandle(final Class<? extends Loggable> clazz, final MethodHandle mh, final Supplier<String> text) { 1347155192Srwatson return addLoggingToHandle(clazz, Level.INFO, mh, Integer.MAX_VALUE, false, text); 1348155192Srwatson } 1349155192Srwatson 1350155192Srwatson /** 1351155192Srwatson * Given a Loggable class, weave debug info info a method handle for that logger. 1352161813Swsalamon * 1353155192Srwatson * @param clazz loggable 1354155192Srwatson * @param level log level 1355155192Srwatson * @param mh method handle 1356155192Srwatson * @param paramStart first parameter to print 1357155192Srwatson * @param printReturnValue should we print the return vaulue? 1358155192Srwatson * @param text debug printout to add 1359155192Srwatson * 1360155192Srwatson * @return instrumented method handle, or null if logger not enabled 1361161813Swsalamon */ 1362155192Srwatson public MethodHandle addLoggingToHandle(final Class<? extends Loggable> clazz, final Level level, final MethodHandle mh, final int paramStart, final boolean printReturnValue, final Supplier<String> text) { 1363155192Srwatson final DebugLogger log = getLogger(clazz); 1364155192Srwatson if (log.isEnabled()) { 1365155192Srwatson return MethodHandleFactory.addDebugPrintout(log, level, mh, paramStart, printReturnValue, text.get()); 1366155192Srwatson } 1367155192Srwatson return mh; 1368155192Srwatson } 1369155192Srwatson 1370155192Srwatson private static String getLoggerName(final Class<?> clazz) { 1371155192Srwatson Class<?> current = clazz; 1372155192Srwatson while (current != null) { 1373155192Srwatson final Logger log = current.getAnnotation(Logger.class); 1374161813Swsalamon if (log != null) { 1375155192Srwatson assert !"".equals(log.name()); 1376155192Srwatson return log.name(); 1377155192Srwatson } 1378155192Srwatson current = current.getSuperclass(); 1379155192Srwatson } 1380155192Srwatson assert false; 1381155192Srwatson return null; 1382161813Swsalamon } 1383155192Srwatson 1384155192Srwatson /** 1385155192Srwatson * This is a special kind of switchpoint used to guard builtin 1386155192Srwatson * properties and prototypes. In the future it might contain 1387155192Srwatson * logic to e.g. multiple switchpoint classes. 1388155192Srwatson */ 1389155192Srwatson public static final class BuiltinSwitchPoint extends SwitchPoint { 1390161813Swsalamon //empty 1391155192Srwatson } 1392155192Srwatson 1393155192Srwatson /** 1394155192Srwatson * Create a new builtin switchpoint and return it 1395155192Srwatson * @param name key name 1396155192Srwatson * @return new builtin switchpoint 1397155192Srwatson */ 1398155192Srwatson public SwitchPoint newBuiltinSwitchPoint(final String name) { 1399161813Swsalamon assert builtinSwitchPoints.get(name) == null; 1400155192Srwatson final SwitchPoint sp = new BuiltinSwitchPoint(); 1401155192Srwatson builtinSwitchPoints.put(name, sp); 1402155192Srwatson return sp; 1403155192Srwatson } 1404155192Srwatson 1405161813Swsalamon /** 1406155192Srwatson * Return the builtin switchpoint for a particular key name 1407155192Srwatson * @param name key name 1408155192Srwatson * @return builtin switchpoint or null if none 1409186647Srwatson */ 1410186647Srwatson public SwitchPoint getBuiltinSwitchPoint(final String name) { 1411186647Srwatson return builtinSwitchPoints.get(name); 1412186647Srwatson } 1413186647Srwatson 1414186647Srwatson} 1415186647Srwatson