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