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