Context.java revision 1794:be6d5fa243d9
1219019Sgabor/* 2219019Sgabor * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3219019Sgabor * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4219019Sgabor * 5219019Sgabor * This code is free software; you can redistribute it and/or modify it 6219019Sgabor * under the terms of the GNU General Public License version 2 only, as 7219019Sgabor * published by the Free Software Foundation. Oracle designates this 8219019Sgabor * particular file as subject to the "Classpath" exception as provided 9219019Sgabor * by Oracle in the LICENSE file that accompanied this code. 10219019Sgabor * 11219019Sgabor * This code is distributed in the hope that it will be useful, but WITHOUT 12219019Sgabor * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13219019Sgabor * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14219019Sgabor * version 2 for more details (a copy is included in the LICENSE file that 15219019Sgabor * accompanied this code). 16219019Sgabor * 17219019Sgabor * You should have received a copy of the GNU General Public License version 18219019Sgabor * 2 along with this work; if not, write to the Free Software Foundation, 19219019Sgabor * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20219019Sgabor * 21219019Sgabor * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22219019Sgabor * or visit www.oracle.com if you need additional information or have any 23219019Sgabor * questions. 24219019Sgabor */ 25219019Sgabor 26219019Sgaborpackage jdk.nashorn.internal.runtime; 27219019Sgabor 28219019Sgaborimport static jdk.internal.org.objectweb.asm.Opcodes.V1_7; 29219019Sgaborimport static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS; 30219019Sgaborimport static jdk.nashorn.internal.codegen.CompilerConstants.CREATE_PROGRAM_FUNCTION; 31219019Sgaborimport static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE; 32219019Sgaborimport static jdk.nashorn.internal.codegen.CompilerConstants.STRICT_MODE; 33219019Sgaborimport static jdk.nashorn.internal.runtime.CodeStore.newCodeStore; 34219019Sgaborimport static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 35219019Sgaborimport static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; 36219019Sgaborimport static jdk.nashorn.internal.runtime.Source.sourceFor; 37219019Sgabor 38219019Sgaborimport java.io.File; 39219019Sgaborimport java.io.InputStream; 40219019Sgaborimport java.io.IOException; 41219019Sgaborimport java.io.PrintWriter; 42219019Sgaborimport java.io.UncheckedIOException; 43219019Sgaborimport java.lang.invoke.MethodHandle; 44219019Sgaborimport java.lang.invoke.MethodHandles; 45219019Sgaborimport java.lang.invoke.MethodType; 46219019Sgaborimport java.lang.invoke.SwitchPoint; 47219019Sgaborimport java.lang.ref.ReferenceQueue; 48219019Sgaborimport java.lang.ref.SoftReference; 49219019Sgaborimport java.lang.module.Configuration; 50219019Sgaborimport java.lang.module.ModuleDescriptor; 51219019Sgaborimport java.lang.module.ModuleFinder; 52219019Sgaborimport java.lang.module.ModuleReference; 53219019Sgaborimport java.lang.reflect.Field; 54219019Sgaborimport java.lang.reflect.Layer; 55219019Sgaborimport java.lang.reflect.Modifier; 56219019Sgaborimport java.lang.reflect.Module; 57219019Sgaborimport java.net.MalformedURLException; 58219019Sgaborimport java.net.URL; 59219019Sgaborimport java.nio.file.Path; 60219019Sgaborimport java.nio.file.Paths; 61219019Sgaborimport java.security.AccessControlContext; 62219019Sgaborimport java.security.AccessController; 63219019Sgaborimport java.security.CodeSigner; 64219019Sgaborimport java.security.CodeSource; 65219019Sgaborimport java.security.Permissions; 66219019Sgaborimport java.security.PrivilegedAction; 67219019Sgaborimport java.security.PrivilegedActionException; 68219019Sgaborimport java.security.PrivilegedExceptionAction; 69219019Sgaborimport java.security.ProtectionDomain; 70219019Sgaborimport java.util.Collection; 71219019Sgaborimport java.util.HashMap; 72219019Sgaborimport java.util.HashSet; 73219019Sgaborimport java.util.LinkedHashMap; 74219019Sgaborimport java.util.Map; 75219019Sgaborimport java.util.Objects; 76219019Sgaborimport java.util.Optional; 77219019Sgaborimport java.util.Set; 78219019Sgaborimport java.util.concurrent.ConcurrentHashMap; 79219019Sgaborimport java.util.concurrent.ConcurrentMap; 80219019Sgaborimport java.util.concurrent.atomic.AtomicLong; 81219019Sgaborimport java.util.concurrent.atomic.AtomicReference; 82219019Sgaborimport java.util.concurrent.atomic.LongAdder; 83219019Sgaborimport java.util.function.Consumer; 84219019Sgaborimport java.util.function.Supplier; 85219019Sgaborimport java.util.logging.Level; 86219019Sgaborimport java.util.stream.Collectors; 87219019Sgaborimport java.util.stream.Stream; 88219019Sgaborimport javax.script.ScriptEngine; 89219019Sgaborimport jdk.dynalink.DynamicLinker; 90219019Sgaborimport jdk.internal.org.objectweb.asm.ClassReader; 91219019Sgaborimport jdk.internal.org.objectweb.asm.ClassWriter; 92219019Sgaborimport jdk.internal.org.objectweb.asm.Opcodes; 93219019Sgaborimport jdk.internal.org.objectweb.asm.util.CheckClassAdapter; 94219019Sgaborimport jdk.nashorn.api.scripting.ClassFilter; 95219019Sgaborimport jdk.nashorn.api.scripting.ScriptObjectMirror; 96219019Sgaborimport jdk.nashorn.internal.WeakValueCache; 97219019Sgaborimport jdk.nashorn.internal.codegen.Compiler; 98219019Sgaborimport jdk.nashorn.internal.codegen.Compiler.CompilationPhases; 99219019Sgaborimport jdk.nashorn.internal.codegen.ObjectClassGenerator; 100219019Sgaborimport jdk.nashorn.internal.ir.FunctionNode; 101219019Sgaborimport jdk.nashorn.internal.ir.debug.ASTWriter; 102219019Sgaborimport jdk.nashorn.internal.ir.debug.PrintVisitor; 103219019Sgaborimport jdk.nashorn.internal.lookup.MethodHandleFactory; 104219019Sgaborimport jdk.nashorn.internal.objects.Global; 105219019Sgaborimport jdk.nashorn.internal.parser.Parser; 106219019Sgaborimport jdk.nashorn.internal.runtime.events.RuntimeEvent; 107219019Sgaborimport jdk.nashorn.internal.runtime.linker.Bootstrap; 108219019Sgaborimport jdk.nashorn.internal.runtime.logging.DebugLogger; 109219019Sgaborimport jdk.nashorn.internal.runtime.logging.Loggable; 110219019Sgaborimport jdk.nashorn.internal.runtime.logging.Logger; 111219019Sgaborimport jdk.nashorn.internal.runtime.options.LoggingOption.LoggerInfo; 112219019Sgaborimport jdk.nashorn.internal.runtime.options.Options; 113219019Sgaborimport jdk.internal.misc.Unsafe; 114219019Sgabor 115219019Sgabor/** 116219019Sgabor * This class manages the global state of execution. Context is immutable. 117219019Sgabor */ 118219019Sgaborpublic final class Context { 119219019Sgabor // nashorn specific security runtime access permission names 120219019Sgabor /** 121219019Sgabor * Permission needed to pass arbitrary nashorn command line options when creating Context. 122219019Sgabor */ 123219019Sgabor public static final String NASHORN_SET_CONFIG = "nashorn.setConfig"; 124219019Sgabor 125219019Sgabor /** 126219019Sgabor * Permission needed to create Nashorn Context instance. 127219019Sgabor */ 128219019Sgabor public static final String NASHORN_CREATE_CONTEXT = "nashorn.createContext"; 129219019Sgabor 130219019Sgabor /** 131219019Sgabor * Permission needed to create Nashorn Global instance. 132219019Sgabor */ 133219019Sgabor public static final String NASHORN_CREATE_GLOBAL = "nashorn.createGlobal"; 134219019Sgabor 135219019Sgabor /** 136219019Sgabor * Permission to get current Nashorn Context from thread local storage. 137219019Sgabor */ 138219019Sgabor public static final String NASHORN_GET_CONTEXT = "nashorn.getContext"; 139219019Sgabor 140219019Sgabor /** 141219019Sgabor * Permission to use Java reflection/jsr292 from script code. 142219019Sgabor */ 143219019Sgabor public static final String NASHORN_JAVA_REFLECTION = "nashorn.JavaReflection"; 144219019Sgabor 145219019Sgabor /** 146219019Sgabor * Permission to enable nashorn debug mode. 147219019Sgabor */ 148219019Sgabor public static final String NASHORN_DEBUG_MODE = "nashorn.debugMode"; 149219019Sgabor 150219019Sgabor // nashorn load psuedo URL prefixes 151219019Sgabor private static final String LOAD_CLASSPATH = "classpath:"; 152219019Sgabor private static final String LOAD_FX = "fx:"; 153219019Sgabor private static final String LOAD_NASHORN = "nashorn:"; 154219019Sgabor 155219019Sgabor private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); 156219019Sgabor private static final MethodType CREATE_PROGRAM_FUNCTION_TYPE = MethodType.methodType(ScriptFunction.class, ScriptObject.class); 157219019Sgabor 158219019Sgabor private static final LongAdder NAMED_INSTALLED_SCRIPT_COUNT = new LongAdder(); 159219019Sgabor private static final LongAdder ANONYMOUS_INSTALLED_SCRIPT_COUNT = new LongAdder(); 160219019Sgabor 161219019Sgabor /** 162219019Sgabor * Should scripts use only object slots for fields, or dual long/object slots? The default 163219019Sgabor * behaviour is to couple this to optimistic types, using dual representation if optimistic types are enabled 164219019Sgabor * and single field representation otherwise. This can be overridden by setting either the "nashorn.fields.objects" 165219019Sgabor * or "nashorn.fields.dual" system property. 166219019Sgabor */ 167219019Sgabor private final FieldMode fieldMode; 168219019Sgabor 169219019Sgabor private static enum FieldMode { 170219019Sgabor /** Value for automatic field representation depending on optimistic types setting */ 171219019Sgabor AUTO, 172219019Sgabor /** Value for object field representation regardless of optimistic types setting */ 173219019Sgabor OBJECTS, 174219019Sgabor /** Value for dual primitive/object field representation regardless of optimistic types setting */ 175219019Sgabor DUAL 176219019Sgabor } 177219019Sgabor 178219019Sgabor /** 179219019Sgabor * Keeps track of which builtin prototypes and properties have been relinked 180219019Sgabor * Currently we are conservative and associate the name of a builtin class with all 181219019Sgabor * its properties, so it's enough to invalidate a property to break all assumptions 182219019Sgabor * about a prototype. This can be changed to a more fine grained approach, but no one 183219019Sgabor * ever needs this, given the very rare occurrence of swapping out only parts of 184219019Sgabor * a builtin v.s. the entire builtin object 185219019Sgabor */ 186219019Sgabor private final Map<String, SwitchPoint> builtinSwitchPoints = new HashMap<>(); 187219019Sgabor 188219019Sgabor /* Force DebuggerSupport to be loaded. */ 189219019Sgabor static { 190219019Sgabor DebuggerSupport.FORCELOAD = true; 191219019Sgabor } 192219019Sgabor 193219019Sgabor static long getNamedInstalledScriptCount() { 194219019Sgabor return NAMED_INSTALLED_SCRIPT_COUNT.sum(); 195219019Sgabor } 196219019Sgabor 197219019Sgabor static long getAnonymousInstalledScriptCount() { 198219019Sgabor return ANONYMOUS_INSTALLED_SCRIPT_COUNT.sum(); 199219019Sgabor } 200219019Sgabor 201219019Sgabor /** 202219019Sgabor * ContextCodeInstaller that has the privilege of installing classes in the Context. 203219019Sgabor * Can only be instantiated from inside the context and is opaque to other classes 204219019Sgabor */ 205219019Sgabor private abstract static class ContextCodeInstaller implements CodeInstaller { 206219019Sgabor final Context context; 207219019Sgabor final CodeSource codeSource; 208219019Sgabor 209219019Sgabor ContextCodeInstaller(final Context context, final CodeSource codeSource) { 210219019Sgabor this.context = context; 211219019Sgabor this.codeSource = codeSource; 212219019Sgabor } 213219019Sgabor 214219019Sgabor @Override 215219019Sgabor public Context getContext() { 216219019Sgabor return context; 217219019Sgabor } 218219019Sgabor 219219019Sgabor @Override 220219019Sgabor public void initialize(final Collection<Class<?>> classes, final Source source, final Object[] constants) { 221219019Sgabor try { 222219019Sgabor AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() { 223219019Sgabor @Override 224219019Sgabor public Void run() throws Exception { 225219019Sgabor for (final Class<?> clazz : classes) { 226219019Sgabor //use reflection to write source and constants table to installed classes 227219019Sgabor final Field sourceField = clazz.getDeclaredField(SOURCE.symbolName()); 228219019Sgabor sourceField.setAccessible(true); 229219019Sgabor sourceField.set(null, source); 230219019Sgabor 231219019Sgabor final Field constantsField = clazz.getDeclaredField(CONSTANTS.symbolName()); 232219019Sgabor constantsField.setAccessible(true); 233219019Sgabor constantsField.set(null, constants); 234219019Sgabor } 235219019Sgabor return null; 236219019Sgabor } 237219019Sgabor }); 238219019Sgabor } catch (final PrivilegedActionException e) { 239219019Sgabor throw new RuntimeException(e); 240219019Sgabor } 241219019Sgabor } 242219019Sgabor 243219019Sgabor @Override 244219019Sgabor public void verify(final byte[] code) { 245219019Sgabor context.verify(code); 246219019Sgabor } 247219019Sgabor 248219019Sgabor @Override 249219019Sgabor public long getUniqueScriptId() { 250219019Sgabor return context.getUniqueScriptId(); 251219019Sgabor } 252219019Sgabor 253219019Sgabor @Override 254219019Sgabor public void storeScript(final String cacheKey, final Source source, final String mainClassName, 255219019Sgabor final Map<String,byte[]> classBytes, final Map<Integer, FunctionInitializer> initializers, 256219019Sgabor final Object[] constants, final int compilationId) { 257219019Sgabor if (context.codeStore != null) { 258219019Sgabor context.codeStore.store(cacheKey, source, mainClassName, classBytes, initializers, constants, compilationId); 259219019Sgabor } 260219019Sgabor } 261219019Sgabor 262219019Sgabor @Override 263219019Sgabor public StoredScript loadScript(final Source source, final String functionKey) { 264219019Sgabor if (context.codeStore != null) { 265219019Sgabor return context.codeStore.load(source, functionKey); 266219019Sgabor } 267219019Sgabor return null; 268219019Sgabor } 269219019Sgabor 270219019Sgabor @Override 271219019Sgabor public boolean isCompatibleWith(final CodeInstaller other) { 272219019Sgabor if (other instanceof ContextCodeInstaller) { 273219019Sgabor final ContextCodeInstaller cci = (ContextCodeInstaller)other; 274219019Sgabor return cci.context == context && cci.codeSource == codeSource; 275219019Sgabor } 276219019Sgabor return false; 277219019Sgabor } 278219019Sgabor } 279219019Sgabor 280219019Sgabor private static class NamedContextCodeInstaller extends ContextCodeInstaller { 281219019Sgabor private final ScriptLoader loader; 282219019Sgabor private int usageCount = 0; 283219019Sgabor private int bytesDefined = 0; 284219019Sgabor 285219019Sgabor // We reuse this installer for 10 compilations or 200000 defined bytes. Usually the first condition 286219019Sgabor // will occur much earlier, the second is a safety measure for very large scripts/functions. 287219019Sgabor private final static int MAX_USAGES = 10; 288219019Sgabor private final static int MAX_BYTES_DEFINED = 200_000; 289219019Sgabor 290219019Sgabor private NamedContextCodeInstaller(final Context context, final CodeSource codeSource, final ScriptLoader loader) { 291219019Sgabor super(context, codeSource); 292219019Sgabor this.loader = loader; 293219019Sgabor } 294219019Sgabor 295219019Sgabor @Override 296219019Sgabor public Class<?> install(final String className, final byte[] bytecode) { 297219019Sgabor usageCount++; 298219019Sgabor bytesDefined += bytecode.length; 299219019Sgabor NAMED_INSTALLED_SCRIPT_COUNT.increment(); 300219019Sgabor return loader.installClass(Compiler.binaryName(className), bytecode, codeSource); 301219019Sgabor } 302219019Sgabor 303219019Sgabor @Override 304219019Sgabor public CodeInstaller getOnDemandCompilationInstaller() { 305219019Sgabor // Reuse this installer if we're within our limits. 306219019Sgabor if (usageCount < MAX_USAGES && bytesDefined < MAX_BYTES_DEFINED) { 307219019Sgabor return this; 308219019Sgabor } 309219019Sgabor return new NamedContextCodeInstaller(context, codeSource, context.createNewLoader()); 310219019Sgabor } 311219019Sgabor 312219019Sgabor @Override 313219019Sgabor public CodeInstaller getMultiClassCodeInstaller() { 314219019Sgabor // This installer is perfectly suitable for installing multiple classes that reference each other 315219019Sgabor // as it produces classes with resolvable names, all defined in a single class loader. 316219019Sgabor return this; 317219019Sgabor } 318219019Sgabor } 319219019Sgabor 320219019Sgabor private final WeakValueCache<CodeSource, Class<?>> anonymousHostClasses = new WeakValueCache<>(); 321219019Sgabor 322219019Sgabor private static final class AnonymousContextCodeInstaller extends ContextCodeInstaller { 323219019Sgabor private static final Unsafe UNSAFE = getUnsafe(); 324219019Sgabor private static final String ANONYMOUS_HOST_CLASS_NAME = Compiler.SCRIPTS_PACKAGE.replace('/', '.') + ".AnonymousHost"; 325219019Sgabor private static final byte[] ANONYMOUS_HOST_CLASS_BYTES = getAnonymousHostClassBytes(); 326219019Sgabor 327219019Sgabor private final Class<?> hostClass; 328219019Sgabor 329219019Sgabor private AnonymousContextCodeInstaller(final Context context, final CodeSource codeSource, final Class<?> hostClass) { 330219019Sgabor super(context, codeSource); 331219019Sgabor this.hostClass = hostClass; 332219019Sgabor } 333219019Sgabor 334219019Sgabor @Override 335219019Sgabor public Class<?> install(final String className, final byte[] bytecode) { 336219019Sgabor ANONYMOUS_INSTALLED_SCRIPT_COUNT.increment(); 337219019Sgabor return UNSAFE.defineAnonymousClass(hostClass, bytecode, null); 338219019Sgabor } 339219019Sgabor 340219019Sgabor @Override 341219019Sgabor public CodeInstaller getOnDemandCompilationInstaller() { 342219019Sgabor // This code loader can be indefinitely reused for on-demand recompilations for the same code source. 343219019Sgabor return this; 344219019Sgabor } 345219019Sgabor 346219019Sgabor @Override 347219019Sgabor public CodeInstaller getMultiClassCodeInstaller() { 348219019Sgabor // This code loader can not be used to install multiple classes that reference each other, as they 349219019Sgabor // would have no resolvable names. Therefore, in such situation we must revert to an installer that 350219019Sgabor // produces named classes. 351219019Sgabor return new NamedContextCodeInstaller(context, codeSource, context.createNewLoader()); 352219019Sgabor } 353219019Sgabor 354219019Sgabor private static byte[] getAnonymousHostClassBytes() { 355219019Sgabor final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); 356219019Sgabor cw.visit(V1_7, Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT, ANONYMOUS_HOST_CLASS_NAME.replace('.', '/'), null, "java/lang/Object", null); 357219019Sgabor cw.visitEnd(); 358219019Sgabor return cw.toByteArray(); 359219019Sgabor } 360219019Sgabor 361219019Sgabor private static Unsafe getUnsafe() { 362219019Sgabor return AccessController.doPrivileged(new PrivilegedAction<Unsafe>() { 363219019Sgabor @Override 364219019Sgabor public Unsafe run() { 365219019Sgabor try { 366219019Sgabor final Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe"); 367219019Sgabor theUnsafeField.setAccessible(true); 368219019Sgabor return (Unsafe)theUnsafeField.get(null); 369219019Sgabor } catch (final ReflectiveOperationException e) { 370219019Sgabor throw new RuntimeException(e); 371219019Sgabor } 372219019Sgabor } 373219019Sgabor }); 374219019Sgabor } 375219019Sgabor } 376219019Sgabor 377219019Sgabor /** Is Context global debug mode enabled ? */ 378219019Sgabor public static final boolean DEBUG = Options.getBooleanProperty("nashorn.debug"); 379219019Sgabor 380219019Sgabor private static final ThreadLocal<Global> currentGlobal = new ThreadLocal<>(); 381219019Sgabor 382219019Sgabor // in-memory cache for loaded classes 383219019Sgabor private ClassCache classCache; 384219019Sgabor 385219019Sgabor // persistent code store 386219019Sgabor private CodeStore codeStore; 387219019Sgabor 388219019Sgabor // A factory for linking global properties as constant method handles. It is created when the first Global 389219019Sgabor // is created, and invalidated forever once the second global is created. 390219019Sgabor private final AtomicReference<GlobalConstants> globalConstantsRef = new AtomicReference<>(); 391219019Sgabor 392219019Sgabor // Are java.sql, java.sql.rowset modules found in the system? 393219019Sgabor static final boolean javaSqlFound, javaSqlRowsetFound; 394219019Sgabor 395219019Sgabor static { 396219019Sgabor final Layer boot = Layer.boot(); 397219019Sgabor javaSqlFound = boot.findModule("java.sql").isPresent(); 398219019Sgabor javaSqlRowsetFound = boot.findModule("java.sql.rowset").isPresent(); 399219019Sgabor } 400219019Sgabor 401219019Sgabor /** 402219019Sgabor * Get the current global scope 403219019Sgabor * @return the current global scope 404219019Sgabor */ 405219019Sgabor public static Global getGlobal() { 406219019Sgabor // This class in a package.access protected package. 407219019Sgabor // Trusted code only can call this method. 408219019Sgabor return currentGlobal.get(); 409219019Sgabor } 410219019Sgabor 411219019Sgabor /** 412219019Sgabor * Set the current global scope 413219019Sgabor * @param global the global scope 414219019Sgabor */ 415219019Sgabor public static void setGlobal(final ScriptObject global) { 416219019Sgabor if (global != null && !(global instanceof Global)) { 417219019Sgabor throw new IllegalArgumentException("not a global!"); 418219019Sgabor } 419219019Sgabor setGlobal((Global)global); 420219019Sgabor } 421219019Sgabor 422219019Sgabor /** 423219019Sgabor * Set the current global scope 424219019Sgabor * @param global the global scope 425219019Sgabor */ 426219019Sgabor public static void setGlobal(final Global global) { 427219019Sgabor // This class in a package.access protected package. 428219019Sgabor // Trusted code only can call this method. 429219019Sgabor assert getGlobal() != global; 430219019Sgabor //same code can be cached between globals, then we need to invalidate method handle constants 431219019Sgabor if (global != null) { 432219019Sgabor final GlobalConstants globalConstants = getContext(global).getGlobalConstants(); 433219019Sgabor if (globalConstants != null) { 434219019Sgabor globalConstants.invalidateAll(); 435219019Sgabor } 436219019Sgabor } 437219019Sgabor currentGlobal.set(global); 438219019Sgabor } 439219019Sgabor 440219019Sgabor /** 441219019Sgabor * Get context of the current global 442219019Sgabor * @return current global scope's context. 443219019Sgabor */ 444219019Sgabor public static Context getContext() { 445219019Sgabor final SecurityManager sm = System.getSecurityManager(); 446219019Sgabor if (sm != null) { 447219019Sgabor sm.checkPermission(new RuntimePermission(NASHORN_GET_CONTEXT)); 448219019Sgabor } 449219019Sgabor return getContextTrusted(); 450219019Sgabor } 451219019Sgabor 452219019Sgabor /** 453219019Sgabor * Get current context's error writer 454219019Sgabor * 455219019Sgabor * @return error writer of the current context 456219019Sgabor */ 457219019Sgabor public static PrintWriter getCurrentErr() { 458219019Sgabor final ScriptObject global = getGlobal(); 459219019Sgabor return (global != null)? global.getContext().getErr() : new PrintWriter(System.err); 460219019Sgabor } 461219019Sgabor 462219019Sgabor /** 463219019Sgabor * Output text to this Context's error stream 464219019Sgabor * @param str text to write 465219019Sgabor */ 466219019Sgabor public static void err(final String str) { 467219019Sgabor err(str, true); 468219019Sgabor } 469219019Sgabor 470219019Sgabor /** 471219019Sgabor * Output text to this Context's error stream, optionally with 472219019Sgabor * a newline afterwards 473219019Sgabor * 474219019Sgabor * @param str text to write 475219019Sgabor * @param crlf write a carriage return/new line after text 476219019Sgabor */ 477219019Sgabor public static void err(final String str, final boolean crlf) { 478219019Sgabor final PrintWriter err = Context.getCurrentErr(); 479219019Sgabor if (err != null) { 480219019Sgabor if (crlf) { 481219019Sgabor err.println(str); 482219019Sgabor } else { 483219019Sgabor err.print(str); 484219019Sgabor } 485219019Sgabor } 486219019Sgabor } 487219019Sgabor 488219019Sgabor /** Current environment. */ 489219019Sgabor private final ScriptEnvironment env; 490219019Sgabor 491219019Sgabor /** is this context in strict mode? Cached from env. as this is used heavily. */ 492219019Sgabor final boolean _strict; 493219019Sgabor 494219019Sgabor /** class loader to resolve classes from script. */ 495219019Sgabor private final ClassLoader appLoader; 496219019Sgabor 497219019Sgabor /*package-private*/ 498219019Sgabor ClassLoader getAppLoader() { 499219019Sgabor return appLoader; 500219019Sgabor } 501219019Sgabor 502219019Sgabor /** Class loader to load classes compiled from scripts. */ 503219019Sgabor private final ScriptLoader scriptLoader; 504219019Sgabor 505219019Sgabor /** Dynamic linker for linking call sites in script code loaded by this context */ 506219019Sgabor private final DynamicLinker dynamicLinker; 507219019Sgabor 508219019Sgabor /** Current error manager. */ 509219019Sgabor private final ErrorManager errors; 510219019Sgabor 511219019Sgabor /** Unique id for script. Used only when --loader-per-compile=false */ 512219019Sgabor private final AtomicLong uniqueScriptId; 513219019Sgabor 514219019Sgabor /** Optional class filter to use for Java classes. Can be null. */ 515219019Sgabor private final ClassFilter classFilter; 516219019Sgabor 517219019Sgabor /** Process-wide singleton structure loader */ 518219019Sgabor private static final StructureLoader theStructLoader; 519219019Sgabor private static final ConcurrentMap<String, Class<?>> structureClasses = new ConcurrentHashMap<>(); 520219019Sgabor 521219019Sgabor /*package-private*/ @SuppressWarnings("static-method") 522219019Sgabor StructureLoader getStructLoader() { 523219019Sgabor return theStructLoader; 524219019Sgabor } 525219019Sgabor 526219019Sgabor private static AccessControlContext createNoPermAccCtxt() { 527219019Sgabor return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, new Permissions()) }); 528219019Sgabor } 529219019Sgabor 530219019Sgabor private static AccessControlContext createPermAccCtxt(final String permName) { 531219019Sgabor final Permissions perms = new Permissions(); 532219019Sgabor perms.add(new RuntimePermission(permName)); 533219019Sgabor return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, perms) }); 534219019Sgabor } 535219019Sgabor 536219019Sgabor private static final AccessControlContext NO_PERMISSIONS_ACC_CTXT = createNoPermAccCtxt(); 537219019Sgabor private static final AccessControlContext CREATE_LOADER_ACC_CTXT = createPermAccCtxt("createClassLoader"); 538219019Sgabor private static final AccessControlContext CREATE_GLOBAL_ACC_CTXT = createPermAccCtxt(NASHORN_CREATE_GLOBAL); 539219019Sgabor private static final AccessControlContext GET_LOADER_ACC_CTXT = createPermAccCtxt("getClassLoader"); 540219019Sgabor 541219019Sgabor static { 542219019Sgabor final ClassLoader myLoader = Context.class.getClassLoader(); 543219019Sgabor theStructLoader = AccessController.doPrivileged(new PrivilegedAction<StructureLoader>() { 544219019Sgabor @Override 545219019Sgabor public StructureLoader run() { 546219019Sgabor return new StructureLoader(myLoader); 547219019Sgabor } 548219019Sgabor }, CREATE_LOADER_ACC_CTXT); 549219019Sgabor } 550219019Sgabor 551219019Sgabor /** 552219019Sgabor * ThrowErrorManager that throws ParserException upon error conditions. 553219019Sgabor */ 554219019Sgabor public static class ThrowErrorManager extends ErrorManager { 555219019Sgabor @Override 556219019Sgabor public void error(final String message) { 557219019Sgabor throw new ParserException(message); 558219019Sgabor } 559219019Sgabor 560219019Sgabor @Override 561219019Sgabor public void error(final ParserException e) { 562219019Sgabor throw e; 563219019Sgabor } 564219019Sgabor } 565219019Sgabor 566219019Sgabor /** 567219019Sgabor * Constructor 568219019Sgabor * 569219019Sgabor * @param options options from command line or Context creator 570219019Sgabor * @param errors error manger 571219019Sgabor * @param appLoader application class loader 572219019Sgabor */ 573219019Sgabor public Context(final Options options, final ErrorManager errors, final ClassLoader appLoader) { 574219019Sgabor this(options, errors, appLoader, null); 575219019Sgabor } 576219019Sgabor 577219019Sgabor /** 578219019Sgabor * Constructor 579219019Sgabor * 580219019Sgabor * @param options options from command line or Context creator 581219019Sgabor * @param errors error manger 582219019Sgabor * @param appLoader application class loader 583219019Sgabor * @param classFilter class filter to use 584219019Sgabor */ 585219019Sgabor public Context(final Options options, final ErrorManager errors, final ClassLoader appLoader, final ClassFilter classFilter) { 586219019Sgabor this(options, errors, new PrintWriter(System.out, true), new PrintWriter(System.err, true), appLoader, classFilter); 587219019Sgabor } 588219019Sgabor 589219019Sgabor /** 590219019Sgabor * Constructor 591219019Sgabor * 592219019Sgabor * @param options options from command line or Context creator 593219019Sgabor * @param errors error manger 594219019Sgabor * @param out output writer for this Context 595219019Sgabor * @param err error writer for this Context 596219019Sgabor * @param appLoader application class loader 597219019Sgabor */ 598219019Sgabor public Context(final Options options, final ErrorManager errors, final PrintWriter out, final PrintWriter err, final ClassLoader appLoader) { 599219019Sgabor this(options, errors, out, err, appLoader, (ClassFilter)null); 600219019Sgabor } 601219019Sgabor 602219019Sgabor /** 603219019Sgabor * Constructor 604219019Sgabor * 605219019Sgabor * @param options options from command line or Context creator 606219019Sgabor * @param errors error manger 607219019Sgabor * @param out output writer for this Context 608219019Sgabor * @param err error writer for this Context 609219019Sgabor * @param appLoader application class loader 610219019Sgabor * @param classFilter class filter to use 611219019Sgabor */ 612219019Sgabor public Context(final Options options, final ErrorManager errors, final PrintWriter out, final PrintWriter err, final ClassLoader appLoader, final ClassFilter classFilter) { 613219019Sgabor final SecurityManager sm = System.getSecurityManager(); 614219019Sgabor if (sm != null) { 615219019Sgabor sm.checkPermission(new RuntimePermission(NASHORN_CREATE_CONTEXT)); 616219019Sgabor } 617219019Sgabor 618219019Sgabor this.classFilter = classFilter; 619219019Sgabor this.env = new ScriptEnvironment(options, out, err); 620219019Sgabor this._strict = env._strict; 621219019Sgabor if (env._loader_per_compile) { 622219019Sgabor this.scriptLoader = null; 623219019Sgabor this.uniqueScriptId = null; 624219019Sgabor } else { 625219019Sgabor this.scriptLoader = createNewLoader(); 626219019Sgabor this.uniqueScriptId = new AtomicLong(); 627219019Sgabor } 628219019Sgabor this.errors = errors; 629219019Sgabor 630219019Sgabor // if user passed --module-path, we create a module class loader with 631219019Sgabor // passed appLoader as the parent. 632219019Sgabor final String modulePath = env._module_path; 633219019Sgabor ClassLoader appCl = null; 634219019Sgabor if (!env._compile_only && modulePath != null && !modulePath.isEmpty()) { 635219019Sgabor // make sure that caller can create a class loader. 636219019Sgabor if (sm != null) { 637219019Sgabor sm.checkCreateClassLoader(); 638219019Sgabor } 639219019Sgabor appCl = AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() { 640219019Sgabor @Override 641219019Sgabor public ClassLoader run() { 642219019Sgabor return createModuleLoader(appLoader, modulePath, env._add_modules); 643219019Sgabor } 644219019Sgabor }); 645219019Sgabor } else { 646219019Sgabor appCl = appLoader; 647219019Sgabor } 648219019Sgabor 649219019Sgabor // if user passed -classpath option, make a URLClassLoader with that and 650219019Sgabor // the app loader or module app loader as the parent. 651219019Sgabor final String classPath = env._classpath; 652219019Sgabor if (!env._compile_only && classPath != null && !classPath.isEmpty()) { 653219019Sgabor // make sure that caller can create a class loader. 654219019Sgabor if (sm != null) { 655219019Sgabor sm.checkCreateClassLoader(); 656219019Sgabor } 657219019Sgabor appCl = NashornLoader.createClassLoader(classPath, appCl); 658219019Sgabor } 659219019Sgabor 660219019Sgabor this.appLoader = appCl; 661219019Sgabor this.dynamicLinker = Bootstrap.createDynamicLinker(this.appLoader, env._unstable_relink_threshold); 662219019Sgabor 663219019Sgabor final int cacheSize = env._class_cache_size; 664219019Sgabor if (cacheSize > 0) { 665219019Sgabor classCache = new ClassCache(this, cacheSize); 666219019Sgabor } 667219019Sgabor 668219019Sgabor if (env._persistent_cache) { 669219019Sgabor codeStore = newCodeStore(this); 670219019Sgabor } 671219019Sgabor 672219019Sgabor // print version info if asked. 673219019Sgabor if (env._version) { 674219019Sgabor getErr().println("nashorn " + Version.version()); 675219019Sgabor } 676219019Sgabor 677219019Sgabor if (env._fullversion) { 678219019Sgabor getErr().println("nashorn full version " + Version.fullVersion()); 679219019Sgabor } 680219019Sgabor 681219019Sgabor if (Options.getBooleanProperty("nashorn.fields.dual")) { 682219019Sgabor fieldMode = FieldMode.DUAL; 683219019Sgabor } else if (Options.getBooleanProperty("nashorn.fields.objects")) { 684219019Sgabor fieldMode = FieldMode.OBJECTS; 685219019Sgabor } else { 686219019Sgabor fieldMode = FieldMode.AUTO; 687219019Sgabor } 688219019Sgabor 689219019Sgabor initLoggers(); 690219019Sgabor } 691219019Sgabor 692219019Sgabor 693219019Sgabor /** 694219019Sgabor * Get the class filter for this context 695219019Sgabor * @return class filter 696219019Sgabor */ 697219019Sgabor public ClassFilter getClassFilter() { 698219019Sgabor return classFilter; 699219019Sgabor } 700219019Sgabor 701219019Sgabor /** 702219019Sgabor * Returns the factory for constant method handles for global properties. The returned factory can be 703219019Sgabor * invalidated if this Context has more than one Global. 704219019Sgabor * @return the factory for constant method handles for global properties. 705219019Sgabor */ 706219019Sgabor GlobalConstants getGlobalConstants() { 707219019Sgabor return globalConstantsRef.get(); 708219019Sgabor } 709219019Sgabor 710219019Sgabor /** 711219019Sgabor * Get the error manager for this context 712219019Sgabor * @return error manger 713219019Sgabor */ 714219019Sgabor public ErrorManager getErrorManager() { 715219019Sgabor return errors; 716219019Sgabor } 717219019Sgabor 718219019Sgabor /** 719219019Sgabor * Get the script environment for this context 720219019Sgabor * @return script environment 721219019Sgabor */ 722219019Sgabor public ScriptEnvironment getEnv() { 723219019Sgabor return env; 724219019Sgabor } 725219019Sgabor 726219019Sgabor /** 727219019Sgabor * Get the output stream for this context 728219019Sgabor * @return output print writer 729219019Sgabor */ 730219019Sgabor public PrintWriter getOut() { 731219019Sgabor return env.getOut(); 732219019Sgabor } 733219019Sgabor 734219019Sgabor /** 735219019Sgabor * Get the error stream for this context 736219019Sgabor * @return error print writer 737219019Sgabor */ 738219019Sgabor public PrintWriter getErr() { 739219019Sgabor return env.getErr(); 740219019Sgabor } 741219019Sgabor 742219019Sgabor /** 743219019Sgabor * Should scripts compiled by this context use dual field representation? 744219019Sgabor * @return true if using dual fields, false for object-only fields 745219019Sgabor */ 746219019Sgabor public boolean useDualFields() { 747219019Sgabor return fieldMode == FieldMode.DUAL || (fieldMode == FieldMode.AUTO && env._optimistic_types); 748219019Sgabor } 749219019Sgabor 750219019Sgabor /** 751219019Sgabor * Get the PropertyMap of the current global scope 752219019Sgabor * @return the property map of the current global scope 753219019Sgabor */ 754219019Sgabor public static PropertyMap getGlobalMap() { 755219019Sgabor return Context.getGlobal().getMap(); 756219019Sgabor } 757219019Sgabor 758219019Sgabor /** 759219019Sgabor * Compile a top level script. 760219019Sgabor * 761219019Sgabor * @param source the source 762219019Sgabor * @param scope the scope 763219019Sgabor * 764219019Sgabor * @return top level function for script 765219019Sgabor */ 766219019Sgabor public ScriptFunction compileScript(final Source source, final ScriptObject scope) { 767219019Sgabor return compileScript(source, scope, this.errors); 768219019Sgabor } 769219019Sgabor 770219019Sgabor /** 771219019Sgabor * Interface to represent compiled code that can be re-used across many 772219019Sgabor * global scope instances 773219019Sgabor */ 774219019Sgabor public static interface MultiGlobalCompiledScript { 775219019Sgabor /** 776219019Sgabor * Obtain script function object for a specific global scope object. 777219019Sgabor * 778219019Sgabor * @param newGlobal global scope for which function object is obtained 779219019Sgabor * @return script function for script level expressions 780 */ 781 public ScriptFunction getFunction(final Global newGlobal); 782 } 783 784 /** 785 * Compile a top level script. 786 * 787 * @param source the script source 788 * @return reusable compiled script across many global scopes. 789 */ 790 public MultiGlobalCompiledScript compileScript(final Source source) { 791 final Class<?> clazz = compile(source, this.errors, this._strict, false); 792 final MethodHandle createProgramFunctionHandle = getCreateProgramFunctionHandle(clazz); 793 794 return new MultiGlobalCompiledScript() { 795 @Override 796 public ScriptFunction getFunction(final Global newGlobal) { 797 return invokeCreateProgramFunctionHandle(createProgramFunctionHandle, newGlobal); 798 } 799 }; 800 } 801 802 /** 803 * Entry point for {@code eval} 804 * 805 * @param initialScope The scope of this eval call 806 * @param string Evaluated code as a String 807 * @param callThis "this" to be passed to the evaluated code 808 * @param location location of the eval call 809 * @return the return value of the {@code eval} 810 */ 811 public Object eval(final ScriptObject initialScope, final String string, 812 final Object callThis, final Object location) { 813 return eval(initialScope, string, callThis, location, false, false); 814 } 815 816 /** 817 * Entry point for {@code eval} 818 * 819 * @param initialScope The scope of this eval call 820 * @param string Evaluated code as a String 821 * @param callThis "this" to be passed to the evaluated code 822 * @param location location of the eval call 823 * @param strict is this {@code eval} call from a strict mode code? 824 * @param evalCall is this called from "eval" builtin? 825 * 826 * @return the return value of the {@code eval} 827 */ 828 public Object eval(final ScriptObject initialScope, final String string, 829 final Object callThis, final Object location, final boolean strict, final boolean evalCall) { 830 final String file = location == UNDEFINED || location == null ? "<eval>" : location.toString(); 831 final Source source = sourceFor(file, string, evalCall); 832 // is this direct 'eval' builtin call? 833 final boolean directEval = evalCall && (location != UNDEFINED); 834 final Global global = Context.getGlobal(); 835 ScriptObject scope = initialScope; 836 837 // ECMA section 10.1.1 point 2 says eval code is strict if it begins 838 // with "use strict" directive or eval direct call itself is made 839 // from from strict mode code. We are passed with caller's strict mode. 840 // Nashorn extension: any 'eval' is unconditionally strict when -strict is specified. 841 boolean strictFlag = strict || this._strict; 842 843 Class<?> clazz; 844 try { 845 clazz = compile(source, new ThrowErrorManager(), strictFlag, true); 846 } catch (final ParserException e) { 847 e.throwAsEcmaException(global); 848 return null; 849 } 850 851 if (!strictFlag) { 852 // We need to get strict mode flag from compiled class. This is 853 // because eval code may start with "use strict" directive. 854 try { 855 strictFlag = clazz.getField(STRICT_MODE.symbolName()).getBoolean(null); 856 } catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { 857 //ignored 858 strictFlag = false; 859 } 860 } 861 862 // In strict mode, eval does not instantiate variables and functions 863 // in the caller's environment. A new environment is created! 864 if (strictFlag) { 865 // Create a new scope object with given scope as its prototype 866 scope = newScope(scope); 867 } 868 869 final ScriptFunction func = getProgramFunction(clazz, scope); 870 Object evalThis; 871 if (directEval) { 872 evalThis = (callThis != UNDEFINED && callThis != null) || strictFlag ? callThis : global; 873 } else { 874 // either indirect evalCall or non-eval (Function, engine.eval, ScriptObjectMirror.eval..) 875 evalThis = callThis; 876 } 877 878 return ScriptRuntime.apply(func, evalThis); 879 } 880 881 private static ScriptObject newScope(final ScriptObject callerScope) { 882 return new Scope(callerScope, PropertyMap.newMap(Scope.class)); 883 } 884 885 private static Source loadInternal(final String srcStr, final String prefix, final String resourcePath) { 886 if (srcStr.startsWith(prefix)) { 887 final String resource = resourcePath + srcStr.substring(prefix.length()); 888 // NOTE: even sandbox scripts should be able to load scripts in nashorn: scheme 889 // These scripts are always available and are loaded from nashorn.jar's resources. 890 return AccessController.doPrivileged( 891 new PrivilegedAction<Source>() { 892 @Override 893 public Source run() { 894 try { 895 final InputStream resStream = Context.class.getResourceAsStream(resource); 896 return resStream != null ? sourceFor(srcStr, Source.readFully(resStream)) : null; 897 } catch (final IOException exp) { 898 return null; 899 } 900 } 901 }); 902 } 903 904 return null; 905 } 906 907 /** 908 * Implementation of {@code load} Nashorn extension. Load a script file from a source 909 * expression 910 * 911 * @param scope the scope 912 * @param from source expression for script 913 * 914 * @return return value for load call (undefined) 915 * 916 * @throws IOException if source cannot be found or loaded 917 */ 918 public Object load(final Object scope, final Object from) throws IOException { 919 final Object src = from instanceof ConsString ? from.toString() : from; 920 Source source = null; 921 922 // load accepts a String (which could be a URL or a file name), a File, a URL 923 // or a ScriptObject that has "name" and "source" (string valued) properties. 924 if (src instanceof String) { 925 final String srcStr = (String)src; 926 if (srcStr.startsWith(LOAD_CLASSPATH)) { 927 final URL url = getResourceURL(srcStr.substring(LOAD_CLASSPATH.length())); 928 source = url != null ? sourceFor(url.toString(), url) : null; 929 } else { 930 final File file = new File(srcStr); 931 if (srcStr.indexOf(':') != -1) { 932 if ((source = loadInternal(srcStr, LOAD_NASHORN, "resources/")) == null && 933 (source = loadInternal(srcStr, LOAD_FX, "resources/fx/")) == null) { 934 URL url; 935 try { 936 //check for malformed url. if malformed, it may still be a valid file 937 url = new URL(srcStr); 938 } catch (final MalformedURLException e) { 939 url = file.toURI().toURL(); 940 } 941 source = sourceFor(url.toString(), url); 942 } 943 } else if (file.isFile()) { 944 source = sourceFor(srcStr, file); 945 } 946 } 947 } else if (src instanceof File && ((File)src).isFile()) { 948 final File file = (File)src; 949 source = sourceFor(file.getName(), file); 950 } else if (src instanceof URL) { 951 final URL url = (URL)src; 952 source = sourceFor(url.toString(), url); 953 } else if (src instanceof ScriptObject) { 954 final ScriptObject sobj = (ScriptObject)src; 955 if (sobj.has("script") && sobj.has("name")) { 956 final String script = JSType.toString(sobj.get("script")); 957 final String name = JSType.toString(sobj.get("name")); 958 source = sourceFor(name, script); 959 } 960 } else if (src instanceof Map) { 961 final Map<?,?> map = (Map<?,?>)src; 962 if (map.containsKey("script") && map.containsKey("name")) { 963 final String script = JSType.toString(map.get("script")); 964 final String name = JSType.toString(map.get("name")); 965 source = sourceFor(name, script); 966 } 967 } 968 969 if (source != null) { 970 if (scope instanceof ScriptObject && ((ScriptObject)scope).isScope()) { 971 final ScriptObject sobj = (ScriptObject)scope; 972 // passed object is a script object 973 // Global is the only user accessible scope ScriptObject 974 assert sobj.isGlobal() : "non-Global scope object!!"; 975 return evaluateSource(source, sobj, sobj); 976 } else if (scope == null || scope == UNDEFINED) { 977 // undefined or null scope. Use current global instance. 978 final Global global = getGlobal(); 979 return evaluateSource(source, global, global); 980 } else { 981 /* 982 * Arbitrary object passed for scope. 983 * Indirect load that is equivalent to: 984 * 985 * (function(scope, source) { 986 * with (scope) { 987 * eval(<script_from_source>); 988 * } 989 * })(scope, source); 990 */ 991 final Global global = getGlobal(); 992 // Create a new object. This is where all declarations 993 // (var, function) from the evaluated code go. 994 // make global to be its __proto__ so that global 995 // definitions are accessible to the evaluated code. 996 final ScriptObject evalScope = newScope(global); 997 998 // finally, make a WithObject around user supplied scope object 999 // so that it's properties are accessible as variables. 1000 final ScriptObject withObj = ScriptRuntime.openWith(evalScope, scope); 1001 1002 // evaluate given source with 'withObj' as scope 1003 // but use global object as "this". 1004 return evaluateSource(source, withObj, global); 1005 } 1006 } 1007 1008 throw typeError("cant.load.script", ScriptRuntime.safeToString(from)); 1009 } 1010 1011 /** 1012 * Implementation of {@code loadWithNewGlobal} Nashorn extension. Load a script file from a source 1013 * expression, after creating a new global scope. 1014 * 1015 * @param from source expression for script 1016 * @param args (optional) arguments to be passed to the loaded script 1017 * 1018 * @return return value for load call (undefined) 1019 * 1020 * @throws IOException if source cannot be found or loaded 1021 */ 1022 public Object loadWithNewGlobal(final Object from, final Object...args) throws IOException { 1023 final Global oldGlobal = getGlobal(); 1024 final Global newGlobal = AccessController.doPrivileged(new PrivilegedAction<Global>() { 1025 @Override 1026 public Global run() { 1027 try { 1028 return newGlobal(); 1029 } catch (final RuntimeException e) { 1030 if (Context.DEBUG) { 1031 e.printStackTrace(); 1032 } 1033 throw e; 1034 } 1035 } 1036 }, CREATE_GLOBAL_ACC_CTXT); 1037 // initialize newly created Global instance 1038 initGlobal(newGlobal); 1039 setGlobal(newGlobal); 1040 1041 final Object[] wrapped = args == null? ScriptRuntime.EMPTY_ARRAY : ScriptObjectMirror.wrapArray(args, oldGlobal); 1042 newGlobal.put("arguments", newGlobal.wrapAsObject(wrapped), env._strict); 1043 1044 try { 1045 // wrap objects from newGlobal's world as mirrors - but if result 1046 // is from oldGlobal's world, unwrap it! 1047 return ScriptObjectMirror.unwrap(ScriptObjectMirror.wrap(load(newGlobal, from), newGlobal), oldGlobal); 1048 } finally { 1049 setGlobal(oldGlobal); 1050 } 1051 } 1052 1053 /** 1054 * Load or get a structure class. Structure class names are based on the number of parameter fields 1055 * and {@link AccessorProperty} fields in them. Structure classes are used to represent ScriptObjects 1056 * 1057 * @see ObjectClassGenerator 1058 * @see AccessorProperty 1059 * @see ScriptObject 1060 * 1061 * @param fullName full name of class, e.g. jdk.nashorn.internal.objects.JO2P1 contains 2 fields and 1 parameter. 1062 * 1063 * @return the {@code Class<?>} for this structure 1064 * 1065 * @throws ClassNotFoundException if structure class cannot be resolved 1066 */ 1067 @SuppressWarnings("unchecked") 1068 public static Class<? extends ScriptObject> forStructureClass(final String fullName) throws ClassNotFoundException { 1069 if (System.getSecurityManager() != null && !StructureLoader.isStructureClass(fullName)) { 1070 throw new ClassNotFoundException(fullName); 1071 } 1072 return (Class<? extends ScriptObject>)structureClasses.computeIfAbsent(fullName, (name) -> { 1073 try { 1074 return Class.forName(name, true, theStructLoader); 1075 } catch (final ClassNotFoundException e) { 1076 throw new AssertionError(e); 1077 } 1078 }); 1079 } 1080 1081 /** 1082 * Is {@code className} the name of a structure class? 1083 * 1084 * @param className a class name 1085 * @return true if className is a structure class name 1086 */ 1087 public static boolean isStructureClass(final String className) { 1088 return StructureLoader.isStructureClass(className); 1089 } 1090 1091 /** 1092 * Checks that the given Class can be accessed from no permissions context. 1093 * 1094 * @param clazz Class object 1095 * @throws SecurityException if not accessible 1096 */ 1097 public static void checkPackageAccess(final Class<?> clazz) { 1098 final SecurityManager sm = System.getSecurityManager(); 1099 if (sm != null) { 1100 Class<?> bottomClazz = clazz; 1101 while (bottomClazz.isArray()) { 1102 bottomClazz = bottomClazz.getComponentType(); 1103 } 1104 checkPackageAccess(sm, bottomClazz.getName()); 1105 } 1106 } 1107 1108 /** 1109 * Checks that the given package name can be accessed from no permissions context. 1110 * 1111 * @param pkgName package name 1112 * @throws SecurityException if not accessible 1113 */ 1114 public static void checkPackageAccess(final String pkgName) { 1115 final SecurityManager sm = System.getSecurityManager(); 1116 if (sm != null) { 1117 checkPackageAccess(sm, pkgName.endsWith(".") ? pkgName : pkgName + "."); 1118 } 1119 } 1120 1121 /** 1122 * Checks that the given package can be accessed from no permissions context. 1123 * 1124 * @param sm current security manager instance 1125 * @param fullName fully qualified package name 1126 * @throw SecurityException if not accessible 1127 */ 1128 private static void checkPackageAccess(final SecurityManager sm, final String fullName) { 1129 Objects.requireNonNull(sm); 1130 final int index = fullName.lastIndexOf('.'); 1131 if (index != -1) { 1132 final String pkgName = fullName.substring(0, index); 1133 AccessController.doPrivileged(new PrivilegedAction<Void>() { 1134 @Override 1135 public Void run() { 1136 sm.checkPackageAccess(pkgName); 1137 return null; 1138 } 1139 }, NO_PERMISSIONS_ACC_CTXT); 1140 } 1141 } 1142 1143 /** 1144 * Checks that the given Class can be accessed from no permissions context. 1145 * 1146 * @param clazz Class object 1147 * @return true if package is accessible, false otherwise 1148 */ 1149 private static boolean isAccessiblePackage(final Class<?> clazz) { 1150 try { 1151 checkPackageAccess(clazz); 1152 return true; 1153 } catch (final SecurityException se) { 1154 return false; 1155 } 1156 } 1157 1158 /** 1159 * Checks that the given Class is public and it can be accessed from no permissions context. 1160 * 1161 * @param clazz Class object to check 1162 * @return true if Class is accessible, false otherwise 1163 */ 1164 public static boolean isAccessibleClass(final Class<?> clazz) { 1165 return Modifier.isPublic(clazz.getModifiers()) && Context.isAccessiblePackage(clazz); 1166 } 1167 1168 /** 1169 * Lookup a Java class. This is used for JSR-223 stuff linking in from 1170 * {@code jdk.nashorn.internal.objects.NativeJava} and {@code jdk.nashorn.internal.runtime.NativeJavaPackage} 1171 * 1172 * @param fullName full name of class to load 1173 * 1174 * @return the {@code Class<?>} for the name 1175 * 1176 * @throws ClassNotFoundException if class cannot be resolved 1177 */ 1178 public Class<?> findClass(final String fullName) throws ClassNotFoundException { 1179 if (fullName.indexOf('[') != -1 || fullName.indexOf('/') != -1) { 1180 // don't allow array class names or internal names. 1181 throw new ClassNotFoundException(fullName); 1182 } 1183 1184 // give chance to ClassFilter to filter out, if present 1185 if (classFilter != null && !classFilter.exposeToScripts(fullName)) { 1186 throw new ClassNotFoundException(fullName); 1187 } 1188 1189 // check package access as soon as possible! 1190 final SecurityManager sm = System.getSecurityManager(); 1191 if (sm != null) { 1192 checkPackageAccess(sm, fullName); 1193 } 1194 1195 // Try finding using the "app" loader. 1196 if (appLoader != null) { 1197 return Class.forName(fullName, true, appLoader); 1198 } else { 1199 final Class<?> cl = Class.forName(fullName); 1200 // return the Class only if it was loaded by boot loader 1201 if (cl.getClassLoader() == null) { 1202 return cl; 1203 } else { 1204 throw new ClassNotFoundException(fullName); 1205 } 1206 } 1207 } 1208 1209 /** 1210 * Hook to print stack trace for a {@link Throwable} that occurred during 1211 * execution 1212 * 1213 * @param t throwable for which to dump stack 1214 */ 1215 public static void printStackTrace(final Throwable t) { 1216 if (Context.DEBUG) { 1217 t.printStackTrace(Context.getCurrentErr()); 1218 } 1219 } 1220 1221 /** 1222 * Verify generated bytecode before emission. This is called back from the 1223 * {@link ObjectClassGenerator} or the {@link Compiler}. If the "--verify-code" parameter 1224 * hasn't been given, this is a nop 1225 * 1226 * Note that verification may load classes -- we don't want to do that unless 1227 * user specified verify option. We check it here even though caller 1228 * may have already checked that flag 1229 * 1230 * @param bytecode bytecode to verify 1231 */ 1232 public void verify(final byte[] bytecode) { 1233 if (env._verify_code) { 1234 // No verification when security manager is around as verifier 1235 // may load further classes - which should be avoided. 1236 if (System.getSecurityManager() == null) { 1237 CheckClassAdapter.verify(new ClassReader(bytecode), theStructLoader, false, new PrintWriter(System.err, true)); 1238 } 1239 } 1240 } 1241 1242 /** 1243 * Create and initialize a new global scope object. 1244 * 1245 * @return the initialized global scope object. 1246 */ 1247 public Global createGlobal() { 1248 return initGlobal(newGlobal()); 1249 } 1250 1251 /** 1252 * Create a new uninitialized global scope object 1253 * @return the global script object 1254 */ 1255 public Global newGlobal() { 1256 createOrInvalidateGlobalConstants(); 1257 return new Global(this); 1258 } 1259 1260 private void createOrInvalidateGlobalConstants() { 1261 for (;;) { 1262 final GlobalConstants currentGlobalConstants = getGlobalConstants(); 1263 if (currentGlobalConstants != null) { 1264 // Subsequent invocation; we're creating our second or later Global. GlobalConstants is not safe to use 1265 // with more than one Global, as the constant method handle linkages it creates create a coupling 1266 // between the Global and the call sites in the compiled code. 1267 currentGlobalConstants.invalidateForever(); 1268 return; 1269 } 1270 final GlobalConstants newGlobalConstants = new GlobalConstants(getLogger(GlobalConstants.class)); 1271 if (globalConstantsRef.compareAndSet(null, newGlobalConstants)) { 1272 // First invocation; we're creating the first Global in this Context. Create the GlobalConstants object 1273 // for this Context. 1274 return; 1275 } 1276 1277 // If we reach here, then we started out as the first invocation, but another concurrent invocation won the 1278 // CAS race. We'll just let the loop repeat and invalidate the CAS race winner. 1279 } 1280 } 1281 1282 /** 1283 * Initialize given global scope object. 1284 * 1285 * @param global the global 1286 * @param engine the associated ScriptEngine instance, can be null 1287 * @return the initialized global scope object. 1288 */ 1289 public Global initGlobal(final Global global, final ScriptEngine engine) { 1290 // Need only minimal global object, if we are just compiling. 1291 if (!env._compile_only) { 1292 final Global oldGlobal = Context.getGlobal(); 1293 try { 1294 Context.setGlobal(global); 1295 // initialize global scope with builtin global objects 1296 global.initBuiltinObjects(engine); 1297 } finally { 1298 Context.setGlobal(oldGlobal); 1299 } 1300 } 1301 1302 return global; 1303 } 1304 1305 /** 1306 * Initialize given global scope object. 1307 * 1308 * @param global the global 1309 * @return the initialized global scope object. 1310 */ 1311 public Global initGlobal(final Global global) { 1312 return initGlobal(global, null); 1313 } 1314 1315 /** 1316 * Return the current global's context 1317 * @return current global's context 1318 */ 1319 static Context getContextTrusted() { 1320 return getContext(getGlobal()); 1321 } 1322 1323 /** 1324 * Gets the Nashorn dynamic linker for the specified class. If the class is 1325 * a script class, the dynamic linker associated with its context is 1326 * returned. Otherwise the dynamic linker associated with the current 1327 * context is returned. 1328 * @param clazz the class for which we want to retrieve a dynamic linker. 1329 * @return the Nashorn dynamic linker for the specified class. 1330 */ 1331 public static DynamicLinker getDynamicLinker(final Class<?> clazz) { 1332 return fromClass(clazz).dynamicLinker; 1333 } 1334 1335 /** 1336 * Gets the Nashorn dynamic linker associated with the current context. 1337 * @return the Nashorn dynamic linker for the current context. 1338 */ 1339 public static DynamicLinker getDynamicLinker() { 1340 return getContextTrusted().dynamicLinker; 1341 } 1342 1343 /** 1344 * Creates a module layer with one module that is defined to the given class 1345 * loader. 1346 * 1347 * @param descriptor the module descriptor for the newly created module 1348 * @param loader the class loader of the module 1349 * @return the new Module 1350 */ 1351 static Module createModuleTrusted(final ModuleDescriptor descriptor, final ClassLoader loader) { 1352 return createModuleTrusted(Layer.boot(), descriptor, loader); 1353 } 1354 1355 /** 1356 * Creates a module layer with one module that is defined to the given class 1357 * loader. 1358 * 1359 * @param parent the parent layer of the new module 1360 * @param descriptor the module descriptor for the newly created module 1361 * @param loader the class loader of the module 1362 * @return the new Module 1363 */ 1364 static Module createModuleTrusted(final Layer parent, final ModuleDescriptor descriptor, final ClassLoader loader) { 1365 final String mn = descriptor.name(); 1366 1367 final ModuleReference mref = new ModuleReference(descriptor, null, () -> { 1368 IOException ioe = new IOException("<dynamic module>"); 1369 throw new UncheckedIOException(ioe); 1370 }); 1371 1372 final ModuleFinder finder = new ModuleFinder() { 1373 @Override 1374 public Optional<ModuleReference> find(final String name) { 1375 if (name.equals(mn)) { 1376 return Optional.of(mref); 1377 } else { 1378 return Optional.empty(); 1379 } 1380 } 1381 @Override 1382 public Set<ModuleReference> findAll() { 1383 return Set.of(mref); 1384 } 1385 }; 1386 1387 final Configuration cf = parent.configuration() 1388 .resolveRequires(finder, ModuleFinder.of(), Set.of(mn)); 1389 1390 final PrivilegedAction<Layer> pa = () -> parent.defineModules(cf, name -> loader); 1391 final Layer layer = AccessController.doPrivileged(pa, GET_LOADER_ACC_CTXT); 1392 1393 final Module m = layer.findModule(mn).get(); 1394 assert m.getLayer() == layer; 1395 1396 return m; 1397 } 1398 1399 static Context getContextTrustedOrNull() { 1400 final Global global = Context.getGlobal(); 1401 return global == null ? null : getContext(global); 1402 } 1403 1404 private static Context getContext(final Global global) { 1405 // We can't invoke Global.getContext() directly, as it's a protected override, and Global isn't in our package. 1406 // In order to access the method, we must cast it to ScriptObject first (which is in our package) and then let 1407 // virtual invocation do its thing. 1408 return ((ScriptObject)global).getContext(); 1409 } 1410 1411 /** 1412 * Try to infer Context instance from the Class. If we cannot, 1413 * then get it from the thread local variable. 1414 * 1415 * @param clazz the class 1416 * @return context 1417 */ 1418 static Context fromClass(final Class<?> clazz) { 1419 ClassLoader loader = null; 1420 try { 1421 loader = clazz.getClassLoader(); 1422 } catch (final SecurityException ignored) { 1423 // This could fail because of anonymous classes being used. 1424 // Accessing loader of anonymous class fails (for extension 1425 // loader class too?). In any case, for us fetching Context 1426 // from class loader is just an optimization. We can always 1427 // get Context from thread local storage (below). 1428 } 1429 1430 if (loader instanceof ScriptLoader) { 1431 return ((ScriptLoader)loader).getContext(); 1432 } 1433 1434 return Context.getContextTrusted(); 1435 } 1436 1437 private URL getResourceURL(final String resName) { 1438 if (appLoader != null) { 1439 return appLoader.getResource(resName); 1440 } 1441 return ClassLoader.getSystemResource(resName); 1442 } 1443 1444 private Object evaluateSource(final Source source, final ScriptObject scope, final ScriptObject thiz) { 1445 ScriptFunction script = null; 1446 1447 try { 1448 script = compileScript(source, scope, new Context.ThrowErrorManager()); 1449 } catch (final ParserException e) { 1450 e.throwAsEcmaException(); 1451 } 1452 1453 return ScriptRuntime.apply(script, thiz); 1454 } 1455 1456 private static ScriptFunction getProgramFunction(final Class<?> script, final ScriptObject scope) { 1457 if (script == null) { 1458 return null; 1459 } 1460 return invokeCreateProgramFunctionHandle(getCreateProgramFunctionHandle(script), scope); 1461 } 1462 1463 private static MethodHandle getCreateProgramFunctionHandle(final Class<?> script) { 1464 try { 1465 return LOOKUP.findStatic(script, CREATE_PROGRAM_FUNCTION.symbolName(), CREATE_PROGRAM_FUNCTION_TYPE); 1466 } catch (NoSuchMethodException | IllegalAccessException e) { 1467 throw new AssertionError("Failed to retrieve a handle for the program function for " + script.getName(), e); 1468 } 1469 } 1470 1471 private static ScriptFunction invokeCreateProgramFunctionHandle(final MethodHandle createProgramFunctionHandle, final ScriptObject scope) { 1472 try { 1473 return (ScriptFunction)createProgramFunctionHandle.invokeExact(scope); 1474 } catch (final RuntimeException|Error e) { 1475 throw e; 1476 } catch (final Throwable t) { 1477 throw new AssertionError("Failed to create a program function", t); 1478 } 1479 } 1480 1481 private ScriptFunction compileScript(final Source source, final ScriptObject scope, final ErrorManager errMan) { 1482 return getProgramFunction(compile(source, errMan, this._strict, false), scope); 1483 } 1484 1485 private synchronized Class<?> compile(final Source source, final ErrorManager errMan, final boolean strict, final boolean isEval) { 1486 // start with no errors, no warnings. 1487 errMan.reset(); 1488 1489 Class<?> script = findCachedClass(source); 1490 if (script != null) { 1491 final DebugLogger log = getLogger(Compiler.class); 1492 if (log.isEnabled()) { 1493 log.fine(new RuntimeEvent<>(Level.INFO, source), "Code cache hit for ", source, " avoiding recompile."); 1494 } 1495 return script; 1496 } 1497 1498 StoredScript storedScript = null; 1499 FunctionNode functionNode = null; 1500 // Don't use code store if optimistic types is enabled but lazy compilation is not. 1501 // This would store a full script compilation with many wrong optimistic assumptions that would 1502 // do more harm than good on later runs with both optimistic types and lazy compilation enabled. 1503 final boolean useCodeStore = codeStore != null && !env._parse_only && (!env._optimistic_types || env._lazy_compilation); 1504 final String cacheKey = useCodeStore ? CodeStore.getCacheKey("script", null) : null; 1505 1506 if (useCodeStore) { 1507 storedScript = codeStore.load(source, cacheKey); 1508 } 1509 1510 if (storedScript == null) { 1511 if (env._dest_dir != null) { 1512 source.dump(env._dest_dir); 1513 } 1514 1515 functionNode = new Parser(env, source, errMan, strict, getLogger(Parser.class)).parse(); 1516 1517 if (errMan.hasErrors()) { 1518 return null; 1519 } 1520 1521 if (env._print_ast || functionNode.getDebugFlag(FunctionNode.DEBUG_PRINT_AST)) { 1522 getErr().println(new ASTWriter(functionNode)); 1523 } 1524 1525 if (env._print_parse || functionNode.getDebugFlag(FunctionNode.DEBUG_PRINT_PARSE)) { 1526 getErr().println(new PrintVisitor(functionNode, true, false)); 1527 } 1528 } 1529 1530 if (env._parse_only) { 1531 return null; 1532 } 1533 1534 final URL url = source.getURL(); 1535 final CodeSource cs = new CodeSource(url, (CodeSigner[])null); 1536 final CodeInstaller installer; 1537 if (!env.useAnonymousClasses(source.getLength()) || env._persistent_cache || !env._lazy_compilation) { 1538 // Persistent code cache and eager compilation preclude use of VM anonymous classes 1539 final ScriptLoader loader = env._loader_per_compile ? createNewLoader() : scriptLoader; 1540 installer = new NamedContextCodeInstaller(this, cs, loader); 1541 } else { 1542 installer = new AnonymousContextCodeInstaller(this, cs, 1543 anonymousHostClasses.getOrCreate(cs, (key) -> 1544 createNewLoader().installClass( 1545 // NOTE: we're defining these constants in AnonymousContextCodeInstaller so they are not 1546 // initialized if we don't use AnonymousContextCodeInstaller. As this method is only ever 1547 // invoked from AnonymousContextCodeInstaller, this is okay. 1548 AnonymousContextCodeInstaller.ANONYMOUS_HOST_CLASS_NAME, 1549 AnonymousContextCodeInstaller.ANONYMOUS_HOST_CLASS_BYTES, cs))); 1550 } 1551 1552 if (storedScript == null) { 1553 final CompilationPhases phases = Compiler.CompilationPhases.COMPILE_ALL; 1554 1555 final Compiler compiler = Compiler.forInitialCompilation( 1556 installer, 1557 source, 1558 errMan, 1559 strict | functionNode.isStrict()); 1560 1561 final FunctionNode compiledFunction = compiler.compile(functionNode, phases); 1562 if (errMan.hasErrors()) { 1563 return null; 1564 } 1565 script = compiledFunction.getRootClass(); 1566 compiler.persistClassInfo(cacheKey, compiledFunction); 1567 } else { 1568 Compiler.updateCompilationId(storedScript.getCompilationId()); 1569 script = storedScript.installScript(source, installer); 1570 } 1571 1572 cacheClass(source, script); 1573 return script; 1574 } 1575 1576 private ScriptLoader createNewLoader() { 1577 return AccessController.doPrivileged( 1578 new PrivilegedAction<ScriptLoader>() { 1579 @Override 1580 public ScriptLoader run() { 1581 return new ScriptLoader(Context.this); 1582 } 1583 }, CREATE_LOADER_ACC_CTXT); 1584 } 1585 1586 private long getUniqueScriptId() { 1587 return uniqueScriptId.getAndIncrement(); 1588 } 1589 1590 /** 1591 * Cache for compiled script classes. 1592 */ 1593 @SuppressWarnings("serial") 1594 @Logger(name="classcache") 1595 private static class ClassCache extends LinkedHashMap<Source, ClassReference> implements Loggable { 1596 private final int size; 1597 private final ReferenceQueue<Class<?>> queue; 1598 private final DebugLogger log; 1599 1600 ClassCache(final Context context, final int size) { 1601 super(size, 0.75f, true); 1602 this.size = size; 1603 this.queue = new ReferenceQueue<>(); 1604 this.log = initLogger(context); 1605 } 1606 1607 void cache(final Source source, final Class<?> clazz) { 1608 if (log.isEnabled()) { 1609 log.info("Caching ", source, " in class cache"); 1610 } 1611 put(source, new ClassReference(clazz, queue, source)); 1612 } 1613 1614 @Override 1615 protected boolean removeEldestEntry(final Map.Entry<Source, ClassReference> eldest) { 1616 return size() > size; 1617 } 1618 1619 @Override 1620 public ClassReference get(final Object key) { 1621 for (ClassReference ref; (ref = (ClassReference)queue.poll()) != null; ) { 1622 final Source source = ref.source; 1623 if (log.isEnabled()) { 1624 log.info("Evicting ", source, " from class cache."); 1625 } 1626 remove(source); 1627 } 1628 1629 final ClassReference ref = super.get(key); 1630 if (ref != null && log.isEnabled()) { 1631 log.info("Retrieved class reference for ", ref.source, " from class cache"); 1632 } 1633 return ref; 1634 } 1635 1636 @Override 1637 public DebugLogger initLogger(final Context context) { 1638 return context.getLogger(getClass()); 1639 } 1640 1641 @Override 1642 public DebugLogger getLogger() { 1643 return log; 1644 } 1645 1646 } 1647 1648 private static class ClassReference extends SoftReference<Class<?>> { 1649 private final Source source; 1650 1651 ClassReference(final Class<?> clazz, final ReferenceQueue<Class<?>> queue, final Source source) { 1652 super(clazz, queue); 1653 this.source = source; 1654 } 1655 } 1656 1657 // Class cache management 1658 private Class<?> findCachedClass(final Source source) { 1659 final ClassReference ref = classCache == null ? null : classCache.get(source); 1660 return ref != null ? ref.get() : null; 1661 } 1662 1663 private void cacheClass(final Source source, final Class<?> clazz) { 1664 if (classCache != null) { 1665 classCache.cache(source, clazz); 1666 } 1667 } 1668 1669 // logging 1670 private final Map<String, DebugLogger> loggers = new HashMap<>(); 1671 1672 private void initLoggers() { 1673 ((Loggable)MethodHandleFactory.getFunctionality()).initLogger(this); 1674 } 1675 1676 /** 1677 * Get a logger, given a loggable class 1678 * @param clazz a Loggable class 1679 * @return debuglogger associated with that class 1680 */ 1681 public DebugLogger getLogger(final Class<? extends Loggable> clazz) { 1682 return getLogger(clazz, null); 1683 } 1684 1685 /** 1686 * Get a logger, given a loggable class 1687 * @param clazz a Loggable class 1688 * @param initHook an init hook - if this is the first time the logger is created in the context, run the init hook 1689 * @return debuglogger associated with that class 1690 */ 1691 public DebugLogger getLogger(final Class<? extends Loggable> clazz, final Consumer<DebugLogger> initHook) { 1692 final String name = getLoggerName(clazz); 1693 DebugLogger logger = loggers.get(name); 1694 if (logger == null) { 1695 if (!env.hasLogger(name)) { 1696 return DebugLogger.DISABLED_LOGGER; 1697 } 1698 final LoggerInfo info = env._loggers.get(name); 1699 logger = new DebugLogger(name, info.getLevel(), info.isQuiet()); 1700 if (initHook != null) { 1701 initHook.accept(logger); 1702 } 1703 loggers.put(name, logger); 1704 } 1705 return logger; 1706 } 1707 1708 /** 1709 * Given a Loggable class, weave debug info info a method handle for that logger. 1710 * Level.INFO is used 1711 * 1712 * @param clazz loggable 1713 * @param mh method handle 1714 * @param text debug printout to add 1715 * 1716 * @return instrumented method handle, or null if logger not enabled 1717 */ 1718 public MethodHandle addLoggingToHandle(final Class<? extends Loggable> clazz, final MethodHandle mh, final Supplier<String> text) { 1719 return addLoggingToHandle(clazz, Level.INFO, mh, Integer.MAX_VALUE, false, text); 1720 } 1721 1722 /** 1723 * Given a Loggable class, weave debug info info a method handle for that logger. 1724 * 1725 * @param clazz loggable 1726 * @param level log level 1727 * @param mh method handle 1728 * @param paramStart first parameter to print 1729 * @param printReturnValue should we print the return value? 1730 * @param text debug printout to add 1731 * 1732 * @return instrumented method handle, or null if logger not enabled 1733 */ 1734 public MethodHandle addLoggingToHandle(final Class<? extends Loggable> clazz, final Level level, final MethodHandle mh, final int paramStart, final boolean printReturnValue, final Supplier<String> text) { 1735 final DebugLogger log = getLogger(clazz); 1736 if (log.isEnabled()) { 1737 return MethodHandleFactory.addDebugPrintout(log, level, mh, paramStart, printReturnValue, text.get()); 1738 } 1739 return mh; 1740 } 1741 1742 private static String getLoggerName(final Class<?> clazz) { 1743 Class<?> current = clazz; 1744 while (current != null) { 1745 final Logger log = current.getAnnotation(Logger.class); 1746 if (log != null) { 1747 assert !"".equals(log.name()); 1748 return log.name(); 1749 } 1750 current = current.getSuperclass(); 1751 } 1752 assert false; 1753 return null; 1754 } 1755 1756 /** 1757 * This is a special kind of switchpoint used to guard builtin 1758 * properties and prototypes. In the future it might contain 1759 * logic to e.g. multiple switchpoint classes. 1760 */ 1761 public static final class BuiltinSwitchPoint extends SwitchPoint { 1762 //empty 1763 } 1764 1765 /** 1766 * Create a new builtin switchpoint and return it 1767 * @param name key name 1768 * @return new builtin switchpoint 1769 */ 1770 public SwitchPoint newBuiltinSwitchPoint(final String name) { 1771 assert builtinSwitchPoints.get(name) == null; 1772 final SwitchPoint sp = new BuiltinSwitchPoint(); 1773 builtinSwitchPoints.put(name, sp); 1774 return sp; 1775 } 1776 1777 /** 1778 * Return the builtin switchpoint for a particular key name 1779 * @param name key name 1780 * @return builtin switchpoint or null if none 1781 */ 1782 public SwitchPoint getBuiltinSwitchPoint(final String name) { 1783 return builtinSwitchPoints.get(name); 1784 } 1785 1786 private static ClassLoader createModuleLoader(final ClassLoader cl, 1787 final String modulePath, final String addModules) { 1788 if (addModules == null) { 1789 throw new IllegalArgumentException("--module-path specified with no --add-modules"); 1790 } 1791 1792 final Path[] paths = Stream.of(modulePath.split(File.pathSeparator)). 1793 map(s -> Paths.get(s)). 1794 toArray(sz -> new Path[sz]); 1795 final ModuleFinder mf = ModuleFinder.of(paths); 1796 final Set<ModuleReference> mrefs = mf.findAll(); 1797 if (mrefs.isEmpty()) { 1798 throw new RuntimeException("No modules in script --module-path: " + modulePath); 1799 } 1800 1801 final Set<String> rootMods; 1802 if (addModules.equals("ALL-MODULE-PATH")) { 1803 rootMods = mrefs.stream(). 1804 map(mr->mr.descriptor().name()). 1805 collect(Collectors.toSet()); 1806 } else { 1807 rootMods = Stream.of(addModules.split(",")). 1808 map(String::trim). 1809 collect(Collectors.toSet()); 1810 } 1811 1812 final Layer boot = Layer.boot(); 1813 final Configuration conf = boot.configuration(). 1814 resolveRequires(mf, ModuleFinder.of(), rootMods); 1815 final String firstMod = rootMods.iterator().next(); 1816 return boot.defineModulesWithOneLoader(conf, cl).findLoader(firstMod); 1817 } 1818} 1819