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