Context.java revision 1790:785843878cf7
1167465Smp/* 259243Sobrien * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 359243Sobrien * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 459243Sobrien * 559243Sobrien * This code is free software; you can redistribute it and/or modify it 659243Sobrien * under the terms of the GNU General Public License version 2 only, as 759243Sobrien * published by the Free Software Foundation. Oracle designates this 859243Sobrien * particular file as subject to the "Classpath" exception as provided 959243Sobrien * by Oracle in the LICENSE file that accompanied this code. 1059243Sobrien * 1159243Sobrien * This code is distributed in the hope that it will be useful, but WITHOUT 1259243Sobrien * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1359243Sobrien * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1459243Sobrien * version 2 for more details (a copy is included in the LICENSE file that 1559243Sobrien * accompanied this code). 1659243Sobrien * 17100616Smp * You should have received a copy of the GNU General Public License version 1859243Sobrien * 2 along with this work; if not, write to the Free Software Foundation, 1959243Sobrien * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 2059243Sobrien * 2159243Sobrien * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2259243Sobrien * or visit www.oracle.com if you need additional information or have any 2359243Sobrien * questions. 2459243Sobrien */ 2559243Sobrien 2659243Sobrienpackage jdk.nashorn.internal.runtime; 2759243Sobrien 2859243Sobrienimport static jdk.internal.org.objectweb.asm.Opcodes.V1_7; 2959243Sobrienimport static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS; 3059243Sobrienimport static jdk.nashorn.internal.codegen.CompilerConstants.CREATE_PROGRAM_FUNCTION; 3159243Sobrienimport static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE; 3259243Sobrienimport static jdk.nashorn.internal.codegen.CompilerConstants.STRICT_MODE; 3359243Sobrienimport static jdk.nashorn.internal.runtime.CodeStore.newCodeStore; 3459243Sobrienimport static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 3559243Sobrienimport static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; 3659243Sobrienimport static jdk.nashorn.internal.runtime.Source.sourceFor; 3759243Sobrien 3859243Sobrienimport java.io.File; 39167465Smpimport java.io.InputStream; 4059243Sobrienimport java.io.IOException; 4159243Sobrienimport java.io.PrintWriter; 4259243Sobrienimport java.io.UncheckedIOException; 4359243Sobrienimport java.lang.invoke.MethodHandle; 44167465Smpimport java.lang.invoke.MethodHandles; 4569408Sacheimport java.lang.invoke.MethodType; 46167465Smpimport java.lang.invoke.SwitchPoint; 47167465Smpimport java.lang.ref.ReferenceQueue; 48167465Smpimport java.lang.ref.SoftReference; 4969408Sacheimport java.lang.module.Configuration; 50167465Smpimport java.lang.module.ModuleDescriptor; 51167465Smpimport java.lang.module.ModuleFinder; 52167465Smpimport java.lang.module.ModuleReference; 53167465Smpimport java.lang.reflect.Field; 54167465Smpimport java.lang.reflect.Layer; 5559243Sobrienimport java.lang.reflect.Modifier; 5659243Sobrienimport java.lang.reflect.Module; 5759243Sobrienimport java.net.MalformedURLException; 5859243Sobrienimport java.net.URL; 59167465Smpimport java.nio.file.Path; 60167465Smpimport java.nio.file.Paths; 61167465Smpimport java.security.AccessControlContext; 62167465Smpimport java.security.AccessController; 63167465Smpimport java.security.CodeSigner; 64167465Smpimport java.security.CodeSource; 65167465Smpimport java.security.Permissions; 66167465Smpimport java.security.PrivilegedAction; 67167465Smpimport java.security.PrivilegedActionException; 68167465Smpimport java.security.PrivilegedExceptionAction; 69167465Smpimport java.security.ProtectionDomain; 70167465Smpimport java.util.Collection; 71167465Smpimport java.util.HashMap; 72167465Smpimport java.util.HashSet; 73167465Smpimport java.util.LinkedHashMap; 74167465Smpimport java.util.Map; 75167465Smpimport java.util.Objects; 76167465Smpimport java.util.Optional; 77167465Smpimport java.util.Set; 78167465Smpimport java.util.concurrent.ConcurrentHashMap; 79167465Smpimport java.util.concurrent.ConcurrentMap; 80167465Smpimport java.util.concurrent.atomic.AtomicLong; 81167465Smpimport java.util.concurrent.atomic.AtomicReference; 82167465Smpimport java.util.concurrent.atomic.LongAdder; 83167465Smpimport java.util.function.Consumer; 84167465Smpimport java.util.function.Supplier; 85167465Smpimport java.util.logging.Level; 86167465Smpimport java.util.stream.Collectors; 87167465Smpimport java.util.stream.Stream; 88167465Smpimport javax.script.ScriptEngine; 89167465Smpimport jdk.dynalink.DynamicLinker; 90167465Smpimport jdk.internal.org.objectweb.asm.ClassReader; 91167465Smpimport jdk.internal.org.objectweb.asm.ClassWriter; 92167465Smpimport jdk.internal.org.objectweb.asm.Opcodes; 93167465Smpimport jdk.internal.org.objectweb.asm.util.CheckClassAdapter; 94167465Smpimport jdk.nashorn.api.scripting.ClassFilter; 95167465Smpimport jdk.nashorn.api.scripting.ScriptObjectMirror; 96167465Smpimport jdk.nashorn.internal.WeakValueCache; 97167465Smpimport jdk.nashorn.internal.codegen.Compiler; 98167465Smpimport jdk.nashorn.internal.codegen.Compiler.CompilationPhases; 99167465Smpimport jdk.nashorn.internal.codegen.ObjectClassGenerator; 100167465Smpimport jdk.nashorn.internal.ir.FunctionNode; 101167465Smpimport jdk.nashorn.internal.ir.debug.ASTWriter; 102167465Smpimport jdk.nashorn.internal.ir.debug.PrintVisitor; 103167465Smpimport jdk.nashorn.internal.lookup.MethodHandleFactory; 10459243Sobrienimport jdk.nashorn.internal.objects.Global; 10559243Sobrienimport jdk.nashorn.internal.parser.Parser; 10659243Sobrienimport jdk.nashorn.internal.runtime.events.RuntimeEvent; 10759243Sobrienimport jdk.nashorn.internal.runtime.linker.Bootstrap; 108167465Smpimport jdk.nashorn.internal.runtime.logging.DebugLogger; 109167465Smpimport jdk.nashorn.internal.runtime.logging.Loggable; 110167465Smpimport jdk.nashorn.internal.runtime.logging.Logger; 111167465Smpimport jdk.nashorn.internal.runtime.options.LoggingOption.LoggerInfo; 112167465Smpimport jdk.nashorn.internal.runtime.options.Options; 11359243Sobrienimport jdk.internal.misc.Unsafe; 11459243Sobrien 11559243Sobrien/** 11659243Sobrien * This class manages the global state of execution. Context is immutable. 117167465Smp */ 118167465Smppublic final class Context { 119167465Smp // nashorn specific security runtime access permission names 120167465Smp /** 12159243Sobrien * Permission needed to pass arbitrary nashorn command line options when creating Context. 12259243Sobrien */ 12359243Sobrien public static final String NASHORN_SET_CONFIG = "nashorn.setConfig"; 12459243Sobrien 125167465Smp /** 126167465Smp * Permission needed to create Nashorn Context instance. 127167465Smp */ 12859243Sobrien public static final String NASHORN_CREATE_CONTEXT = "nashorn.createContext"; 12959243Sobrien 13059243Sobrien /** 131 * Permission needed to create Nashorn Global instance. 132 */ 133 public static final String NASHORN_CREATE_GLOBAL = "nashorn.createGlobal"; 134 135 /** 136 * Permission to get current Nashorn Context from thread local storage. 137 */ 138 public static final String NASHORN_GET_CONTEXT = "nashorn.getContext"; 139 140 /** 141 * Permission to use Java reflection/jsr292 from script code. 142 */ 143 public static final String NASHORN_JAVA_REFLECTION = "nashorn.JavaReflection"; 144 145 /** 146 * Permission to enable nashorn debug mode. 147 */ 148 public static final String NASHORN_DEBUG_MODE = "nashorn.debugMode"; 149 150 // nashorn load psuedo URL prefixes 151 private static final String LOAD_CLASSPATH = "classpath:"; 152 private static final String LOAD_FX = "fx:"; 153 private static final String LOAD_NASHORN = "nashorn:"; 154 155 private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); 156 private static final MethodType CREATE_PROGRAM_FUNCTION_TYPE = MethodType.methodType(ScriptFunction.class, ScriptObject.class); 157 158 private static final LongAdder NAMED_INSTALLED_SCRIPT_COUNT = new LongAdder(); 159 private static final LongAdder ANONYMOUS_INSTALLED_SCRIPT_COUNT = new LongAdder(); 160 161 /** 162 * Should scripts use only object slots for fields, or dual long/object slots? The default 163 * behaviour is to couple this to optimistic types, using dual representation if optimistic types are enabled 164 * and single field representation otherwise. This can be overridden by setting either the "nashorn.fields.objects" 165 * or "nashorn.fields.dual" system property. 166 */ 167 private final FieldMode fieldMode; 168 169 private static enum FieldMode { 170 /** Value for automatic field representation depending on optimistic types setting */ 171 AUTO, 172 /** Value for object field representation regardless of optimistic types setting */ 173 OBJECTS, 174 /** Value for dual primitive/object field representation regardless of optimistic types setting */ 175 DUAL 176 } 177 178 /** 179 * Keeps track of which builtin prototypes and properties have been relinked 180 * Currently we are conservative and associate the name of a builtin class with all 181 * its properties, so it's enough to invalidate a property to break all assumptions 182 * about a prototype. This can be changed to a more fine grained approach, but no one 183 * ever needs this, given the very rare occurrence of swapping out only parts of 184 * a builtin v.s. the entire builtin object 185 */ 186 private final Map<String, SwitchPoint> builtinSwitchPoints = new HashMap<>(); 187 188 /* Force DebuggerSupport to be loaded. */ 189 static { 190 DebuggerSupport.FORCELOAD = true; 191 } 192 193 static long getNamedInstalledScriptCount() { 194 return NAMED_INSTALLED_SCRIPT_COUNT.sum(); 195 } 196 197 static long getAnonymousInstalledScriptCount() { 198 return ANONYMOUS_INSTALLED_SCRIPT_COUNT.sum(); 199 } 200 201 /** 202 * ContextCodeInstaller that has the privilege of installing classes in the Context. 203 * Can only be instantiated from inside the context and is opaque to other classes 204 */ 205 private abstract static class ContextCodeInstaller implements CodeInstaller { 206 final Context context; 207 final CodeSource codeSource; 208 209 ContextCodeInstaller(final Context context, final CodeSource codeSource) { 210 this.context = context; 211 this.codeSource = codeSource; 212 } 213 214 @Override 215 public Context getContext() { 216 return context; 217 } 218 219 @Override 220 public void initialize(final Collection<Class<?>> classes, final Source source, final Object[] constants) { 221 try { 222 AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() { 223 @Override 224 public Void run() throws Exception { 225 for (final Class<?> clazz : classes) { 226 //use reflection to write source and constants table to installed classes 227 final Field sourceField = clazz.getDeclaredField(SOURCE.symbolName()); 228 sourceField.setAccessible(true); 229 sourceField.set(null, source); 230 231 final Field constantsField = clazz.getDeclaredField(CONSTANTS.symbolName()); 232 constantsField.setAccessible(true); 233 constantsField.set(null, constants); 234 } 235 return null; 236 } 237 }); 238 } catch (final PrivilegedActionException e) { 239 throw new RuntimeException(e); 240 } 241 } 242 243 @Override 244 public void verify(final byte[] code) { 245 context.verify(code); 246 } 247 248 @Override 249 public long getUniqueScriptId() { 250 return context.getUniqueScriptId(); 251 } 252 253 @Override 254 public void storeScript(final String cacheKey, final Source source, final String mainClassName, 255 final Map<String,byte[]> classBytes, final Map<Integer, FunctionInitializer> initializers, 256 final Object[] constants, final int compilationId) { 257 if (context.codeStore != null) { 258 context.codeStore.store(cacheKey, source, mainClassName, classBytes, initializers, constants, compilationId); 259 } 260 } 261 262 @Override 263 public StoredScript loadScript(final Source source, final String functionKey) { 264 if (context.codeStore != null) { 265 return context.codeStore.load(source, functionKey); 266 } 267 return null; 268 } 269 270 @Override 271 public boolean isCompatibleWith(final CodeInstaller other) { 272 if (other instanceof ContextCodeInstaller) { 273 final ContextCodeInstaller cci = (ContextCodeInstaller)other; 274 return cci.context == context && cci.codeSource == codeSource; 275 } 276 return false; 277 } 278 } 279 280 private static class NamedContextCodeInstaller extends ContextCodeInstaller { 281 private final ScriptLoader loader; 282 private int usageCount = 0; 283 private int bytesDefined = 0; 284 285 // We reuse this installer for 10 compilations or 200000 defined bytes. Usually the first condition 286 // will occur much earlier, the second is a safety measure for very large scripts/functions. 287 private final static int MAX_USAGES = 10; 288 private final static int MAX_BYTES_DEFINED = 200_000; 289 290 private NamedContextCodeInstaller(final Context context, final CodeSource codeSource, final ScriptLoader loader) { 291 super(context, codeSource); 292 this.loader = loader; 293 } 294 295 @Override 296 public Class<?> install(final String className, final byte[] bytecode) { 297 usageCount++; 298 bytesDefined += bytecode.length; 299 NAMED_INSTALLED_SCRIPT_COUNT.increment(); 300 return loader.installClass(Compiler.binaryName(className), bytecode, codeSource); 301 } 302 303 @Override 304 public CodeInstaller getOnDemandCompilationInstaller() { 305 // Reuse this installer if we're within our limits. 306 if (usageCount < MAX_USAGES && bytesDefined < MAX_BYTES_DEFINED) { 307 return this; 308 } 309 return new NamedContextCodeInstaller(context, codeSource, context.createNewLoader()); 310 } 311 312 @Override 313 public CodeInstaller getMultiClassCodeInstaller() { 314 // This installer is perfectly suitable for installing multiple classes that reference each other 315 // as it produces classes with resolvable names, all defined in a single class loader. 316 return this; 317 } 318 } 319 320 private final WeakValueCache<CodeSource, Class<?>> anonymousHostClasses = new WeakValueCache<>(); 321 322 private static final class AnonymousContextCodeInstaller extends ContextCodeInstaller { 323 private static final Unsafe UNSAFE = getUnsafe(); 324 private static final String ANONYMOUS_HOST_CLASS_NAME = Compiler.SCRIPTS_PACKAGE.replace('/', '.') + ".AnonymousHost"; 325 private static final byte[] ANONYMOUS_HOST_CLASS_BYTES = getAnonymousHostClassBytes(); 326 327 private final Class<?> hostClass; 328 329 private AnonymousContextCodeInstaller(final Context context, final CodeSource codeSource, final Class<?> hostClass) { 330 super(context, codeSource); 331 this.hostClass = hostClass; 332 } 333 334 @Override 335 public Class<?> install(final String className, final byte[] bytecode) { 336 ANONYMOUS_INSTALLED_SCRIPT_COUNT.increment(); 337 return UNSAFE.defineAnonymousClass(hostClass, bytecode, null); 338 } 339 340 @Override 341 public CodeInstaller getOnDemandCompilationInstaller() { 342 // This code loader can be indefinitely reused for on-demand recompilations for the same code source. 343 return this; 344 } 345 346 @Override 347 public CodeInstaller getMultiClassCodeInstaller() { 348 // This code loader can not be used to install multiple classes that reference each other, as they 349 // would have no resolvable names. Therefore, in such situation we must revert to an installer that 350 // produces named classes. 351 return new NamedContextCodeInstaller(context, codeSource, context.createNewLoader()); 352 } 353 354 private static byte[] getAnonymousHostClassBytes() { 355 final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS); 356 cw.visit(V1_7, Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT, ANONYMOUS_HOST_CLASS_NAME.replace('.', '/'), null, "java/lang/Object", null); 357 cw.visitEnd(); 358 return cw.toByteArray(); 359 } 360 361 private static Unsafe getUnsafe() { 362 return AccessController.doPrivileged(new PrivilegedAction<Unsafe>() { 363 @Override 364 public Unsafe run() { 365 try { 366 final Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe"); 367 theUnsafeField.setAccessible(true); 368 return (Unsafe)theUnsafeField.get(null); 369 } catch (final ReflectiveOperationException e) { 370 throw new RuntimeException(e); 371 } 372 } 373 }); 374 } 375 } 376 377 /** Is Context global debug mode enabled ? */ 378 public static final boolean DEBUG = Options.getBooleanProperty("nashorn.debug"); 379 380 private static final ThreadLocal<Global> currentGlobal = new ThreadLocal<>(); 381 382 // in-memory cache for loaded classes 383 private ClassCache classCache; 384 385 // persistent code store 386 private CodeStore codeStore; 387 388 // A factory for linking global properties as constant method handles. It is created when the first Global 389 // is created, and invalidated forever once the second global is created. 390 private final AtomicReference<GlobalConstants> globalConstantsRef = new AtomicReference<>(); 391 392 /** 393 * Get the current global scope 394 * @return the current global scope 395 */ 396 public static Global getGlobal() { 397 // This class in a package.access protected package. 398 // Trusted code only can call this method. 399 return currentGlobal.get(); 400 } 401 402 /** 403 * Set the current global scope 404 * @param global the global scope 405 */ 406 public static void setGlobal(final ScriptObject global) { 407 if (global != null && !(global instanceof Global)) { 408 throw new IllegalArgumentException("not a global!"); 409 } 410 setGlobal((Global)global); 411 } 412 413 /** 414 * Set the current global scope 415 * @param global the global scope 416 */ 417 public static void setGlobal(final Global global) { 418 // This class in a package.access protected package. 419 // Trusted code only can call this method. 420 assert getGlobal() != global; 421 //same code can be cached between globals, then we need to invalidate method handle constants 422 if (global != null) { 423 final GlobalConstants globalConstants = getContext(global).getGlobalConstants(); 424 if (globalConstants != null) { 425 globalConstants.invalidateAll(); 426 } 427 } 428 currentGlobal.set(global); 429 } 430 431 /** 432 * Get context of the current global 433 * @return current global scope's context. 434 */ 435 public static Context getContext() { 436 final SecurityManager sm = System.getSecurityManager(); 437 if (sm != null) { 438 sm.checkPermission(new RuntimePermission(NASHORN_GET_CONTEXT)); 439 } 440 return getContextTrusted(); 441 } 442 443 /** 444 * Get current context's error writer 445 * 446 * @return error writer of the current context 447 */ 448 public static PrintWriter getCurrentErr() { 449 final ScriptObject global = getGlobal(); 450 return (global != null)? global.getContext().getErr() : new PrintWriter(System.err); 451 } 452 453 /** 454 * Output text to this Context's error stream 455 * @param str text to write 456 */ 457 public static void err(final String str) { 458 err(str, true); 459 } 460 461 /** 462 * Output text to this Context's error stream, optionally with 463 * a newline afterwards 464 * 465 * @param str text to write 466 * @param crlf write a carriage return/new line after text 467 */ 468 public static void err(final String str, final boolean crlf) { 469 final PrintWriter err = Context.getCurrentErr(); 470 if (err != null) { 471 if (crlf) { 472 err.println(str); 473 } else { 474 err.print(str); 475 } 476 } 477 } 478 479 /** Current environment. */ 480 private final ScriptEnvironment env; 481 482 /** is this context in strict mode? Cached from env. as this is used heavily. */ 483 final boolean _strict; 484 485 /** class loader to resolve classes from script. */ 486 private final ClassLoader appLoader; 487 488 /*package-private*/ 489 ClassLoader getAppLoader() { 490 return appLoader; 491 } 492 493 /** Class loader to load classes compiled from scripts. */ 494 private final ScriptLoader scriptLoader; 495 496 /** Dynamic linker for linking call sites in script code loaded by this context */ 497 private final DynamicLinker dynamicLinker; 498 499 /** Current error manager. */ 500 private final ErrorManager errors; 501 502 /** Unique id for script. Used only when --loader-per-compile=false */ 503 private final AtomicLong uniqueScriptId; 504 505 /** Optional class filter to use for Java classes. Can be null. */ 506 private final ClassFilter classFilter; 507 508 /** Process-wide singleton structure loader */ 509 private static final StructureLoader theStructLoader; 510 private static final ConcurrentMap<String, Class<?>> structureClasses = new ConcurrentHashMap<>(); 511 512 /*package-private*/ @SuppressWarnings("static-method") 513 StructureLoader getStructLoader() { 514 return theStructLoader; 515 } 516 517 private static AccessControlContext createNoPermAccCtxt() { 518 return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, new Permissions()) }); 519 } 520 521 private static AccessControlContext createPermAccCtxt(final String permName) { 522 final Permissions perms = new Permissions(); 523 perms.add(new RuntimePermission(permName)); 524 return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, perms) }); 525 } 526 527 private static final AccessControlContext NO_PERMISSIONS_ACC_CTXT = createNoPermAccCtxt(); 528 private static final AccessControlContext CREATE_LOADER_ACC_CTXT = createPermAccCtxt("createClassLoader"); 529 private static final AccessControlContext CREATE_GLOBAL_ACC_CTXT = createPermAccCtxt(NASHORN_CREATE_GLOBAL); 530 private static final AccessControlContext GET_LOADER_ACC_CTXT = createPermAccCtxt("getClassLoader"); 531 532 static { 533 final ClassLoader myLoader = Context.class.getClassLoader(); 534 theStructLoader = AccessController.doPrivileged(new PrivilegedAction<StructureLoader>() { 535 @Override 536 public StructureLoader run() { 537 return new StructureLoader(myLoader); 538 } 539 }, CREATE_LOADER_ACC_CTXT); 540 } 541 542 /** 543 * ThrowErrorManager that throws ParserException upon error conditions. 544 */ 545 public static class ThrowErrorManager extends ErrorManager { 546 @Override 547 public void error(final String message) { 548 throw new ParserException(message); 549 } 550 551 @Override 552 public void error(final ParserException e) { 553 throw e; 554 } 555 } 556 557 /** 558 * Constructor 559 * 560 * @param options options from command line or Context creator 561 * @param errors error manger 562 * @param appLoader application class loader 563 */ 564 public Context(final Options options, final ErrorManager errors, final ClassLoader appLoader) { 565 this(options, errors, appLoader, null); 566 } 567 568 /** 569 * Constructor 570 * 571 * @param options options from command line or Context creator 572 * @param errors error manger 573 * @param appLoader application class loader 574 * @param classFilter class filter to use 575 */ 576 public Context(final Options options, final ErrorManager errors, final ClassLoader appLoader, final ClassFilter classFilter) { 577 this(options, errors, new PrintWriter(System.out, true), new PrintWriter(System.err, true), appLoader, classFilter); 578 } 579 580 /** 581 * Constructor 582 * 583 * @param options options from command line or Context creator 584 * @param errors error manger 585 * @param out output writer for this Context 586 * @param err error writer for this Context 587 * @param appLoader application class loader 588 */ 589 public Context(final Options options, final ErrorManager errors, final PrintWriter out, final PrintWriter err, final ClassLoader appLoader) { 590 this(options, errors, out, err, appLoader, (ClassFilter)null); 591 } 592 593 /** 594 * Constructor 595 * 596 * @param options options from command line or Context creator 597 * @param errors error manger 598 * @param out output writer for this Context 599 * @param err error writer for this Context 600 * @param appLoader application class loader 601 * @param classFilter class filter to use 602 */ 603 public Context(final Options options, final ErrorManager errors, final PrintWriter out, final PrintWriter err, final ClassLoader appLoader, final ClassFilter classFilter) { 604 final SecurityManager sm = System.getSecurityManager(); 605 if (sm != null) { 606 sm.checkPermission(new RuntimePermission(NASHORN_CREATE_CONTEXT)); 607 } 608 609 this.classFilter = classFilter; 610 this.env = new ScriptEnvironment(options, out, err); 611 this._strict = env._strict; 612 if (env._loader_per_compile) { 613 this.scriptLoader = null; 614 this.uniqueScriptId = null; 615 } else { 616 this.scriptLoader = createNewLoader(); 617 this.uniqueScriptId = new AtomicLong(); 618 } 619 this.errors = errors; 620 621 // if user passed --module-path, we create a module class loader with 622 // passed appLoader as the parent. 623 final String modulePath = env._module_path; 624 ClassLoader appCl = null; 625 if (!env._compile_only && modulePath != null && !modulePath.isEmpty()) { 626 // make sure that caller can create a class loader. 627 if (sm != null) { 628 sm.checkCreateClassLoader(); 629 } 630 appCl = AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() { 631 @Override 632 public ClassLoader run() { 633 return createModuleLoader(appLoader, modulePath, env._add_modules); 634 } 635 }); 636 } else { 637 appCl = appLoader; 638 } 639 640 // if user passed -classpath option, make a URLClassLoader with that and 641 // the app loader or module app loader as the parent. 642 final String classPath = env._classpath; 643 if (!env._compile_only && classPath != null && !classPath.isEmpty()) { 644 // make sure that caller can create a class loader. 645 if (sm != null) { 646 sm.checkCreateClassLoader(); 647 } 648 appCl = NashornLoader.createClassLoader(classPath, appCl); 649 } 650 651 this.appLoader = appCl; 652 this.dynamicLinker = Bootstrap.createDynamicLinker(this.appLoader, env._unstable_relink_threshold); 653 654 final int cacheSize = env._class_cache_size; 655 if (cacheSize > 0) { 656 classCache = new ClassCache(this, cacheSize); 657 } 658 659 if (env._persistent_cache) { 660 codeStore = newCodeStore(this); 661 } 662 663 // print version info if asked. 664 if (env._version) { 665 getErr().println("nashorn " + Version.version()); 666 } 667 668 if (env._fullversion) { 669 getErr().println("nashorn full version " + Version.fullVersion()); 670 } 671 672 if (Options.getBooleanProperty("nashorn.fields.dual")) { 673 fieldMode = FieldMode.DUAL; 674 } else if (Options.getBooleanProperty("nashorn.fields.objects")) { 675 fieldMode = FieldMode.OBJECTS; 676 } else { 677 fieldMode = FieldMode.AUTO; 678 } 679 680 initLoggers(); 681 } 682 683 684 /** 685 * Get the class filter for this context 686 * @return class filter 687 */ 688 public ClassFilter getClassFilter() { 689 return classFilter; 690 } 691 692 /** 693 * Returns the factory for constant method handles for global properties. The returned factory can be 694 * invalidated if this Context has more than one Global. 695 * @return the factory for constant method handles for global properties. 696 */ 697 GlobalConstants getGlobalConstants() { 698 return globalConstantsRef.get(); 699 } 700 701 /** 702 * Get the error manager for this context 703 * @return error manger 704 */ 705 public ErrorManager getErrorManager() { 706 return errors; 707 } 708 709 /** 710 * Get the script environment for this context 711 * @return script environment 712 */ 713 public ScriptEnvironment getEnv() { 714 return env; 715 } 716 717 /** 718 * Get the output stream for this context 719 * @return output print writer 720 */ 721 public PrintWriter getOut() { 722 return env.getOut(); 723 } 724 725 /** 726 * Get the error stream for this context 727 * @return error print writer 728 */ 729 public PrintWriter getErr() { 730 return env.getErr(); 731 } 732 733 /** 734 * Should scripts compiled by this context use dual field representation? 735 * @return true if using dual fields, false for object-only fields 736 */ 737 public boolean useDualFields() { 738 return fieldMode == FieldMode.DUAL || (fieldMode == FieldMode.AUTO && env._optimistic_types); 739 } 740 741 /** 742 * Get the PropertyMap of the current global scope 743 * @return the property map of the current global scope 744 */ 745 public static PropertyMap getGlobalMap() { 746 return Context.getGlobal().getMap(); 747 } 748 749 /** 750 * Compile a top level script. 751 * 752 * @param source the source 753 * @param scope the scope 754 * 755 * @return top level function for script 756 */ 757 public ScriptFunction compileScript(final Source source, final ScriptObject scope) { 758 return compileScript(source, scope, this.errors); 759 } 760 761 /** 762 * Interface to represent compiled code that can be re-used across many 763 * global scope instances 764 */ 765 public static interface MultiGlobalCompiledScript { 766 /** 767 * Obtain script function object for a specific global scope object. 768 * 769 * @param newGlobal global scope for which function object is obtained 770 * @return script function for script level expressions 771 */ 772 public ScriptFunction getFunction(final Global newGlobal); 773 } 774 775 /** 776 * Compile a top level script. 777 * 778 * @param source the script source 779 * @return reusable compiled script across many global scopes. 780 */ 781 public MultiGlobalCompiledScript compileScript(final Source source) { 782 final Class<?> clazz = compile(source, this.errors, this._strict, false); 783 final MethodHandle createProgramFunctionHandle = getCreateProgramFunctionHandle(clazz); 784 785 return new MultiGlobalCompiledScript() { 786 @Override 787 public ScriptFunction getFunction(final Global newGlobal) { 788 return invokeCreateProgramFunctionHandle(createProgramFunctionHandle, newGlobal); 789 } 790 }; 791 } 792 793 /** 794 * Entry point for {@code eval} 795 * 796 * @param initialScope The scope of this eval call 797 * @param string Evaluated code as a String 798 * @param callThis "this" to be passed to the evaluated code 799 * @param location location of the eval call 800 * @return the return value of the {@code eval} 801 */ 802 public Object eval(final ScriptObject initialScope, final String string, 803 final Object callThis, final Object location) { 804 return eval(initialScope, string, callThis, location, false, false); 805 } 806 807 /** 808 * Entry point for {@code eval} 809 * 810 * @param initialScope The scope of this eval call 811 * @param string Evaluated code as a String 812 * @param callThis "this" to be passed to the evaluated code 813 * @param location location of the eval call 814 * @param strict is this {@code eval} call from a strict mode code? 815 * @param evalCall is this called from "eval" builtin? 816 * 817 * @return the return value of the {@code eval} 818 */ 819 public Object eval(final ScriptObject initialScope, final String string, 820 final Object callThis, final Object location, final boolean strict, final boolean evalCall) { 821 final String file = location == UNDEFINED || location == null ? "<eval>" : location.toString(); 822 final Source source = sourceFor(file, string, evalCall); 823 // is this direct 'eval' builtin call? 824 final boolean directEval = evalCall && (location != UNDEFINED); 825 final Global global = Context.getGlobal(); 826 ScriptObject scope = initialScope; 827 828 // ECMA section 10.1.1 point 2 says eval code is strict if it begins 829 // with "use strict" directive or eval direct call itself is made 830 // from from strict mode code. We are passed with caller's strict mode. 831 // Nashorn extension: any 'eval' is unconditionally strict when -strict is specified. 832 boolean strictFlag = strict || this._strict; 833 834 Class<?> clazz; 835 try { 836 clazz = compile(source, new ThrowErrorManager(), strictFlag, true); 837 } catch (final ParserException e) { 838 e.throwAsEcmaException(global); 839 return null; 840 } 841 842 if (!strictFlag) { 843 // We need to get strict mode flag from compiled class. This is 844 // because eval code may start with "use strict" directive. 845 try { 846 strictFlag = clazz.getField(STRICT_MODE.symbolName()).getBoolean(null); 847 } catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { 848 //ignored 849 strictFlag = false; 850 } 851 } 852 853 // In strict mode, eval does not instantiate variables and functions 854 // in the caller's environment. A new environment is created! 855 if (strictFlag) { 856 // Create a new scope object with given scope as its prototype 857 scope = newScope(scope); 858 } 859 860 final ScriptFunction func = getProgramFunction(clazz, scope); 861 Object evalThis; 862 if (directEval) { 863 evalThis = (callThis != UNDEFINED && callThis != null) || strictFlag ? callThis : global; 864 } else { 865 // either indirect evalCall or non-eval (Function, engine.eval, ScriptObjectMirror.eval..) 866 evalThis = callThis; 867 } 868 869 return ScriptRuntime.apply(func, evalThis); 870 } 871 872 private static ScriptObject newScope(final ScriptObject callerScope) { 873 return new Scope(callerScope, PropertyMap.newMap(Scope.class)); 874 } 875 876 private static Source loadInternal(final String srcStr, final String prefix, final String resourcePath) { 877 if (srcStr.startsWith(prefix)) { 878 final String resource = resourcePath + srcStr.substring(prefix.length()); 879 // NOTE: even sandbox scripts should be able to load scripts in nashorn: scheme 880 // These scripts are always available and are loaded from nashorn.jar's resources. 881 return AccessController.doPrivileged( 882 new PrivilegedAction<Source>() { 883 @Override 884 public Source run() { 885 try { 886 final InputStream resStream = Context.class.getResourceAsStream(resource); 887 return resStream != null ? sourceFor(srcStr, Source.readFully(resStream)) : null; 888 } catch (final IOException exp) { 889 return null; 890 } 891 } 892 }); 893 } 894 895 return null; 896 } 897 898 /** 899 * Implementation of {@code load} Nashorn extension. Load a script file from a source 900 * expression 901 * 902 * @param scope the scope 903 * @param from source expression for script 904 * 905 * @return return value for load call (undefined) 906 * 907 * @throws IOException if source cannot be found or loaded 908 */ 909 public Object load(final Object scope, final Object from) throws IOException { 910 final Object src = from instanceof ConsString ? from.toString() : from; 911 Source source = null; 912 913 // load accepts a String (which could be a URL or a file name), a File, a URL 914 // or a ScriptObject that has "name" and "source" (string valued) properties. 915 if (src instanceof String) { 916 final String srcStr = (String)src; 917 if (srcStr.startsWith(LOAD_CLASSPATH)) { 918 final URL url = getResourceURL(srcStr.substring(LOAD_CLASSPATH.length())); 919 source = url != null ? sourceFor(url.toString(), url) : null; 920 } else { 921 final File file = new File(srcStr); 922 if (srcStr.indexOf(':') != -1) { 923 if ((source = loadInternal(srcStr, LOAD_NASHORN, "resources/")) == null && 924 (source = loadInternal(srcStr, LOAD_FX, "resources/fx/")) == null) { 925 URL url; 926 try { 927 //check for malformed url. if malformed, it may still be a valid file 928 url = new URL(srcStr); 929 } catch (final MalformedURLException e) { 930 url = file.toURI().toURL(); 931 } 932 source = sourceFor(url.toString(), url); 933 } 934 } else if (file.isFile()) { 935 source = sourceFor(srcStr, file); 936 } 937 } 938 } else if (src instanceof File && ((File)src).isFile()) { 939 final File file = (File)src; 940 source = sourceFor(file.getName(), file); 941 } else if (src instanceof URL) { 942 final URL url = (URL)src; 943 source = sourceFor(url.toString(), url); 944 } else if (src instanceof ScriptObject) { 945 final ScriptObject sobj = (ScriptObject)src; 946 if (sobj.has("script") && sobj.has("name")) { 947 final String script = JSType.toString(sobj.get("script")); 948 final String name = JSType.toString(sobj.get("name")); 949 source = sourceFor(name, script); 950 } 951 } else if (src instanceof Map) { 952 final Map<?,?> map = (Map<?,?>)src; 953 if (map.containsKey("script") && map.containsKey("name")) { 954 final String script = JSType.toString(map.get("script")); 955 final String name = JSType.toString(map.get("name")); 956 source = sourceFor(name, script); 957 } 958 } 959 960 if (source != null) { 961 if (scope instanceof ScriptObject && ((ScriptObject)scope).isScope()) { 962 final ScriptObject sobj = (ScriptObject)scope; 963 // passed object is a script object 964 // Global is the only user accessible scope ScriptObject 965 assert sobj.isGlobal() : "non-Global scope object!!"; 966 return evaluateSource(source, sobj, sobj); 967 } else if (scope == null || scope == UNDEFINED) { 968 // undefined or null scope. Use current global instance. 969 final Global global = getGlobal(); 970 return evaluateSource(source, global, global); 971 } else { 972 /* 973 * Arbitrary object passed for scope. 974 * Indirect load that is equivalent to: 975 * 976 * (function(scope, source) { 977 * with (scope) { 978 * eval(<script_from_source>); 979 * } 980 * })(scope, source); 981 */ 982 final Global global = getGlobal(); 983 // Create a new object. This is where all declarations 984 // (var, function) from the evaluated code go. 985 // make global to be its __proto__ so that global 986 // definitions are accessible to the evaluated code. 987 final ScriptObject evalScope = newScope(global); 988 989 // finally, make a WithObject around user supplied scope object 990 // so that it's properties are accessible as variables. 991 final ScriptObject withObj = ScriptRuntime.openWith(evalScope, scope); 992 993 // evaluate given source with 'withObj' as scope 994 // but use global object as "this". 995 return evaluateSource(source, withObj, global); 996 } 997 } 998 999 throw typeError("cant.load.script", ScriptRuntime.safeToString(from)); 1000 } 1001 1002 /** 1003 * Implementation of {@code loadWithNewGlobal} Nashorn extension. Load a script file from a source 1004 * expression, after creating a new global scope. 1005 * 1006 * @param from source expression for script 1007 * @param args (optional) arguments to be passed to the loaded script 1008 * 1009 * @return return value for load call (undefined) 1010 * 1011 * @throws IOException if source cannot be found or loaded 1012 */ 1013 public Object loadWithNewGlobal(final Object from, final Object...args) throws IOException { 1014 final Global oldGlobal = getGlobal(); 1015 final Global newGlobal = AccessController.doPrivileged(new PrivilegedAction<Global>() { 1016 @Override 1017 public Global run() { 1018 try { 1019 return newGlobal(); 1020 } catch (final RuntimeException e) { 1021 if (Context.DEBUG) { 1022 e.printStackTrace(); 1023 } 1024 throw e; 1025 } 1026 } 1027 }, CREATE_GLOBAL_ACC_CTXT); 1028 // initialize newly created Global instance 1029 initGlobal(newGlobal); 1030 setGlobal(newGlobal); 1031 1032 final Object[] wrapped = args == null? ScriptRuntime.EMPTY_ARRAY : ScriptObjectMirror.wrapArray(args, oldGlobal); 1033 newGlobal.put("arguments", newGlobal.wrapAsObject(wrapped), env._strict); 1034 1035 try { 1036 // wrap objects from newGlobal's world as mirrors - but if result 1037 // is from oldGlobal's world, unwrap it! 1038 return ScriptObjectMirror.unwrap(ScriptObjectMirror.wrap(load(newGlobal, from), newGlobal), oldGlobal); 1039 } finally { 1040 setGlobal(oldGlobal); 1041 } 1042 } 1043 1044 /** 1045 * Load or get a structure class. Structure class names are based on the number of parameter fields 1046 * and {@link AccessorProperty} fields in them. Structure classes are used to represent ScriptObjects 1047 * 1048 * @see ObjectClassGenerator 1049 * @see AccessorProperty 1050 * @see ScriptObject 1051 * 1052 * @param fullName full name of class, e.g. jdk.nashorn.internal.objects.JO2P1 contains 2 fields and 1 parameter. 1053 * 1054 * @return the {@code Class<?>} for this structure 1055 * 1056 * @throws ClassNotFoundException if structure class cannot be resolved 1057 */ 1058 @SuppressWarnings("unchecked") 1059 public static Class<? extends ScriptObject> forStructureClass(final String fullName) throws ClassNotFoundException { 1060 if (System.getSecurityManager() != null && !StructureLoader.isStructureClass(fullName)) { 1061 throw new ClassNotFoundException(fullName); 1062 } 1063 return (Class<? extends ScriptObject>)structureClasses.computeIfAbsent(fullName, (name) -> { 1064 try { 1065 return Class.forName(name, true, theStructLoader); 1066 } catch (final ClassNotFoundException e) { 1067 throw new AssertionError(e); 1068 } 1069 }); 1070 } 1071 1072 /** 1073 * Is {@code className} the name of a structure class? 1074 * 1075 * @param className a class name 1076 * @return true if className is a structure class name 1077 */ 1078 public static boolean isStructureClass(final String className) { 1079 return StructureLoader.isStructureClass(className); 1080 } 1081 1082 /** 1083 * Checks that the given Class can be accessed from no permissions context. 1084 * 1085 * @param clazz Class object 1086 * @throws SecurityException if not accessible 1087 */ 1088 public static void checkPackageAccess(final Class<?> clazz) { 1089 final SecurityManager sm = System.getSecurityManager(); 1090 if (sm != null) { 1091 Class<?> bottomClazz = clazz; 1092 while (bottomClazz.isArray()) { 1093 bottomClazz = bottomClazz.getComponentType(); 1094 } 1095 checkPackageAccess(sm, bottomClazz.getName()); 1096 } 1097 } 1098 1099 /** 1100 * Checks that the given package name can be accessed from no permissions context. 1101 * 1102 * @param pkgName package name 1103 * @throws SecurityException if not accessible 1104 */ 1105 public static void checkPackageAccess(final String pkgName) { 1106 final SecurityManager sm = System.getSecurityManager(); 1107 if (sm != null) { 1108 checkPackageAccess(sm, pkgName.endsWith(".") ? pkgName : pkgName + "."); 1109 } 1110 } 1111 1112 /** 1113 * Checks that the given package can be accessed from no permissions context. 1114 * 1115 * @param sm current security manager instance 1116 * @param fullName fully qualified package name 1117 * @throw SecurityException if not accessible 1118 */ 1119 private static void checkPackageAccess(final SecurityManager sm, final String fullName) { 1120 Objects.requireNonNull(sm); 1121 final int index = fullName.lastIndexOf('.'); 1122 if (index != -1) { 1123 final String pkgName = fullName.substring(0, index); 1124 AccessController.doPrivileged(new PrivilegedAction<Void>() { 1125 @Override 1126 public Void run() { 1127 sm.checkPackageAccess(pkgName); 1128 return null; 1129 } 1130 }, NO_PERMISSIONS_ACC_CTXT); 1131 } 1132 } 1133 1134 /** 1135 * Checks that the given Class can be accessed from no permissions context. 1136 * 1137 * @param clazz Class object 1138 * @return true if package is accessible, false otherwise 1139 */ 1140 private static boolean isAccessiblePackage(final Class<?> clazz) { 1141 try { 1142 checkPackageAccess(clazz); 1143 return true; 1144 } catch (final SecurityException se) { 1145 return false; 1146 } 1147 } 1148 1149 /** 1150 * Checks that the given Class is public and it can be accessed from no permissions context. 1151 * 1152 * @param clazz Class object to check 1153 * @return true if Class is accessible, false otherwise 1154 */ 1155 public static boolean isAccessibleClass(final Class<?> clazz) { 1156 return Modifier.isPublic(clazz.getModifiers()) && Context.isAccessiblePackage(clazz); 1157 } 1158 1159 /** 1160 * Lookup a Java class. This is used for JSR-223 stuff linking in from 1161 * {@code jdk.nashorn.internal.objects.NativeJava} and {@code jdk.nashorn.internal.runtime.NativeJavaPackage} 1162 * 1163 * @param fullName full name of class to load 1164 * 1165 * @return the {@code Class<?>} for the name 1166 * 1167 * @throws ClassNotFoundException if class cannot be resolved 1168 */ 1169 public Class<?> findClass(final String fullName) throws ClassNotFoundException { 1170 if (fullName.indexOf('[') != -1 || fullName.indexOf('/') != -1) { 1171 // don't allow array class names or internal names. 1172 throw new ClassNotFoundException(fullName); 1173 } 1174 1175 // give chance to ClassFilter to filter out, if present 1176 if (classFilter != null && !classFilter.exposeToScripts(fullName)) { 1177 throw new ClassNotFoundException(fullName); 1178 } 1179 1180 // check package access as soon as possible! 1181 final SecurityManager sm = System.getSecurityManager(); 1182 if (sm != null) { 1183 checkPackageAccess(sm, fullName); 1184 } 1185 1186 // Try finding using the "app" loader. 1187 if (appLoader != null) { 1188 return Class.forName(fullName, true, appLoader); 1189 } else { 1190 final Class<?> cl = Class.forName(fullName); 1191 // return the Class only if it was loaded by boot loader 1192 if (cl.getClassLoader() == null) { 1193 return cl; 1194 } else { 1195 throw new ClassNotFoundException(fullName); 1196 } 1197 } 1198 } 1199 1200 /** 1201 * Hook to print stack trace for a {@link Throwable} that occurred during 1202 * execution 1203 * 1204 * @param t throwable for which to dump stack 1205 */ 1206 public static void printStackTrace(final Throwable t) { 1207 if (Context.DEBUG) { 1208 t.printStackTrace(Context.getCurrentErr()); 1209 } 1210 } 1211 1212 /** 1213 * Verify generated bytecode before emission. This is called back from the 1214 * {@link ObjectClassGenerator} or the {@link Compiler}. If the "--verify-code" parameter 1215 * hasn't been given, this is a nop 1216 * 1217 * Note that verification may load classes -- we don't want to do that unless 1218 * user specified verify option. We check it here even though caller 1219 * may have already checked that flag 1220 * 1221 * @param bytecode bytecode to verify 1222 */ 1223 public void verify(final byte[] bytecode) { 1224 if (env._verify_code) { 1225 // No verification when security manager is around as verifier 1226 // may load further classes - which should be avoided. 1227 if (System.getSecurityManager() == null) { 1228 CheckClassAdapter.verify(new ClassReader(bytecode), theStructLoader, false, new PrintWriter(System.err, true)); 1229 } 1230 } 1231 } 1232 1233 /** 1234 * Create and initialize a new global scope object. 1235 * 1236 * @return the initialized global scope object. 1237 */ 1238 public Global createGlobal() { 1239 return initGlobal(newGlobal()); 1240 } 1241 1242 /** 1243 * Create a new uninitialized global scope object 1244 * @return the global script object 1245 */ 1246 public Global newGlobal() { 1247 createOrInvalidateGlobalConstants(); 1248 return new Global(this); 1249 } 1250 1251 private void createOrInvalidateGlobalConstants() { 1252 for (;;) { 1253 final GlobalConstants currentGlobalConstants = getGlobalConstants(); 1254 if (currentGlobalConstants != null) { 1255 // Subsequent invocation; we're creating our second or later Global. GlobalConstants is not safe to use 1256 // with more than one Global, as the constant method handle linkages it creates create a coupling 1257 // between the Global and the call sites in the compiled code. 1258 currentGlobalConstants.invalidateForever(); 1259 return; 1260 } 1261 final GlobalConstants newGlobalConstants = new GlobalConstants(getLogger(GlobalConstants.class)); 1262 if (globalConstantsRef.compareAndSet(null, newGlobalConstants)) { 1263 // First invocation; we're creating the first Global in this Context. Create the GlobalConstants object 1264 // for this Context. 1265 return; 1266 } 1267 1268 // If we reach here, then we started out as the first invocation, but another concurrent invocation won the 1269 // CAS race. We'll just let the loop repeat and invalidate the CAS race winner. 1270 } 1271 } 1272 1273 /** 1274 * Initialize given global scope object. 1275 * 1276 * @param global the global 1277 * @param engine the associated ScriptEngine instance, can be null 1278 * @return the initialized global scope object. 1279 */ 1280 public Global initGlobal(final Global global, final ScriptEngine engine) { 1281 // Need only minimal global object, if we are just compiling. 1282 if (!env._compile_only) { 1283 final Global oldGlobal = Context.getGlobal(); 1284 try { 1285 Context.setGlobal(global); 1286 // initialize global scope with builtin global objects 1287 global.initBuiltinObjects(engine); 1288 } finally { 1289 Context.setGlobal(oldGlobal); 1290 } 1291 } 1292 1293 return global; 1294 } 1295 1296 /** 1297 * Initialize given global scope object. 1298 * 1299 * @param global the global 1300 * @return the initialized global scope object. 1301 */ 1302 public Global initGlobal(final Global global) { 1303 return initGlobal(global, null); 1304 } 1305 1306 /** 1307 * Return the current global's context 1308 * @return current global's context 1309 */ 1310 static Context getContextTrusted() { 1311 return getContext(getGlobal()); 1312 } 1313 1314 /** 1315 * Gets the Nashorn dynamic linker for the specified class. If the class is 1316 * a script class, the dynamic linker associated with its context is 1317 * returned. Otherwise the dynamic linker associated with the current 1318 * context is returned. 1319 * @param clazz the class for which we want to retrieve a dynamic linker. 1320 * @return the Nashorn dynamic linker for the specified class. 1321 */ 1322 public static DynamicLinker getDynamicLinker(final Class<?> clazz) { 1323 return fromClass(clazz).dynamicLinker; 1324 } 1325 1326 /** 1327 * Gets the Nashorn dynamic linker associated with the current context. 1328 * @return the Nashorn dynamic linker for the current context. 1329 */ 1330 public static DynamicLinker getDynamicLinker() { 1331 return getContextTrusted().dynamicLinker; 1332 } 1333 1334 /** 1335 * Creates a module layer with one module that is defined to the given class 1336 * loader. 1337 * 1338 * @param descriptor the module descriptor for the newly created module 1339 * @param loader the class loader of the module 1340 * @return the new Module 1341 */ 1342 static Module createModuleTrusted(final ModuleDescriptor descriptor, final ClassLoader loader) { 1343 return createModuleTrusted(Layer.boot(), descriptor, loader); 1344 } 1345 1346 /** 1347 * Creates a module layer with one module that is defined to the given class 1348 * loader. 1349 * 1350 * @param parent the parent layer of the new module 1351 * @param descriptor the module descriptor for the newly created module 1352 * @param loader the class loader of the module 1353 * @return the new Module 1354 */ 1355 static Module createModuleTrusted(final Layer parent, final ModuleDescriptor descriptor, final ClassLoader loader) { 1356 final String mn = descriptor.name(); 1357 1358 final ModuleReference mref = new ModuleReference(descriptor, null, () -> { 1359 IOException ioe = new IOException("<dynamic module>"); 1360 throw new UncheckedIOException(ioe); 1361 }); 1362 1363 final ModuleFinder finder = new ModuleFinder() { 1364 @Override 1365 public Optional<ModuleReference> find(final String name) { 1366 if (name.equals(mn)) { 1367 return Optional.of(mref); 1368 } else { 1369 return Optional.empty(); 1370 } 1371 } 1372 @Override 1373 public Set<ModuleReference> findAll() { 1374 return Set.of(mref); 1375 } 1376 }; 1377 1378 final Configuration cf = parent.configuration() 1379 .resolveRequires(finder, ModuleFinder.of(), Set.of(mn)); 1380 1381 final PrivilegedAction<Layer> pa = () -> parent.defineModules(cf, name -> loader); 1382 final Layer layer = AccessController.doPrivileged(pa, GET_LOADER_ACC_CTXT); 1383 1384 final Module m = layer.findModule(mn).get(); 1385 assert m.getLayer() == layer; 1386 1387 return m; 1388 } 1389 1390 static Context getContextTrustedOrNull() { 1391 final Global global = Context.getGlobal(); 1392 return global == null ? null : getContext(global); 1393 } 1394 1395 private static Context getContext(final Global global) { 1396 // We can't invoke Global.getContext() directly, as it's a protected override, and Global isn't in our package. 1397 // In order to access the method, we must cast it to ScriptObject first (which is in our package) and then let 1398 // virtual invocation do its thing. 1399 return ((ScriptObject)global).getContext(); 1400 } 1401 1402 /** 1403 * Try to infer Context instance from the Class. If we cannot, 1404 * then get it from the thread local variable. 1405 * 1406 * @param clazz the class 1407 * @return context 1408 */ 1409 static Context fromClass(final Class<?> clazz) { 1410 ClassLoader loader = null; 1411 try { 1412 loader = clazz.getClassLoader(); 1413 } catch (final SecurityException ignored) { 1414 // This could fail because of anonymous classes being used. 1415 // Accessing loader of anonymous class fails (for extension 1416 // loader class too?). In any case, for us fetching Context 1417 // from class loader is just an optimization. We can always 1418 // get Context from thread local storage (below). 1419 } 1420 1421 if (loader instanceof ScriptLoader) { 1422 return ((ScriptLoader)loader).getContext(); 1423 } 1424 1425 return Context.getContextTrusted(); 1426 } 1427 1428 private URL getResourceURL(final String resName) { 1429 if (appLoader != null) { 1430 return appLoader.getResource(resName); 1431 } 1432 return ClassLoader.getSystemResource(resName); 1433 } 1434 1435 private Object evaluateSource(final Source source, final ScriptObject scope, final ScriptObject thiz) { 1436 ScriptFunction script = null; 1437 1438 try { 1439 script = compileScript(source, scope, new Context.ThrowErrorManager()); 1440 } catch (final ParserException e) { 1441 e.throwAsEcmaException(); 1442 } 1443 1444 return ScriptRuntime.apply(script, thiz); 1445 } 1446 1447 private static ScriptFunction getProgramFunction(final Class<?> script, final ScriptObject scope) { 1448 if (script == null) { 1449 return null; 1450 } 1451 return invokeCreateProgramFunctionHandle(getCreateProgramFunctionHandle(script), scope); 1452 } 1453 1454 private static MethodHandle getCreateProgramFunctionHandle(final Class<?> script) { 1455 try { 1456 return LOOKUP.findStatic(script, CREATE_PROGRAM_FUNCTION.symbolName(), CREATE_PROGRAM_FUNCTION_TYPE); 1457 } catch (NoSuchMethodException | IllegalAccessException e) { 1458 throw new AssertionError("Failed to retrieve a handle for the program function for " + script.getName(), e); 1459 } 1460 } 1461 1462 private static ScriptFunction invokeCreateProgramFunctionHandle(final MethodHandle createProgramFunctionHandle, final ScriptObject scope) { 1463 try { 1464 return (ScriptFunction)createProgramFunctionHandle.invokeExact(scope); 1465 } catch (final RuntimeException|Error e) { 1466 throw e; 1467 } catch (final Throwable t) { 1468 throw new AssertionError("Failed to create a program function", t); 1469 } 1470 } 1471 1472 private ScriptFunction compileScript(final Source source, final ScriptObject scope, final ErrorManager errMan) { 1473 return getProgramFunction(compile(source, errMan, this._strict, false), scope); 1474 } 1475 1476 private synchronized Class<?> compile(final Source source, final ErrorManager errMan, final boolean strict, final boolean isEval) { 1477 // start with no errors, no warnings. 1478 errMan.reset(); 1479 1480 Class<?> script = findCachedClass(source); 1481 if (script != null) { 1482 final DebugLogger log = getLogger(Compiler.class); 1483 if (log.isEnabled()) { 1484 log.fine(new RuntimeEvent<>(Level.INFO, source), "Code cache hit for ", source, " avoiding recompile."); 1485 } 1486 return script; 1487 } 1488 1489 StoredScript storedScript = null; 1490 FunctionNode functionNode = null; 1491 // Don't use code store if optimistic types is enabled but lazy compilation is not. 1492 // This would store a full script compilation with many wrong optimistic assumptions that would 1493 // do more harm than good on later runs with both optimistic types and lazy compilation enabled. 1494 final boolean useCodeStore = codeStore != null && !env._parse_only && (!env._optimistic_types || env._lazy_compilation); 1495 final String cacheKey = useCodeStore ? CodeStore.getCacheKey("script", null) : null; 1496 1497 if (useCodeStore) { 1498 storedScript = codeStore.load(source, cacheKey); 1499 } 1500 1501 if (storedScript == null) { 1502 if (env._dest_dir != null) { 1503 source.dump(env._dest_dir); 1504 } 1505 1506 functionNode = new Parser(env, source, errMan, strict, getLogger(Parser.class)).parse(); 1507 1508 if (errMan.hasErrors()) { 1509 return null; 1510 } 1511 1512 if (env._print_ast || functionNode.getDebugFlag(FunctionNode.DEBUG_PRINT_AST)) { 1513 getErr().println(new ASTWriter(functionNode)); 1514 } 1515 1516 if (env._print_parse || functionNode.getDebugFlag(FunctionNode.DEBUG_PRINT_PARSE)) { 1517 getErr().println(new PrintVisitor(functionNode, true, false)); 1518 } 1519 } 1520 1521 if (env._parse_only) { 1522 return null; 1523 } 1524 1525 final URL url = source.getURL(); 1526 final CodeSource cs = new CodeSource(url, (CodeSigner[])null); 1527 final CodeInstaller installer; 1528 if (!env.useAnonymousClasses(source.getLength()) || env._persistent_cache || !env._lazy_compilation) { 1529 // Persistent code cache and eager compilation preclude use of VM anonymous classes 1530 final ScriptLoader loader = env._loader_per_compile ? createNewLoader() : scriptLoader; 1531 installer = new NamedContextCodeInstaller(this, cs, loader); 1532 } else { 1533 installer = new AnonymousContextCodeInstaller(this, cs, 1534 anonymousHostClasses.getOrCreate(cs, (key) -> 1535 createNewLoader().installClass( 1536 // NOTE: we're defining these constants in AnonymousContextCodeInstaller so they are not 1537 // initialized if we don't use AnonymousContextCodeInstaller. As this method is only ever 1538 // invoked from AnonymousContextCodeInstaller, this is okay. 1539 AnonymousContextCodeInstaller.ANONYMOUS_HOST_CLASS_NAME, 1540 AnonymousContextCodeInstaller.ANONYMOUS_HOST_CLASS_BYTES, cs))); 1541 } 1542 1543 if (storedScript == null) { 1544 final CompilationPhases phases = Compiler.CompilationPhases.COMPILE_ALL; 1545 1546 final Compiler compiler = Compiler.forInitialCompilation( 1547 installer, 1548 source, 1549 errMan, 1550 strict | functionNode.isStrict()); 1551 1552 final FunctionNode compiledFunction = compiler.compile(functionNode, phases); 1553 if (errMan.hasErrors()) { 1554 return null; 1555 } 1556 script = compiledFunction.getRootClass(); 1557 compiler.persistClassInfo(cacheKey, compiledFunction); 1558 } else { 1559 Compiler.updateCompilationId(storedScript.getCompilationId()); 1560 script = storedScript.installScript(source, installer); 1561 } 1562 1563 cacheClass(source, script); 1564 return script; 1565 } 1566 1567 private ScriptLoader createNewLoader() { 1568 return AccessController.doPrivileged( 1569 new PrivilegedAction<ScriptLoader>() { 1570 @Override 1571 public ScriptLoader run() { 1572 return new ScriptLoader(Context.this); 1573 } 1574 }, CREATE_LOADER_ACC_CTXT); 1575 } 1576 1577 private long getUniqueScriptId() { 1578 return uniqueScriptId.getAndIncrement(); 1579 } 1580 1581 /** 1582 * Cache for compiled script classes. 1583 */ 1584 @SuppressWarnings("serial") 1585 @Logger(name="classcache") 1586 private static class ClassCache extends LinkedHashMap<Source, ClassReference> implements Loggable { 1587 private final int size; 1588 private final ReferenceQueue<Class<?>> queue; 1589 private final DebugLogger log; 1590 1591 ClassCache(final Context context, final int size) { 1592 super(size, 0.75f, true); 1593 this.size = size; 1594 this.queue = new ReferenceQueue<>(); 1595 this.log = initLogger(context); 1596 } 1597 1598 void cache(final Source source, final Class<?> clazz) { 1599 if (log.isEnabled()) { 1600 log.info("Caching ", source, " in class cache"); 1601 } 1602 put(source, new ClassReference(clazz, queue, source)); 1603 } 1604 1605 @Override 1606 protected boolean removeEldestEntry(final Map.Entry<Source, ClassReference> eldest) { 1607 return size() > size; 1608 } 1609 1610 @Override 1611 public ClassReference get(final Object key) { 1612 for (ClassReference ref; (ref = (ClassReference)queue.poll()) != null; ) { 1613 final Source source = ref.source; 1614 if (log.isEnabled()) { 1615 log.info("Evicting ", source, " from class cache."); 1616 } 1617 remove(source); 1618 } 1619 1620 final ClassReference ref = super.get(key); 1621 if (ref != null && log.isEnabled()) { 1622 log.info("Retrieved class reference for ", ref.source, " from class cache"); 1623 } 1624 return ref; 1625 } 1626 1627 @Override 1628 public DebugLogger initLogger(final Context context) { 1629 return context.getLogger(getClass()); 1630 } 1631 1632 @Override 1633 public DebugLogger getLogger() { 1634 return log; 1635 } 1636 1637 } 1638 1639 private static class ClassReference extends SoftReference<Class<?>> { 1640 private final Source source; 1641 1642 ClassReference(final Class<?> clazz, final ReferenceQueue<Class<?>> queue, final Source source) { 1643 super(clazz, queue); 1644 this.source = source; 1645 } 1646 } 1647 1648 // Class cache management 1649 private Class<?> findCachedClass(final Source source) { 1650 final ClassReference ref = classCache == null ? null : classCache.get(source); 1651 return ref != null ? ref.get() : null; 1652 } 1653 1654 private void cacheClass(final Source source, final Class<?> clazz) { 1655 if (classCache != null) { 1656 classCache.cache(source, clazz); 1657 } 1658 } 1659 1660 // logging 1661 private final Map<String, DebugLogger> loggers = new HashMap<>(); 1662 1663 private void initLoggers() { 1664 ((Loggable)MethodHandleFactory.getFunctionality()).initLogger(this); 1665 } 1666 1667 /** 1668 * Get a logger, given a loggable class 1669 * @param clazz a Loggable class 1670 * @return debuglogger associated with that class 1671 */ 1672 public DebugLogger getLogger(final Class<? extends Loggable> clazz) { 1673 return getLogger(clazz, null); 1674 } 1675 1676 /** 1677 * Get a logger, given a loggable class 1678 * @param clazz a Loggable class 1679 * @param initHook an init hook - if this is the first time the logger is created in the context, run the init hook 1680 * @return debuglogger associated with that class 1681 */ 1682 public DebugLogger getLogger(final Class<? extends Loggable> clazz, final Consumer<DebugLogger> initHook) { 1683 final String name = getLoggerName(clazz); 1684 DebugLogger logger = loggers.get(name); 1685 if (logger == null) { 1686 if (!env.hasLogger(name)) { 1687 return DebugLogger.DISABLED_LOGGER; 1688 } 1689 final LoggerInfo info = env._loggers.get(name); 1690 logger = new DebugLogger(name, info.getLevel(), info.isQuiet()); 1691 if (initHook != null) { 1692 initHook.accept(logger); 1693 } 1694 loggers.put(name, logger); 1695 } 1696 return logger; 1697 } 1698 1699 /** 1700 * Given a Loggable class, weave debug info info a method handle for that logger. 1701 * Level.INFO is used 1702 * 1703 * @param clazz loggable 1704 * @param mh method handle 1705 * @param text debug printout to add 1706 * 1707 * @return instrumented method handle, or null if logger not enabled 1708 */ 1709 public MethodHandle addLoggingToHandle(final Class<? extends Loggable> clazz, final MethodHandle mh, final Supplier<String> text) { 1710 return addLoggingToHandle(clazz, Level.INFO, mh, Integer.MAX_VALUE, false, text); 1711 } 1712 1713 /** 1714 * Given a Loggable class, weave debug info info a method handle for that logger. 1715 * 1716 * @param clazz loggable 1717 * @param level log level 1718 * @param mh method handle 1719 * @param paramStart first parameter to print 1720 * @param printReturnValue should we print the return value? 1721 * @param text debug printout to add 1722 * 1723 * @return instrumented method handle, or null if logger not enabled 1724 */ 1725 public MethodHandle addLoggingToHandle(final Class<? extends Loggable> clazz, final Level level, final MethodHandle mh, final int paramStart, final boolean printReturnValue, final Supplier<String> text) { 1726 final DebugLogger log = getLogger(clazz); 1727 if (log.isEnabled()) { 1728 return MethodHandleFactory.addDebugPrintout(log, level, mh, paramStart, printReturnValue, text.get()); 1729 } 1730 return mh; 1731 } 1732 1733 private static String getLoggerName(final Class<?> clazz) { 1734 Class<?> current = clazz; 1735 while (current != null) { 1736 final Logger log = current.getAnnotation(Logger.class); 1737 if (log != null) { 1738 assert !"".equals(log.name()); 1739 return log.name(); 1740 } 1741 current = current.getSuperclass(); 1742 } 1743 assert false; 1744 return null; 1745 } 1746 1747 /** 1748 * This is a special kind of switchpoint used to guard builtin 1749 * properties and prototypes. In the future it might contain 1750 * logic to e.g. multiple switchpoint classes. 1751 */ 1752 public static final class BuiltinSwitchPoint extends SwitchPoint { 1753 //empty 1754 } 1755 1756 /** 1757 * Create a new builtin switchpoint and return it 1758 * @param name key name 1759 * @return new builtin switchpoint 1760 */ 1761 public SwitchPoint newBuiltinSwitchPoint(final String name) { 1762 assert builtinSwitchPoints.get(name) == null; 1763 final SwitchPoint sp = new BuiltinSwitchPoint(); 1764 builtinSwitchPoints.put(name, sp); 1765 return sp; 1766 } 1767 1768 /** 1769 * Return the builtin switchpoint for a particular key name 1770 * @param name key name 1771 * @return builtin switchpoint or null if none 1772 */ 1773 public SwitchPoint getBuiltinSwitchPoint(final String name) { 1774 return builtinSwitchPoints.get(name); 1775 } 1776 1777 private static ClassLoader createModuleLoader(final ClassLoader cl, 1778 final String modulePath, final String addModules) { 1779 if (addModules == null) { 1780 throw new IllegalArgumentException("--module-path specified with no --add-modules"); 1781 } 1782 1783 final Path[] paths = Stream.of(modulePath.split(File.pathSeparator)). 1784 map(s -> Paths.get(s)). 1785 toArray(sz -> new Path[sz]); 1786 final ModuleFinder mf = ModuleFinder.of(paths); 1787 final Set<ModuleReference> mrefs = mf.findAll(); 1788 if (mrefs.isEmpty()) { 1789 throw new RuntimeException("No modules in script --module-path: " + modulePath); 1790 } 1791 1792 final Set<String> rootMods; 1793 if (addModules.equals("ALL-MODULE-PATH")) { 1794 rootMods = mrefs.stream(). 1795 map(mr->mr.descriptor().name()). 1796 collect(Collectors.toSet()); 1797 } else { 1798 rootMods = Stream.of(addModules.split(",")). 1799 map(String::trim). 1800 collect(Collectors.toSet()); 1801 } 1802 1803 final Layer boot = Layer.boot(); 1804 final Configuration conf = boot.configuration(). 1805 resolveRequires(mf, ModuleFinder.of(), rootMods); 1806 final String firstMod = rootMods.iterator().next(); 1807 return boot.defineModulesWithOneLoader(conf, cl).findLoader(firstMod); 1808 } 1809} 1810