Context.java revision 1790:785843878cf7
1167465Smp/*
259243Sobrien * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
359243Sobrien * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
459243Sobrien *
559243Sobrien * This code is free software; you can redistribute it and/or modify it
659243Sobrien * under the terms of the GNU General Public License version 2 only, as
759243Sobrien * published by the Free Software Foundation.  Oracle designates this
859243Sobrien * particular file as subject to the "Classpath" exception as provided
959243Sobrien * by Oracle in the LICENSE file that accompanied this code.
1059243Sobrien *
1159243Sobrien * This code is distributed in the hope that it will be useful, but WITHOUT
1259243Sobrien * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
1359243Sobrien * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
1459243Sobrien * version 2 for more details (a copy is included in the LICENSE file that
1559243Sobrien * accompanied this code).
1659243Sobrien *
17100616Smp * You should have received a copy of the GNU General Public License version
1859243Sobrien * 2 along with this work; if not, write to the Free Software Foundation,
1959243Sobrien * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
2059243Sobrien *
2159243Sobrien * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2259243Sobrien * or visit www.oracle.com if you need additional information or have any
2359243Sobrien * questions.
2459243Sobrien */
2559243Sobrien
2659243Sobrienpackage jdk.nashorn.internal.runtime;
2759243Sobrien
2859243Sobrienimport static jdk.internal.org.objectweb.asm.Opcodes.V1_7;
2959243Sobrienimport static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS;
3059243Sobrienimport static jdk.nashorn.internal.codegen.CompilerConstants.CREATE_PROGRAM_FUNCTION;
3159243Sobrienimport static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE;
3259243Sobrienimport static jdk.nashorn.internal.codegen.CompilerConstants.STRICT_MODE;
3359243Sobrienimport static jdk.nashorn.internal.runtime.CodeStore.newCodeStore;
3459243Sobrienimport static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
3559243Sobrienimport static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
3659243Sobrienimport static jdk.nashorn.internal.runtime.Source.sourceFor;
3759243Sobrien
3859243Sobrienimport java.io.File;
39167465Smpimport java.io.InputStream;
4059243Sobrienimport java.io.IOException;
4159243Sobrienimport java.io.PrintWriter;
4259243Sobrienimport java.io.UncheckedIOException;
4359243Sobrienimport java.lang.invoke.MethodHandle;
44167465Smpimport java.lang.invoke.MethodHandles;
4569408Sacheimport java.lang.invoke.MethodType;
46167465Smpimport java.lang.invoke.SwitchPoint;
47167465Smpimport java.lang.ref.ReferenceQueue;
48167465Smpimport java.lang.ref.SoftReference;
4969408Sacheimport java.lang.module.Configuration;
50167465Smpimport java.lang.module.ModuleDescriptor;
51167465Smpimport java.lang.module.ModuleFinder;
52167465Smpimport java.lang.module.ModuleReference;
53167465Smpimport java.lang.reflect.Field;
54167465Smpimport java.lang.reflect.Layer;
5559243Sobrienimport java.lang.reflect.Modifier;
5659243Sobrienimport java.lang.reflect.Module;
5759243Sobrienimport java.net.MalformedURLException;
5859243Sobrienimport java.net.URL;
59167465Smpimport java.nio.file.Path;
60167465Smpimport java.nio.file.Paths;
61167465Smpimport java.security.AccessControlContext;
62167465Smpimport java.security.AccessController;
63167465Smpimport java.security.CodeSigner;
64167465Smpimport java.security.CodeSource;
65167465Smpimport java.security.Permissions;
66167465Smpimport java.security.PrivilegedAction;
67167465Smpimport java.security.PrivilegedActionException;
68167465Smpimport java.security.PrivilegedExceptionAction;
69167465Smpimport java.security.ProtectionDomain;
70167465Smpimport java.util.Collection;
71167465Smpimport java.util.HashMap;
72167465Smpimport java.util.HashSet;
73167465Smpimport java.util.LinkedHashMap;
74167465Smpimport java.util.Map;
75167465Smpimport java.util.Objects;
76167465Smpimport java.util.Optional;
77167465Smpimport java.util.Set;
78167465Smpimport java.util.concurrent.ConcurrentHashMap;
79167465Smpimport java.util.concurrent.ConcurrentMap;
80167465Smpimport java.util.concurrent.atomic.AtomicLong;
81167465Smpimport java.util.concurrent.atomic.AtomicReference;
82167465Smpimport java.util.concurrent.atomic.LongAdder;
83167465Smpimport java.util.function.Consumer;
84167465Smpimport java.util.function.Supplier;
85167465Smpimport java.util.logging.Level;
86167465Smpimport java.util.stream.Collectors;
87167465Smpimport java.util.stream.Stream;
88167465Smpimport javax.script.ScriptEngine;
89167465Smpimport jdk.dynalink.DynamicLinker;
90167465Smpimport jdk.internal.org.objectweb.asm.ClassReader;
91167465Smpimport jdk.internal.org.objectweb.asm.ClassWriter;
92167465Smpimport jdk.internal.org.objectweb.asm.Opcodes;
93167465Smpimport jdk.internal.org.objectweb.asm.util.CheckClassAdapter;
94167465Smpimport jdk.nashorn.api.scripting.ClassFilter;
95167465Smpimport jdk.nashorn.api.scripting.ScriptObjectMirror;
96167465Smpimport jdk.nashorn.internal.WeakValueCache;
97167465Smpimport jdk.nashorn.internal.codegen.Compiler;
98167465Smpimport jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
99167465Smpimport jdk.nashorn.internal.codegen.ObjectClassGenerator;
100167465Smpimport jdk.nashorn.internal.ir.FunctionNode;
101167465Smpimport jdk.nashorn.internal.ir.debug.ASTWriter;
102167465Smpimport jdk.nashorn.internal.ir.debug.PrintVisitor;
103167465Smpimport jdk.nashorn.internal.lookup.MethodHandleFactory;
10459243Sobrienimport jdk.nashorn.internal.objects.Global;
10559243Sobrienimport jdk.nashorn.internal.parser.Parser;
10659243Sobrienimport jdk.nashorn.internal.runtime.events.RuntimeEvent;
10759243Sobrienimport jdk.nashorn.internal.runtime.linker.Bootstrap;
108167465Smpimport jdk.nashorn.internal.runtime.logging.DebugLogger;
109167465Smpimport jdk.nashorn.internal.runtime.logging.Loggable;
110167465Smpimport jdk.nashorn.internal.runtime.logging.Logger;
111167465Smpimport jdk.nashorn.internal.runtime.options.LoggingOption.LoggerInfo;
112167465Smpimport jdk.nashorn.internal.runtime.options.Options;
11359243Sobrienimport jdk.internal.misc.Unsafe;
11459243Sobrien
11559243Sobrien/**
11659243Sobrien * This class manages the global state of execution. Context is immutable.
117167465Smp */
118167465Smppublic final class Context {
119167465Smp    // nashorn specific security runtime access permission names
120167465Smp    /**
12159243Sobrien     * Permission needed to pass arbitrary nashorn command line options when creating Context.
12259243Sobrien     */
12359243Sobrien    public static final String NASHORN_SET_CONFIG      = "nashorn.setConfig";
12459243Sobrien
125167465Smp    /**
126167465Smp     * Permission needed to create Nashorn Context instance.
127167465Smp     */
12859243Sobrien    public static final String NASHORN_CREATE_CONTEXT  = "nashorn.createContext";
12959243Sobrien
13059243Sobrien    /**
131     * Permission needed to create Nashorn Global instance.
132     */
133    public static final String NASHORN_CREATE_GLOBAL   = "nashorn.createGlobal";
134
135    /**
136     * Permission to get current Nashorn Context from thread local storage.
137     */
138    public static final String NASHORN_GET_CONTEXT     = "nashorn.getContext";
139
140    /**
141     * Permission to use Java reflection/jsr292 from script code.
142     */
143    public static final String NASHORN_JAVA_REFLECTION = "nashorn.JavaReflection";
144
145    /**
146     * Permission to enable nashorn debug mode.
147     */
148    public static final String NASHORN_DEBUG_MODE = "nashorn.debugMode";
149
150    // nashorn load psuedo URL prefixes
151    private static final String LOAD_CLASSPATH = "classpath:";
152    private static final String LOAD_FX = "fx:";
153    private static final String LOAD_NASHORN = "nashorn:";
154
155    private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
156    private static final MethodType CREATE_PROGRAM_FUNCTION_TYPE = MethodType.methodType(ScriptFunction.class, ScriptObject.class);
157
158    private static final LongAdder NAMED_INSTALLED_SCRIPT_COUNT = new LongAdder();
159    private static final LongAdder ANONYMOUS_INSTALLED_SCRIPT_COUNT = new LongAdder();
160
161    /**
162     * Should scripts use only object slots for fields, or dual long/object slots? The default
163     * behaviour is to couple this to optimistic types, using dual representation if optimistic types are enabled
164     * and single field representation otherwise. This can be overridden by setting either the "nashorn.fields.objects"
165     * or "nashorn.fields.dual" system property.
166     */
167    private final FieldMode fieldMode;
168
169    private static enum FieldMode {
170        /** Value for automatic field representation depending on optimistic types setting */
171        AUTO,
172        /** Value for object field representation regardless of optimistic types setting */
173        OBJECTS,
174        /** Value for dual primitive/object field representation regardless of optimistic types setting */
175        DUAL
176    }
177
178    /**
179     * Keeps track of which builtin prototypes and properties have been relinked
180     * Currently we are conservative and associate the name of a builtin class with all
181     * its properties, so it's enough to invalidate a property to break all assumptions
182     * about a prototype. This can be changed to a more fine grained approach, but no one
183     * ever needs this, given the very rare occurrence of swapping out only parts of
184     * a builtin v.s. the entire builtin object
185     */
186    private final Map<String, SwitchPoint> builtinSwitchPoints = new HashMap<>();
187
188    /* Force DebuggerSupport to be loaded. */
189    static {
190        DebuggerSupport.FORCELOAD = true;
191    }
192
193    static long getNamedInstalledScriptCount() {
194        return NAMED_INSTALLED_SCRIPT_COUNT.sum();
195    }
196
197    static long getAnonymousInstalledScriptCount() {
198        return ANONYMOUS_INSTALLED_SCRIPT_COUNT.sum();
199    }
200
201    /**
202     * ContextCodeInstaller that has the privilege of installing classes in the Context.
203     * Can only be instantiated from inside the context and is opaque to other classes
204     */
205    private abstract static class ContextCodeInstaller implements CodeInstaller {
206        final Context context;
207        final CodeSource codeSource;
208
209        ContextCodeInstaller(final Context context, final CodeSource codeSource) {
210            this.context = context;
211            this.codeSource = codeSource;
212        }
213
214        @Override
215        public Context getContext() {
216            return context;
217        }
218
219        @Override
220        public void initialize(final Collection<Class<?>> classes, final Source source, final Object[] constants) {
221            try {
222                AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
223                    @Override
224                    public Void run() throws Exception {
225                        for (final Class<?> clazz : classes) {
226                            //use reflection to write source and constants table to installed classes
227                            final Field sourceField = clazz.getDeclaredField(SOURCE.symbolName());
228                            sourceField.setAccessible(true);
229                            sourceField.set(null, source);
230
231                            final Field constantsField = clazz.getDeclaredField(CONSTANTS.symbolName());
232                            constantsField.setAccessible(true);
233                            constantsField.set(null, constants);
234                        }
235                        return null;
236                    }
237                });
238            } catch (final PrivilegedActionException e) {
239                throw new RuntimeException(e);
240            }
241        }
242
243        @Override
244        public void verify(final byte[] code) {
245            context.verify(code);
246        }
247
248        @Override
249        public long getUniqueScriptId() {
250            return context.getUniqueScriptId();
251        }
252
253        @Override
254        public void storeScript(final String cacheKey, final Source source, final String mainClassName,
255                                final Map<String,byte[]> classBytes, final Map<Integer, FunctionInitializer> initializers,
256                                final Object[] constants, final int compilationId) {
257            if (context.codeStore != null) {
258                context.codeStore.store(cacheKey, source, mainClassName, classBytes, initializers, constants, compilationId);
259            }
260        }
261
262        @Override
263        public StoredScript loadScript(final Source source, final String functionKey) {
264            if (context.codeStore != null) {
265                return context.codeStore.load(source, functionKey);
266            }
267            return null;
268        }
269
270        @Override
271        public boolean isCompatibleWith(final CodeInstaller other) {
272            if (other instanceof ContextCodeInstaller) {
273                final ContextCodeInstaller cci = (ContextCodeInstaller)other;
274                return cci.context == context && cci.codeSource == codeSource;
275            }
276            return false;
277        }
278    }
279
280    private static class NamedContextCodeInstaller extends ContextCodeInstaller {
281        private final ScriptLoader loader;
282        private int usageCount = 0;
283        private int bytesDefined = 0;
284
285        // We reuse this installer for 10 compilations or 200000 defined bytes. Usually the first condition
286        // will occur much earlier, the second is a safety measure for very large scripts/functions.
287        private final static int MAX_USAGES = 10;
288        private final static int MAX_BYTES_DEFINED = 200_000;
289
290        private NamedContextCodeInstaller(final Context context, final CodeSource codeSource, final ScriptLoader loader) {
291            super(context, codeSource);
292            this.loader = loader;
293        }
294
295        @Override
296        public Class<?> install(final String className, final byte[] bytecode) {
297            usageCount++;
298            bytesDefined += bytecode.length;
299            NAMED_INSTALLED_SCRIPT_COUNT.increment();
300            return loader.installClass(Compiler.binaryName(className), bytecode, codeSource);
301        }
302
303        @Override
304        public CodeInstaller getOnDemandCompilationInstaller() {
305            // Reuse this installer if we're within our limits.
306            if (usageCount < MAX_USAGES && bytesDefined < MAX_BYTES_DEFINED) {
307                return this;
308            }
309            return new NamedContextCodeInstaller(context, codeSource, context.createNewLoader());
310        }
311
312        @Override
313        public CodeInstaller getMultiClassCodeInstaller() {
314            // This installer is perfectly suitable for installing multiple classes that reference each other
315            // as it produces classes with resolvable names, all defined in a single class loader.
316            return this;
317        }
318    }
319
320    private final WeakValueCache<CodeSource, Class<?>> anonymousHostClasses = new WeakValueCache<>();
321
322    private static final class AnonymousContextCodeInstaller extends ContextCodeInstaller {
323        private static final Unsafe UNSAFE = getUnsafe();
324        private static final String ANONYMOUS_HOST_CLASS_NAME = Compiler.SCRIPTS_PACKAGE.replace('/', '.') + ".AnonymousHost";
325        private static final byte[] ANONYMOUS_HOST_CLASS_BYTES = getAnonymousHostClassBytes();
326
327        private final Class<?> hostClass;
328
329        private AnonymousContextCodeInstaller(final Context context, final CodeSource codeSource, final Class<?> hostClass) {
330            super(context, codeSource);
331            this.hostClass = hostClass;
332        }
333
334        @Override
335        public Class<?> install(final String className, final byte[] bytecode) {
336            ANONYMOUS_INSTALLED_SCRIPT_COUNT.increment();
337            return UNSAFE.defineAnonymousClass(hostClass, bytecode, null);
338        }
339
340        @Override
341        public CodeInstaller getOnDemandCompilationInstaller() {
342            // This code loader can be indefinitely reused for on-demand recompilations for the same code source.
343            return this;
344        }
345
346        @Override
347        public CodeInstaller getMultiClassCodeInstaller() {
348            // This code loader can not be used to install multiple classes that reference each other, as they
349            // would have no resolvable names. Therefore, in such situation we must revert to an installer that
350            // produces named classes.
351            return new NamedContextCodeInstaller(context, codeSource, context.createNewLoader());
352        }
353
354        private static byte[] getAnonymousHostClassBytes() {
355            final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
356            cw.visit(V1_7, Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT, ANONYMOUS_HOST_CLASS_NAME.replace('.', '/'), null, "java/lang/Object", null);
357            cw.visitEnd();
358            return cw.toByteArray();
359        }
360
361        private static Unsafe getUnsafe() {
362            return AccessController.doPrivileged(new PrivilegedAction<Unsafe>() {
363                @Override
364                public Unsafe run() {
365                    try {
366                        final Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
367                        theUnsafeField.setAccessible(true);
368                        return (Unsafe)theUnsafeField.get(null);
369                    } catch (final ReflectiveOperationException e) {
370                        throw new RuntimeException(e);
371                    }
372                }
373            });
374        }
375    }
376
377    /** Is Context global debug mode enabled ? */
378    public static final boolean DEBUG = Options.getBooleanProperty("nashorn.debug");
379
380    private static final ThreadLocal<Global> currentGlobal = new ThreadLocal<>();
381
382    // in-memory cache for loaded classes
383    private ClassCache classCache;
384
385    // persistent code store
386    private CodeStore codeStore;
387
388    // A factory for linking global properties as constant method handles. It is created when the first Global
389    // is created, and invalidated forever once the second global is created.
390    private final AtomicReference<GlobalConstants> globalConstantsRef = new AtomicReference<>();
391
392    /**
393     * Get the current global scope
394     * @return the current global scope
395     */
396    public static Global getGlobal() {
397        // This class in a package.access protected package.
398        // Trusted code only can call this method.
399        return currentGlobal.get();
400    }
401
402    /**
403     * Set the current global scope
404     * @param global the global scope
405     */
406    public static void setGlobal(final ScriptObject global) {
407        if (global != null && !(global instanceof Global)) {
408            throw new IllegalArgumentException("not a global!");
409        }
410        setGlobal((Global)global);
411    }
412
413    /**
414     * Set the current global scope
415     * @param global the global scope
416     */
417    public static void setGlobal(final Global global) {
418        // This class in a package.access protected package.
419        // Trusted code only can call this method.
420        assert getGlobal() != global;
421        //same code can be cached between globals, then we need to invalidate method handle constants
422        if (global != null) {
423            final GlobalConstants globalConstants = getContext(global).getGlobalConstants();
424            if (globalConstants != null) {
425                globalConstants.invalidateAll();
426            }
427        }
428        currentGlobal.set(global);
429    }
430
431    /**
432     * Get context of the current global
433     * @return current global scope's context.
434     */
435    public static Context getContext() {
436        final SecurityManager sm = System.getSecurityManager();
437        if (sm != null) {
438            sm.checkPermission(new RuntimePermission(NASHORN_GET_CONTEXT));
439        }
440        return getContextTrusted();
441    }
442
443    /**
444     * Get current context's error writer
445     *
446     * @return error writer of the current context
447     */
448    public static PrintWriter getCurrentErr() {
449        final ScriptObject global = getGlobal();
450        return (global != null)? global.getContext().getErr() : new PrintWriter(System.err);
451    }
452
453    /**
454     * Output text to this Context's error stream
455     * @param str text to write
456     */
457    public static void err(final String str) {
458        err(str, true);
459    }
460
461    /**
462     * Output text to this Context's error stream, optionally with
463     * a newline afterwards
464     *
465     * @param str  text to write
466     * @param crlf write a carriage return/new line after text
467     */
468    public static void err(final String str, final boolean crlf) {
469        final PrintWriter err = Context.getCurrentErr();
470        if (err != null) {
471            if (crlf) {
472                err.println(str);
473            } else {
474                err.print(str);
475            }
476        }
477    }
478
479    /** Current environment. */
480    private final ScriptEnvironment env;
481
482    /** is this context in strict mode? Cached from env. as this is used heavily. */
483    final boolean _strict;
484
485    /** class loader to resolve classes from script. */
486    private final ClassLoader appLoader;
487
488    /*package-private*/
489    ClassLoader getAppLoader() {
490        return appLoader;
491    }
492
493    /** Class loader to load classes compiled from scripts. */
494    private final ScriptLoader scriptLoader;
495
496    /** Dynamic linker for linking call sites in script code loaded by this context */
497    private final DynamicLinker dynamicLinker;
498
499    /** Current error manager. */
500    private final ErrorManager errors;
501
502    /** Unique id for script. Used only when --loader-per-compile=false */
503    private final AtomicLong uniqueScriptId;
504
505    /** Optional class filter to use for Java classes. Can be null. */
506    private final ClassFilter classFilter;
507
508    /** Process-wide singleton structure loader */
509    private static final StructureLoader theStructLoader;
510    private static final ConcurrentMap<String, Class<?>> structureClasses = new ConcurrentHashMap<>();
511
512    /*package-private*/ @SuppressWarnings("static-method")
513    StructureLoader getStructLoader() {
514        return theStructLoader;
515    }
516
517    private static AccessControlContext createNoPermAccCtxt() {
518        return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, new Permissions()) });
519    }
520
521    private static AccessControlContext createPermAccCtxt(final String permName) {
522        final Permissions perms = new Permissions();
523        perms.add(new RuntimePermission(permName));
524        return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, perms) });
525    }
526
527    private static final AccessControlContext NO_PERMISSIONS_ACC_CTXT = createNoPermAccCtxt();
528    private static final AccessControlContext CREATE_LOADER_ACC_CTXT  = createPermAccCtxt("createClassLoader");
529    private static final AccessControlContext CREATE_GLOBAL_ACC_CTXT  = createPermAccCtxt(NASHORN_CREATE_GLOBAL);
530    private static final AccessControlContext GET_LOADER_ACC_CTXT     = createPermAccCtxt("getClassLoader");
531
532    static {
533        final ClassLoader myLoader = Context.class.getClassLoader();
534        theStructLoader = AccessController.doPrivileged(new PrivilegedAction<StructureLoader>() {
535            @Override
536            public StructureLoader run() {
537                return new StructureLoader(myLoader);
538            }
539        }, CREATE_LOADER_ACC_CTXT);
540    }
541
542    /**
543     * ThrowErrorManager that throws ParserException upon error conditions.
544     */
545    public static class ThrowErrorManager extends ErrorManager {
546        @Override
547        public void error(final String message) {
548            throw new ParserException(message);
549        }
550
551        @Override
552        public void error(final ParserException e) {
553            throw e;
554        }
555    }
556
557    /**
558     * Constructor
559     *
560     * @param options options from command line or Context creator
561     * @param errors  error manger
562     * @param appLoader application class loader
563     */
564    public Context(final Options options, final ErrorManager errors, final ClassLoader appLoader) {
565        this(options, errors, appLoader, null);
566    }
567
568    /**
569     * Constructor
570     *
571     * @param options options from command line or Context creator
572     * @param errors  error manger
573     * @param appLoader application class loader
574     * @param classFilter class filter to use
575     */
576    public Context(final Options options, final ErrorManager errors, final ClassLoader appLoader, final ClassFilter classFilter) {
577        this(options, errors, new PrintWriter(System.out, true), new PrintWriter(System.err, true), appLoader, classFilter);
578    }
579
580    /**
581     * Constructor
582     *
583     * @param options options from command line or Context creator
584     * @param errors  error manger
585     * @param out     output writer for this Context
586     * @param err     error writer for this Context
587     * @param appLoader application class loader
588     */
589    public Context(final Options options, final ErrorManager errors, final PrintWriter out, final PrintWriter err, final ClassLoader appLoader) {
590        this(options, errors, out, err, appLoader, (ClassFilter)null);
591    }
592
593    /**
594     * Constructor
595     *
596     * @param options options from command line or Context creator
597     * @param errors  error manger
598     * @param out     output writer for this Context
599     * @param err     error writer for this Context
600     * @param appLoader application class loader
601     * @param classFilter class filter to use
602     */
603    public Context(final Options options, final ErrorManager errors, final PrintWriter out, final PrintWriter err, final ClassLoader appLoader, final ClassFilter classFilter) {
604        final SecurityManager sm = System.getSecurityManager();
605        if (sm != null) {
606            sm.checkPermission(new RuntimePermission(NASHORN_CREATE_CONTEXT));
607        }
608
609        this.classFilter = classFilter;
610        this.env       = new ScriptEnvironment(options, out, err);
611        this._strict   = env._strict;
612        if (env._loader_per_compile) {
613            this.scriptLoader = null;
614            this.uniqueScriptId = null;
615        } else {
616            this.scriptLoader = createNewLoader();
617            this.uniqueScriptId = new AtomicLong();
618        }
619        this.errors    = errors;
620
621        // if user passed --module-path, we create a module class loader with
622        // passed appLoader as the parent.
623        final String modulePath = env._module_path;
624        ClassLoader appCl = null;
625        if (!env._compile_only && modulePath != null && !modulePath.isEmpty()) {
626            // make sure that caller can create a class loader.
627            if (sm != null) {
628                sm.checkCreateClassLoader();
629            }
630            appCl = AccessController.doPrivileged(new PrivilegedAction<ClassLoader>() {
631                @Override
632                public ClassLoader run() {
633                    return createModuleLoader(appLoader, modulePath, env._add_modules);
634                }
635            });
636        } else {
637            appCl = appLoader;
638        }
639
640        // if user passed -classpath option, make a URLClassLoader with that and
641        // the app loader or module app loader as the parent.
642        final String classPath = env._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            appCl = NashornLoader.createClassLoader(classPath, appCl);
649        }
650
651        this.appLoader = appCl;
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;
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 InputStream resStream = Context.class.getResourceAsStream(resource);
887                                return resStream != null ? sourceFor(srcStr, Source.readFully(resStream)) : 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, theStructLoader);
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        if (appLoader != null) {
1188            return Class.forName(fullName, true, appLoader);
1189        } else {
1190            final Class<?> cl = Class.forName(fullName);
1191            // return the Class only if it was loaded by boot loader
1192            if (cl.getClassLoader() == null) {
1193                return cl;
1194            } else {
1195                throw new ClassNotFoundException(fullName);
1196            }
1197        }
1198    }
1199
1200    /**
1201     * Hook to print stack trace for a {@link Throwable} that occurred during
1202     * execution
1203     *
1204     * @param t throwable for which to dump stack
1205     */
1206    public static void printStackTrace(final Throwable t) {
1207        if (Context.DEBUG) {
1208            t.printStackTrace(Context.getCurrentErr());
1209        }
1210    }
1211
1212    /**
1213     * Verify generated bytecode before emission. This is called back from the
1214     * {@link ObjectClassGenerator} or the {@link Compiler}. If the "--verify-code" parameter
1215     * hasn't been given, this is a nop
1216     *
1217     * Note that verification may load classes -- we don't want to do that unless
1218     * user specified verify option. We check it here even though caller
1219     * may have already checked that flag
1220     *
1221     * @param bytecode bytecode to verify
1222     */
1223    public void verify(final byte[] bytecode) {
1224        if (env._verify_code) {
1225            // No verification when security manager is around as verifier
1226            // may load further classes - which should be avoided.
1227            if (System.getSecurityManager() == null) {
1228                CheckClassAdapter.verify(new ClassReader(bytecode), theStructLoader, false, new PrintWriter(System.err, true));
1229            }
1230        }
1231    }
1232
1233    /**
1234     * Create and initialize a new global scope object.
1235     *
1236     * @return the initialized global scope object.
1237     */
1238    public Global createGlobal() {
1239        return initGlobal(newGlobal());
1240    }
1241
1242    /**
1243     * Create a new uninitialized global scope object
1244     * @return the global script object
1245     */
1246    public Global newGlobal() {
1247        createOrInvalidateGlobalConstants();
1248        return new Global(this);
1249    }
1250
1251    private void createOrInvalidateGlobalConstants() {
1252        for (;;) {
1253            final GlobalConstants currentGlobalConstants = getGlobalConstants();
1254            if (currentGlobalConstants != null) {
1255                // Subsequent invocation; we're creating our second or later Global. GlobalConstants is not safe to use
1256                // with more than one Global, as the constant method handle linkages it creates create a coupling
1257                // between the Global and the call sites in the compiled code.
1258                currentGlobalConstants.invalidateForever();
1259                return;
1260            }
1261            final GlobalConstants newGlobalConstants = new GlobalConstants(getLogger(GlobalConstants.class));
1262            if (globalConstantsRef.compareAndSet(null, newGlobalConstants)) {
1263                // First invocation; we're creating the first Global in this Context. Create the GlobalConstants object
1264                // for this Context.
1265                return;
1266            }
1267
1268            // If we reach here, then we started out as the first invocation, but another concurrent invocation won the
1269            // CAS race. We'll just let the loop repeat and invalidate the CAS race winner.
1270        }
1271    }
1272
1273    /**
1274     * Initialize given global scope object.
1275     *
1276     * @param global the global
1277     * @param engine the associated ScriptEngine instance, can be null
1278     * @return the initialized global scope object.
1279     */
1280    public Global initGlobal(final Global global, final ScriptEngine engine) {
1281        // Need only minimal global object, if we are just compiling.
1282        if (!env._compile_only) {
1283            final Global oldGlobal = Context.getGlobal();
1284            try {
1285                Context.setGlobal(global);
1286                // initialize global scope with builtin global objects
1287                global.initBuiltinObjects(engine);
1288            } finally {
1289                Context.setGlobal(oldGlobal);
1290            }
1291        }
1292
1293        return global;
1294    }
1295
1296    /**
1297     * Initialize given global scope object.
1298     *
1299     * @param global the global
1300     * @return the initialized global scope object.
1301     */
1302    public Global initGlobal(final Global global) {
1303        return initGlobal(global, null);
1304    }
1305
1306    /**
1307     * Return the current global's context
1308     * @return current global's context
1309     */
1310    static Context getContextTrusted() {
1311        return getContext(getGlobal());
1312    }
1313
1314    /**
1315     * Gets the Nashorn dynamic linker for the specified class. If the class is
1316     * a script class, the dynamic linker associated with its context is
1317     * returned. Otherwise the dynamic linker associated with the current
1318     * context is returned.
1319     * @param clazz the class for which we want to retrieve a dynamic linker.
1320     * @return the Nashorn dynamic linker for the specified class.
1321     */
1322    public static DynamicLinker getDynamicLinker(final Class<?> clazz) {
1323        return fromClass(clazz).dynamicLinker;
1324    }
1325
1326    /**
1327     * Gets the Nashorn dynamic linker associated with the current context.
1328     * @return the Nashorn dynamic linker for the current context.
1329     */
1330    public static DynamicLinker getDynamicLinker() {
1331        return getContextTrusted().dynamicLinker;
1332    }
1333
1334    /**
1335     * Creates a module layer with one module that is defined to the given class
1336     * loader.
1337     *
1338     * @param descriptor the module descriptor for the newly created module
1339     * @param loader the class loader of the module
1340     * @return the new Module
1341     */
1342    static Module createModuleTrusted(final ModuleDescriptor descriptor, final ClassLoader loader) {
1343        return createModuleTrusted(Layer.boot(), descriptor, loader);
1344    }
1345
1346    /**
1347     * Creates a module layer with one module that is defined to the given class
1348     * loader.
1349     *
1350     * @param parent the parent layer of the new module
1351     * @param descriptor the module descriptor for the newly created module
1352     * @param loader the class loader of the module
1353     * @return the new Module
1354     */
1355    static Module createModuleTrusted(final Layer parent, final ModuleDescriptor descriptor, final ClassLoader loader) {
1356        final String mn = descriptor.name();
1357
1358        final ModuleReference mref = new ModuleReference(descriptor, null, () -> {
1359            IOException ioe = new IOException("<dynamic module>");
1360            throw new UncheckedIOException(ioe);
1361        });
1362
1363        final ModuleFinder finder = new ModuleFinder() {
1364            @Override
1365            public Optional<ModuleReference> find(final String name) {
1366                if (name.equals(mn)) {
1367                    return Optional.of(mref);
1368                } else {
1369                    return Optional.empty();
1370                }
1371            }
1372            @Override
1373            public Set<ModuleReference> findAll() {
1374                return Set.of(mref);
1375            }
1376        };
1377
1378        final Configuration cf = parent.configuration()
1379                .resolveRequires(finder, ModuleFinder.of(), Set.of(mn));
1380
1381        final PrivilegedAction<Layer> pa = () -> parent.defineModules(cf, name -> loader);
1382        final Layer layer = AccessController.doPrivileged(pa, GET_LOADER_ACC_CTXT);
1383
1384        final Module m = layer.findModule(mn).get();
1385        assert m.getLayer() == layer;
1386
1387        return m;
1388    }
1389
1390    static Context getContextTrustedOrNull() {
1391        final Global global = Context.getGlobal();
1392        return global == null ? null : getContext(global);
1393    }
1394
1395    private static Context getContext(final Global global) {
1396        // We can't invoke Global.getContext() directly, as it's a protected override, and Global isn't in our package.
1397        // In order to access the method, we must cast it to ScriptObject first (which is in our package) and then let
1398        // virtual invocation do its thing.
1399        return ((ScriptObject)global).getContext();
1400    }
1401
1402    /**
1403     * Try to infer Context instance from the Class. If we cannot,
1404     * then get it from the thread local variable.
1405     *
1406     * @param clazz the class
1407     * @return context
1408     */
1409    static Context fromClass(final Class<?> clazz) {
1410        ClassLoader loader = null;
1411        try {
1412            loader = clazz.getClassLoader();
1413        } catch (final SecurityException ignored) {
1414            // This could fail because of anonymous classes being used.
1415            // Accessing loader of anonymous class fails (for extension
1416            // loader class too?). In any case, for us fetching Context
1417            // from class loader is just an optimization. We can always
1418            // get Context from thread local storage (below).
1419        }
1420
1421        if (loader instanceof ScriptLoader) {
1422            return ((ScriptLoader)loader).getContext();
1423        }
1424
1425        return Context.getContextTrusted();
1426    }
1427
1428    private URL getResourceURL(final String resName) {
1429        if (appLoader != null) {
1430            return appLoader.getResource(resName);
1431        }
1432        return ClassLoader.getSystemResource(resName);
1433    }
1434
1435    private Object evaluateSource(final Source source, final ScriptObject scope, final ScriptObject thiz) {
1436        ScriptFunction script = null;
1437
1438        try {
1439            script = compileScript(source, scope, new Context.ThrowErrorManager());
1440        } catch (final ParserException e) {
1441            e.throwAsEcmaException();
1442        }
1443
1444        return ScriptRuntime.apply(script, thiz);
1445    }
1446
1447    private static ScriptFunction getProgramFunction(final Class<?> script, final ScriptObject scope) {
1448        if (script == null) {
1449            return null;
1450        }
1451        return invokeCreateProgramFunctionHandle(getCreateProgramFunctionHandle(script), scope);
1452    }
1453
1454    private static MethodHandle getCreateProgramFunctionHandle(final Class<?> script) {
1455        try {
1456            return LOOKUP.findStatic(script, CREATE_PROGRAM_FUNCTION.symbolName(), CREATE_PROGRAM_FUNCTION_TYPE);
1457        } catch (NoSuchMethodException | IllegalAccessException e) {
1458            throw new AssertionError("Failed to retrieve a handle for the program function for " + script.getName(), e);
1459        }
1460    }
1461
1462    private static ScriptFunction invokeCreateProgramFunctionHandle(final MethodHandle createProgramFunctionHandle, final ScriptObject scope) {
1463        try {
1464            return (ScriptFunction)createProgramFunctionHandle.invokeExact(scope);
1465        } catch (final RuntimeException|Error e) {
1466            throw e;
1467        } catch (final Throwable t) {
1468            throw new AssertionError("Failed to create a program function", t);
1469        }
1470    }
1471
1472    private ScriptFunction compileScript(final Source source, final ScriptObject scope, final ErrorManager errMan) {
1473        return getProgramFunction(compile(source, errMan, this._strict, false), scope);
1474    }
1475
1476    private synchronized Class<?> compile(final Source source, final ErrorManager errMan, final boolean strict, final boolean isEval) {
1477        // start with no errors, no warnings.
1478        errMan.reset();
1479
1480        Class<?> script = findCachedClass(source);
1481        if (script != null) {
1482            final DebugLogger log = getLogger(Compiler.class);
1483            if (log.isEnabled()) {
1484                log.fine(new RuntimeEvent<>(Level.INFO, source), "Code cache hit for ", source, " avoiding recompile.");
1485            }
1486            return script;
1487        }
1488
1489        StoredScript storedScript = null;
1490        FunctionNode functionNode = null;
1491        // Don't use code store if optimistic types is enabled but lazy compilation is not.
1492        // This would store a full script compilation with many wrong optimistic assumptions that would
1493        // do more harm than good on later runs with both optimistic types and lazy compilation enabled.
1494        final boolean useCodeStore = codeStore != null && !env._parse_only && (!env._optimistic_types || env._lazy_compilation);
1495        final String cacheKey = useCodeStore ? CodeStore.getCacheKey("script", null) : null;
1496
1497        if (useCodeStore) {
1498            storedScript = codeStore.load(source, cacheKey);
1499        }
1500
1501        if (storedScript == null) {
1502            if (env._dest_dir != null) {
1503                source.dump(env._dest_dir);
1504            }
1505
1506            functionNode = new Parser(env, source, errMan, strict, getLogger(Parser.class)).parse();
1507
1508            if (errMan.hasErrors()) {
1509                return null;
1510            }
1511
1512            if (env._print_ast || functionNode.getDebugFlag(FunctionNode.DEBUG_PRINT_AST)) {
1513                getErr().println(new ASTWriter(functionNode));
1514            }
1515
1516            if (env._print_parse || functionNode.getDebugFlag(FunctionNode.DEBUG_PRINT_PARSE)) {
1517                getErr().println(new PrintVisitor(functionNode, true, false));
1518            }
1519        }
1520
1521        if (env._parse_only) {
1522            return null;
1523        }
1524
1525        final URL          url    = source.getURL();
1526        final CodeSource   cs     = new CodeSource(url, (CodeSigner[])null);
1527        final CodeInstaller installer;
1528        if (!env.useAnonymousClasses(source.getLength()) || env._persistent_cache || !env._lazy_compilation) {
1529            // Persistent code cache and eager compilation preclude use of VM anonymous classes
1530            final ScriptLoader loader = env._loader_per_compile ? createNewLoader() : scriptLoader;
1531            installer = new NamedContextCodeInstaller(this, cs, loader);
1532        } else {
1533            installer = new AnonymousContextCodeInstaller(this, cs,
1534                    anonymousHostClasses.getOrCreate(cs, (key) ->
1535                            createNewLoader().installClass(
1536                                    // NOTE: we're defining these constants in AnonymousContextCodeInstaller so they are not
1537                                    // initialized if we don't use AnonymousContextCodeInstaller. As this method is only ever
1538                                    // invoked from AnonymousContextCodeInstaller, this is okay.
1539                                    AnonymousContextCodeInstaller.ANONYMOUS_HOST_CLASS_NAME,
1540                                    AnonymousContextCodeInstaller.ANONYMOUS_HOST_CLASS_BYTES, cs)));
1541        }
1542
1543        if (storedScript == null) {
1544            final CompilationPhases phases = Compiler.CompilationPhases.COMPILE_ALL;
1545
1546            final Compiler compiler = Compiler.forInitialCompilation(
1547                    installer,
1548                    source,
1549                    errMan,
1550                    strict | functionNode.isStrict());
1551
1552            final FunctionNode compiledFunction = compiler.compile(functionNode, phases);
1553            if (errMan.hasErrors()) {
1554                return null;
1555            }
1556            script = compiledFunction.getRootClass();
1557            compiler.persistClassInfo(cacheKey, compiledFunction);
1558        } else {
1559            Compiler.updateCompilationId(storedScript.getCompilationId());
1560            script = storedScript.installScript(source, installer);
1561        }
1562
1563        cacheClass(source, script);
1564        return script;
1565    }
1566
1567    private ScriptLoader createNewLoader() {
1568        return AccessController.doPrivileged(
1569             new PrivilegedAction<ScriptLoader>() {
1570                @Override
1571                public ScriptLoader run() {
1572                    return new ScriptLoader(Context.this);
1573                }
1574             }, CREATE_LOADER_ACC_CTXT);
1575    }
1576
1577    private long getUniqueScriptId() {
1578        return uniqueScriptId.getAndIncrement();
1579    }
1580
1581    /**
1582     * Cache for compiled script classes.
1583     */
1584    @SuppressWarnings("serial")
1585    @Logger(name="classcache")
1586    private static class ClassCache extends LinkedHashMap<Source, ClassReference> implements Loggable {
1587        private final int size;
1588        private final ReferenceQueue<Class<?>> queue;
1589        private final DebugLogger log;
1590
1591        ClassCache(final Context context, final int size) {
1592            super(size, 0.75f, true);
1593            this.size = size;
1594            this.queue = new ReferenceQueue<>();
1595            this.log   = initLogger(context);
1596        }
1597
1598        void cache(final Source source, final Class<?> clazz) {
1599            if (log.isEnabled()) {
1600                log.info("Caching ", source, " in class cache");
1601            }
1602            put(source, new ClassReference(clazz, queue, source));
1603        }
1604
1605        @Override
1606        protected boolean removeEldestEntry(final Map.Entry<Source, ClassReference> eldest) {
1607            return size() > size;
1608        }
1609
1610        @Override
1611        public ClassReference get(final Object key) {
1612            for (ClassReference ref; (ref = (ClassReference)queue.poll()) != null; ) {
1613                final Source source = ref.source;
1614                if (log.isEnabled()) {
1615                    log.info("Evicting ", source, " from class cache.");
1616                }
1617                remove(source);
1618            }
1619
1620            final ClassReference ref = super.get(key);
1621            if (ref != null && log.isEnabled()) {
1622                log.info("Retrieved class reference for ", ref.source, " from class cache");
1623            }
1624            return ref;
1625        }
1626
1627        @Override
1628        public DebugLogger initLogger(final Context context) {
1629            return context.getLogger(getClass());
1630        }
1631
1632        @Override
1633        public DebugLogger getLogger() {
1634            return log;
1635        }
1636
1637    }
1638
1639    private static class ClassReference extends SoftReference<Class<?>> {
1640        private final Source source;
1641
1642        ClassReference(final Class<?> clazz, final ReferenceQueue<Class<?>> queue, final Source source) {
1643            super(clazz, queue);
1644            this.source = source;
1645        }
1646    }
1647
1648    // Class cache management
1649    private Class<?> findCachedClass(final Source source) {
1650        final ClassReference ref = classCache == null ? null : classCache.get(source);
1651        return ref != null ? ref.get() : null;
1652    }
1653
1654    private void cacheClass(final Source source, final Class<?> clazz) {
1655        if (classCache != null) {
1656            classCache.cache(source, clazz);
1657        }
1658    }
1659
1660    // logging
1661    private final Map<String, DebugLogger> loggers = new HashMap<>();
1662
1663    private void initLoggers() {
1664        ((Loggable)MethodHandleFactory.getFunctionality()).initLogger(this);
1665    }
1666
1667    /**
1668     * Get a logger, given a loggable class
1669     * @param clazz a Loggable class
1670     * @return debuglogger associated with that class
1671     */
1672    public DebugLogger getLogger(final Class<? extends Loggable> clazz) {
1673        return getLogger(clazz, null);
1674    }
1675
1676    /**
1677     * Get a logger, given a loggable class
1678     * @param clazz a Loggable class
1679     * @param initHook an init hook - if this is the first time the logger is created in the context, run the init hook
1680     * @return debuglogger associated with that class
1681     */
1682    public DebugLogger getLogger(final Class<? extends Loggable> clazz, final Consumer<DebugLogger> initHook) {
1683        final String name = getLoggerName(clazz);
1684        DebugLogger logger = loggers.get(name);
1685        if (logger == null) {
1686            if (!env.hasLogger(name)) {
1687                return DebugLogger.DISABLED_LOGGER;
1688            }
1689            final LoggerInfo info = env._loggers.get(name);
1690            logger = new DebugLogger(name, info.getLevel(), info.isQuiet());
1691            if (initHook != null) {
1692                initHook.accept(logger);
1693            }
1694            loggers.put(name, logger);
1695        }
1696        return logger;
1697    }
1698
1699    /**
1700     * Given a Loggable class, weave debug info info a method handle for that logger.
1701     * Level.INFO is used
1702     *
1703     * @param clazz loggable
1704     * @param mh    method handle
1705     * @param text  debug printout to add
1706     *
1707     * @return instrumented method handle, or null if logger not enabled
1708     */
1709    public MethodHandle addLoggingToHandle(final Class<? extends Loggable> clazz, final MethodHandle mh, final Supplier<String> text) {
1710        return addLoggingToHandle(clazz, Level.INFO, mh, Integer.MAX_VALUE, false, text);
1711    }
1712
1713    /**
1714     * Given a Loggable class, weave debug info info a method handle for that logger.
1715     *
1716     * @param clazz            loggable
1717     * @param level            log level
1718     * @param mh               method handle
1719     * @param paramStart       first parameter to print
1720     * @param printReturnValue should we print the return value?
1721     * @param text             debug printout to add
1722     *
1723     * @return instrumented method handle, or null if logger not enabled
1724     */
1725    public MethodHandle addLoggingToHandle(final Class<? extends Loggable> clazz, final Level level, final MethodHandle mh, final int paramStart, final boolean printReturnValue, final Supplier<String> text) {
1726        final DebugLogger log = getLogger(clazz);
1727        if (log.isEnabled()) {
1728            return MethodHandleFactory.addDebugPrintout(log, level, mh, paramStart, printReturnValue, text.get());
1729        }
1730        return mh;
1731    }
1732
1733    private static String getLoggerName(final Class<?> clazz) {
1734        Class<?> current = clazz;
1735        while (current != null) {
1736            final Logger log = current.getAnnotation(Logger.class);
1737            if (log != null) {
1738                assert !"".equals(log.name());
1739                return log.name();
1740            }
1741            current = current.getSuperclass();
1742        }
1743        assert false;
1744        return null;
1745    }
1746
1747    /**
1748     * This is a special kind of switchpoint used to guard builtin
1749     * properties and prototypes. In the future it might contain
1750     * logic to e.g. multiple switchpoint classes.
1751     */
1752    public static final class BuiltinSwitchPoint extends SwitchPoint {
1753        //empty
1754    }
1755
1756    /**
1757     * Create a new builtin switchpoint and return it
1758     * @param name key name
1759     * @return new builtin switchpoint
1760     */
1761    public SwitchPoint newBuiltinSwitchPoint(final String name) {
1762        assert builtinSwitchPoints.get(name) == null;
1763        final SwitchPoint sp = new BuiltinSwitchPoint();
1764        builtinSwitchPoints.put(name, sp);
1765        return sp;
1766    }
1767
1768    /**
1769     * Return the builtin switchpoint for a particular key name
1770     * @param name key name
1771     * @return builtin switchpoint or null if none
1772     */
1773    public SwitchPoint getBuiltinSwitchPoint(final String name) {
1774        return builtinSwitchPoints.get(name);
1775    }
1776
1777    private static ClassLoader createModuleLoader(final ClassLoader cl,
1778            final String modulePath, final String addModules) {
1779        if (addModules == null) {
1780            throw new IllegalArgumentException("--module-path specified with no --add-modules");
1781        }
1782
1783        final Path[] paths = Stream.of(modulePath.split(File.pathSeparator)).
1784            map(s -> Paths.get(s)).
1785            toArray(sz -> new Path[sz]);
1786        final ModuleFinder mf = ModuleFinder.of(paths);
1787        final Set<ModuleReference> mrefs = mf.findAll();
1788        if (mrefs.isEmpty()) {
1789            throw new RuntimeException("No modules in script --module-path: " + modulePath);
1790        }
1791
1792        final Set<String> rootMods;
1793        if (addModules.equals("ALL-MODULE-PATH")) {
1794            rootMods = mrefs.stream().
1795                map(mr->mr.descriptor().name()).
1796                collect(Collectors.toSet());
1797        } else {
1798            rootMods = Stream.of(addModules.split(",")).
1799                map(String::trim).
1800                collect(Collectors.toSet());
1801        }
1802
1803        final Layer boot = Layer.boot();
1804        final Configuration conf = boot.configuration().
1805            resolveRequires(mf, ModuleFinder.of(), rootMods);
1806        final String firstMod = rootMods.iterator().next();
1807        return boot.defineModulesWithOneLoader(conf, cl).findLoader(firstMod);
1808    }
1809}
1810