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