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