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