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