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