1 /* 2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package jdk.nashorn.internal.runtime; 27 28 import static jdk.internal.org.objectweb.asm.Opcodes.V1_7; 29 import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS; 30 import static jdk.nashorn.internal.codegen.CompilerConstants.CREATE_PROGRAM_FUNCTION; 31 import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE; 32 import static jdk.nashorn.internal.codegen.CompilerConstants.STRICT_MODE; 33 import static jdk.nashorn.internal.runtime.CodeStore.newCodeStore; 34 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 35 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; 36 import static jdk.nashorn.internal.runtime.Source.sourceFor; 37 38 import java.io.File; 39 import java.io.InputStream; 40 import java.io.IOException; 41 import java.io.PrintWriter; 42 import java.lang.invoke.MethodHandle; 43 import java.lang.invoke.MethodHandles; 44 import java.lang.invoke.MethodType; 45 import java.lang.invoke.SwitchPoint; 46 import java.lang.ref.ReferenceQueue; 47 import java.lang.ref.SoftReference; 48 import java.lang.module.Configuration; 49 import java.lang.module.ModuleDescriptor; 50 import java.lang.module.ModuleFinder; 51 import java.lang.module.ModuleReader; 52 import java.lang.module.ModuleReference; 53 import java.lang.reflect.Field; 54 import java.lang.reflect.Layer; 55 import java.lang.reflect.Modifier; 56 import java.lang.reflect.Module; 57 import java.net.MalformedURLException; 58 import java.net.URL; 59 import java.nio.file.Path; 60 import java.nio.file.Paths; 61 import java.security.AccessControlContext; 62 import java.security.AccessController; 63 import java.security.CodeSigner; 64 import java.security.CodeSource; 65 import java.security.Permissions; 66 import java.security.PrivilegedAction; 67 import java.security.PrivilegedActionException; 68 import java.security.PrivilegedExceptionAction; 69 import java.security.ProtectionDomain; 70 import java.util.Collection; 71 import java.util.HashMap; 72 import java.util.HashSet; 73 import java.util.LinkedHashMap; 74 import java.util.Map; 75 import java.util.Objects; 76 import java.util.Optional; 77 import java.util.Set; 78 import java.util.concurrent.ConcurrentHashMap; 79 import java.util.concurrent.ConcurrentMap; 80 import java.util.concurrent.atomic.AtomicLong; 81 import java.util.concurrent.atomic.AtomicReference; 82 import java.util.concurrent.atomic.LongAdder; 83 import java.util.function.Consumer; 84 import java.util.function.Supplier; 85 import java.util.logging.Level; 86 import java.util.stream.Collectors; 87 import java.util.stream.Stream; 88 import javax.script.ScriptEngine; 89 import jdk.dynalink.DynamicLinker; 90 import jdk.internal.org.objectweb.asm.ClassReader; 91 import jdk.internal.org.objectweb.asm.ClassWriter; 92 import jdk.internal.org.objectweb.asm.Opcodes; 93 import jdk.internal.org.objectweb.asm.util.CheckClassAdapter; 94 import jdk.nashorn.api.scripting.ClassFilter; 95 import jdk.nashorn.api.scripting.ScriptObjectMirror; 96 import jdk.nashorn.internal.WeakValueCache; 97 import jdk.nashorn.internal.codegen.Compiler; 98 import jdk.nashorn.internal.codegen.Compiler.CompilationPhases; 99 import jdk.nashorn.internal.codegen.ObjectClassGenerator; 100 import jdk.nashorn.internal.ir.FunctionNode; 101 import jdk.nashorn.internal.ir.debug.ASTWriter; 102 import jdk.nashorn.internal.ir.debug.PrintVisitor; 103 import jdk.nashorn.internal.lookup.MethodHandleFactory; 104 import jdk.nashorn.internal.objects.Global; 105 import jdk.nashorn.internal.parser.Parser; 106 import jdk.nashorn.internal.runtime.events.RuntimeEvent; 107 import jdk.nashorn.internal.runtime.linker.Bootstrap; 108 import jdk.nashorn.internal.runtime.logging.DebugLogger; 109 import jdk.nashorn.internal.runtime.logging.Loggable; 110 import jdk.nashorn.internal.runtime.logging.Logger; 111 import jdk.nashorn.internal.runtime.options.LoggingOption.LoggerInfo; 112 import jdk.nashorn.internal.runtime.options.Options; 113 import jdk.internal.misc.Unsafe; 114 115 /** 116 * This class manages the global state of execution. Context is immutable. 117 */ 118 public final class Context { 119 // nashorn specific security runtime access permission names 120 /** 121 * Permission needed to pass arbitrary nashorn command line options when creating Context. 122 */ 123 public static final String NASHORN_SET_CONFIG = "nashorn.setConfig"; 124 125 /** 126 * Permission needed to create Nashorn Context instance. 127 */ 128 public static final String NASHORN_CREATE_CONTEXT = "nashorn.createContext"; 129 130 /** 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 = 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 362 /** Is Context global debug mode enabled ? */ 363 public static final boolean DEBUG = Options.getBooleanProperty("nashorn.debug"); 364 365 private static final ThreadLocal<Global> currentGlobal = new ThreadLocal<>(); 366 367 // in-memory cache for loaded classes 368 private ClassCache classCache; 369 370 // persistent code store 371 private CodeStore codeStore; 372 373 // A factory for linking global properties as constant method handles. It is created when the first Global 374 // is created, and invalidated forever once the second global is created. 375 private final AtomicReference<GlobalConstants> globalConstantsRef = new AtomicReference<>(); 376 377 // Are java.sql, java.sql.rowset modules found in the system? 378 static final boolean javaSqlFound, javaSqlRowsetFound; 379 380 static { 381 final Layer boot = Layer.boot(); 382 javaSqlFound = boot.findModule("java.sql").isPresent(); 383 javaSqlRowsetFound = boot.findModule("java.sql.rowset").isPresent(); 384 } 385 386 /** 387 * Get the current global scope 388 * @return the current global scope 389 */ 390 public static Global getGlobal() { 391 // This class in a package.access protected package. 392 // Trusted code only can call this method. 393 return currentGlobal.get(); 394 } 395 396 /** 397 * Set the current global scope 398 * @param global the global scope 399 */ 400 public static void setGlobal(final ScriptObject global) { 401 if (global != null && !(global instanceof Global)) { 402 throw new IllegalArgumentException("not a global!"); 403 } 404 setGlobal((Global)global); 405 } 406 407 /** 408 * Set the current global scope 409 * @param global the global scope 410 */ 411 public static void setGlobal(final Global global) { 412 // This class in a package.access protected package. 413 // Trusted code only can call this method. 414 assert getGlobal() != global; 415 //same code can be cached between globals, then we need to invalidate method handle constants 416 if (global != null) { 417 final GlobalConstants globalConstants = getContext(global).getGlobalConstants(); 418 if (globalConstants != null) { 419 globalConstants.invalidateAll(); 420 } 421 } 422 currentGlobal.set(global); 423 } 424 425 /** 426 * Get context of the current global 427 * @return current global scope's context. 428 */ 429 public static Context getContext() { 430 final SecurityManager sm = System.getSecurityManager(); 431 if (sm != null) { 432 sm.checkPermission(new RuntimePermission(NASHORN_GET_CONTEXT)); 433 } 434 return getContextTrusted(); 435 } 436 437 /** 438 * Get current context's error writer 439 * 440 * @return error writer of the current context 441 */ 442 public static PrintWriter getCurrentErr() { 443 final ScriptObject global = getGlobal(); 444 return (global != null)? global.getContext().getErr() : new PrintWriter(System.err); 445 } 446 447 /** 448 * Output text to this Context's error stream 449 * @param str text to write 450 */ 451 public static void err(final String str) { 452 err(str, true); 453 } 454 455 /** 456 * Output text to this Context's error stream, optionally with 457 * a newline afterwards 458 * 459 * @param str text to write 460 * @param crlf write a carriage return/new line after text 461 */ 462 public static void err(final String str, final boolean crlf) { 463 final PrintWriter err = Context.getCurrentErr(); 464 if (err != null) { 465 if (crlf) { 466 err.println(str); 467 } else { 468 err.print(str); 469 } 470 } 471 } 472 473 /** Current environment. */ 474 private final ScriptEnvironment env; 475 476 /** is this context in strict mode? Cached from env. as this is used heavily. */ 477 final boolean _strict; 478 479 /** class loader to resolve classes from script. */ 480 private final ClassLoader appLoader; 481 482 /*package-private*/ 483 ClassLoader getAppLoader() { 484 return appLoader; 485 } 486 487 /** Class loader to load classes compiled from scripts. */ 488 private final ScriptLoader scriptLoader; 489 490 /** Dynamic linker for linking call sites in script code loaded by this context */ 491 private final DynamicLinker dynamicLinker; 492 493 /** Current error manager. */ 494 private final ErrorManager errors; 495 496 /** Unique id for script. Used only when --loader-per-compile=false */ 497 private final AtomicLong uniqueScriptId; 498 499 /** Optional class filter to use for Java classes. Can be null. */ 500 private final ClassFilter classFilter; 501 502 /** Process-wide singleton structure loader */ 503 private static final StructureLoader theStructLoader; 504 private static final ConcurrentMap<String, Class<?>> structureClasses = new ConcurrentHashMap<>(); 505 506 /*package-private*/ @SuppressWarnings("static-method") 507 StructureLoader getStructLoader() { 508 return theStructLoader; 509 } 510 511 private static AccessControlContext createNoPermAccCtxt() { 512 return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, new Permissions()) }); 513 } 514 515 private static AccessControlContext createPermAccCtxt(final String permName) { 516 final Permissions perms = new Permissions(); 517 perms.add(new RuntimePermission(permName)); 518 return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, perms) }); 519 } 520 521 private static final AccessControlContext NO_PERMISSIONS_ACC_CTXT = createNoPermAccCtxt(); 522 private static final AccessControlContext CREATE_LOADER_ACC_CTXT = createPermAccCtxt("createClassLoader"); 523 private static final AccessControlContext CREATE_GLOBAL_ACC_CTXT = createPermAccCtxt(NASHORN_CREATE_GLOBAL); 524 private static final AccessControlContext GET_LOADER_ACC_CTXT = createPermAccCtxt("getClassLoader"); 525 526 static { 527 final ClassLoader myLoader = Context.class.getClassLoader(); 528 theStructLoader = AccessController.doPrivileged(new PrivilegedAction<StructureLoader>() { 529 @Override 530 public StructureLoader run() { 531 return new StructureLoader(myLoader); 532 } 533 }, CREATE_LOADER_ACC_CTXT); 534 } 535 536 /** 537 * ThrowErrorManager that throws ParserException upon error conditions. 538 */ 539 public static class ThrowErrorManager extends ErrorManager { 540 @Override 541 public void error(final String message) { 542 throw new ParserException(message); 543 } 544 545 @Override 546 public void error(final ParserException e) { 547 throw e; 548 } 549 } 550 551 /** 552 * Constructor 553 * 554 * @param options options from command line or Context creator 555 * @param errors error manger 556 * @param appLoader application class loader 557 */ 558 public Context(final Options options, final ErrorManager errors, final ClassLoader appLoader) { 559 this(options, errors, appLoader, null); 560 } 561 562 /** 563 * Constructor 564 * 565 * @param options options from command line or Context creator 566 * @param errors error manger 567 * @param appLoader application class loader 568 * @param classFilter class filter to use 569 */ 570 public Context(final Options options, final ErrorManager errors, final ClassLoader appLoader, final ClassFilter classFilter) { 571 this(options, errors, new PrintWriter(System.out, true), new PrintWriter(System.err, true), appLoader, classFilter); 572 } 573 574 /** 575 * Constructor 576 * 577 * @param options options from command line or Context creator 578 * @param errors error manger 579 * @param out output writer for this Context 580 * @param err error writer for this Context 581 * @param appLoader application class loader 582 */ 583 public Context(final Options options, final ErrorManager errors, final PrintWriter out, final PrintWriter err, final ClassLoader appLoader) { 584 this(options, errors, out, err, appLoader, (ClassFilter)null); 585 } 586 587 /** 588 * Constructor 589 * 590 * @param options options from command line or Context creator 591 * @param errors error manger 592 * @param out output writer for this Context 593 * @param err error writer for this Context 594 * @param appLoader application class loader 595 * @param classFilter class filter to use 596 */ 597 public Context(final Options options, final ErrorManager errors, final PrintWriter out, final PrintWriter err, final ClassLoader appLoader, final ClassFilter classFilter) { 598 final SecurityManager sm = System.getSecurityManager(); 599 if (sm != null) { 600 sm.checkPermission(new RuntimePermission(NASHORN_CREATE_CONTEXT)); 601 } 602 603 this.classFilter = classFilter; 604 this.env = new ScriptEnvironment(options, out, err); 605 this._strict = env._strict; 606 if (env._loader_per_compile) { 607 this.scriptLoader = null; 608 this.uniqueScriptId = null; 609 } else { 610 this.scriptLoader = createNewLoader(); 611 this.uniqueScriptId = new AtomicLong(); 612 } 613 this.errors = errors; 614 615 // if user passed --module-path, we create a module class loader with 616 // passed appLoader as the parent. 617 final String modulePath = env._module_path; 618 ClassLoader appCl = null; 619 if (!env._compile_only && modulePath != null && !modulePath.isEmpty()) { 620 // make sure that caller can create a class loader. 621 if (sm != null) { 622 sm.checkCreateClassLoader(); 623 } 624 appCl = AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() { 625 @Override 626 public ClassLoader run() { 627 return createModuleLoader(appLoader, modulePath, env._add_modules); 628 } 629 }); 630 } else { 631 appCl = appLoader; 632 } 633 634 // if user passed -classpath option, make a URLClassLoader with that and 635 // the app loader or module app loader as the parent. 636 final String classPath = env._classpath; 637 if (!env._compile_only && classPath != null && !classPath.isEmpty()) { 638 // make sure that caller can create a class loader. 639 if (sm != null) { 640 sm.checkCreateClassLoader(); 641 } 642 appCl = NashornLoader.createClassLoader(classPath, appCl); 643 } 644 645 this.appLoader = appCl; 646 this.dynamicLinker = Bootstrap.createDynamicLinker(this.appLoader, env._unstable_relink_threshold); 647 648 final int cacheSize = env._class_cache_size; 649 if (cacheSize > 0) { 650 classCache = new ClassCache(this, cacheSize); 651 } 652 653 if (env._persistent_cache) { 654 codeStore = newCodeStore(this); 655 } 656 657 // print version info if asked. 658 if (env._version) { 659 getErr().println("nashorn " + Version.version()); 660 } 661 662 if (env._fullversion) { 663 getErr().println("nashorn full version " + Version.fullVersion()); 664 } 665 666 if (Options.getBooleanProperty("nashorn.fields.dual")) { 667 fieldMode = FieldMode.DUAL; 668 } else if (Options.getBooleanProperty("nashorn.fields.objects")) { 669 fieldMode = FieldMode.OBJECTS; 670 } else { 671 fieldMode = FieldMode.AUTO; 672 } 673 674 initLoggers(); 675 } 676 677 678 /** 679 * Get the class filter for this context 680 * @return class filter 681 */ 682 public ClassFilter getClassFilter() { 683 return classFilter; 684 } 685 686 /** 687 * Returns the factory for constant method handles for global properties. The returned factory can be 688 * invalidated if this Context has more than one Global. 689 * @return the factory for constant method handles for global properties. 690 */ 691 GlobalConstants getGlobalConstants() { 692 return globalConstantsRef.get(); 693 } 694 695 /** 696 * Get the error manager for this context 697 * @return error manger 698 */ 699 public ErrorManager getErrorManager() { 700 return errors; 701 } 702 703 /** 704 * Get the script environment for this context 705 * @return script environment 706 */ 707 public ScriptEnvironment getEnv() { 708 return env; 709 } 710 711 /** 712 * Get the output stream for this context 713 * @return output print writer 714 */ 715 public PrintWriter getOut() { 716 return env.getOut(); 717 } 718 719 /** 720 * Get the error stream for this context 721 * @return error print writer 722 */ 723 public PrintWriter getErr() { 724 return env.getErr(); 725 } 726 727 /** 728 * Should scripts compiled by this context use dual field representation? 729 * @return true if using dual fields, false for object-only fields 730 */ 731 public boolean useDualFields() { 732 return fieldMode == FieldMode.DUAL || (fieldMode == FieldMode.AUTO && env._optimistic_types); 733 } 734 735 /** 736 * Get the PropertyMap of the current global scope 737 * @return the property map of the current global scope 738 */ 739 public static PropertyMap getGlobalMap() { 740 return Context.getGlobal().getMap(); 741 } 742 743 /** 744 * Compile a top level script. 745 * 746 * @param source the source 747 * @param scope the scope 748 * 749 * @return top level function for script 750 */ 751 public ScriptFunction compileScript(final Source source, final ScriptObject scope) { 752 return compileScript(source, scope, this.errors); 753 } 754 755 /** 756 * Interface to represent compiled code that can be re-used across many 757 * global scope instances 758 */ 759 public static interface MultiGlobalCompiledScript { 760 /** 761 * Obtain script function object for a specific global scope object. 762 * 763 * @param newGlobal global scope for which function object is obtained 764 * @return script function for script level expressions 765 */ 766 public ScriptFunction getFunction(final Global newGlobal); 767 } 768 769 /** 770 * Compile a top level script. 771 * 772 * @param source the script source 773 * @return reusable compiled script across many global scopes. 774 */ 775 public MultiGlobalCompiledScript compileScript(final Source source) { 776 final Class<?> clazz = compile(source, this.errors, this._strict, false); 777 final MethodHandle createProgramFunctionHandle = getCreateProgramFunctionHandle(clazz); 778 779 return new MultiGlobalCompiledScript() { 780 @Override 781 public ScriptFunction getFunction(final Global newGlobal) { 782 return invokeCreateProgramFunctionHandle(createProgramFunctionHandle, newGlobal); 783 } 784 }; 785 } 786 787 /** 788 * Entry point for {@code eval} 789 * 790 * @param initialScope The scope of this eval call 791 * @param string Evaluated code as a String 792 * @param callThis "this" to be passed to the evaluated code 793 * @param location location of the eval call 794 * @return the return value of the {@code eval} 795 */ 796 public Object eval(final ScriptObject initialScope, final String string, 797 final Object callThis, final Object location) { 798 return eval(initialScope, string, callThis, location, false, false); 799 } 800 801 /** 802 * Entry point for {@code eval} 803 * 804 * @param initialScope The scope of this eval call 805 * @param string Evaluated code as a String 806 * @param callThis "this" to be passed to the evaluated code 807 * @param location location of the eval call 808 * @param strict is this {@code eval} call from a strict mode code? 809 * @param evalCall is this called from "eval" builtin? 810 * 811 * @return the return value of the {@code eval} 812 */ 813 public Object eval(final ScriptObject initialScope, final String string, 814 final Object callThis, final Object location, final boolean strict, final boolean evalCall) { 815 final String file = location == UNDEFINED || location == null ? "<eval>" : location.toString(); 816 final Source source = sourceFor(file, string, evalCall); 817 // is this direct 'eval' builtin call? 818 final boolean directEval = evalCall && (location != UNDEFINED); 819 final Global global = Context.getGlobal(); 820 ScriptObject scope = initialScope; 821 822 // ECMA section 10.1.1 point 2 says eval code is strict if it begins 823 // with "use strict" directive or eval direct call itself is made 824 // from from strict mode code. We are passed with caller's strict mode. 825 // Nashorn extension: any 'eval' is unconditionally strict when -strict is specified. 826 boolean strictFlag = strict || this._strict; 827 828 Class<?> clazz; 829 try { 830 clazz = compile(source, new ThrowErrorManager(), strictFlag, true); 831 } catch (final ParserException e) { 832 e.throwAsEcmaException(global); 833 return null; 834 } 835 836 if (!strictFlag) { 837 // We need to get strict mode flag from compiled class. This is 838 // because eval code may start with "use strict" directive. 839 try { 840 strictFlag = clazz.getField(STRICT_MODE.symbolName()).getBoolean(null); 841 } catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { 842 //ignored 843 strictFlag = false; 844 } 845 } 846 847 // In strict mode, eval does not instantiate variables and functions 848 // in the caller's environment. A new environment is created! 849 if (strictFlag) { 850 // Create a new scope object with given scope as its prototype 851 scope = newScope(scope); 852 } 853 854 final ScriptFunction func = getProgramFunction(clazz, scope); 855 Object evalThis; 856 if (directEval) { 857 evalThis = (callThis != UNDEFINED && callThis != null) || strictFlag ? callThis : global; 858 } else { 859 // either indirect evalCall or non-eval (Function, engine.eval, ScriptObjectMirror.eval..) 860 evalThis = callThis; 861 } 862 863 return ScriptRuntime.apply(func, evalThis); 864 } 865 866 private static ScriptObject newScope(final ScriptObject callerScope) { 867 return new Scope(callerScope, PropertyMap.newMap(Scope.class)); 868 } 869 870 private static Source loadInternal(final String srcStr, final String prefix, final String resourcePath) { 871 if (srcStr.startsWith(prefix)) { 872 final String resource = resourcePath + srcStr.substring(prefix.length()); 873 // NOTE: even sandbox scripts should be able to load scripts in nashorn: scheme 874 // These scripts are always available and are loaded from nashorn.jar's resources. 875 return AccessController.doPrivileged( 876 new PrivilegedAction<Source>() { 877 @Override 878 public Source run() { 879 try { 880 final InputStream resStream = Context.class.getResourceAsStream(resource); 881 return resStream != null ? sourceFor(srcStr, Source.readFully(resStream)) : null; 882 } catch (final IOException exp) { 883 return null; 884 } 885 } 886 }); 887 } 888 889 return null; 890 } 891 892 /** 893 * Implementation of {@code load} Nashorn extension. Load a script file from a source 894 * expression 895 * 896 * @param scope the scope 897 * @param from source expression for script 898 * 899 * @return return value for load call (undefined) 900 * 901 * @throws IOException if source cannot be found or loaded 902 */ 903 public Object load(final Object scope, final Object from) throws IOException { 904 final Object src = from instanceof ConsString ? from.toString() : from; 905 Source source = null; 906 907 // load accepts a String (which could be a URL or a file name), a File, a URL 908 // or a ScriptObject that has "name" and "source" (string valued) properties. 909 if (src instanceof String) { 910 final String srcStr = (String)src; 911 if (srcStr.startsWith(LOAD_CLASSPATH)) { 912 final URL url = getResourceURL(srcStr.substring(LOAD_CLASSPATH.length())); 913 source = url != null ? sourceFor(url.toString(), url) : null; 914 } else { 915 final File file = new File(srcStr); 916 if (srcStr.indexOf(':') != -1) { 917 if ((source = loadInternal(srcStr, LOAD_NASHORN, "resources/")) == null && 918 (source = loadInternal(srcStr, LOAD_FX, "resources/fx/")) == null) { 919 URL url; 920 try { 921 //check for malformed url. if malformed, it may still be a valid file 922 url = new URL(srcStr); 923 } catch (final MalformedURLException e) { 924 url = file.toURI().toURL(); 925 } 926 source = sourceFor(url.toString(), url); 927 } 928 } else if (file.isFile()) { 929 source = sourceFor(srcStr, file); 930 } 931 } 932 } else if (src instanceof File && ((File)src).isFile()) { 933 final File file = (File)src; 934 source = sourceFor(file.getName(), file); 935 } else if (src instanceof URL) { 936 final URL url = (URL)src; 937 source = sourceFor(url.toString(), url); 938 } else if (src instanceof ScriptObject) { 939 final ScriptObject sobj = (ScriptObject)src; 940 if (sobj.has("script") && sobj.has("name")) { 941 final String script = JSType.toString(sobj.get("script")); 942 final String name = JSType.toString(sobj.get("name")); 943 source = sourceFor(name, script); 944 } 945 } else if (src instanceof Map) { 946 final Map<?,?> map = (Map<?,?>)src; 947 if (map.containsKey("script") && map.containsKey("name")) { 948 final String script = JSType.toString(map.get("script")); 949 final String name = JSType.toString(map.get("name")); 950 source = sourceFor(name, script); 951 } 952 } 953 954 if (source != null) { 955 if (scope instanceof ScriptObject && ((ScriptObject)scope).isScope()) { 956 final ScriptObject sobj = (ScriptObject)scope; 957 // passed object is a script object 958 // Global is the only user accessible scope ScriptObject 959 assert sobj.isGlobal() : "non-Global scope object!!"; 960 return evaluateSource(source, sobj, sobj); 961 } else if (scope == null || scope == UNDEFINED) { 962 // undefined or null scope. Use current global instance. 963 final Global global = getGlobal(); 964 return evaluateSource(source, global, global); 965 } else { 966 /* 967 * Arbitrary object passed for scope. 968 * Indirect load that is equivalent to: 969 * 970 * (function(scope, source) { 971 * with (scope) { 972 * eval(<script_from_source>); 973 * } 974 * })(scope, source); 975 */ 976 final Global global = getGlobal(); 977 // Create a new object. This is where all declarations 978 // (var, function) from the evaluated code go. 979 // make global to be its __proto__ so that global 980 // definitions are accessible to the evaluated code. 981 final ScriptObject evalScope = newScope(global); 982 983 // finally, make a WithObject around user supplied scope object 984 // so that it's properties are accessible as variables. 985 final ScriptObject withObj = ScriptRuntime.openWith(evalScope, scope); 986 987 // evaluate given source with 'withObj' as scope 988 // but use global object as "this". 989 return evaluateSource(source, withObj, global); 990 } 991 } 992 993 throw typeError("cant.load.script", ScriptRuntime.safeToString(from)); 994 } 995 996 /** 997 * Implementation of {@code loadWithNewGlobal} Nashorn extension. Load a script file from a source 998 * expression, after creating a new global scope. 999 * 1000 * @param from source expression for script 1001 * @param args (optional) arguments to be passed to the loaded script 1002 * 1003 * @return return value for load call (undefined) 1004 * 1005 * @throws IOException if source cannot be found or loaded 1006 */ 1007 public Object loadWithNewGlobal(final Object from, final Object...args) throws IOException { 1008 final Global oldGlobal = getGlobal(); 1009 final Global newGlobal = AccessController.doPrivileged(new PrivilegedAction<Global>() { 1010 @Override 1011 public Global run() { 1012 try { 1013 return newGlobal(); 1014 } catch (final RuntimeException e) { 1015 if (Context.DEBUG) { 1016 e.printStackTrace(); 1017 } 1018 throw e; 1019 } 1020 } 1021 }, CREATE_GLOBAL_ACC_CTXT); 1022 // initialize newly created Global instance 1023 initGlobal(newGlobal); 1024 setGlobal(newGlobal); 1025 1026 final Object[] wrapped = args == null? ScriptRuntime.EMPTY_ARRAY : ScriptObjectMirror.wrapArray(args, oldGlobal); 1027 newGlobal.put("arguments", newGlobal.wrapAsObject(wrapped), env._strict); 1028 1029 try { 1030 // wrap objects from newGlobal's world as mirrors - but if result 1031 // is from oldGlobal's world, unwrap it! 1032 return ScriptObjectMirror.unwrap(ScriptObjectMirror.wrap(load(newGlobal, from), newGlobal), oldGlobal); 1033 } finally { 1034 setGlobal(oldGlobal); 1035 } 1036 } 1037 1038 /** 1039 * Load or get a structure class. Structure class names are based on the number of parameter fields 1040 * and {@link AccessorProperty} fields in them. Structure classes are used to represent ScriptObjects 1041 * 1042 * @see ObjectClassGenerator 1043 * @see AccessorProperty 1044 * @see ScriptObject 1045 * 1046 * @param fullName full name of class, e.g. jdk.nashorn.internal.objects.JO2P1 contains 2 fields and 1 parameter. 1047 * 1048 * @return the {@code Class<?>} for this structure 1049 * 1050 * @throws ClassNotFoundException if structure class cannot be resolved 1051 */ 1052 @SuppressWarnings("unchecked") 1053 public static Class<? extends ScriptObject> forStructureClass(final String fullName) throws ClassNotFoundException { 1054 if (System.getSecurityManager() != null && !StructureLoader.isStructureClass(fullName)) { 1055 throw new ClassNotFoundException(fullName); 1056 } 1057 return (Class<? extends ScriptObject>)structureClasses.computeIfAbsent(fullName, (name) -> { 1058 try { 1059 return Class.forName(name, true, theStructLoader); 1060 } catch (final ClassNotFoundException e) { 1061 throw new AssertionError(e); 1062 } 1063 }); 1064 } 1065 1066 /** 1067 * Is {@code className} the name of a structure class? 1068 * 1069 * @param className a class name 1070 * @return true if className is a structure class name 1071 */ 1072 public static boolean isStructureClass(final String className) { 1073 return StructureLoader.isStructureClass(className); 1074 } 1075 1076 /** 1077 * Checks that the given Class can be accessed from no permissions context. 1078 * 1079 * @param clazz Class object 1080 * @throws SecurityException if not accessible 1081 */ 1082 public static void checkPackageAccess(final Class<?> clazz) { 1083 final SecurityManager sm = System.getSecurityManager(); 1084 if (sm != null) { 1085 Class<?> bottomClazz = clazz; 1086 while (bottomClazz.isArray()) { 1087 bottomClazz = bottomClazz.getComponentType(); 1088 } 1089 checkPackageAccess(sm, bottomClazz.getName()); 1090 } 1091 } 1092 1093 /** 1094 * Checks that the given package name can be accessed from no permissions context. 1095 * 1096 * @param pkgName package name 1097 * @throws SecurityException if not accessible 1098 */ 1099 public static void checkPackageAccess(final String pkgName) { 1100 final SecurityManager sm = System.getSecurityManager(); 1101 if (sm != null) { 1102 checkPackageAccess(sm, pkgName.endsWith(".") ? pkgName : pkgName + "."); 1103 } 1104 } 1105 1106 /** 1107 * Checks that the given package can be accessed from no permissions context. 1108 * 1109 * @param sm current security manager instance 1110 * @param fullName fully qualified package name 1111 * @throw SecurityException if not accessible 1112 */ 1113 private static void checkPackageAccess(final SecurityManager sm, final String fullName) { 1114 Objects.requireNonNull(sm); 1115 final int index = fullName.lastIndexOf('.'); 1116 if (index != -1) { 1117 final String pkgName = fullName.substring(0, index); 1118 AccessController.doPrivileged(new PrivilegedAction<Void>() { 1119 @Override 1120 public Void run() { 1121 sm.checkPackageAccess(pkgName); 1122 return null; 1123 } 1124 }, NO_PERMISSIONS_ACC_CTXT); 1125 } 1126 } 1127 1128 /** 1129 * Checks that the given Class can be accessed from no permissions context. 1130 * 1131 * @param clazz Class object 1132 * @return true if package is accessible, false otherwise 1133 */ 1134 private static boolean isAccessiblePackage(final Class<?> clazz) { 1135 try { 1136 checkPackageAccess(clazz); 1137 return true; 1138 } catch (final SecurityException se) { 1139 return false; 1140 } 1141 } 1142 1143 /** 1144 * Checks that the given Class is public and it can be accessed from no permissions context. 1145 * 1146 * @param clazz Class object to check 1147 * @return true if Class is accessible, false otherwise 1148 */ 1149 public static boolean isAccessibleClass(final Class<?> clazz) { 1150 return Modifier.isPublic(clazz.getModifiers()) && Context.isAccessiblePackage(clazz); 1151 } 1152 1153 /** 1154 * Lookup a Java class. This is used for JSR-223 stuff linking in from 1155 * {@code jdk.nashorn.internal.objects.NativeJava} and {@code jdk.nashorn.internal.runtime.NativeJavaPackage} 1156 * 1157 * @param fullName full name of class to load 1158 * 1159 * @return the {@code Class<?>} for the name 1160 * 1161 * @throws ClassNotFoundException if class cannot be resolved 1162 */ 1163 public Class<?> findClass(final String fullName) throws ClassNotFoundException { 1164 if (fullName.indexOf('[') != -1 || fullName.indexOf('/') != -1) { 1165 // don't allow array class names or internal names. 1166 throw new ClassNotFoundException(fullName); 1167 } 1168 1169 // give chance to ClassFilter to filter out, if present 1170 if (classFilter != null && !classFilter.exposeToScripts(fullName)) { 1171 throw new ClassNotFoundException(fullName); 1172 } 1173 1174 // check package access as soon as possible! 1175 final SecurityManager sm = System.getSecurityManager(); 1176 if (sm != null) { 1177 checkPackageAccess(sm, fullName); 1178 } 1179 1180 // Try finding using the "app" loader. 1181 if (appLoader != null) { 1182 return Class.forName(fullName, true, appLoader); 1183 } else { 1184 final Class<?> cl = Class.forName(fullName); 1185 // return the Class only if it was loaded by boot loader 1186 if (cl.getClassLoader() == null) { 1187 return cl; 1188 } else { 1189 throw new ClassNotFoundException(fullName); 1190 } 1191 } 1192 } 1193 1194 /** 1195 * Hook to print stack trace for a {@link Throwable} that occurred during 1196 * execution 1197 * 1198 * @param t throwable for which to dump stack 1199 */ 1200 public static void printStackTrace(final Throwable t) { 1201 if (Context.DEBUG) { 1202 t.printStackTrace(Context.getCurrentErr()); 1203 } 1204 } 1205 1206 /** 1207 * Verify generated bytecode before emission. This is called back from the 1208 * {@link ObjectClassGenerator} or the {@link Compiler}. If the "--verify-code" parameter 1209 * hasn't been given, this is a nop 1210 * 1211 * Note that verification may load classes -- we don't want to do that unless 1212 * user specified verify option. We check it here even though caller 1213 * may have already checked that flag 1214 * 1215 * @param bytecode bytecode to verify 1216 */ 1217 public void verify(final byte[] bytecode) { 1218 if (env._verify_code) { 1219 // No verification when security manager is around as verifier 1220 // may load further classes - which should be avoided. 1221 if (System.getSecurityManager() == null) { 1222 CheckClassAdapter.verify(new ClassReader(bytecode), theStructLoader, false, new PrintWriter(System.err, true)); 1223 } 1224 } 1225 } 1226 1227 /** 1228 * Create and initialize a new global scope object. 1229 * 1230 * @return the initialized global scope object. 1231 */ 1232 public Global createGlobal() { 1233 return initGlobal(newGlobal()); 1234 } 1235 1236 /** 1237 * Create a new uninitialized global scope object 1238 * @return the global script object 1239 */ 1240 public Global newGlobal() { 1241 createOrInvalidateGlobalConstants(); 1242 return new Global(this); 1243 } 1244 1245 private void createOrInvalidateGlobalConstants() { 1246 for (;;) { 1247 final GlobalConstants currentGlobalConstants = getGlobalConstants(); 1248 if (currentGlobalConstants != null) { 1249 // Subsequent invocation; we're creating our second or later Global. GlobalConstants is not safe to use 1250 // with more than one Global, as the constant method handle linkages it creates create a coupling 1251 // between the Global and the call sites in the compiled code. 1252 currentGlobalConstants.invalidateForever(); 1253 return; 1254 } 1255 final GlobalConstants newGlobalConstants = new GlobalConstants(getLogger(GlobalConstants.class)); 1256 if (globalConstantsRef.compareAndSet(null, newGlobalConstants)) { 1257 // First invocation; we're creating the first Global in this Context. Create the GlobalConstants object 1258 // for this Context. 1259 return; 1260 } 1261 1262 // If we reach here, then we started out as the first invocation, but another concurrent invocation won the 1263 // CAS race. We'll just let the loop repeat and invalidate the CAS race winner. 1264 } 1265 } 1266 1267 /** 1268 * Initialize given global scope object. 1269 * 1270 * @param global the global 1271 * @param engine the associated ScriptEngine instance, can be null 1272 * @return the initialized global scope object. 1273 */ 1274 public Global initGlobal(final Global global, final ScriptEngine engine) { 1275 // Need only minimal global object, if we are just compiling. 1276 if (!env._compile_only) { 1277 final Global oldGlobal = Context.getGlobal(); 1278 try { 1279 Context.setGlobal(global); 1280 // initialize global scope with builtin global objects 1281 global.initBuiltinObjects(engine); 1282 } finally { 1283 Context.setGlobal(oldGlobal); 1284 } 1285 } 1286 1287 return global; 1288 } 1289 1290 /** 1291 * Initialize given global scope object. 1292 * 1293 * @param global the global 1294 * @return the initialized global scope object. 1295 */ 1296 public Global initGlobal(final Global global) { 1297 return initGlobal(global, null); 1298 } 1299 1300 /** 1301 * Return the current global's context 1302 * @return current global's context 1303 */ 1304 static Context getContextTrusted() { 1305 return getContext(getGlobal()); 1306 } 1307 1308 /** 1309 * Gets the Nashorn dynamic linker for the specified class. If the class is 1310 * a script class, the dynamic linker associated with its context is 1311 * returned. Otherwise the dynamic linker associated with the current 1312 * context is returned. 1313 * @param clazz the class for which we want to retrieve a dynamic linker. 1314 * @return the Nashorn dynamic linker for the specified class. 1315 */ 1316 public static DynamicLinker getDynamicLinker(final Class<?> clazz) { 1317 return fromClass(clazz).dynamicLinker; 1318 } 1319 1320 /** 1321 * Gets the Nashorn dynamic linker associated with the current context. 1322 * @return the Nashorn dynamic linker for the current context. 1323 */ 1324 public static DynamicLinker getDynamicLinker() { 1325 return getContextTrusted().dynamicLinker; 1326 } 1327 1328 /** 1329 * Creates a module layer with one module that is defined to the given class 1330 * loader. 1331 * 1332 * @param descriptor the module descriptor for the newly created module 1333 * @param loader the class loader of the module 1334 * @return the new Module 1335 */ 1336 static Module createModuleTrusted(final ModuleDescriptor descriptor, final ClassLoader loader) { 1337 return createModuleTrusted(Layer.boot(), descriptor, loader); 1338 } 1339 1340 /** 1341 * Creates a module layer with one module that is defined to the given class 1342 * loader. 1343 * 1344 * @param parent the parent layer of the new module 1345 * @param descriptor the module descriptor for the newly created module 1346 * @param loader the class loader of the module 1347 * @return the new Module 1348 */ 1349 static Module createModuleTrusted(final Layer parent, final ModuleDescriptor descriptor, final ClassLoader loader) { 1350 final String mn = descriptor.name(); 1351 1352 final ModuleReference mref = new ModuleReference(descriptor, null) { 1353 @Override 1354 public ModuleReader open() { 1355 throw new UnsupportedOperationException(); 1356 } 1357 }; 1358 1359 final ModuleFinder finder = new ModuleFinder() { 1360 @Override 1361 public Optional<ModuleReference> find(final String name) { 1362 if (name.equals(mn)) { 1363 return Optional.of(mref); 1364 } else { 1365 return Optional.empty(); 1366 } 1367 } 1368 @Override 1369 public Set<ModuleReference> findAll() { 1370 return Set.of(mref); 1371 } 1372 }; 1373 1374 final Configuration cf = parent.configuration() 1375 .resolve(finder, ModuleFinder.of(), Set.of(mn)); 1376 1377 final PrivilegedAction<Layer> pa = () -> parent.defineModules(cf, name -> loader); 1378 final Layer layer = AccessController.doPrivileged(pa, GET_LOADER_ACC_CTXT); 1379 1380 final Module m = layer.findModule(mn).get(); 1381 assert m.getLayer() == layer; 1382 1383 return m; 1384 } 1385 1386 static Context getContextTrustedOrNull() { 1387 final Global global = Context.getGlobal(); 1388 return global == null ? null : getContext(global); 1389 } 1390 1391 private static Context getContext(final Global global) { 1392 // We can't invoke Global.getContext() directly, as it's a protected override, and Global isn't in our package. 1393 // In order to access the method, we must cast it to ScriptObject first (which is in our package) and then let 1394 // virtual invocation do its thing. 1395 return ((ScriptObject)global).getContext(); 1396 } 1397 1398 /** 1399 * Try to infer Context instance from the Class. If we cannot, 1400 * then get it from the thread local variable. 1401 * 1402 * @param clazz the class 1403 * @return context 1404 */ 1405 static Context fromClass(final Class<?> clazz) { 1406 ClassLoader loader = null; 1407 try { 1408 loader = clazz.getClassLoader(); 1409 } catch (final SecurityException ignored) { 1410 // This could fail because of anonymous classes being used. 1411 // Accessing loader of anonymous class fails (for extension 1412 // loader class too?). In any case, for us fetching Context 1413 // from class loader is just an optimization. We can always 1414 // get Context from thread local storage (below). 1415 } 1416 1417 if (loader instanceof ScriptLoader) { 1418 return ((ScriptLoader)loader).getContext(); 1419 } 1420 1421 return Context.getContextTrusted(); 1422 } 1423 1424 private URL getResourceURL(final String resName) { 1425 if (appLoader != null) { 1426 return appLoader.getResource(resName); 1427 } 1428 return ClassLoader.getSystemResource(resName); 1429 } 1430 1431 private Object evaluateSource(final Source source, final ScriptObject scope, final ScriptObject thiz) { 1432 ScriptFunction script = null; 1433 1434 try { 1435 script = compileScript(source, scope, new Context.ThrowErrorManager()); 1436 } catch (final ParserException e) { 1437 e.throwAsEcmaException(); 1438 } 1439 1440 return ScriptRuntime.apply(script, thiz); 1441 } 1442 1443 private static ScriptFunction getProgramFunction(final Class<?> script, final ScriptObject scope) { 1444 if (script == null) { 1445 return null; 1446 } 1447 return invokeCreateProgramFunctionHandle(getCreateProgramFunctionHandle(script), scope); 1448 } 1449 1450 private static MethodHandle getCreateProgramFunctionHandle(final Class<?> script) { 1451 try { 1452 return LOOKUP.findStatic(script, CREATE_PROGRAM_FUNCTION.symbolName(), CREATE_PROGRAM_FUNCTION_TYPE); 1453 } catch (NoSuchMethodException | IllegalAccessException e) { 1454 throw new AssertionError("Failed to retrieve a handle for the program function for " + script.getName(), e); 1455 } 1456 } 1457 1458 private static ScriptFunction invokeCreateProgramFunctionHandle(final MethodHandle createProgramFunctionHandle, final ScriptObject scope) { 1459 try { 1460 return (ScriptFunction)createProgramFunctionHandle.invokeExact(scope); 1461 } catch (final RuntimeException|Error e) { 1462 throw e; 1463 } catch (final Throwable t) { 1464 throw new AssertionError("Failed to create a program function", t); 1465 } 1466 } 1467 1468 private ScriptFunction compileScript(final Source source, final ScriptObject scope, final ErrorManager errMan) { 1469 return getProgramFunction(compile(source, errMan, this._strict, false), scope); 1470 } 1471 1472 private synchronized Class<?> compile(final Source source, final ErrorManager errMan, final boolean strict, final boolean isEval) { 1473 // start with no errors, no warnings. 1474 errMan.reset(); 1475 1476 Class<?> script = findCachedClass(source); 1477 if (script != null) { 1478 final DebugLogger log = getLogger(Compiler.class); 1479 if (log.isEnabled()) { 1480 log.fine(new RuntimeEvent<>(Level.INFO, source), "Code cache hit for ", source, " avoiding recompile."); 1481 } 1482 return script; 1483 } 1484 1485 StoredScript storedScript = null; 1486 FunctionNode functionNode = null; 1487 // Don't use code store if optimistic types is enabled but lazy compilation is not. 1488 // This would store a full script compilation with many wrong optimistic assumptions that would 1489 // do more harm than good on later runs with both optimistic types and lazy compilation enabled. 1490 final boolean useCodeStore = codeStore != null && !env._parse_only && (!env._optimistic_types || env._lazy_compilation); 1491 final String cacheKey = useCodeStore ? CodeStore.getCacheKey("script", null) : null; 1492 1493 if (useCodeStore) { 1494 storedScript = codeStore.load(source, cacheKey); 1495 } 1496 1497 if (storedScript == null) { 1498 if (env._dest_dir != null) { 1499 source.dump(env._dest_dir); 1500 } 1501 1502 functionNode = new Parser(env, source, errMan, strict, getLogger(Parser.class)).parse(); 1503 1504 if (errMan.hasErrors()) { 1505 return null; 1506 } 1507 1508 if (env._print_ast || functionNode.getDebugFlag(FunctionNode.DEBUG_PRINT_AST)) { 1509 getErr().println(new ASTWriter(functionNode)); 1510 } 1511 1512 if (env._print_parse || functionNode.getDebugFlag(FunctionNode.DEBUG_PRINT_PARSE)) { 1513 getErr().println(new PrintVisitor(functionNode, true, false)); 1514 } 1515 } 1516 1517 if (env._parse_only) { 1518 return null; 1519 } 1520 1521 final URL url = source.getURL(); 1522 final CodeSource cs = new CodeSource(url, (CodeSigner[])null); 1523 final CodeInstaller installer; 1524 if (!env.useAnonymousClasses(source.getLength()) || env._persistent_cache || !env._lazy_compilation) { 1525 // Persistent code cache and eager compilation preclude use of VM anonymous classes 1526 final ScriptLoader loader = env._loader_per_compile ? createNewLoader() : scriptLoader; 1527 installer = new NamedContextCodeInstaller(this, cs, loader); 1528 } else { 1529 installer = new AnonymousContextCodeInstaller(this, cs, 1530 anonymousHostClasses.getOrCreate(cs, (key) -> 1531 createNewLoader().installClass( 1532 // NOTE: we're defining these constants in AnonymousContextCodeInstaller so they are not 1533 // initialized if we don't use AnonymousContextCodeInstaller. As this method is only ever 1534 // invoked from AnonymousContextCodeInstaller, this is okay. 1535 AnonymousContextCodeInstaller.ANONYMOUS_HOST_CLASS_NAME, 1536 AnonymousContextCodeInstaller.ANONYMOUS_HOST_CLASS_BYTES, cs))); 1537 } 1538 1539 if (storedScript == null) { 1540 final CompilationPhases phases = Compiler.CompilationPhases.COMPILE_ALL; 1541 1542 final Compiler compiler = Compiler.forInitialCompilation( 1543 installer, 1544 source, 1545 errMan, 1546 strict | functionNode.isStrict()); 1547 1548 final FunctionNode compiledFunction = compiler.compile(functionNode, phases); 1549 if (errMan.hasErrors()) { 1550 return null; 1551 } 1552 script = compiledFunction.getRootClass(); 1553 compiler.persistClassInfo(cacheKey, compiledFunction); 1554 } else { 1555 Compiler.updateCompilationId(storedScript.getCompilationId()); 1556 script = storedScript.installScript(source, installer); 1557 } 1558 1559 cacheClass(source, script); 1560 return script; 1561 } 1562 1563 private ScriptLoader createNewLoader() { 1564 return AccessController.doPrivileged( 1565 new PrivilegedAction<ScriptLoader>() { 1566 @Override 1567 public ScriptLoader run() { 1568 return new ScriptLoader(Context.this); 1569 } 1570 }, CREATE_LOADER_ACC_CTXT); 1571 } 1572 1573 private long getUniqueScriptId() { 1574 return uniqueScriptId.getAndIncrement(); 1575 } 1576 1577 /** 1578 * Cache for compiled script classes. 1579 */ 1580 @SuppressWarnings("serial") 1581 @Logger(name="classcache") 1582 private static class ClassCache extends LinkedHashMap<Source, ClassReference> implements Loggable { 1583 private final int size; 1584 private final ReferenceQueue<Class<?>> queue; 1585 private final DebugLogger log; 1586 1587 ClassCache(final Context context, final int size) { 1588 super(size, 0.75f, true); 1589 this.size = size; 1590 this.queue = new ReferenceQueue<>(); 1591 this.log = initLogger(context); 1592 } 1593 1594 void cache(final Source source, final Class<?> clazz) { 1595 if (log.isEnabled()) { 1596 log.info("Caching ", source, " in class cache"); 1597 } 1598 put(source, new ClassReference(clazz, queue, source)); 1599 } 1600 1601 @Override 1602 protected boolean removeEldestEntry(final Map.Entry<Source, ClassReference> eldest) { 1603 return size() > size; 1604 } 1605 1606 @Override 1607 public ClassReference get(final Object key) { 1608 for (ClassReference ref; (ref = (ClassReference)queue.poll()) != null; ) { 1609 final Source source = ref.source; 1610 if (log.isEnabled()) { 1611 log.info("Evicting ", source, " from class cache."); 1612 } 1613 remove(source); 1614 } 1615 1616 final ClassReference ref = super.get(key); 1617 if (ref != null && log.isEnabled()) { 1618 log.info("Retrieved class reference for ", ref.source, " from class cache"); 1619 } 1620 return ref; 1621 } 1622 1623 @Override 1624 public DebugLogger initLogger(final Context context) { 1625 return context.getLogger(getClass()); 1626 } 1627 1628 @Override 1629 public DebugLogger getLogger() { 1630 return log; 1631 } 1632 1633 } 1634 1635 private static class ClassReference extends SoftReference<Class<?>> { 1636 private final Source source; 1637 1638 ClassReference(final Class<?> clazz, final ReferenceQueue<Class<?>> queue, final Source source) { 1639 super(clazz, queue); 1640 this.source = source; 1641 } 1642 } 1643 1644 // Class cache management 1645 private Class<?> findCachedClass(final Source source) { 1646 final ClassReference ref = classCache == null ? null : classCache.get(source); 1647 return ref != null ? ref.get() : null; 1648 } 1649 1650 private void cacheClass(final Source source, final Class<?> clazz) { 1651 if (classCache != null) { 1652 classCache.cache(source, clazz); 1653 } 1654 } 1655 1656 // logging 1657 private final Map<String, DebugLogger> loggers = new HashMap<>(); 1658 1659 private void initLoggers() { 1660 ((Loggable)MethodHandleFactory.getFunctionality()).initLogger(this); 1661 } 1662 1663 /** 1664 * Get a logger, given a loggable class 1665 * @param clazz a Loggable class 1666 * @return debuglogger associated with that class 1667 */ 1668 public DebugLogger getLogger(final Class<? extends Loggable> clazz) { 1669 return getLogger(clazz, null); 1670 } 1671 1672 /** 1673 * Get a logger, given a loggable class 1674 * @param clazz a Loggable class 1675 * @param initHook an init hook - if this is the first time the logger is created in the context, run the init hook 1676 * @return debuglogger associated with that class 1677 */ 1678 public DebugLogger getLogger(final Class<? extends Loggable> clazz, final Consumer<DebugLogger> initHook) { 1679 final String name = getLoggerName(clazz); 1680 DebugLogger logger = loggers.get(name); 1681 if (logger == null) { 1682 if (!env.hasLogger(name)) { 1683 return DebugLogger.DISABLED_LOGGER; 1684 } 1685 final LoggerInfo info = env._loggers.get(name); 1686 logger = new DebugLogger(name, info.getLevel(), info.isQuiet()); 1687 if (initHook != null) { 1688 initHook.accept(logger); 1689 } 1690 loggers.put(name, logger); 1691 } 1692 return logger; 1693 } 1694 1695 /** 1696 * Given a Loggable class, weave debug info info a method handle for that logger. 1697 * Level.INFO is used 1698 * 1699 * @param clazz loggable 1700 * @param mh method handle 1701 * @param text debug printout to add 1702 * 1703 * @return instrumented method handle, or null if logger not enabled 1704 */ 1705 public MethodHandle addLoggingToHandle(final Class<? extends Loggable> clazz, final MethodHandle mh, final Supplier<String> text) { 1706 return addLoggingToHandle(clazz, Level.INFO, mh, Integer.MAX_VALUE, false, text); 1707 } 1708 1709 /** 1710 * Given a Loggable class, weave debug info info a method handle for that logger. 1711 * 1712 * @param clazz loggable 1713 * @param level log level 1714 * @param mh method handle 1715 * @param paramStart first parameter to print 1716 * @param printReturnValue should we print the return value? 1717 * @param text debug printout to add 1718 * 1719 * @return instrumented method handle, or null if logger not enabled 1720 */ 1721 public MethodHandle addLoggingToHandle(final Class<? extends Loggable> clazz, final Level level, final MethodHandle mh, final int paramStart, final boolean printReturnValue, final Supplier<String> text) { 1722 final DebugLogger log = getLogger(clazz); 1723 if (log.isEnabled()) { 1724 return MethodHandleFactory.addDebugPrintout(log, level, mh, paramStart, printReturnValue, text.get()); 1725 } 1726 return mh; 1727 } 1728 1729 private static String getLoggerName(final Class<?> clazz) { 1730 Class<?> current = clazz; 1731 while (current != null) { 1732 final Logger log = current.getAnnotation(Logger.class); 1733 if (log != null) { 1734 assert !"".equals(log.name()); 1735 return log.name(); 1736 } 1737 current = current.getSuperclass(); 1738 } 1739 assert false; 1740 return null; 1741 } 1742 1743 /** 1744 * This is a special kind of switchpoint used to guard builtin 1745 * properties and prototypes. In the future it might contain 1746 * logic to e.g. multiple switchpoint classes. 1747 */ 1748 public static final class BuiltinSwitchPoint extends SwitchPoint { 1749 //empty 1750 } 1751 1752 /** 1753 * Create a new builtin switchpoint and return it 1754 * @param name key name 1755 * @return new builtin switchpoint 1756 */ 1757 public SwitchPoint newBuiltinSwitchPoint(final String name) { 1758 assert builtinSwitchPoints.get(name) == null; 1759 final SwitchPoint sp = new BuiltinSwitchPoint(); 1760 builtinSwitchPoints.put(name, sp); 1761 return sp; 1762 } 1763 1764 /** 1765 * Return the builtin switchpoint for a particular key name 1766 * @param name key name 1767 * @return builtin switchpoint or null if none 1768 */ 1769 public SwitchPoint getBuiltinSwitchPoint(final String name) { 1770 return builtinSwitchPoints.get(name); 1771 } 1772 1773 private static ClassLoader createModuleLoader(final ClassLoader cl, 1774 final String modulePath, final String addModules) { 1775 if (addModules == null) { 1776 throw new IllegalArgumentException("--module-path specified with no --add-modules"); 1777 } 1778 1779 final Path[] paths = Stream.of(modulePath.split(File.pathSeparator)). 1780 map(s -> Paths.get(s)). 1781 toArray(sz -> new Path[sz]); 1782 final ModuleFinder mf = ModuleFinder.of(paths); 1783 final Set<ModuleReference> mrefs = mf.findAll(); 1784 if (mrefs.isEmpty()) { 1785 throw new RuntimeException("No modules in script --module-path: " + modulePath); 1786 } 1787 1788 final Set<String> rootMods; 1789 if (addModules.equals("ALL-MODULE-PATH")) { 1790 rootMods = mrefs.stream(). 1791 map(mr->mr.descriptor().name()). 1792 collect(Collectors.toSet()); 1793 } else { 1794 rootMods = Stream.of(addModules.split(",")). 1795 map(String::trim). 1796 collect(Collectors.toSet()); 1797 } 1798 1799 final Layer boot = Layer.boot(); 1800 final Configuration conf = boot.configuration(). 1801 resolve(mf, ModuleFinder.of(), rootMods); 1802 final String firstMod = rootMods.iterator().next(); 1803 return boot.defineModulesWithOneLoader(conf, cl).findLoader(firstMod); 1804 } 1805 }