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