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