Context.java revision 1709:39dececd7338
1155517Sambrisko/* 2155517Sambrisko * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3155517Sambrisko * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4155517Sambrisko * 5155517Sambrisko * This code is free software; you can redistribute it and/or modify it 6155517Sambrisko * under the terms of the GNU General Public License version 2 only, as 7155517Sambrisko * published by the Free Software Foundation. Oracle designates this 8155517Sambrisko * particular file as subject to the "Classpath" exception as provided 9155517Sambrisko * by Oracle in the LICENSE file that accompanied this code. 10155517Sambrisko * 11155517Sambrisko * This code is distributed in the hope that it will be useful, but WITHOUT 12155517Sambrisko * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13155517Sambrisko * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14155517Sambrisko * version 2 for more details (a copy is included in the LICENSE file that 15155517Sambrisko * accompanied this code). 16155517Sambrisko * 17155517Sambrisko * You should have received a copy of the GNU General Public License version 18155517Sambrisko * 2 along with this work; if not, write to the Free Software Foundation, 19155517Sambrisko * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20155517Sambrisko * 21155517Sambrisko * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22155517Sambrisko * or visit www.oracle.com if you need additional information or have any 23155517Sambrisko * questions. 24155517Sambrisko */ 25155517Sambrisko 26155517Sambriskopackage jdk.nashorn.internal.runtime; 27155517Sambrisko 28155517Sambriskoimport static jdk.internal.org.objectweb.asm.Opcodes.V1_7; 29155517Sambriskoimport static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS; 30155517Sambriskoimport static jdk.nashorn.internal.codegen.CompilerConstants.CREATE_PROGRAM_FUNCTION; 31155517Sambriskoimport static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE; 32162562Sjhbimport static jdk.nashorn.internal.codegen.CompilerConstants.STRICT_MODE; 33162562Sjhbimport static jdk.nashorn.internal.runtime.CodeStore.newCodeStore; 34162562Sjhbimport static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 35155517Sambriskoimport static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; 36155517Sambriskoimport static jdk.nashorn.internal.runtime.Source.sourceFor; 37162562Sjhb 38155517Sambriskoimport java.io.File; 39162562Sjhbimport java.io.InputStream; 40155517Sambriskoimport java.io.IOException; 41162562Sjhbimport java.io.PrintWriter; 42155517Sambriskoimport java.io.UncheckedIOException; 43155517Sambriskoimport java.lang.invoke.MethodHandle; 44155517Sambriskoimport java.lang.invoke.MethodHandles; 45155517Sambriskoimport java.lang.invoke.MethodType; 46155517Sambriskoimport java.lang.invoke.SwitchPoint; 47155517Sambriskoimport java.lang.ref.ReferenceQueue; 48155517Sambriskoimport java.lang.ref.SoftReference; 49155517Sambriskoimport java.lang.module.Configuration; 50155517Sambriskoimport java.lang.module.ModuleDescriptor; 51155517Sambriskoimport java.lang.module.ModuleFinder; 52278321Sjhbimport java.lang.module.ModuleReference; 53278321Sjhbimport java.lang.reflect.Field; 54278321Sjhbimport java.lang.reflect.Layer; 55278321Sjhbimport java.lang.reflect.Modifier; 56278321Sjhbimport java.lang.reflect.Module; 57278321Sjhbimport java.net.MalformedURLException; 58278321Sjhbimport java.net.URL; 59278321Sjhbimport java.security.AccessControlContext; 60278321Sjhbimport java.security.AccessController; 61278321Sjhbimport java.security.CodeSigner; 62278321Sjhbimport java.security.CodeSource; 63278321Sjhbimport java.security.Permissions; 64278321Sjhbimport java.security.PrivilegedAction; 65278321Sjhbimport java.security.PrivilegedActionException; 66278321Sjhbimport java.security.PrivilegedExceptionAction; 67278321Sjhbimport java.security.ProtectionDomain; 68278321Sjhbimport java.util.Collection; 69155517Sambriskoimport java.util.HashMap; 70155517Sambriskoimport java.util.HashSet; 71155517Sambriskoimport java.util.LinkedHashMap; 72155517Sambriskoimport java.util.Map; 73155517Sambriskoimport java.util.Objects; 74155517Sambriskoimport java.util.Optional; 75155517Sambriskoimport java.util.Set; 76155517Sambriskoimport java.util.concurrent.ConcurrentHashMap; 77155517Sambriskoimport java.util.concurrent.ConcurrentMap; 78182322Sjhbimport java.util.concurrent.atomic.AtomicLong; 79155517Sambriskoimport java.util.concurrent.atomic.AtomicReference; 80155517Sambriskoimport java.util.concurrent.atomic.LongAdder; 81155517Sambriskoimport java.util.function.Consumer; 82155517Sambriskoimport java.util.function.Supplier; 83227309Sedimport java.util.logging.Level; 84227309Sedimport javax.script.ScriptEngine; 85155517Sambriskoimport jdk.dynalink.DynamicLinker; 86162562Sjhbimport jdk.internal.org.objectweb.asm.ClassReader; 87155517Sambriskoimport jdk.internal.org.objectweb.asm.ClassWriter; 88155517Sambriskoimport jdk.internal.org.objectweb.asm.Opcodes; 89155517Sambriskoimport jdk.internal.org.objectweb.asm.util.CheckClassAdapter; 90155517Sambriskoimport jdk.nashorn.api.scripting.ClassFilter; 91155517Sambriskoimport jdk.nashorn.api.scripting.ScriptObjectMirror; 92155517Sambriskoimport jdk.nashorn.internal.WeakValueCache; 93155517Sambriskoimport jdk.nashorn.internal.codegen.Compiler; 94155517Sambriskoimport jdk.nashorn.internal.codegen.Compiler.CompilationPhases; 95155517Sambriskoimport jdk.nashorn.internal.codegen.ObjectClassGenerator; 96227293Sedimport jdk.nashorn.internal.ir.FunctionNode; 97155517Sambriskoimport jdk.nashorn.internal.ir.debug.ASTWriter; 98162562Sjhbimport jdk.nashorn.internal.ir.debug.PrintVisitor; 99162562Sjhbimport jdk.nashorn.internal.lookup.MethodHandleFactory; 100155517Sambriskoimport jdk.nashorn.internal.objects.Global; 101162562Sjhbimport jdk.nashorn.internal.parser.Parser; 102155517Sambriskoimport jdk.nashorn.internal.runtime.events.RuntimeEvent; 103182322Sjhbimport jdk.nashorn.internal.runtime.linker.Bootstrap; 104155517Sambriskoimport jdk.nashorn.internal.runtime.logging.DebugLogger; 105155517Sambriskoimport jdk.nashorn.internal.runtime.logging.Loggable; 106162562Sjhbimport jdk.nashorn.internal.runtime.logging.Logger; 107155517Sambriskoimport jdk.nashorn.internal.runtime.options.LoggingOption.LoggerInfo; 108182322Sjhbimport jdk.nashorn.internal.runtime.options.Options; 109182322Sjhbimport jdk.internal.misc.Unsafe; 110182322Sjhb 111182322Sjhb/** 112182322Sjhb * This class manages the global state of execution. Context is immutable. 113182322Sjhb */ 114182322Sjhbpublic final class Context { 115182322Sjhb // nashorn specific security runtime access permission names 116182322Sjhb /** 117182322Sjhb * Permission needed to pass arbitrary nashorn command line options when creating Context. 118182322Sjhb */ 119182322Sjhb public static final String NASHORN_SET_CONFIG = "nashorn.setConfig"; 120182322Sjhb 121162562Sjhb /** 122182322Sjhb * Permission needed to create Nashorn Context instance. 123162562Sjhb */ 124155517Sambrisko public static final String NASHORN_CREATE_CONTEXT = "nashorn.createContext"; 125162562Sjhb 126155517Sambrisko /** 127155517Sambrisko * Permission needed to create Nashorn Global instance. 128162562Sjhb */ 129162562Sjhb public static final String NASHORN_CREATE_GLOBAL = "nashorn.createGlobal"; 130155517Sambrisko 131162562Sjhb /** 132155517Sambrisko * Permission to get current Nashorn Context from thread local storage. 133155517Sambrisko */ 134155517Sambrisko public static final String NASHORN_GET_CONTEXT = "nashorn.getContext"; 135182322Sjhb 136182322Sjhb /** 137155517Sambrisko * Permission to use Java reflection/jsr292 from script code. 138182322Sjhb */ 139162562Sjhb public static final String NASHORN_JAVA_REFLECTION = "nashorn.JavaReflection"; 140155517Sambrisko 141162562Sjhb /** 142155517Sambrisko * Permission to create a new Module 143162562Sjhb */ 144155517Sambrisko public static final String NASHORN_CREATE_MODULE = "nashorn.createModule"; 145155517Sambrisko 146155517Sambrisko /** 147155517Sambrisko * Permission to enable nashorn debug mode. 148155517Sambrisko */ 149162562Sjhb public static final String NASHORN_DEBUG_MODE = "nashorn.debugMode"; 150155517Sambrisko 151162562Sjhb // nashorn load psuedo URL prefixes 152155517Sambrisko private static final String LOAD_CLASSPATH = "classpath:"; 153162562Sjhb private static final String LOAD_FX = "fx:"; 154155517Sambrisko private static final String LOAD_NASHORN = "nashorn:"; 155155517Sambrisko 156162562Sjhb private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); 157162562Sjhb private static final MethodType CREATE_PROGRAM_FUNCTION_TYPE = MethodType.methodType(ScriptFunction.class, ScriptObject.class); 158155517Sambrisko 159162562Sjhb private static final LongAdder NAMED_INSTALLED_SCRIPT_COUNT = new LongAdder(); 160162562Sjhb private static final LongAdder ANONYMOUS_INSTALLED_SCRIPT_COUNT = new LongAdder(); 161162562Sjhb 162162562Sjhb /** 163162562Sjhb * Should scripts use only object slots for fields, or dual long/object slots? The default 164162562Sjhb * behaviour is to couple this to optimistic types, using dual representation if optimistic types are enabled 165162562Sjhb * and single field representation otherwise. This can be overridden by setting either the "nashorn.fields.objects" 166162562Sjhb * or "nashorn.fields.dual" system property. 167162562Sjhb */ 168162562Sjhb private final FieldMode fieldMode; 169182322Sjhb 170182322Sjhb private static enum FieldMode { 171162562Sjhb /** Value for automatic field representation depending on optimistic types setting */ 172162562Sjhb AUTO, 173162562Sjhb /** Value for object field representation regardless of optimistic types setting */ 174155517Sambrisko OBJECTS, 175155517Sambrisko /** Value for dual primitive/object field representation regardless of optimistic types setting */ 176182322Sjhb DUAL 177162562Sjhb } 178155517Sambrisko 179162562Sjhb /** 180162562Sjhb * Keeps track of which builtin prototypes and properties have been relinked 181162562Sjhb * Currently we are conservative and associate the name of a builtin class with all 182162562Sjhb * its properties, so it's enough to invalidate a property to break all assumptions 183162562Sjhb * about a prototype. This can be changed to a more fine grained approach, but no one 184162562Sjhb * ever needs this, given the very rare occurrence of swapping out only parts of 185162562Sjhb * a builtin v.s. the entire builtin object 186162562Sjhb */ 187162562Sjhb private final Map<String, SwitchPoint> builtinSwitchPoints = new HashMap<>(); 188162562Sjhb 189162562Sjhb /* Force DebuggerSupport to be loaded. */ 190162562Sjhb static { 191155517Sambrisko DebuggerSupport.FORCELOAD = true; 192162562Sjhb } 193162562Sjhb 194162562Sjhb static long getNamedInstalledScriptCount() { 195162562Sjhb return NAMED_INSTALLED_SCRIPT_COUNT.sum(); 196162562Sjhb } 197162562Sjhb 198162562Sjhb static long getAnonymousInstalledScriptCount() { 199162562Sjhb return ANONYMOUS_INSTALLED_SCRIPT_COUNT.sum(); 200162562Sjhb } 201278321Sjhb 202278321Sjhb /** 203162562Sjhb * ContextCodeInstaller that has the privilege of installing classes in the Context. 204162562Sjhb * Can only be instantiated from inside the context and is opaque to other classes 205162562Sjhb */ 206182322Sjhb private abstract static class ContextCodeInstaller implements CodeInstaller { 207162562Sjhb final Context context; 208162562Sjhb final CodeSource codeSource; 209162562Sjhb 210162562Sjhb ContextCodeInstaller(final Context context, final CodeSource codeSource) { 211155517Sambrisko this.context = context; 212155517Sambrisko this.codeSource = codeSource; 213155517Sambrisko } 214155517Sambrisko 215155517Sambrisko @Override 216155517Sambrisko public Context getContext() { 217155517Sambrisko return context; 218155517Sambrisko } 219155517Sambrisko 220155517Sambrisko @Override 221155517Sambrisko public void initialize(final Collection<Class<?>> classes, final Source source, final Object[] constants) { 222162562Sjhb try { 223155517Sambrisko AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() { 224155517Sambrisko @Override 225162562Sjhb public Void run() throws Exception { 226155517Sambrisko for (final Class<?> clazz : classes) { 227155517Sambrisko //use reflection to write source and constants table to installed classes 228155517Sambrisko final Field sourceField = clazz.getDeclaredField(SOURCE.symbolName()); 229155517Sambrisko sourceField.setAccessible(true); 230155517Sambrisko sourceField.set(null, source); 231162562Sjhb 232162562Sjhb final Field constantsField = clazz.getDeclaredField(CONSTANTS.symbolName()); 233155517Sambrisko constantsField.setAccessible(true); 234155517Sambrisko constantsField.set(null, constants); 235278321Sjhb } 236162562Sjhb return null; 237162562Sjhb } 238162562Sjhb }); 239162562Sjhb } catch (final PrivilegedActionException e) { 240162562Sjhb throw new RuntimeException(e); 241162562Sjhb } 242162562Sjhb } 243162562Sjhb 244155517Sambrisko @Override 245162562Sjhb public void verify(final byte[] code) { 246162562Sjhb context.verify(code); 247162562Sjhb } 248155517Sambrisko 249162562Sjhb @Override 250162562Sjhb public long getUniqueScriptId() { 251155517Sambrisko return context.getUniqueScriptId(); 252162562Sjhb } 253155517Sambrisko 254155517Sambrisko @Override 255155517Sambrisko public void storeScript(final String cacheKey, final Source source, final String mainClassName, 256162562Sjhb final Map<String,byte[]> classBytes, final Map<Integer, FunctionInitializer> initializers, 257155517Sambrisko final Object[] constants, final int compilationId) { 258162562Sjhb if (context.codeStore != null) { 259155517Sambrisko context.codeStore.store(cacheKey, source, mainClassName, classBytes, initializers, constants, compilationId); 260155517Sambrisko } 261155517Sambrisko } 262278321Sjhb 263162562Sjhb @Override 264155517Sambrisko public StoredScript loadScript(final Source source, final String functionKey) { 265162562Sjhb if (context.codeStore != null) { 266162562Sjhb return context.codeStore.load(source, functionKey); 267162562Sjhb } 268162562Sjhb return null; 269155517Sambrisko } 270155517Sambrisko 271162562Sjhb @Override 272155517Sambrisko public boolean isCompatibleWith(final CodeInstaller other) { 273155517Sambrisko if (other instanceof ContextCodeInstaller) { 274155517Sambrisko final ContextCodeInstaller cci = (ContextCodeInstaller)other; 275162562Sjhb return cci.context == context && cci.codeSource == codeSource; 276278321Sjhb } 277162562Sjhb return false; 278162562Sjhb } 279162562Sjhb } 280155517Sambrisko 281155517Sambrisko private static class NamedContextCodeInstaller extends ContextCodeInstaller { 282155517Sambrisko private final ScriptLoader loader; 283162562Sjhb private int usageCount = 0; 284162562Sjhb private int bytesDefined = 0; 285162562Sjhb 286162562Sjhb // We reuse this installer for 10 compilations or 200000 defined bytes. Usually the first condition 287155517Sambrisko // will occur much earlier, the second is a safety measure for very large scripts/functions. 288155517Sambrisko private final static int MAX_USAGES = 10; 289155517Sambrisko private final static int MAX_BYTES_DEFINED = 200_000; 290162562Sjhb 291162562Sjhb private NamedContextCodeInstaller(final Context context, final CodeSource codeSource, final ScriptLoader loader) { 292162562Sjhb super(context, codeSource); 293155517Sambrisko this.loader = loader; 294155517Sambrisko } 295155517Sambrisko 296162562Sjhb @Override 297155517Sambrisko public Class<?> install(final String className, final byte[] bytecode) { 298155517Sambrisko usageCount++; 299155517Sambrisko bytesDefined += bytecode.length; 300162562Sjhb NAMED_INSTALLED_SCRIPT_COUNT.increment(); 301162562Sjhb return loader.installClass(Compiler.binaryName(className), bytecode, codeSource); 302155517Sambrisko } 303155517Sambrisko 304155517Sambrisko @Override 305162562Sjhb public CodeInstaller getOnDemandCompilationInstaller() { 306162562Sjhb // Reuse this installer if we're within our limits. 307162562Sjhb if (usageCount < MAX_USAGES && bytesDefined < MAX_BYTES_DEFINED) { 308162562Sjhb return this; 309162562Sjhb } 310162562Sjhb return new NamedContextCodeInstaller(context, codeSource, context.createNewLoader()); 311162562Sjhb } 312162562Sjhb 313155517Sambrisko @Override 314155517Sambrisko public CodeInstaller getMultiClassCodeInstaller() { 315182322Sjhb // This installer is perfectly suitable for installing multiple classes that reference each other 316182322Sjhb // as it produces classes with resolvable names, all defined in a single class loader. 317182322Sjhb return this; 318155517Sambrisko } 319182322Sjhb } 320182322Sjhb 321162562Sjhb private final WeakValueCache<CodeSource, Class<?>> anonymousHostClasses = new WeakValueCache<>(); 322162562Sjhb 323155517Sambrisko private static final class AnonymousContextCodeInstaller extends ContextCodeInstaller { 324162562Sjhb private static final Unsafe UNSAFE = getUnsafe(); 325162562Sjhb private static final String ANONYMOUS_HOST_CLASS_NAME = Compiler.SCRIPTS_PACKAGE.replace('/', '.') + ".AnonymousHost"; 326162562Sjhb private static final byte[] ANONYMOUS_HOST_CLASS_BYTES = getAnonymousHostClassBytes(); 327162562Sjhb 328162562Sjhb private final Class<?> hostClass; 329162562Sjhb 330162562Sjhb private AnonymousContextCodeInstaller(final Context context, final CodeSource codeSource, final Class<?> hostClass) { 331162562Sjhb super(context, codeSource); 332162562Sjhb this.hostClass = hostClass; 333162562Sjhb } 334162562Sjhb 335162562Sjhb @Override 336162562Sjhb public Class<?> install(final String className, final byte[] bytecode) { 337162562Sjhb ANONYMOUS_INSTALLED_SCRIPT_COUNT.increment(); 338162562Sjhb return UNSAFE.defineAnonymousClass(hostClass, bytecode, null); 339162562Sjhb } 340162562Sjhb 341162562Sjhb @Override 342162562Sjhb public CodeInstaller getOnDemandCompilationInstaller() { 343162562Sjhb // This code loader can be indefinitely reused for on-demand recompilations for the same code source. 344162562Sjhb return this; 345162562Sjhb } 346162562Sjhb 347162562Sjhb @Override 348162562Sjhb public CodeInstaller getMultiClassCodeInstaller() { 349155517Sambrisko // This code loader can not be used to install multiple classes that reference each other, as they 350162562Sjhb // would have no resolvable names. Therefore, in such situation we must revert to an installer that 351162562Sjhb // produces named classes. 352162562Sjhb return new NamedContextCodeInstaller(context, codeSource, context.createNewLoader()); 353162562Sjhb } 354162562Sjhb 355162562Sjhb private static byte[] getAnonymousHostClassBytes() { 356162562Sjhb final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); 357162562Sjhb cw.visit(V1_7, Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT, ANONYMOUS_HOST_CLASS_NAME.replace('.', '/'), null, "java/lang/Object", null); 358155517Sambrisko cw.visitEnd(); 359162562Sjhb return cw.toByteArray(); 360162562Sjhb } 361162562Sjhb 362162562Sjhb private static Unsafe getUnsafe() { 363162562Sjhb return AccessController.doPrivileged(new PrivilegedAction<Unsafe>() { 364162562Sjhb @Override 365155517Sambrisko public Unsafe run() { 366162562Sjhb try { 367155517Sambrisko final Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe"); 368162562Sjhb theUnsafeField.setAccessible(true); 369162562Sjhb return (Unsafe)theUnsafeField.get(null); 370162562Sjhb } catch (final ReflectiveOperationException e) { 371162562Sjhb throw new RuntimeException(e); 372155517Sambrisko } 373162562Sjhb } 374162562Sjhb }); 375162562Sjhb } 376155517Sambrisko } 377162562Sjhb 378162562Sjhb /** Is Context global debug mode enabled ? */ 379162562Sjhb public static final boolean DEBUG = Options.getBooleanProperty("nashorn.debug"); 380162562Sjhb 381162562Sjhb private static final ThreadLocal<Global> currentGlobal = new ThreadLocal<>(); 382162562Sjhb 383162562Sjhb // in-memory cache for loaded classes 384162562Sjhb private ClassCache classCache; 385162562Sjhb 386162562Sjhb // persistent code store 387162562Sjhb private CodeStore codeStore; 388155517Sambrisko 389155517Sambrisko // A factory for linking global properties as constant method handles. It is created when the first Global 390162562Sjhb // is created, and invalidated forever once the second global is created. 391162562Sjhb private final AtomicReference<GlobalConstants> globalConstantsRef = new AtomicReference<>(); 392162562Sjhb 393162562Sjhb /** 394162562Sjhb * Get the current global scope 395162562Sjhb * @return the current global scope 396162562Sjhb */ 397162562Sjhb public static Global getGlobal() { 398162562Sjhb // This class in a package.access protected package. 399155517Sambrisko // Trusted code only can call this method. 400155517Sambrisko return currentGlobal.get(); 401162562Sjhb } 402155517Sambrisko 403162562Sjhb /** 404162562Sjhb * Set the current global scope 405162562Sjhb * @param global the global scope 406162562Sjhb */ 407162562Sjhb public static void setGlobal(final ScriptObject global) { 408162562Sjhb if (global != null && !(global instanceof Global)) { 409162562Sjhb throw new IllegalArgumentException("not a global!"); 410162562Sjhb } 411162562Sjhb setGlobal((Global)global); 412162562Sjhb } 413162562Sjhb 414155517Sambrisko /** 415162562Sjhb * Set the current global scope 416162562Sjhb * @param global the global scope 417162562Sjhb */ 418162562Sjhb public static void setGlobal(final Global global) { 419184949Sobrien // This class in a package.access protected package. 420162562Sjhb // Trusted code only can call this method. 421162562Sjhb assert getGlobal() != global; 422162562Sjhb //same code can be cached between globals, then we need to invalidate method handle constants 423162562Sjhb if (global != null) { 424162562Sjhb final GlobalConstants globalConstants = getContext(global).getGlobalConstants(); 425162562Sjhb if (globalConstants != null) { 426162562Sjhb globalConstants.invalidateAll(); 427162562Sjhb } 428155517Sambrisko } 429155517Sambrisko currentGlobal.set(global); 430155517Sambrisko } 431155517Sambrisko 432162562Sjhb /** 433162562Sjhb * Get context of the current global 434162562Sjhb * @return current global scope's context. 435162562Sjhb */ 436162562Sjhb public static Context getContext() { 437162562Sjhb final SecurityManager sm = System.getSecurityManager(); 438162562Sjhb if (sm != null) { 439162562Sjhb sm.checkPermission(new RuntimePermission(NASHORN_GET_CONTEXT)); 440155517Sambrisko } 441162562Sjhb return getContextTrusted(); 442162562Sjhb } 443162562Sjhb 444162562Sjhb /** 445155517Sambrisko * Get current context's error writer 446162562Sjhb * 447162562Sjhb * @return error writer of the current context 448162562Sjhb */ 449162562Sjhb public static PrintWriter getCurrentErr() { 450155517Sambrisko final ScriptObject global = getGlobal(); 451162562Sjhb return (global != null)? global.getContext().getErr() : new PrintWriter(System.err); 452162562Sjhb } 453162562Sjhb 454162562Sjhb /** 455155517Sambrisko * Output text to this Context's error stream 456162562Sjhb * @param str text to write 457162562Sjhb */ 458162562Sjhb public static void err(final String str) { 459162562Sjhb err(str, true); 460155517Sambrisko } 461155517Sambrisko 462155517Sambrisko /** 463155517Sambrisko * Output text to this Context's error stream, optionally with 464155517Sambrisko * a newline afterwards 465162562Sjhb * 466155517Sambrisko * @param str text to write 467155517Sambrisko * @param crlf write a carriage return/new line after text 468162562Sjhb */ 469162562Sjhb public static void err(final String str, final boolean crlf) { 470162562Sjhb final PrintWriter err = Context.getCurrentErr(); 471162562Sjhb if (err != null) { 472155517Sambrisko if (crlf) { 473155517Sambrisko err.println(str); 474162562Sjhb } else { 475162562Sjhb err.print(str); 476162562Sjhb } 477162562Sjhb } 478162562Sjhb } 479162562Sjhb 480162562Sjhb /** Current environment. */ 481162562Sjhb private final ScriptEnvironment env; 482162562Sjhb 483162562Sjhb /** is this context in strict mode? Cached from env. as this is used heavily. */ 484162562Sjhb final boolean _strict; 485162562Sjhb 486162562Sjhb /** class loader to resolve classes from script. */ 487162562Sjhb private final ClassLoader appLoader; 488155517Sambrisko 489155517Sambrisko /** Class loader to load classes compiled from scripts. */ 490162562Sjhb private final ScriptLoader scriptLoader; 491162562Sjhb 492162562Sjhb /** Dynamic linker for linking call sites in script code loaded by this context */ 493155517Sambrisko private final DynamicLinker dynamicLinker; 494278321Sjhb 495278321Sjhb /** Current error manager. */ 496278321Sjhb private final ErrorManager errors; 497162562Sjhb 498155517Sambrisko /** Unique id for script. Used only when --loader-per-compile=false */ 499162562Sjhb private final AtomicLong uniqueScriptId; 500162562Sjhb 501162562Sjhb /** Optional class filter to use for Java classes. Can be null. */ 502162562Sjhb private final ClassFilter classFilter; 503162562Sjhb 504162562Sjhb private static final StructureLoader sharedLoader; 505162562Sjhb private static final ConcurrentMap<String, Class<?>> structureClasses = new ConcurrentHashMap<>(); 506155517Sambrisko 507162562Sjhb /*package-private*/ @SuppressWarnings("static-method") 508162562Sjhb StructureLoader getSharedLoader() { 509162562Sjhb return sharedLoader; 510162562Sjhb } 511278321Sjhb 512278321Sjhb private static AccessControlContext createNoPermAccCtxt() { 513278321Sjhb return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, new Permissions()) }); 514278321Sjhb } 515278321Sjhb 516278321Sjhb private static AccessControlContext createPermAccCtxt(final String permName) { 517278321Sjhb final Permissions perms = new Permissions(); 518278321Sjhb perms.add(new RuntimePermission(permName)); 519278321Sjhb return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, perms) }); 520278321Sjhb } 521278321Sjhb 522278321Sjhb private static final AccessControlContext NO_PERMISSIONS_ACC_CTXT = createNoPermAccCtxt(); 523162562Sjhb private static final AccessControlContext CREATE_LOADER_ACC_CTXT = createPermAccCtxt("createClassLoader"); 524155517Sambrisko private static final AccessControlContext CREATE_GLOBAL_ACC_CTXT = createPermAccCtxt(NASHORN_CREATE_GLOBAL); 525155517Sambrisko private static final AccessControlContext GET_LOADER_ACC_CTXT = createPermAccCtxt("getClassLoader"); 526162562Sjhb 527162562Sjhb static { 528162562Sjhb final ClassLoader myLoader = Context.class.getClassLoader(); 529162562Sjhb sharedLoader = AccessController.doPrivileged(new PrivilegedAction<StructureLoader>() { 530155517Sambrisko @Override 531162562Sjhb public StructureLoader run() { 532155517Sambrisko return new StructureLoader(myLoader); 533155517Sambrisko } 534162562Sjhb }, CREATE_LOADER_ACC_CTXT); 535162562Sjhb } 536162562Sjhb 537162562Sjhb /** 538162562Sjhb * ThrowErrorManager that throws ParserException upon error conditions. 539155517Sambrisko */ 540162562Sjhb public static class ThrowErrorManager extends ErrorManager { 541155517Sambrisko @Override 542162562Sjhb public void error(final String message) { 543162562Sjhb throw new ParserException(message); 544162562Sjhb } 545162562Sjhb 546162562Sjhb @Override 547162562Sjhb public void error(final ParserException e) { 548162562Sjhb throw e; 549162562Sjhb } 550162562Sjhb } 551162562Sjhb 552162562Sjhb /** 553162562Sjhb * Constructor 554155517Sambrisko * 555155517Sambrisko * @param options options from command line or Context creator 556155517Sambrisko * @param errors error manger 557278321Sjhb * @param appLoader application class loader 558162562Sjhb */ 559162562Sjhb public Context(final Options options, final ErrorManager errors, final ClassLoader appLoader) { 560162562Sjhb this(options, errors, appLoader, null); 561162562Sjhb } 562155517Sambrisko 563278321Sjhb /** 564155517Sambrisko * Constructor 565155517Sambrisko * 566162562Sjhb * @param options options from command line or Context creator 567162562Sjhb * @param errors error manger 568162562Sjhb * @param appLoader application class loader 569162562Sjhb * @param classFilter class filter to use 570162562Sjhb */ 571162562Sjhb public Context(final Options options, final ErrorManager errors, final ClassLoader appLoader, final ClassFilter classFilter) { 572162562Sjhb this(options, errors, new PrintWriter(System.out, true), new PrintWriter(System.err, true), appLoader, classFilter); 573162562Sjhb } 574162562Sjhb 575162562Sjhb /** 576155517Sambrisko * Constructor 577162562Sjhb * 578155517Sambrisko * @param options options from command line or Context creator 579162562Sjhb * @param errors error manger 580278321Sjhb * @param out output writer for this Context 581162562Sjhb * @param err error writer for this Context 582162562Sjhb * @param appLoader application class loader 583155517Sambrisko */ 584162562Sjhb public Context(final Options options, final ErrorManager errors, final PrintWriter out, final PrintWriter err, final ClassLoader appLoader) { 585162562Sjhb this(options, errors, out, err, appLoader, (ClassFilter)null); 586162562Sjhb } 587155517Sambrisko 588155517Sambrisko /** 589162562Sjhb * Constructor 590155517Sambrisko * 591162562Sjhb * @param options options from command line or Context creator 592162562Sjhb * @param errors error manger 593155517Sambrisko * @param out output writer for this Context 594163278Sjhb * @param err error writer for this Context 595163278Sjhb * @param appLoader application class loader 596162562Sjhb * @param classFilter class filter to use 597162562Sjhb */ 598162562Sjhb public Context(final Options options, final ErrorManager errors, final PrintWriter out, final PrintWriter err, final ClassLoader appLoader, final ClassFilter classFilter) { 599155517Sambrisko final SecurityManager sm = System.getSecurityManager(); 600155517Sambrisko if (sm != null) { 601155517Sambrisko sm.checkPermission(new RuntimePermission(NASHORN_CREATE_CONTEXT)); 602155517Sambrisko } 603155517Sambrisko 604155517Sambrisko this.classFilter = classFilter; 605200666Sru this.env = new ScriptEnvironment(options, out, err); 606297179Smav this._strict = env._strict; 607297179Smav if (env._loader_per_compile) { 608297179Smav this.scriptLoader = null; 609297179Smav this.uniqueScriptId = null; 610297179Smav } else { 611297179Smav this.scriptLoader = createNewLoader(); 612297179Smav this.uniqueScriptId = new AtomicLong(); 613297179Smav } 614297179Smav this.errors = errors; 615297179Smav 616297179Smav // if user passed -classpath option, make a URLClassLoader with that and 617297179Smav // the app loader as the parent. 618297179Smav final String classPath = options.getString("classpath"); 619297179Smav if (!env._compile_only && classPath != null && !classPath.isEmpty()) { 620200666Sru // make sure that caller can create a class loader. 621162562Sjhb if (sm != null) { 622162562Sjhb sm.checkCreateClassLoader(); 623162562Sjhb } 624155517Sambrisko this.appLoader = NashornLoader.createClassLoader(classPath, appLoader); 625200666Sru } else { 626200666Sru this.appLoader = appLoader; 627200666Sru } 628278321Sjhb this.dynamicLinker = Bootstrap.createDynamicLinker(this.appLoader, env._unstable_relink_threshold); 629162562Sjhb 630155517Sambrisko final int cacheSize = env._class_cache_size; 631162562Sjhb if (cacheSize > 0) { 632155517Sambrisko classCache = new ClassCache(this, cacheSize); 633162562Sjhb } 634162562Sjhb 635162562Sjhb if (env._persistent_cache) { 636162562Sjhb codeStore = newCodeStore(this); 637200666Sru } 638155517Sambrisko 639162562Sjhb // print version info if asked. 640162562Sjhb if (env._version) { 641162562Sjhb getErr().println("nashorn " + Version.version()); 642162562Sjhb } 643162562Sjhb 644162562Sjhb if (env._fullversion) { 645155517Sambrisko getErr().println("nashorn full version " + Version.fullVersion()); 646162562Sjhb } 647162562Sjhb 648162562Sjhb if (Options.getBooleanProperty("nashorn.fields.dual")) { 649200666Sru fieldMode = FieldMode.DUAL; 650155517Sambrisko } else if (Options.getBooleanProperty("nashorn.fields.objects")) { 651155517Sambrisko fieldMode = FieldMode.OBJECTS; 652155517Sambrisko } else { 653155517Sambrisko fieldMode = FieldMode.AUTO; 654155517Sambrisko } 655155517Sambrisko 656155517Sambrisko initLoggers(); 657200666Sru } 658155517Sambrisko 659257421Sglebius 660257421Sglebius /** 661257421Sglebius * Get the class filter for this context 662165260Sn_hibma * @return class filter 663165260Sn_hibma */ 664200666Sru public ClassFilter getClassFilter() { 665200666Sru return classFilter; 666200666Sru } 667297179Smav 668297179Smav /** 669297179Smav * Returns the factory for constant method handles for global properties. The returned factory can be 670297179Smav * invalidated if this Context has more than one Global. 671297179Smav * @return the factory for constant method handles for global properties. 672297179Smav */ 673297179Smav GlobalConstants getGlobalConstants() { 674297179Smav return globalConstantsRef.get(); 675297179Smav } 676297179Smav 677297179Smav /** 678297179Smav * Get the error manager for this context 679297179Smav * @return error manger 680297179Smav */ 681297179Smav public ErrorManager getErrorManager() { 682297179Smav return errors; 683297179Smav } 684297179Smav 685239128Sjhb /** 686200666Sru * Get the script environment for this context 687200666Sru * @return script environment 688200666Sru */ 689155517Sambrisko public ScriptEnvironment getEnv() { 690155517Sambrisko return env; 691155517Sambrisko } 692162562Sjhb 693162562Sjhb /** 694162562Sjhb * Get the output stream for this context 695162562Sjhb * @return output print writer 696162562Sjhb */ 697162562Sjhb public PrintWriter getOut() { 698162562Sjhb return env.getOut(); 699162562Sjhb } 700162562Sjhb 701162562Sjhb /** 702162562Sjhb * Get the error stream for this context 703162562Sjhb * @return error print writer 704278321Sjhb */ 705278321Sjhb public PrintWriter getErr() { 706162562Sjhb return env.getErr(); 707162562Sjhb } 708162562Sjhb 709162562Sjhb /** 710162562Sjhb * Should scripts compiled by this context use dual field representation? 711162562Sjhb * @return true if using dual fields, false for object-only fields 712162562Sjhb */ 713162562Sjhb public boolean useDualFields() { 714162562Sjhb return fieldMode == FieldMode.DUAL || (fieldMode == FieldMode.AUTO && env._optimistic_types); 715162562Sjhb } 716162562Sjhb 717162562Sjhb /** 718278321Sjhb * Get the PropertyMap of the current global scope 719162562Sjhb * @return the property map of the current global scope 720162562Sjhb */ 721162562Sjhb public static PropertyMap getGlobalMap() { 722162562Sjhb return Context.getGlobal().getMap(); 723162562Sjhb } 724162562Sjhb 725162562Sjhb /** 726162562Sjhb * Compile a top level script. 727162562Sjhb * 728162562Sjhb * @param source the source 729162562Sjhb * @param scope the scope 730162562Sjhb * 731162562Sjhb * @return top level function for script 732162562Sjhb */ 733162562Sjhb public ScriptFunction compileScript(final Source source, final ScriptObject scope) { 734162562Sjhb return compileScript(source, scope, this.errors); 735162562Sjhb } 736162562Sjhb 737162562Sjhb /** 738162562Sjhb * Interface to represent compiled code that can be re-used across many 739220614Sru * global scope instances 740155517Sambrisko */ 741162562Sjhb public static interface MultiGlobalCompiledScript { 742220614Sru /** 743162562Sjhb * Obtain script function object for a specific global scope object. 744155517Sambrisko * 745278321Sjhb * @param newGlobal global scope for which function object is obtained 746162562Sjhb * @return script function for script level expressions 747162562Sjhb */ 748162562Sjhb public ScriptFunction getFunction(final Global newGlobal); 749162562Sjhb } 750162562Sjhb 751162562Sjhb /** 752155517Sambrisko * Compile a top level script. 753155517Sambrisko * 754162562Sjhb * @param source the script source 755155517Sambrisko * @return reusable compiled script across many global scopes. 756155517Sambrisko */ 757155517Sambrisko public MultiGlobalCompiledScript compileScript(final Source source) { 758162562Sjhb final Class<?> clazz = compile(source, this.errors, this._strict, false); 759278321Sjhb final MethodHandle createProgramFunctionHandle = getCreateProgramFunctionHandle(clazz); 760162562Sjhb 761162562Sjhb return new MultiGlobalCompiledScript() { 762162562Sjhb @Override 763162562Sjhb public ScriptFunction getFunction(final Global newGlobal) { 764162562Sjhb return invokeCreateProgramFunctionHandle(createProgramFunctionHandle, newGlobal); 765278321Sjhb } 766155517Sambrisko }; 767155517Sambrisko } 768155517Sambrisko 769155517Sambrisko /** 770281941Sjhb * Entry point for {@code eval} 771281941Sjhb * 772281941Sjhb * @param initialScope The scope of this eval call 773281941Sjhb * @param string Evaluated code as a String 774281941Sjhb * @param callThis "this" to be passed to the evaluated code 775281941Sjhb * @param location location of the eval call 776281941Sjhb * @return the return value of the {@code eval} 777162562Sjhb */ 778281941Sjhb public Object eval(final ScriptObject initialScope, final String string, 779162562Sjhb final Object callThis, final Object location) { 780281941Sjhb return eval(initialScope, string, callThis, location, false, false); 781281941Sjhb } 782281941Sjhb 783281941Sjhb /** 784281941Sjhb * Entry point for {@code eval} 785281941Sjhb * 786155517Sambrisko * @param initialScope The scope of this eval call 787155517Sambrisko * @param string Evaluated code as a String 788182322Sjhb * @param callThis "this" to be passed to the evaluated code 789162562Sjhb * @param location location of the eval call 790182322Sjhb * @param strict is this {@code eval} call from a strict mode code? 791162562Sjhb * @param evalCall is this called from "eval" builtin? 792162562Sjhb * 793162562Sjhb * @return the return value of the {@code eval} 794182322Sjhb */ 795162562Sjhb public Object eval(final ScriptObject initialScope, final String string, 796162562Sjhb final Object callThis, final Object location, final boolean strict, final boolean evalCall) { 797162562Sjhb final String file = location == UNDEFINED || location == null ? "<eval>" : location.toString(); 798162562Sjhb final Source source = sourceFor(file, string, evalCall); 799162562Sjhb // is this direct 'eval' builtin call? 800162562Sjhb final boolean directEval = evalCall && (location != UNDEFINED); 801162562Sjhb final Global global = Context.getGlobal(); 802162562Sjhb ScriptObject scope = initialScope; 803162562Sjhb 804162562Sjhb // ECMA section 10.1.1 point 2 says eval code is strict if it begins 805166901Spiso // with "use strict" directive or eval direct call itself is made 806162562Sjhb // from from strict mode code. We are passed with caller's strict mode. 807162562Sjhb // Nashorn extension: any 'eval' is unconditionally strict when -strict is specified. 808162562Sjhb boolean strictFlag = strict || this._strict; 809162562Sjhb 810162562Sjhb Class<?> clazz; 811162562Sjhb try { 812162562Sjhb clazz = compile(source, new ThrowErrorManager(), strictFlag, true); 813162562Sjhb } catch (final ParserException e) { 814162562Sjhb e.throwAsEcmaException(global); 815162562Sjhb return null; 816162562Sjhb } 817162562Sjhb 818162562Sjhb if (!strictFlag) { 819162562Sjhb // We need to get strict mode flag from compiled class. This is 820155517Sambrisko // because eval code may start with "use strict" directive. 821162562Sjhb try { 822155517Sambrisko strictFlag = clazz.getField(STRICT_MODE.symbolName()).getBoolean(null); 823155517Sambrisko } catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { 824155517Sambrisko //ignored 825155517Sambrisko strictFlag = false; 826155517Sambrisko } 827155517Sambrisko } 828155517Sambrisko 829155517Sambrisko // In strict mode, eval does not instantiate variables and functions 830162562Sjhb // in the caller's environment. A new environment is created! 831162562Sjhb if (strictFlag) { 832162562Sjhb // Create a new scope object with given scope as its prototype 833182322Sjhb scope = newScope(scope); 834162562Sjhb } 835162562Sjhb 836162562Sjhb final ScriptFunction func = getProgramFunction(clazz, scope); 837162562Sjhb Object evalThis; 838182322Sjhb if (directEval) { 839182322Sjhb evalThis = (callThis != UNDEFINED && callThis != null) || strictFlag ? callThis : global; 840162562Sjhb } else { 841162562Sjhb // either indirect evalCall or non-eval (Function, engine.eval, ScriptObjectMirror.eval..) 842162562Sjhb evalThis = callThis; 843162562Sjhb } 844162562Sjhb 845162562Sjhb return ScriptRuntime.apply(func, evalThis); 846162562Sjhb } 847162562Sjhb 848162562Sjhb private static ScriptObject newScope(final ScriptObject callerScope) { 849162562Sjhb return new Scope(callerScope, PropertyMap.newMap(Scope.class)); 850162562Sjhb } 851162562Sjhb 852162562Sjhb private static Source loadInternal(final String srcStr, final String prefix, final String resourcePath) { 853278321Sjhb if (srcStr.startsWith(prefix)) { 854278321Sjhb final String resource = resourcePath + srcStr.substring(prefix.length()); 855162562Sjhb // NOTE: even sandbox scripts should be able to load scripts in nashorn: scheme 856162562Sjhb // These scripts are always available and are loaded from nashorn.jar's resources. 857162562Sjhb return AccessController.doPrivileged( 858162562Sjhb new PrivilegedAction<Source>() { 859162562Sjhb @Override 860162562Sjhb public Source run() { 861278321Sjhb try { 862278321Sjhb final InputStream resStream = Context.class.getResourceAsStream(resource); 863162562Sjhb return resStream != null ? sourceFor(srcStr, Source.readFully(resStream)) : null; 864155517Sambrisko } catch (final IOException exp) { 865155517Sambrisko return null; 866162562Sjhb } 867162562Sjhb } 868162562Sjhb }); 869162562Sjhb } 870162562Sjhb 871162562Sjhb return null; 872162562Sjhb } 873162562Sjhb 874162562Sjhb /** 875162562Sjhb * Implementation of {@code load} Nashorn extension. Load a script file from a source 876162562Sjhb * expression 877162562Sjhb * 878162562Sjhb * @param scope the scope 879162562Sjhb * @param from source expression for script 880162562Sjhb * 881162562Sjhb * @return return value for load call (undefined) 882162562Sjhb * 883162562Sjhb * @throws IOException if source cannot be found or loaded 884162562Sjhb */ 885162562Sjhb public Object load(final Object scope, final Object from) throws IOException { 886162562Sjhb final Object src = from instanceof ConsString ? from.toString() : from; 887162562Sjhb Source source = null; 888162562Sjhb 889162562Sjhb // load accepts a String (which could be a URL or a file name), a File, a URL 890162562Sjhb // or a ScriptObject that has "name" and "source" (string valued) properties. 891162562Sjhb if (src instanceof String) { 892162562Sjhb final String srcStr = (String)src; 893162562Sjhb if (srcStr.startsWith(LOAD_CLASSPATH)) { 894162664Sjhb final URL url = getResourceURL(srcStr.substring(LOAD_CLASSPATH.length())); 895162664Sjhb source = url != null ? sourceFor(url.toString(), url) : null; 896162562Sjhb } else { 897162562Sjhb final File file = new File(srcStr); 898162664Sjhb if (srcStr.indexOf(':') != -1) { 899162562Sjhb if ((source = loadInternal(srcStr, LOAD_NASHORN, "resources/")) == null && 900162562Sjhb (source = loadInternal(srcStr, LOAD_FX, "resources/fx/")) == null) { 901162562Sjhb URL url; 902155605Sambrisko try { 903155517Sambrisko //check for malformed url. if malformed, it may still be a valid file 904162562Sjhb url = new URL(srcStr); 905162562Sjhb } catch (final MalformedURLException e) { 906155517Sambrisko url = file.toURI().toURL(); 907155517Sambrisko } 908155517Sambrisko source = sourceFor(url.toString(), url); 909155517Sambrisko } 910155517Sambrisko } else if (file.isFile()) { 911155517Sambrisko source = sourceFor(srcStr, file); 912155517Sambrisko } 913155517Sambrisko } 914155517Sambrisko } else if (src instanceof File && ((File)src).isFile()) { 915155517Sambrisko final File file = (File)src; 916155517Sambrisko source = sourceFor(file.getName(), file); 917155517Sambrisko } else if (src instanceof URL) { 918155517Sambrisko final URL url = (URL)src; 919155517Sambrisko source = sourceFor(url.toString(), url); 920155517Sambrisko } else if (src instanceof ScriptObject) { 921155517Sambrisko final ScriptObject sobj = (ScriptObject)src; 922155517Sambrisko if (sobj.has("script") && sobj.has("name")) { 923155517Sambrisko final String script = JSType.toString(sobj.get("script")); 924155517Sambrisko final String name = JSType.toString(sobj.get("name")); 925155517Sambrisko source = sourceFor(name, script); 926155517Sambrisko } 927155517Sambrisko } else if (src instanceof Map) { 928155517Sambrisko final Map<?,?> map = (Map<?,?>)src; 929155517Sambrisko if (map.containsKey("script") && map.containsKey("name")) { 930155517Sambrisko final String script = JSType.toString(map.get("script")); 931155517Sambrisko final String name = JSType.toString(map.get("name")); 932155517Sambrisko source = sourceFor(name, script); 933155517Sambrisko } 934155517Sambrisko } 935155517Sambrisko 936155517Sambrisko if (source != null) { 937155517Sambrisko if (scope instanceof ScriptObject && ((ScriptObject)scope).isScope()) { 938155517Sambrisko final ScriptObject sobj = (ScriptObject)scope; 939155517Sambrisko // passed object is a script object 940155517Sambrisko // Global is the only user accessible scope ScriptObject 941 assert sobj.isGlobal() : "non-Global scope object!!"; 942 return evaluateSource(source, sobj, sobj); 943 } else if (scope == null || scope == UNDEFINED) { 944 // undefined or null scope. Use current global instance. 945 final Global global = getGlobal(); 946 return evaluateSource(source, global, global); 947 } else { 948 /* 949 * Arbitrary object passed for scope. 950 * Indirect load that is equivalent to: 951 * 952 * (function(scope, source) { 953 * with (scope) { 954 * eval(<script_from_source>); 955 * } 956 * })(scope, source); 957 */ 958 final Global global = getGlobal(); 959 // Create a new object. This is where all declarations 960 // (var, function) from the evaluated code go. 961 // make global to be its __proto__ so that global 962 // definitions are accessible to the evaluated code. 963 final ScriptObject evalScope = newScope(global); 964 965 // finally, make a WithObject around user supplied scope object 966 // so that it's properties are accessible as variables. 967 final ScriptObject withObj = ScriptRuntime.openWith(evalScope, scope); 968 969 // evaluate given source with 'withObj' as scope 970 // but use global object as "this". 971 return evaluateSource(source, withObj, global); 972 } 973 } 974 975 throw typeError("cant.load.script", ScriptRuntime.safeToString(from)); 976 } 977 978 /** 979 * Implementation of {@code loadWithNewGlobal} Nashorn extension. Load a script file from a source 980 * expression, after creating a new global scope. 981 * 982 * @param from source expression for script 983 * @param args (optional) arguments to be passed to the loaded script 984 * 985 * @return return value for load call (undefined) 986 * 987 * @throws IOException if source cannot be found or loaded 988 */ 989 public Object loadWithNewGlobal(final Object from, final Object...args) throws IOException { 990 final Global oldGlobal = getGlobal(); 991 final Global newGlobal = AccessController.doPrivileged(new PrivilegedAction<Global>() { 992 @Override 993 public Global run() { 994 try { 995 return newGlobal(); 996 } catch (final RuntimeException e) { 997 if (Context.DEBUG) { 998 e.printStackTrace(); 999 } 1000 throw e; 1001 } 1002 } 1003 }, CREATE_GLOBAL_ACC_CTXT); 1004 // initialize newly created Global instance 1005 initGlobal(newGlobal); 1006 setGlobal(newGlobal); 1007 1008 final Object[] wrapped = args == null? ScriptRuntime.EMPTY_ARRAY : ScriptObjectMirror.wrapArray(args, oldGlobal); 1009 newGlobal.put("arguments", newGlobal.wrapAsObject(wrapped), env._strict); 1010 1011 try { 1012 // wrap objects from newGlobal's world as mirrors - but if result 1013 // is from oldGlobal's world, unwrap it! 1014 return ScriptObjectMirror.unwrap(ScriptObjectMirror.wrap(load(newGlobal, from), newGlobal), oldGlobal); 1015 } finally { 1016 setGlobal(oldGlobal); 1017 } 1018 } 1019 1020 /** 1021 * Load or get a structure class. Structure class names are based on the number of parameter fields 1022 * and {@link AccessorProperty} fields in them. Structure classes are used to represent ScriptObjects 1023 * 1024 * @see ObjectClassGenerator 1025 * @see AccessorProperty 1026 * @see ScriptObject 1027 * 1028 * @param fullName full name of class, e.g. jdk.nashorn.internal.objects.JO2P1 contains 2 fields and 1 parameter. 1029 * 1030 * @return the {@code Class<?>} for this structure 1031 * 1032 * @throws ClassNotFoundException if structure class cannot be resolved 1033 */ 1034 @SuppressWarnings("unchecked") 1035 public static Class<? extends ScriptObject> forStructureClass(final String fullName) throws ClassNotFoundException { 1036 if (System.getSecurityManager() != null && !StructureLoader.isStructureClass(fullName)) { 1037 throw new ClassNotFoundException(fullName); 1038 } 1039 return (Class<? extends ScriptObject>)structureClasses.computeIfAbsent(fullName, (name) -> { 1040 try { 1041 return Class.forName(name, true, sharedLoader); 1042 } catch (final ClassNotFoundException e) { 1043 throw new AssertionError(e); 1044 } 1045 }); 1046 } 1047 1048 /** 1049 * Is {@code className} the name of a structure class? 1050 * 1051 * @param className a class name 1052 * @return true if className is a structure class name 1053 */ 1054 public static boolean isStructureClass(final String className) { 1055 return StructureLoader.isStructureClass(className); 1056 } 1057 1058 /** 1059 * Checks that the given Class can be accessed from no permissions context. 1060 * 1061 * @param clazz Class object 1062 * @throws SecurityException if not accessible 1063 */ 1064 public static void checkPackageAccess(final Class<?> clazz) { 1065 final SecurityManager sm = System.getSecurityManager(); 1066 if (sm != null) { 1067 Class<?> bottomClazz = clazz; 1068 while (bottomClazz.isArray()) { 1069 bottomClazz = bottomClazz.getComponentType(); 1070 } 1071 checkPackageAccess(sm, bottomClazz.getName()); 1072 } 1073 } 1074 1075 /** 1076 * Checks that the given package name can be accessed from no permissions context. 1077 * 1078 * @param pkgName package name 1079 * @throws SecurityException if not accessible 1080 */ 1081 public static void checkPackageAccess(final String pkgName) { 1082 final SecurityManager sm = System.getSecurityManager(); 1083 if (sm != null) { 1084 checkPackageAccess(sm, pkgName.endsWith(".") ? pkgName : pkgName + "."); 1085 } 1086 } 1087 1088 /** 1089 * Checks that the given package can be accessed from no permissions context. 1090 * 1091 * @param sm current security manager instance 1092 * @param fullName fully qualified package name 1093 * @throw SecurityException if not accessible 1094 */ 1095 private static void checkPackageAccess(final SecurityManager sm, final String fullName) { 1096 Objects.requireNonNull(sm); 1097 final int index = fullName.lastIndexOf('.'); 1098 if (index != -1) { 1099 final String pkgName = fullName.substring(0, index); 1100 AccessController.doPrivileged(new PrivilegedAction<Void>() { 1101 @Override 1102 public Void run() { 1103 sm.checkPackageAccess(pkgName); 1104 return null; 1105 } 1106 }, NO_PERMISSIONS_ACC_CTXT); 1107 } 1108 } 1109 1110 /** 1111 * Checks that the given Class can be accessed from no permissions context. 1112 * 1113 * @param clazz Class object 1114 * @return true if package is accessible, false otherwise 1115 */ 1116 private static boolean isAccessiblePackage(final Class<?> clazz) { 1117 try { 1118 checkPackageAccess(clazz); 1119 return true; 1120 } catch (final SecurityException se) { 1121 return false; 1122 } 1123 } 1124 1125 /** 1126 * Checks that the given Class is public and it can be accessed from no permissions context. 1127 * 1128 * @param clazz Class object to check 1129 * @return true if Class is accessible, false otherwise 1130 */ 1131 public static boolean isAccessibleClass(final Class<?> clazz) { 1132 return Modifier.isPublic(clazz.getModifiers()) && Context.isAccessiblePackage(clazz); 1133 } 1134 1135 /** 1136 * Lookup a Java class. This is used for JSR-223 stuff linking in from 1137 * {@code jdk.nashorn.internal.objects.NativeJava} and {@code jdk.nashorn.internal.runtime.NativeJavaPackage} 1138 * 1139 * @param fullName full name of class to load 1140 * 1141 * @return the {@code Class<?>} for the name 1142 * 1143 * @throws ClassNotFoundException if class cannot be resolved 1144 */ 1145 public Class<?> findClass(final String fullName) throws ClassNotFoundException { 1146 if (fullName.indexOf('[') != -1 || fullName.indexOf('/') != -1) { 1147 // don't allow array class names or internal names. 1148 throw new ClassNotFoundException(fullName); 1149 } 1150 1151 // give chance to ClassFilter to filter out, if present 1152 if (classFilter != null && !classFilter.exposeToScripts(fullName)) { 1153 throw new ClassNotFoundException(fullName); 1154 } 1155 1156 // check package access as soon as possible! 1157 final SecurityManager sm = System.getSecurityManager(); 1158 if (sm != null) { 1159 checkPackageAccess(sm, fullName); 1160 } 1161 1162 // Try finding using the "app" loader. 1163 return Class.forName(fullName, true, appLoader); 1164 } 1165 1166 /** 1167 * Hook to print stack trace for a {@link Throwable} that occurred during 1168 * execution 1169 * 1170 * @param t throwable for which to dump stack 1171 */ 1172 public static void printStackTrace(final Throwable t) { 1173 if (Context.DEBUG) { 1174 t.printStackTrace(Context.getCurrentErr()); 1175 } 1176 } 1177 1178 /** 1179 * Verify generated bytecode before emission. This is called back from the 1180 * {@link ObjectClassGenerator} or the {@link Compiler}. If the "--verify-code" parameter 1181 * hasn't been given, this is a nop 1182 * 1183 * Note that verification may load classes -- we don't want to do that unless 1184 * user specified verify option. We check it here even though caller 1185 * may have already checked that flag 1186 * 1187 * @param bytecode bytecode to verify 1188 */ 1189 public void verify(final byte[] bytecode) { 1190 if (env._verify_code) { 1191 // No verification when security manager is around as verifier 1192 // may load further classes - which should be avoided. 1193 if (System.getSecurityManager() == null) { 1194 CheckClassAdapter.verify(new ClassReader(bytecode), sharedLoader, false, new PrintWriter(System.err, true)); 1195 } 1196 } 1197 } 1198 1199 /** 1200 * Create and initialize a new global scope object. 1201 * 1202 * @return the initialized global scope object. 1203 */ 1204 public Global createGlobal() { 1205 return initGlobal(newGlobal()); 1206 } 1207 1208 /** 1209 * Create a new uninitialized global scope object 1210 * @return the global script object 1211 */ 1212 public Global newGlobal() { 1213 createOrInvalidateGlobalConstants(); 1214 return new Global(this); 1215 } 1216 1217 private void createOrInvalidateGlobalConstants() { 1218 for (;;) { 1219 final GlobalConstants currentGlobalConstants = getGlobalConstants(); 1220 if (currentGlobalConstants != null) { 1221 // Subsequent invocation; we're creating our second or later Global. GlobalConstants is not safe to use 1222 // with more than one Global, as the constant method handle linkages it creates create a coupling 1223 // between the Global and the call sites in the compiled code. 1224 currentGlobalConstants.invalidateForever(); 1225 return; 1226 } 1227 final GlobalConstants newGlobalConstants = new GlobalConstants(getLogger(GlobalConstants.class)); 1228 if (globalConstantsRef.compareAndSet(null, newGlobalConstants)) { 1229 // First invocation; we're creating the first Global in this Context. Create the GlobalConstants object 1230 // for this Context. 1231 return; 1232 } 1233 1234 // If we reach here, then we started out as the first invocation, but another concurrent invocation won the 1235 // CAS race. We'll just let the loop repeat and invalidate the CAS race winner. 1236 } 1237 } 1238 1239 /** 1240 * Initialize given global scope object. 1241 * 1242 * @param global the global 1243 * @param engine the associated ScriptEngine instance, can be null 1244 * @return the initialized global scope object. 1245 */ 1246 public Global initGlobal(final Global global, final ScriptEngine engine) { 1247 // Need only minimal global object, if we are just compiling. 1248 if (!env._compile_only) { 1249 final Global oldGlobal = Context.getGlobal(); 1250 try { 1251 Context.setGlobal(global); 1252 // initialize global scope with builtin global objects 1253 global.initBuiltinObjects(engine); 1254 } finally { 1255 Context.setGlobal(oldGlobal); 1256 } 1257 } 1258 1259 return global; 1260 } 1261 1262 /** 1263 * Initialize given global scope object. 1264 * 1265 * @param global the global 1266 * @return the initialized global scope object. 1267 */ 1268 public Global initGlobal(final Global global) { 1269 return initGlobal(global, null); 1270 } 1271 1272 /** 1273 * Return the current global's context 1274 * @return current global's context 1275 */ 1276 static Context getContextTrusted() { 1277 return getContext(getGlobal()); 1278 } 1279 1280 /** 1281 * Gets the Nashorn dynamic linker for the specified class. If the class is 1282 * a script class, the dynamic linker associated with its context is 1283 * returned. Otherwise the dynamic linker associated with the current 1284 * context is returned. 1285 * @param clazz the class for which we want to retrieve a dynamic linker. 1286 * @return the Nashorn dynamic linker for the specified class. 1287 */ 1288 public static DynamicLinker getDynamicLinker(final Class<?> clazz) { 1289 return fromClass(clazz).dynamicLinker; 1290 } 1291 1292 /** 1293 * Gets the Nashorn dynamic linker associated with the current context. 1294 * @return the Nashorn dynamic linker for the current context. 1295 */ 1296 public static DynamicLinker getDynamicLinker() { 1297 return getContextTrusted().dynamicLinker; 1298 } 1299 1300 /** 1301 * Creates a module layer with one module that is defined to the given class 1302 * loader. 1303 * 1304 * @param descriptor the module descriptor for the newly created module 1305 * @param loader the class loader of the module 1306 * @return the new Module 1307 */ 1308 public static Module createModule(final ModuleDescriptor descriptor, final ClassLoader loader) { 1309 final SecurityManager sm = System.getSecurityManager(); 1310 if (sm != null) { 1311 sm.checkPermission(new RuntimePermission(NASHORN_CREATE_MODULE)); 1312 } 1313 return createModuleTrusted(descriptor, loader); 1314 } 1315 1316 /** 1317 * Creates a module layer with one module that is defined to the given class 1318 * loader. 1319 * 1320 * @param descriptor the module descriptor for the newly created module 1321 * @param loader the class loader of the module 1322 * @return the new Module 1323 */ 1324 static Module createModuleTrusted(final ModuleDescriptor descriptor, final ClassLoader loader) { 1325 return createModuleTrusted(Layer.boot(), descriptor, loader); 1326 } 1327 1328 /** 1329 * Creates a module layer with one module that is defined to the given class 1330 * loader. 1331 * 1332 * @param parent the parent layer of the new module 1333 * @param descriptor the module descriptor for the newly created module 1334 * @param loader the class loader of the module 1335 * @return the new Module 1336 */ 1337 static Module createModuleTrusted(final Layer parent, final ModuleDescriptor descriptor, final ClassLoader loader) { 1338 final String mn = descriptor.name(); 1339 1340 final ModuleReference mref = new ModuleReference(descriptor, null, () -> { 1341 IOException ioe = new IOException("<dynamic module>"); 1342 throw new UncheckedIOException(ioe); 1343 }); 1344 1345 final ModuleFinder finder = new ModuleFinder() { 1346 @Override 1347 public Optional<ModuleReference> find(String name) { 1348 if (name.equals(mn)) { 1349 return Optional.of(mref); 1350 } else { 1351 return Optional.empty(); 1352 } 1353 } 1354 @Override 1355 public Set<ModuleReference> findAll() { 1356 return Set.of(mref); 1357 } 1358 }; 1359 1360 final Configuration cf = parent.configuration() 1361 .resolveRequires(finder, ModuleFinder.of(), Set.of(mn)); 1362 1363 final PrivilegedAction<Layer> pa = () -> parent.defineModules(cf, name -> loader); 1364 final Layer layer = AccessController.doPrivileged(pa, GET_LOADER_ACC_CTXT); 1365 1366 final Module m = layer.findModule(mn).get(); 1367 assert m.getLayer() == layer; 1368 1369 return m; 1370 } 1371 1372 static Context getContextTrustedOrNull() { 1373 final Global global = Context.getGlobal(); 1374 return global == null ? null : getContext(global); 1375 } 1376 1377 private static Context getContext(final Global global) { 1378 // We can't invoke Global.getContext() directly, as it's a protected override, and Global isn't in our package. 1379 // In order to access the method, we must cast it to ScriptObject first (which is in our package) and then let 1380 // virtual invocation do its thing. 1381 return ((ScriptObject)global).getContext(); 1382 } 1383 1384 /** 1385 * Try to infer Context instance from the Class. If we cannot, 1386 * then get it from the thread local variable. 1387 * 1388 * @param clazz the class 1389 * @return context 1390 */ 1391 static Context fromClass(final Class<?> clazz) { 1392 ClassLoader loader = null; 1393 try { 1394 loader = clazz.getClassLoader(); 1395 } catch (SecurityException ignored) { 1396 // This could fail because of anonymous classes being used. 1397 // Accessing loader of anonymous class fails (for extension 1398 // loader class too?). In any case, for us fetching Context 1399 // from class loader is just an optimization. We can always 1400 // get Context from thread local storage (below). 1401 } 1402 1403 if (loader instanceof ScriptLoader) { 1404 return ((ScriptLoader)loader).getContext(); 1405 } 1406 1407 return Context.getContextTrusted(); 1408 } 1409 1410 private URL getResourceURL(final String resName) { 1411 if (appLoader != null) { 1412 return appLoader.getResource(resName); 1413 } 1414 return ClassLoader.getSystemResource(resName); 1415 } 1416 1417 private Object evaluateSource(final Source source, final ScriptObject scope, final ScriptObject thiz) { 1418 ScriptFunction script = null; 1419 1420 try { 1421 script = compileScript(source, scope, new Context.ThrowErrorManager()); 1422 } catch (final ParserException e) { 1423 e.throwAsEcmaException(); 1424 } 1425 1426 return ScriptRuntime.apply(script, thiz); 1427 } 1428 1429 private static ScriptFunction getProgramFunction(final Class<?> script, final ScriptObject scope) { 1430 if (script == null) { 1431 return null; 1432 } 1433 return invokeCreateProgramFunctionHandle(getCreateProgramFunctionHandle(script), scope); 1434 } 1435 1436 private static MethodHandle getCreateProgramFunctionHandle(final Class<?> script) { 1437 try { 1438 return LOOKUP.findStatic(script, CREATE_PROGRAM_FUNCTION.symbolName(), CREATE_PROGRAM_FUNCTION_TYPE); 1439 } catch (NoSuchMethodException | IllegalAccessException e) { 1440 throw new AssertionError("Failed to retrieve a handle for the program function for " + script.getName(), e); 1441 } 1442 } 1443 1444 private static ScriptFunction invokeCreateProgramFunctionHandle(final MethodHandle createProgramFunctionHandle, final ScriptObject scope) { 1445 try { 1446 return (ScriptFunction)createProgramFunctionHandle.invokeExact(scope); 1447 } catch (final RuntimeException|Error e) { 1448 throw e; 1449 } catch (final Throwable t) { 1450 throw new AssertionError("Failed to create a program function", t); 1451 } 1452 } 1453 1454 private ScriptFunction compileScript(final Source source, final ScriptObject scope, final ErrorManager errMan) { 1455 return getProgramFunction(compile(source, errMan, this._strict, false), scope); 1456 } 1457 1458 private synchronized Class<?> compile(final Source source, final ErrorManager errMan, final boolean strict, final boolean isEval) { 1459 // start with no errors, no warnings. 1460 errMan.reset(); 1461 1462 Class<?> script = findCachedClass(source); 1463 if (script != null) { 1464 final DebugLogger log = getLogger(Compiler.class); 1465 if (log.isEnabled()) { 1466 log.fine(new RuntimeEvent<>(Level.INFO, source), "Code cache hit for ", source, " avoiding recompile."); 1467 } 1468 return script; 1469 } 1470 1471 StoredScript storedScript = null; 1472 FunctionNode functionNode = null; 1473 // Don't use code store if optimistic types is enabled but lazy compilation is not. 1474 // This would store a full script compilation with many wrong optimistic assumptions that would 1475 // do more harm than good on later runs with both optimistic types and lazy compilation enabled. 1476 final boolean useCodeStore = codeStore != null && !env._parse_only && (!env._optimistic_types || env._lazy_compilation); 1477 final String cacheKey = useCodeStore ? CodeStore.getCacheKey("script", null) : null; 1478 1479 if (useCodeStore) { 1480 storedScript = codeStore.load(source, cacheKey); 1481 } 1482 1483 if (storedScript == null) { 1484 if (env._dest_dir != null) { 1485 source.dump(env._dest_dir); 1486 } 1487 1488 functionNode = new Parser(env, source, errMan, strict, getLogger(Parser.class)).parse(); 1489 1490 if (errMan.hasErrors()) { 1491 return null; 1492 } 1493 1494 if (env._print_ast || functionNode.getDebugFlag(FunctionNode.DEBUG_PRINT_AST)) { 1495 getErr().println(new ASTWriter(functionNode)); 1496 } 1497 1498 if (env._print_parse || functionNode.getDebugFlag(FunctionNode.DEBUG_PRINT_PARSE)) { 1499 getErr().println(new PrintVisitor(functionNode, true, false)); 1500 } 1501 } 1502 1503 if (env._parse_only) { 1504 return null; 1505 } 1506 1507 final URL url = source.getURL(); 1508 final CodeSource cs = new CodeSource(url, (CodeSigner[])null); 1509 final CodeInstaller installer; 1510 if (!env.useAnonymousClasses(isEval) || env._persistent_cache || !env._lazy_compilation) { 1511 // Persistent code cache and eager compilation preclude use of VM anonymous classes 1512 final ScriptLoader loader = env._loader_per_compile ? createNewLoader() : scriptLoader; 1513 installer = new NamedContextCodeInstaller(this, cs, loader); 1514 } else { 1515 installer = new AnonymousContextCodeInstaller(this, cs, 1516 anonymousHostClasses.getOrCreate(cs, (key) -> 1517 createNewLoader().installClass( 1518 // NOTE: we're defining these constants in AnonymousContextCodeInstaller so they are not 1519 // initialized if we don't use AnonymousContextCodeInstaller. As this method is only ever 1520 // invoked from AnonymousContextCodeInstaller, this is okay. 1521 AnonymousContextCodeInstaller.ANONYMOUS_HOST_CLASS_NAME, 1522 AnonymousContextCodeInstaller.ANONYMOUS_HOST_CLASS_BYTES, cs))); 1523 } 1524 1525 if (storedScript == null) { 1526 final CompilationPhases phases = Compiler.CompilationPhases.COMPILE_ALL; 1527 1528 final Compiler compiler = Compiler.forInitialCompilation( 1529 installer, 1530 source, 1531 errMan, 1532 strict | functionNode.isStrict()); 1533 1534 final FunctionNode compiledFunction = compiler.compile(functionNode, phases); 1535 if (errMan.hasErrors()) { 1536 return null; 1537 } 1538 script = compiledFunction.getRootClass(); 1539 compiler.persistClassInfo(cacheKey, compiledFunction); 1540 } else { 1541 Compiler.updateCompilationId(storedScript.getCompilationId()); 1542 script = storedScript.installScript(source, installer); 1543 } 1544 1545 cacheClass(source, script); 1546 return script; 1547 } 1548 1549 private ScriptLoader createNewLoader() { 1550 return AccessController.doPrivileged( 1551 new PrivilegedAction<ScriptLoader>() { 1552 @Override 1553 public ScriptLoader run() { 1554 return new ScriptLoader(appLoader, Context.this); 1555 } 1556 }, CREATE_LOADER_ACC_CTXT); 1557 } 1558 1559 private long getUniqueScriptId() { 1560 return uniqueScriptId.getAndIncrement(); 1561 } 1562 1563 /** 1564 * Cache for compiled script classes. 1565 */ 1566 @SuppressWarnings("serial") 1567 @Logger(name="classcache") 1568 private static class ClassCache extends LinkedHashMap<Source, ClassReference> implements Loggable { 1569 private final int size; 1570 private final ReferenceQueue<Class<?>> queue; 1571 private final DebugLogger log; 1572 1573 ClassCache(final Context context, final int size) { 1574 super(size, 0.75f, true); 1575 this.size = size; 1576 this.queue = new ReferenceQueue<>(); 1577 this.log = initLogger(context); 1578 } 1579 1580 void cache(final Source source, final Class<?> clazz) { 1581 if (log.isEnabled()) { 1582 log.info("Caching ", source, " in class cache"); 1583 } 1584 put(source, new ClassReference(clazz, queue, source)); 1585 } 1586 1587 @Override 1588 protected boolean removeEldestEntry(final Map.Entry<Source, ClassReference> eldest) { 1589 return size() > size; 1590 } 1591 1592 @Override 1593 public ClassReference get(final Object key) { 1594 for (ClassReference ref; (ref = (ClassReference)queue.poll()) != null; ) { 1595 final Source source = ref.source; 1596 if (log.isEnabled()) { 1597 log.info("Evicting ", source, " from class cache."); 1598 } 1599 remove(source); 1600 } 1601 1602 final ClassReference ref = super.get(key); 1603 if (ref != null && log.isEnabled()) { 1604 log.info("Retrieved class reference for ", ref.source, " from class cache"); 1605 } 1606 return ref; 1607 } 1608 1609 @Override 1610 public DebugLogger initLogger(final Context context) { 1611 return context.getLogger(getClass()); 1612 } 1613 1614 @Override 1615 public DebugLogger getLogger() { 1616 return log; 1617 } 1618 1619 } 1620 1621 private static class ClassReference extends SoftReference<Class<?>> { 1622 private final Source source; 1623 1624 ClassReference(final Class<?> clazz, final ReferenceQueue<Class<?>> queue, final Source source) { 1625 super(clazz, queue); 1626 this.source = source; 1627 } 1628 } 1629 1630 // Class cache management 1631 private Class<?> findCachedClass(final Source source) { 1632 final ClassReference ref = classCache == null ? null : classCache.get(source); 1633 return ref != null ? ref.get() : null; 1634 } 1635 1636 private void cacheClass(final Source source, final Class<?> clazz) { 1637 if (classCache != null) { 1638 classCache.cache(source, clazz); 1639 } 1640 } 1641 1642 // logging 1643 private final Map<String, DebugLogger> loggers = new HashMap<>(); 1644 1645 private void initLoggers() { 1646 ((Loggable)MethodHandleFactory.getFunctionality()).initLogger(this); 1647 } 1648 1649 /** 1650 * Get a logger, given a loggable class 1651 * @param clazz a Loggable class 1652 * @return debuglogger associated with that class 1653 */ 1654 public DebugLogger getLogger(final Class<? extends Loggable> clazz) { 1655 return getLogger(clazz, null); 1656 } 1657 1658 /** 1659 * Get a logger, given a loggable class 1660 * @param clazz a Loggable class 1661 * @param initHook an init hook - if this is the first time the logger is created in the context, run the init hook 1662 * @return debuglogger associated with that class 1663 */ 1664 public DebugLogger getLogger(final Class<? extends Loggable> clazz, final Consumer<DebugLogger> initHook) { 1665 final String name = getLoggerName(clazz); 1666 DebugLogger logger = loggers.get(name); 1667 if (logger == null) { 1668 if (!env.hasLogger(name)) { 1669 return DebugLogger.DISABLED_LOGGER; 1670 } 1671 final LoggerInfo info = env._loggers.get(name); 1672 logger = new DebugLogger(name, info.getLevel(), info.isQuiet()); 1673 if (initHook != null) { 1674 initHook.accept(logger); 1675 } 1676 loggers.put(name, logger); 1677 } 1678 return logger; 1679 } 1680 1681 /** 1682 * Given a Loggable class, weave debug info info a method handle for that logger. 1683 * Level.INFO is used 1684 * 1685 * @param clazz loggable 1686 * @param mh method handle 1687 * @param text debug printout to add 1688 * 1689 * @return instrumented method handle, or null if logger not enabled 1690 */ 1691 public MethodHandle addLoggingToHandle(final Class<? extends Loggable> clazz, final MethodHandle mh, final Supplier<String> text) { 1692 return addLoggingToHandle(clazz, Level.INFO, mh, Integer.MAX_VALUE, false, text); 1693 } 1694 1695 /** 1696 * Given a Loggable class, weave debug info info a method handle for that logger. 1697 * 1698 * @param clazz loggable 1699 * @param level log level 1700 * @param mh method handle 1701 * @param paramStart first parameter to print 1702 * @param printReturnValue should we print the return value? 1703 * @param text debug printout to add 1704 * 1705 * @return instrumented method handle, or null if logger not enabled 1706 */ 1707 public MethodHandle addLoggingToHandle(final Class<? extends Loggable> clazz, final Level level, final MethodHandle mh, final int paramStart, final boolean printReturnValue, final Supplier<String> text) { 1708 final DebugLogger log = getLogger(clazz); 1709 if (log.isEnabled()) { 1710 return MethodHandleFactory.addDebugPrintout(log, level, mh, paramStart, printReturnValue, text.get()); 1711 } 1712 return mh; 1713 } 1714 1715 private static String getLoggerName(final Class<?> clazz) { 1716 Class<?> current = clazz; 1717 while (current != null) { 1718 final Logger log = current.getAnnotation(Logger.class); 1719 if (log != null) { 1720 assert !"".equals(log.name()); 1721 return log.name(); 1722 } 1723 current = current.getSuperclass(); 1724 } 1725 assert false; 1726 return null; 1727 } 1728 1729 /** 1730 * This is a special kind of switchpoint used to guard builtin 1731 * properties and prototypes. In the future it might contain 1732 * logic to e.g. multiple switchpoint classes. 1733 */ 1734 public static final class BuiltinSwitchPoint extends SwitchPoint { 1735 //empty 1736 } 1737 1738 /** 1739 * Create a new builtin switchpoint and return it 1740 * @param name key name 1741 * @return new builtin switchpoint 1742 */ 1743 public SwitchPoint newBuiltinSwitchPoint(final String name) { 1744 assert builtinSwitchPoints.get(name) == null; 1745 final SwitchPoint sp = new BuiltinSwitchPoint(); 1746 builtinSwitchPoints.put(name, sp); 1747 return sp; 1748 } 1749 1750 /** 1751 * Return the builtin switchpoint for a particular key name 1752 * @param name key name 1753 * @return builtin switchpoint or null if none 1754 */ 1755 public SwitchPoint getBuiltinSwitchPoint(final String name) { 1756 return builtinSwitchPoints.get(name); 1757 } 1758} 1759