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