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