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