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