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