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