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