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