Context.java revision 953:221a84ef44c0
1193323Sed/*
2193323Sed * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3193323Sed * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4193323Sed *
5193323Sed * This code is free software; you can redistribute it and/or modify it
6193323Sed * under the terms of the GNU General Public License version 2 only, as
7193323Sed * published by the Free Software Foundation.  Oracle designates this
8193323Sed * particular file as subject to the "Classpath" exception as provided
9193323Sed * by Oracle in the LICENSE file that accompanied this code.
10193323Sed *
11193323Sed * This code is distributed in the hope that it will be useful, but WITHOUT
12193323Sed * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13201360Srdivacky * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14193323Sed * version 2 for more details (a copy is included in the LICENSE file that
15249423Sdim * accompanied this code).
16201360Srdivacky *
17249423Sdim * You should have received a copy of the GNU General Public License version
18249423Sdim * 2 along with this work; if not, write to the Free Software Foundation,
19249423Sdim * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20249423Sdim *
21249423Sdim * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22249423Sdim * or visit www.oracle.com if you need additional information or have any
23239462Sdim * questions.
24193323Sed */
25193323Sed
26193323Sedpackage jdk.nashorn.internal.runtime;
27193323Sed
28193323Sedimport static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS;
29249423Sdimimport static jdk.nashorn.internal.codegen.CompilerConstants.CREATE_PROGRAM_FUNCTION;
30249423Sdimimport static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE;
31249423Sdimimport static jdk.nashorn.internal.codegen.CompilerConstants.STRICT_MODE;
32249423Sdimimport static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
33249423Sdimimport static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
34249423Sdimimport static jdk.nashorn.internal.runtime.Source.sourceFor;
35249423Sdim
36249423Sdimimport java.io.File;
37249423Sdimimport java.io.IOException;
38193323Sedimport java.io.PrintWriter;
39202375Srdivackyimport java.lang.invoke.MethodHandle;
40198090Srdivackyimport java.lang.invoke.MethodHandles;
41195098Sedimport java.lang.invoke.MethodType;
42193323Sedimport java.lang.ref.ReferenceQueue;
43249423Sdimimport java.lang.ref.SoftReference;
44193323Sedimport java.lang.reflect.Field;
45249423Sdimimport java.lang.reflect.Modifier;
46249423Sdimimport java.net.MalformedURLException;
47249423Sdimimport java.net.URL;
48249423Sdimimport java.security.AccessControlContext;
49249423Sdimimport java.security.AccessController;
50249423Sdimimport java.security.CodeSigner;
51249423Sdimimport java.security.CodeSource;
52193323Sedimport java.security.Permissions;
53193323Sedimport java.security.PrivilegedAction;
54193323Sedimport java.security.PrivilegedActionException;
55193323Sedimport java.security.PrivilegedExceptionAction;
56193323Sedimport java.security.ProtectionDomain;
57193323Sedimport java.util.Collection;
58198090Srdivackyimport java.util.HashMap;
59193323Sedimport java.util.LinkedHashMap;
60193323Sedimport java.util.Map;
61193323Sedimport java.util.concurrent.atomic.AtomicLong;
62193323Sedimport java.util.function.Consumer;
63239462Sdimimport java.util.function.Supplier;
64239462Sdimimport java.util.logging.Level;
65239462Sdimimport javax.script.ScriptEngine;
66193323Sedimport jdk.internal.org.objectweb.asm.ClassReader;
67193323Sedimport jdk.internal.org.objectweb.asm.util.CheckClassAdapter;
68193323Sedimport jdk.nashorn.api.scripting.ScriptObjectMirror;
69193323Sedimport jdk.nashorn.internal.codegen.Compiler;
70193323Sedimport jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
71193323Sedimport jdk.nashorn.internal.codegen.ObjectClassGenerator;
72193323Sedimport jdk.nashorn.internal.ir.FunctionNode;
73193323Sedimport jdk.nashorn.internal.ir.debug.ASTWriter;
74193323Sedimport jdk.nashorn.internal.ir.debug.PrintVisitor;
75193323Sedimport jdk.nashorn.internal.lookup.MethodHandleFactory;
76193323Sedimport jdk.nashorn.internal.objects.Global;
77193323Sedimport jdk.nashorn.internal.parser.Parser;
78193323Sedimport jdk.nashorn.internal.runtime.events.RuntimeEvent;
79198090Srdivackyimport jdk.nashorn.internal.runtime.logging.DebugLogger;
80193323Sedimport jdk.nashorn.internal.runtime.logging.Loggable;
81193323Sedimport jdk.nashorn.internal.runtime.logging.Logger;
82193323Sedimport jdk.nashorn.internal.runtime.options.LoggingOption.LoggerInfo;
83193323Sedimport jdk.nashorn.internal.runtime.options.Options;
84193323Sed
85193323Sed/**
86249423Sdim * This class manages the global state of execution. Context is immutable.
87249423Sdim */
88193323Sedpublic final class Context {
89193323Sed    // nashorn specific security runtime access permission names
90193323Sed    /**
91193323Sed     * Permission needed to pass arbitrary nashorn command line options when creating Context.
92193323Sed     */
93193323Sed    public static final String NASHORN_SET_CONFIG      = "nashorn.setConfig";
94193323Sed
95193323Sed    /**
96193323Sed     * Permission needed to create Nashorn Context instance.
97193323Sed     */
98193323Sed    public static final String NASHORN_CREATE_CONTEXT  = "nashorn.createContext";
99193323Sed
100218893Sdim    /**
101193323Sed     * Permission needed to create Nashorn Global instance.
102193323Sed     */
103193323Sed    public static final String NASHORN_CREATE_GLOBAL   = "nashorn.createGlobal";
104193323Sed
105193323Sed    /**
106193323Sed     * Permission to get current Nashorn Context from thread local storage.
107193323Sed     */
108193323Sed    public static final String NASHORN_GET_CONTEXT     = "nashorn.getContext";
109193323Sed
110193323Sed    /**
111193323Sed     * Permission to use Java reflection/jsr292 from script code.
112193323Sed     */
113193323Sed    public static final String NASHORN_JAVA_REFLECTION = "nashorn.JavaReflection";
114193323Sed
115234353Sdim    /**
116234353Sdim     * Permission to enable nashorn debug mode.
117234353Sdim     */
118234353Sdim    public static final String NASHORN_DEBUG_MODE = "nashorn.debugMode";
119234353Sdim
120234353Sdim    // nashorn load psuedo URL prefixes
121234353Sdim    private static final String LOAD_CLASSPATH = "classpath:";
122193323Sed    private static final String LOAD_FX = "fx:";
123234353Sdim    private static final String LOAD_NASHORN = "nashorn:";
124243830Sdim
125243830Sdim    private static MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
126193323Sed    private static MethodType CREATE_PROGRAM_FUNCTION_TYPE = MethodType.methodType(ScriptFunction.class, ScriptObject.class);
127243830Sdim
128243830Sdim    /* Force DebuggerSupport to be loaded. */
129193323Sed    static {
130193323Sed        DebuggerSupport.FORCELOAD = true;
131193323Sed    }
132193323Sed
133193323Sed    /**
134234353Sdim     * ContextCodeInstaller that has the privilege of installing classes in the Context.
135234353Sdim     * Can only be instantiated from inside the context and is opaque to other classes
136193323Sed     */
137193323Sed    public static class ContextCodeInstaller implements CodeInstaller<ScriptEnvironment> {
138193323Sed        private final Context      context;
139193323Sed        private final ScriptLoader loader;
140193323Sed        private final CodeSource   codeSource;
141193323Sed
142193323Sed        private ContextCodeInstaller(final Context context, final ScriptLoader loader, final CodeSource codeSource) {
143193323Sed            this.context    = context;
144193323Sed            this.loader     = loader;
145193323Sed            this.codeSource = codeSource;
146193323Sed        }
147193323Sed
148218893Sdim        /**
149193323Sed         * Return the context for this installer
150193323Sed         * @return ScriptEnvironment
151193323Sed         */
152193323Sed        @Override
153193323Sed        public ScriptEnvironment getOwner() {
154193323Sed            return context.env;
155193323Sed        }
156193323Sed
157193323Sed        @Override
158193323Sed        public Class<?> install(final String className, final byte[] bytecode) {
159193323Sed            final String   binaryName = Compiler.binaryName(className);
160193323Sed            return loader.installClass(binaryName, bytecode, codeSource);
161193323Sed        }
162193574Sed
163193323Sed        @Override
164193323Sed        public void initialize(final Collection<Class<?>> classes, final Source source, final Object[] constants) {
165243830Sdim            // do these in parallel, this significantly reduces class installation overhead
166243830Sdim            // however - it still means that every thread needs a separate doPrivileged
167193323Sed            classes.parallelStream().forEach(
168243830Sdim                new Consumer<Class<?>>() {
169243830Sdim                    @Override
170193323Sed                    public void accept(final Class<?> clazz) {
171193323Sed                        try {
172193323Sed                            AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
173193323Sed                                @Override
174193574Sed                                public Void run() {
175193323Sed                                    try {
176193323Sed                                        //use reflection to write source and constants table to installed classes
177193323Sed                                        final Field sourceField = clazz.getDeclaredField(SOURCE.symbolName());
178193323Sed                                        sourceField.setAccessible(true);
179193323Sed                                        sourceField.set(null, source);
180193323Sed
181193323Sed                                        final Field constantsField = clazz.getDeclaredField(CONSTANTS.symbolName());
182193323Sed                                        constantsField.setAccessible(true);
183193323Sed                                        constantsField.set(null, constants);
184193323Sed                                    } catch (final IllegalAccessException | NoSuchFieldException e) {
185193323Sed                                        throw new RuntimeException(e);
186193323Sed                                    }
187193323Sed                                    return null;
188193323Sed                                }
189193323Sed                            });
190193323Sed                        } catch (final PrivilegedActionException e) {
191193323Sed                            throw new RuntimeException(e);
192193323Sed                        }
193193323Sed                    }
194193323Sed                });
195218893Sdim        }
196218893Sdim
197193323Sed        @Override
198193323Sed        public void verify(final byte[] code) {
199193323Sed            context.verify(code);
200193323Sed        }
201193323Sed
202193323Sed        @Override
203193323Sed        public long getUniqueScriptId() {
204193323Sed            return context.getUniqueScriptId();
205239462Sdim        }
206239462Sdim
207239462Sdim        @Override
208239462Sdim        public long getUniqueEvalId() {
209239462Sdim            return context.getUniqueEvalId();
210239462Sdim        }
211239462Sdim
212239462Sdim        @Override
213239462Sdim        public void storeCompiledScript(final Source source, final String mainClassName,
214239462Sdim                                        final Map<String, byte[]> classBytes, final Object[] constants) {
215239462Sdim            if (context.codeStore != null) {
216239462Sdim                try {
217239462Sdim                    context.codeStore.putScript(source, mainClassName, classBytes, constants);
218239462Sdim                } catch (final IOException e) {
219239462Sdim                    throw new RuntimeException(e);
220239462Sdim                }
221193323Sed            }
222193323Sed        }
223193323Sed    }
224193323Sed
225193323Sed    /** Is Context global debug mode enabled ? */
226193323Sed    public static final boolean DEBUG = Options.getBooleanProperty("nashorn.debug");
227193323Sed
228193323Sed    private static final ThreadLocal<Global> currentGlobal = new ThreadLocal<>();
229193323Sed
230193323Sed    // in-memory cache for loaded classes
231193323Sed    private ClassCache classCache;
232193323Sed
233193323Sed    // persistent code store
234193323Sed    private CodeStore codeStore;
235193323Sed
236193323Sed    /**
237193323Sed     * Get the current global scope
238193323Sed     * @return the current global scope
239193323Sed     */
240193323Sed    public static Global getGlobal() {
241193323Sed        // This class in a package.access protected package.
242193323Sed        // Trusted code only can call this method.
243193323Sed        return currentGlobal.get();
244193323Sed    }
245193323Sed
246193323Sed    /**
247193323Sed     * Set the current global scope
248193323Sed     * @param global the global scope
249193323Sed     */
250193323Sed    public static void setGlobal(final ScriptObject global) {
251193323Sed        if (global != null && !(global instanceof Global)) {
252193323Sed            throw new IllegalArgumentException("not a global!");
253193323Sed        }
254198090Srdivacky        setGlobal((Global)global);
255193323Sed    }
256193323Sed
257193323Sed    /**
258193323Sed     * Set the current global scope
259193323Sed     * @param global the global scope
260193323Sed     */
261193323Sed    public static void setGlobal(final Global global) {
262193323Sed        // This class in a package.access protected package.
263193323Sed        // Trusted code only can call this method.
264193323Sed        assert getGlobal() != global;
265193323Sed        //same code can be cached between globals, then we need to invalidate method handle constants
266193323Sed        if (global != null) {
267193323Sed            Global.getConstants().invalidateAll();
268193323Sed        }
269193323Sed        currentGlobal.set(global);
270193323Sed    }
271193323Sed
272193323Sed    /**
273193323Sed     * Get context of the current global
274193323Sed     * @return current global scope's context.
275193323Sed     */
276193323Sed    public static Context getContext() {
277193323Sed        final SecurityManager sm = System.getSecurityManager();
278193323Sed        if (sm != null) {
279193323Sed            sm.checkPermission(new RuntimePermission(NASHORN_GET_CONTEXT));
280193323Sed        }
281193323Sed        return getContextTrusted();
282193323Sed    }
283193323Sed
284193323Sed    /**
285193323Sed     * Get current context's error writer
286193323Sed     *
287193323Sed     * @return error writer of the current context
288193323Sed     */
289193323Sed    public static PrintWriter getCurrentErr() {
290193323Sed        final ScriptObject global = getGlobal();
291193323Sed        return (global != null)? global.getContext().getErr() : new PrintWriter(System.err);
292193323Sed    }
293193323Sed
294193323Sed    /**
295193323Sed     * Output text to this Context's error stream
296193323Sed     * @param str text to write
297193323Sed     */
298193323Sed    public static void err(final String str) {
299193323Sed        err(str, true);
300193323Sed    }
301193323Sed
302193323Sed    /**
303193323Sed     * Output text to this Context's error stream, optionally with
304193323Sed     * a newline afterwards
305193323Sed     *
306193323Sed     * @param str  text to write
307193323Sed     * @param crlf write a carriage return/new line after text
308193323Sed     */
309193323Sed    public static void err(final String str, final boolean crlf) {
310193323Sed        final PrintWriter err = Context.getCurrentErr();
311193323Sed        if (err != null) {
312193323Sed            if (crlf) {
313193323Sed                err.println(str);
314193323Sed            } else {
315193323Sed                err.print(str);
316193323Sed            }
317193323Sed        }
318193323Sed    }
319193323Sed
320193323Sed    /** Current environment. */
321193323Sed    private final ScriptEnvironment env;
322193323Sed
323193323Sed    /** is this context in strict mode? Cached from env. as this is used heavily. */
324193323Sed    final boolean _strict;
325193323Sed
326193323Sed    /** class loader to resolve classes from script. */
327193323Sed    private final ClassLoader  appLoader;
328193323Sed
329193323Sed    /** Class loader to load classes from -classpath option, if set. */
330193323Sed    private final ClassLoader  classPathLoader;
331193323Sed
332193323Sed    /** Class loader to load classes compiled from scripts. */
333193323Sed    private final ScriptLoader scriptLoader;
334193323Sed
335193323Sed    /** Current error manager. */
336193323Sed    private final ErrorManager errors;
337193323Sed
338193323Sed    /** Unique id for script. Used only when --loader-per-compile=false */
339193323Sed    private final AtomicLong uniqueScriptId;
340193323Sed
341193323Sed    /** Unique id for 'eval' */
342193323Sed    private final AtomicLong uniqueEvalId;
343193323Sed
344193323Sed    private static final ClassLoader myLoader = Context.class.getClassLoader();
345193323Sed    private static final StructureLoader sharedLoader;
346193323Sed
347193323Sed    /*package-private*/ @SuppressWarnings("static-method")
348193323Sed    ClassLoader getSharedLoader() {
349193323Sed        return sharedLoader;
350193323Sed    }
351193323Sed
352193323Sed    private static AccessControlContext createNoPermAccCtxt() {
353193323Sed        return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, new Permissions()) });
354193323Sed    }
355193323Sed
356193323Sed    private static AccessControlContext createPermAccCtxt(final String permName) {
357193323Sed        final Permissions perms = new Permissions();
358193323Sed        perms.add(new RuntimePermission(permName));
359193323Sed        return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, perms) });
360193323Sed    }
361193323Sed
362193323Sed    private static final AccessControlContext NO_PERMISSIONS_ACC_CTXT = createNoPermAccCtxt();
363193323Sed    private static final AccessControlContext CREATE_LOADER_ACC_CTXT  = createPermAccCtxt("createClassLoader");
364193323Sed    private static final AccessControlContext CREATE_GLOBAL_ACC_CTXT  = createPermAccCtxt(NASHORN_CREATE_GLOBAL);
365193323Sed
366193323Sed    static {
367193323Sed        sharedLoader = AccessController.doPrivileged(new PrivilegedAction<StructureLoader>() {
368195098Sed            @Override
369195098Sed            public StructureLoader run() {
370198090Srdivacky                return new StructureLoader(myLoader);
371193323Sed            }
372193323Sed        }, CREATE_LOADER_ACC_CTXT);
373193323Sed    }
374193323Sed
375193323Sed    /**
376193323Sed     * ThrowErrorManager that throws ParserException upon error conditions.
377193323Sed     */
378193323Sed    public static class ThrowErrorManager extends ErrorManager {
379193323Sed        @Override
380193323Sed        public void error(final String message) {
381193323Sed            throw new ParserException(message);
382193323Sed        }
383193323Sed
384193323Sed        @Override
385193323Sed        public void error(final ParserException e) {
386193323Sed            throw e;
387193323Sed        }
388195098Sed    }
389239462Sdim
390193323Sed    /**
391193323Sed     * Constructor
392193323Sed     *
393193323Sed     * @param options options from command line or Context creator
394193323Sed     * @param errors  error manger
395193323Sed     * @param appLoader application class loader
396193323Sed     */
397193323Sed    public Context(final Options options, final ErrorManager errors, final ClassLoader appLoader) {
398234353Sdim        this(options, errors, new PrintWriter(System.out, true), new PrintWriter(System.err, true), appLoader);
399234353Sdim    }
400234353Sdim
401193323Sed    /**
402193323Sed     * Constructor
403193323Sed     *
404193323Sed     * @param options options from command line or Context creator
405193323Sed     * @param errors  error manger
406193323Sed     * @param out     output writer for this Context
407193323Sed     * @param err     error writer for this Context
408193323Sed     * @param appLoader application class loader
409193323Sed     */
410193323Sed    public Context(final Options options, final ErrorManager errors, final PrintWriter out, final PrintWriter err, final ClassLoader appLoader) {
411195098Sed        final SecurityManager sm = System.getSecurityManager();
412193323Sed        if (sm != null) {
413193323Sed            sm.checkPermission(new RuntimePermission(NASHORN_CREATE_CONTEXT));
414193323Sed        }
415193323Sed
416193323Sed        this.env       = new ScriptEnvironment(options, out, err);
417193323Sed        this._strict   = env._strict;
418193323Sed        this.appLoader = appLoader;
419226633Sdim        if (env._loader_per_compile) {
420193323Sed            this.scriptLoader = null;
421193323Sed            this.uniqueScriptId = null;
422195098Sed        } else {
423193323Sed            this.scriptLoader = createNewLoader();
424193323Sed            this.uniqueScriptId = new AtomicLong();
425239462Sdim        }
426239462Sdim        this.errors    = errors;
427239462Sdim        this.uniqueEvalId = new AtomicLong();
428239462Sdim
429239462Sdim        // if user passed -classpath option, make a class loader with that and set it as
430239462Sdim        // thread context class loader so that script can access classes from that path.
431239462Sdim        final String classPath = options.getString("classpath");
432193323Sed        if (!env._compile_only && classPath != null && !classPath.isEmpty()) {
433193323Sed            // make sure that caller can create a class loader.
434193323Sed            if (sm != null) {
435193323Sed                sm.checkPermission(new RuntimePermission("createClassLoader"));
436239462Sdim            }
437193323Sed            this.classPathLoader = NashornLoader.createClassLoader(classPath);
438193323Sed        } else {
439193323Sed            this.classPathLoader = null;
440193323Sed        }
441193323Sed
442193323Sed        final int cacheSize = env._class_cache_size;
443239462Sdim        if (cacheSize > 0) {
444193323Sed            classCache = new ClassCache(cacheSize);
445193323Sed        }
446193323Sed
447193323Sed        if (env._persistent_cache) {
448193323Sed            try {
449193323Sed                final String cacheDir = Options.getStringProperty("nashorn.persistent.code.cache", "nashorn_code_cache");
450193323Sed                codeStore = new CodeStore(cacheDir);
451193323Sed            } catch (final IOException e) {
452193323Sed                throw new RuntimeException("Error initializing code cache", e);
453193323Sed            }
454193323Sed        }
455193323Sed
456193323Sed        // print version info if asked.
457226633Sdim        if (env._version) {
458226633Sdim            getErr().println("nashorn " + Version.version());
459226633Sdim        }
460193323Sed
461193323Sed        if (env._fullversion) {
462193323Sed            getErr().println("nashorn full version " + Version.fullVersion());
463239462Sdim        }
464193323Sed
465193323Sed        initLoggers();
466239462Sdim    }
467239462Sdim
468239462Sdim    /**
469239462Sdim     * Get the error manager for this context
470239462Sdim     * @return error manger
471193323Sed     */
472193323Sed    public ErrorManager getErrorManager() {
473198090Srdivacky        return errors;
474193323Sed    }
475193323Sed
476193323Sed    /**
477193323Sed     * Get the script environment for this context
478198892Srdivacky     * @return script environment
479198892Srdivacky     */
480243830Sdim    public ScriptEnvironment getEnv() {
481243830Sdim        return env;
482243830Sdim    }
483243830Sdim
484198892Srdivacky    /**
485198892Srdivacky     * Get the output stream for this context
486193323Sed     * @return output print writer
487239462Sdim     */
488239462Sdim    public PrintWriter getOut() {
489239462Sdim        return env.getOut();
490239462Sdim    }
491193323Sed
492193323Sed    /**
493193323Sed     * Get the error stream for this context
494193323Sed     * @return error print writer
495193323Sed     */
496193323Sed    public PrintWriter getErr() {
497193323Sed        return env.getErr();
498193323Sed    }
499193323Sed
500193323Sed    /**
501193323Sed     * Get the PropertyMap of the current global scope
502193323Sed     * @return the property map of the current global scope
503193323Sed     */
504193323Sed    public static PropertyMap getGlobalMap() {
505193323Sed        return Context.getGlobal().getMap();
506193323Sed    }
507204642Srdivacky
508193323Sed    /**
509193323Sed     * Compile a top level script.
510193323Sed     *
511204642Srdivacky     * @param source the source
512234353Sdim     * @param scope  the scope
513193323Sed     *
514193323Sed     * @return top level function for script
515193323Sed     */
516193323Sed    public ScriptFunction compileScript(final Source source, final ScriptObject scope) {
517193323Sed        return compileScript(source, scope, this.errors);
518193323Sed    }
519204642Srdivacky
520234353Sdim    /**
521234353Sdim     * Interface to represent compiled code that can be re-used across many
522193323Sed     * global scope instances
523193323Sed     */
524193323Sed    public static interface MultiGlobalCompiledScript {
525193323Sed        /**
526193323Sed         * Obtain script function object for a specific global scope object.
527193323Sed         *
528193323Sed         * @param newGlobal global scope for which function object is obtained
529193323Sed         * @return script function for script level expressions
530218893Sdim         */
531193323Sed        public ScriptFunction getFunction(final Global newGlobal);
532193323Sed    }
533193323Sed
534193323Sed    /**
535193323Sed     * Compile a top level script.
536193323Sed     *
537193323Sed     * @param source the script source
538193323Sed     * @return reusable compiled script across many global scopes.
539193323Sed     */
540193323Sed    public MultiGlobalCompiledScript compileScript(final Source source) {
541193323Sed        final Class<?> clazz = compile(source, this.errors, this._strict);
542218893Sdim        final MethodHandle createProgramFunctionHandle = getCreateProgramFunctionHandle(clazz);
543193323Sed
544193323Sed        return new MultiGlobalCompiledScript() {
545193323Sed            @Override
546193323Sed            public ScriptFunction getFunction(final Global newGlobal) {
547193323Sed                return invokeCreateProgramFunctionHandle(createProgramFunctionHandle, newGlobal);
548193323Sed            }
549193323Sed        };
550193323Sed    }
551193323Sed
552193323Sed    /**
553193323Sed     * Entry point for {@code eval}
554193323Sed     *
555193323Sed     * @param initialScope The scope of this eval call
556193323Sed     * @param string       Evaluated code as a String
557193323Sed     * @param callThis     "this" to be passed to the evaluated code
558193323Sed     * @param location     location of the eval call
559193323Sed     * @param strict       is this {@code eval} call from a strict mode code?
560193323Sed     * @return the return value of the {@code eval}
561193323Sed     */
562193323Sed    public Object eval(final ScriptObject initialScope, final String string,
563193323Sed            final Object callThis, final Object location, final boolean strict) {
564193323Sed        return eval(initialScope, string, callThis, location, strict, false);
565193323Sed    }
566193323Sed
567193323Sed    /**
568193323Sed     * Entry point for {@code eval}
569193323Sed     *
570239462Sdim     * @param initialScope The scope of this eval call
571193323Sed     * @param string       Evaluated code as a String
572193323Sed     * @param callThis     "this" to be passed to the evaluated code
573193323Sed     * @param location     location of the eval call
574193323Sed     * @param strict       is this {@code eval} call from a strict mode code?
575193323Sed     * @param evalCall     is this called from "eval" builtin?
576193323Sed     *
577239462Sdim     * @return the return value of the {@code eval}
578239462Sdim     */
579193323Sed    public Object eval(final ScriptObject initialScope, final String string,
580193323Sed            final Object callThis, final Object location, final boolean strict, final boolean evalCall) {
581193323Sed        final String  file       = location == UNDEFINED || location == null ? "<eval>" : location.toString();
582193323Sed        final Source  source     = sourceFor(file, string, evalCall);
583193323Sed        final boolean directEval = location != UNDEFINED; // is this direct 'eval' call or indirectly invoked eval?
584193323Sed        final Global  global = Context.getGlobal();
585193323Sed        ScriptObject scope = initialScope;
586193323Sed
587193323Sed        // ECMA section 10.1.1 point 2 says eval code is strict if it begins
588193323Sed        // with "use strict" directive or eval direct call itself is made
589193323Sed        // from from strict mode code. We are passed with caller's strict mode.
590193323Sed        boolean strictFlag = directEval && strict;
591193323Sed
592193323Sed        Class<?> clazz = null;
593193323Sed        try {
594193323Sed            clazz = compile(source, new ThrowErrorManager(), strictFlag);
595193323Sed        } catch (final ParserException e) {
596193323Sed            e.throwAsEcmaException(global);
597193323Sed            return null;
598193323Sed        }
599239462Sdim
600193323Sed        if (!strictFlag) {
601234353Sdim            // We need to get strict mode flag from compiled class. This is
602234353Sdim            // because eval code may start with "use strict" directive.
603234353Sdim            try {
604234353Sdim                strictFlag = clazz.getField(STRICT_MODE.symbolName()).getBoolean(null);
605234353Sdim            } catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
606234353Sdim                //ignored
607239462Sdim                strictFlag = false;
608193323Sed            }
609193323Sed        }
610193323Sed
611193323Sed        // In strict mode, eval does not instantiate variables and functions
612193323Sed        // in the caller's environment. A new environment is created!
613193323Sed        if (strictFlag) {
614193323Sed            // Create a new scope object
615193323Sed            final ScriptObject strictEvalScope = global.newObject();
616193323Sed
617193323Sed            // bless it as a "scope"
618193323Sed            strictEvalScope.setIsScope();
619193323Sed
620193323Sed            // set given scope to be it's proto so that eval can still
621193323Sed            // access caller environment vars in the new environment.
622193323Sed            strictEvalScope.setProto(scope);
623193323Sed            scope = strictEvalScope;
624193323Sed        }
625193323Sed
626193323Sed        final ScriptFunction func = getProgramFunction(clazz, scope);
627193323Sed        Object evalThis;
628193323Sed        if (directEval) {
629193323Sed            evalThis = callThis instanceof ScriptObject || strictFlag ? callThis : global;
630193323Sed        } else {
631193323Sed            evalThis = global;
632193323Sed        }
633193323Sed
634193323Sed        return ScriptRuntime.apply(func, evalThis);
635193323Sed    }
636193323Sed
637193323Sed    private static Source loadInternal(final String srcStr, final String prefix, final String resourcePath) {
638200581Srdivacky        if (srcStr.startsWith(prefix)) {
639200581Srdivacky            final String resource = resourcePath + srcStr.substring(prefix.length());
640202878Srdivacky            // NOTE: even sandbox scripts should be able to load scripts in nashorn: scheme
641205218Srdivacky            // These scripts are always available and are loaded from nashorn.jar's resources.
642206083Srdivacky            return AccessController.doPrivileged(
643224145Sdim                    new PrivilegedAction<Source>() {
644206083Srdivacky                        @Override
645206083Srdivacky                        public Source run() {
646193323Sed                            try {
647193323Sed                                final URL resURL = Context.class.getResource(resource);
648193323Sed                                return resURL != null ? sourceFor(srcStr, resURL) : null;
649193323Sed                            } catch (final IOException exp) {
650193323Sed                                return null;
651193323Sed                            }
652193323Sed                        }
653193323Sed                    });
654193323Sed        }
655193323Sed
656193323Sed        return null;
657193323Sed    }
658193323Sed
659193323Sed    /**
660193323Sed     * Implementation of {@code load} Nashorn extension. Load a script file from a source
661193323Sed     * expression
662193323Sed     *
663193323Sed     * @param scope  the scope
664193323Sed     * @param from   source expression for script
665195098Sed     *
666195098Sed     * @return return value for load call (undefined)
667195098Sed     *
668195098Sed     * @throws IOException if source cannot be found or loaded
669195098Sed     */
670193323Sed    public Object load(final ScriptObject scope, final Object from) throws IOException {
671195098Sed        final Object src = from instanceof ConsString ? from.toString() : from;
672193323Sed        Source source = null;
673198090Srdivacky
674193323Sed        // load accepts a String (which could be a URL or a file name), a File, a URL
675193323Sed        // or a ScriptObject that has "name" and "source" (string valued) properties.
676193323Sed        if (src instanceof String) {
677198090Srdivacky            final String srcStr = (String)src;
678198090Srdivacky            if (srcStr.startsWith(LOAD_CLASSPATH)) {
679193323Sed                final URL url = getResourceURL(srcStr.substring(LOAD_CLASSPATH.length()));
680193323Sed                source = url != null ? sourceFor(url.toString(), url) : null;
681193323Sed            } else {
682193323Sed                final File file = new File(srcStr);
683193323Sed                if (srcStr.indexOf(':') != -1) {
684218893Sdim                    if ((source = loadInternal(srcStr, LOAD_NASHORN, "resources/")) == null &&
685218893Sdim                        (source = loadInternal(srcStr, LOAD_FX, "resources/fx/")) == null) {
686193323Sed                        URL url;
687193323Sed                        try {
688193323Sed                            //check for malformed url. if malformed, it may still be a valid file
689193323Sed                            url = new URL(srcStr);
690193323Sed                        } catch (final MalformedURLException e) {
691193323Sed                            url = file.toURI().toURL();
692193323Sed                        }
693218893Sdim                        source = sourceFor(url.toString(), url);
694193323Sed                    }
695193323Sed                } else if (file.isFile()) {
696202375Srdivacky                    source = sourceFor(srcStr, file);
697198090Srdivacky                }
698193323Sed            }
699193323Sed        } else if (src instanceof File && ((File)src).isFile()) {
700193323Sed            final File file = (File)src;
701193323Sed            source = sourceFor(file.getName(), file);
702193323Sed        } else if (src instanceof URL) {
703193323Sed            final URL url = (URL)src;
704193323Sed            source = sourceFor(url.toString(), url);
705193323Sed        } else if (src instanceof ScriptObject) {
706193323Sed            final ScriptObject sobj = (ScriptObject)src;
707193323Sed            if (sobj.has("script") && sobj.has("name")) {
708193323Sed                final String script = JSType.toString(sobj.get("script"));
709239462Sdim                final String name   = JSType.toString(sobj.get("name"));
710193323Sed                source = sourceFor(name, script);
711193323Sed            }
712193323Sed        } else if (src instanceof Map) {
713193323Sed            final Map<?,?> map = (Map<?,?>)src;
714193323Sed            if (map.containsKey("script") && map.containsKey("name")) {
715193323Sed                final String script = JSType.toString(map.get("script"));
716193323Sed                final String name   = JSType.toString(map.get("name"));
717193323Sed                source = sourceFor(name, script);
718239462Sdim            }
719193323Sed        }
720239462Sdim
721239462Sdim        if (source != null) {
722239462Sdim            return evaluateSource(source, scope, scope);
723193323Sed        }
724193323Sed
725193323Sed        throw typeError("cant.load.script", ScriptRuntime.safeToString(from));
726193323Sed    }
727193323Sed
728239462Sdim    /**
729239462Sdim     * Implementation of {@code loadWithNewGlobal} Nashorn extension. Load a script file from a source
730239462Sdim     * expression, after creating a new global scope.
731193323Sed     *
732193323Sed     * @param from source expression for script
733193323Sed     * @param args (optional) arguments to be passed to the loaded script
734193323Sed     *
735193323Sed     * @return return value for load call (undefined)
736193323Sed     *
737193323Sed     * @throws IOException if source cannot be found or loaded
738193323Sed     */
739193323Sed    public Object loadWithNewGlobal(final Object from, final Object...args) throws IOException {
740193323Sed        final Global oldGlobal = getGlobal();
741193323Sed        final Global newGlobal = AccessController.doPrivileged(new PrivilegedAction<Global>() {
742193323Sed           @Override
743193323Sed           public Global run() {
744193323Sed               try {
745193323Sed                   return newGlobal();
746200581Srdivacky               } catch (final RuntimeException e) {
747200581Srdivacky                   if (Context.DEBUG) {
748193323Sed                       e.printStackTrace();
749193323Sed                   }
750193323Sed                   throw e;
751193323Sed               }
752193323Sed           }
753193323Sed        }, CREATE_GLOBAL_ACC_CTXT);
754193323Sed        // initialize newly created Global instance
755193323Sed        initGlobal(newGlobal);
756193323Sed        setGlobal(newGlobal);
757193323Sed
758193323Sed        final Object[] wrapped = args == null? ScriptRuntime.EMPTY_ARRAY :  ScriptObjectMirror.wrapArray(args, oldGlobal);
759193323Sed        newGlobal.put("arguments", newGlobal.wrapAsObject(wrapped), env._strict);
760193323Sed
761193323Sed        try {
762193323Sed            // wrap objects from newGlobal's world as mirrors - but if result
763193323Sed            // is from oldGlobal's world, unwrap it!
764200581Srdivacky            return ScriptObjectMirror.unwrap(ScriptObjectMirror.wrap(load(newGlobal, from), newGlobal), oldGlobal);
765200581Srdivacky        } finally {
766193323Sed            setGlobal(oldGlobal);
767193323Sed        }
768193323Sed    }
769193323Sed
770193323Sed    /**
771193323Sed     * Load or get a structure class. Structure class names are based on the number of parameter fields
772193323Sed     * and {@link AccessorProperty} fields in them. Structure classes are used to represent ScriptObjects
773193323Sed     *
774193323Sed     * @see ObjectClassGenerator
775193323Sed     * @see AccessorProperty
776193323Sed     * @see ScriptObject
777193323Sed     *
778193323Sed     * @param fullName  full name of class, e.g. jdk.nashorn.internal.objects.JO2P1 contains 2 fields and 1 parameter.
779193323Sed     *
780193323Sed     * @return the {@code Class<?>} for this structure
781193323Sed     *
782200581Srdivacky     * @throws ClassNotFoundException if structure class cannot be resolved
783200581Srdivacky     */
784193323Sed    @SuppressWarnings("unchecked")
785193323Sed    public static Class<? extends ScriptObject> forStructureClass(final String fullName) throws ClassNotFoundException {
786218893Sdim        if (System.getSecurityManager() != null && !StructureLoader.isStructureClass(fullName)) {
787218893Sdim            throw new ClassNotFoundException(fullName);
788218893Sdim        }
789193323Sed        return (Class<? extends ScriptObject>)Class.forName(fullName, true, sharedLoader);
790193323Sed    }
791193323Sed
792193323Sed    /**
793198090Srdivacky     * Checks that the given Class can be accessed from no permissions context.
794193323Sed     *
795193323Sed     * @param clazz Class object
796193323Sed     * @throws SecurityException if not accessible
797193323Sed     */
798193323Sed    public static void checkPackageAccess(final Class<?> clazz) {
799193323Sed        final SecurityManager sm = System.getSecurityManager();
800193323Sed        if (sm != null) {
801193323Sed            Class<?> bottomClazz = clazz;
802193323Sed            while (bottomClazz.isArray()) {
803193323Sed                bottomClazz = bottomClazz.getComponentType();
804193323Sed            }
805193323Sed            checkPackageAccess(sm, bottomClazz.getName());
806193323Sed        }
807193323Sed    }
808193323Sed
809193323Sed    /**
810193323Sed     * Checks that the given package name can be accessed from no permissions context.
811198090Srdivacky     *
812226633Sdim     * @param pkgName package name
813193323Sed     * @throws SecurityException if not accessible
814193323Sed     */
815193323Sed    public static void checkPackageAccess(final String pkgName) {
816193323Sed        final SecurityManager sm = System.getSecurityManager();
817226633Sdim        if (sm != null) {
818226633Sdim            checkPackageAccess(sm, pkgName.endsWith(".") ? pkgName : pkgName + ".");
819226633Sdim        }
820193323Sed    }
821193323Sed
822193323Sed    /**
823193323Sed     * Checks that the given package can be accessed from no permissions context.
824193323Sed     *
825218893Sdim     * @param sm current security manager instance
826218893Sdim     * @param fullName fully qualified package name
827218893Sdim     * @throw SecurityException if not accessible
828218893Sdim     */
829218893Sdim    private static void checkPackageAccess(final SecurityManager sm, final String fullName) {
830218893Sdim        sm.getClass(); // null check
831218893Sdim        final int index = fullName.lastIndexOf('.');
832218893Sdim        if (index != -1) {
833218893Sdim            final String pkgName = fullName.substring(0, index);
834218893Sdim            AccessController.doPrivileged(new PrivilegedAction<Void>() {
835218893Sdim                @Override
836218893Sdim                public Void run() {
837218893Sdim                    sm.checkPackageAccess(pkgName);
838218893Sdim                    return null;
839218893Sdim                }
840218893Sdim            }, NO_PERMISSIONS_ACC_CTXT);
841218893Sdim        }
842218893Sdim    }
843218893Sdim
844218893Sdim    /**
845218893Sdim     * Checks that the given Class can be accessed from no permissions context.
846218893Sdim     *
847218893Sdim     * @param clazz Class object
848218893Sdim     * @return true if package is accessible, false otherwise
849218893Sdim     */
850218893Sdim    private static boolean isAccessiblePackage(final Class<?> clazz) {
851218893Sdim        try {
852218893Sdim            checkPackageAccess(clazz);
853218893Sdim            return true;
854218893Sdim        } catch (final SecurityException se) {
855218893Sdim            return false;
856218893Sdim        }
857218893Sdim    }
858218893Sdim
859218893Sdim    /**
860218893Sdim     * Checks that the given Class is public and it can be accessed from no permissions context.
861218893Sdim     *
862218893Sdim     * @param clazz Class object to check
863198090Srdivacky     * @return true if Class is accessible, false otherwise
864193323Sed     */
865193323Sed    public static boolean isAccessibleClass(final Class<?> clazz) {
866198090Srdivacky        return Modifier.isPublic(clazz.getModifiers()) && Context.isAccessiblePackage(clazz);
867226633Sdim    }
868198090Srdivacky
869198090Srdivacky    /**
870193323Sed     * Lookup a Java class. This is used for JSR-223 stuff linking in from
871243830Sdim     * {@code jdk.nashorn.internal.objects.NativeJava} and {@code jdk.nashorn.internal.runtime.NativeJavaPackage}
872193323Sed     *
873193323Sed     * @param fullName full name of class to load
874193323Sed     *
875234353Sdim     * @return the {@code Class<?>} for the name
876208599Srdivacky     *
877249423Sdim     * @throws ClassNotFoundException if class cannot be resolved
878249423Sdim     */
879239462Sdim    public Class<?> findClass(final String fullName) throws ClassNotFoundException {
880193323Sed        if (fullName.indexOf('[') != -1 || fullName.indexOf('/') != -1) {
881202878Srdivacky            // don't allow array class names or internal names.
882205218Srdivacky            throw new ClassNotFoundException(fullName);
883193323Sed        }
884193323Sed
885249423Sdim        // check package access as soon as possible!
886193323Sed        final SecurityManager sm = System.getSecurityManager();
887249423Sdim        if (sm != null) {
888198090Srdivacky            checkPackageAccess(sm, fullName);
889193323Sed        }
890193323Sed
891193323Sed        // try the script -classpath loader, if that is set
892239462Sdim        if (classPathLoader != null) {
893193323Sed            try {
894200581Srdivacky                return Class.forName(fullName, true, classPathLoader);
895205218Srdivacky            } catch (final ClassNotFoundException ignored) {
896193323Sed                // ignore, continue search
897193323Sed            }
898193323Sed        }
899193323Sed
900193323Sed        // Try finding using the "app" loader.
901193323Sed        return Class.forName(fullName, true, appLoader);
902193323Sed    }
903193323Sed
904193323Sed    /**
905193323Sed     * Hook to print stack trace for a {@link Throwable} that occurred during
906193323Sed     * execution
907193323Sed     *
908193323Sed     * @param t throwable for which to dump stack
909193323Sed     */
910193323Sed    public static void printStackTrace(final Throwable t) {
911193323Sed        if (Context.DEBUG) {
912193323Sed            t.printStackTrace(Context.getCurrentErr());
913193323Sed        }
914193323Sed    }
915193323Sed
916193323Sed    /**
917193323Sed     * Verify generated bytecode before emission. This is called back from the
918193323Sed     * {@link ObjectClassGenerator} or the {@link Compiler}. If the "--verify-code" parameter
919193323Sed     * hasn't been given, this is a nop
920193323Sed     *
921210299Sed     * Note that verification may load classes -- we don't want to do that unless
922206083Srdivacky     * user specified verify option. We check it here even though caller
923193323Sed     * may have already checked that flag
924193323Sed     *
925226633Sdim     * @param bytecode bytecode to verify
926226633Sdim     */
927226633Sdim    public void verify(final byte[] bytecode) {
928226633Sdim        if (env._verify_code) {
929226633Sdim            // No verification when security manager is around as verifier
930226633Sdim            // may load further classes - which should be avoided.
931198090Srdivacky            if (System.getSecurityManager() == null) {
932198090Srdivacky                CheckClassAdapter.verify(new ClassReader(bytecode), sharedLoader, false, new PrintWriter(System.err, true));
933198090Srdivacky            }
934198090Srdivacky        }
935198090Srdivacky    }
936198090Srdivacky
937198090Srdivacky    /**
938198090Srdivacky     * Create and initialize a new global scope object.
939198090Srdivacky     *
940198090Srdivacky     * @return the initialized global scope object.
941198090Srdivacky     */
942198090Srdivacky    public Global createGlobal() {
943198090Srdivacky        return initGlobal(newGlobal());
944200581Srdivacky    }
945200581Srdivacky
946200581Srdivacky    /**
947193323Sed     * Create a new uninitialized global scope object
948200581Srdivacky     * @return the global script object
949200581Srdivacky     */
950193323Sed    public Global newGlobal() {
951193323Sed        return new Global(this);
952193323Sed    }
953193323Sed
954193323Sed    /**
955193323Sed     * Initialize given global scope object.
956193323Sed     *
957198090Srdivacky     * @param global the global
958204642Srdivacky     * @param engine the associated ScriptEngine instance, can be null
959193323Sed     * @return the initialized global scope object.
960193323Sed     */
961193323Sed    public Global initGlobal(final Global global, final ScriptEngine engine) {
962193323Sed        // Need only minimal global object, if we are just compiling.
963193323Sed        if (!env._compile_only) {
964198090Srdivacky            final Global oldGlobal = Context.getGlobal();
965204642Srdivacky            try {
966193323Sed                Context.setGlobal(global);
967193323Sed                // initialize global scope with builtin global objects
968193323Sed                global.initBuiltinObjects(engine);
969193323Sed            } finally {
970193323Sed                Context.setGlobal(oldGlobal);
971193323Sed            }
972198090Srdivacky        }
973198090Srdivacky
974193323Sed        return global;
975193323Sed    }
976198090Srdivacky
977193323Sed    /**
978193323Sed     * Initialize given global scope object.
979204642Srdivacky     *
980226633Sdim     * @param global the global
981226633Sdim     * @return the initialized global scope object.
982226633Sdim     */
983226633Sdim    public Global initGlobal(final Global global) {
984226633Sdim        return initGlobal(global, null);
985226633Sdim    }
986226633Sdim
987226633Sdim    /**
988226633Sdim     * Return the current global's context
989226633Sdim     * @return current global's context
990226633Sdim     */
991226633Sdim    static Context getContextTrusted() {
992226633Sdim        return ((ScriptObject)Context.getGlobal()).getContext();
993226633Sdim    }
994193323Sed
995193323Sed    static Context getContextTrustedOrNull() {
996193323Sed        final Global global = Context.getGlobal();
997193323Sed        return global == null ? null : ((ScriptObject)global).getContext();
998226633Sdim    }
999193323Sed
1000193323Sed    /**
1001201360Srdivacky     * Try to infer Context instance from the Class. If we cannot,
1002193323Sed     * then get it from the thread local variable.
1003193323Sed     *
1004201360Srdivacky     * @param clazz the class
1005193323Sed     * @return context
1006226633Sdim     */
1007193323Sed    static Context fromClass(final Class<?> clazz) {
1008193323Sed        final ClassLoader loader = clazz.getClassLoader();
1009193323Sed
1010193323Sed        if (loader instanceof ScriptLoader) {
1011193323Sed            return ((ScriptLoader)loader).getContext();
1012193323Sed        }
1013193323Sed
1014193323Sed        return Context.getContextTrusted();
1015206124Srdivacky    }
1016193323Sed
1017193323Sed    private URL getResourceURL(final String resName) {
1018193323Sed        // try the classPathLoader if we have and then
1019193323Sed        // try the appLoader if non-null.
1020193323Sed        if (classPathLoader != null) {
1021193323Sed            return classPathLoader.getResource(resName);
1022193323Sed        } else if (appLoader != null) {
1023193323Sed            return appLoader.getResource(resName);
1024193323Sed        }
1025198090Srdivacky
1026198090Srdivacky        return null;
1027193323Sed    }
1028193323Sed
1029198090Srdivacky    private Object evaluateSource(final Source source, final ScriptObject scope, final ScriptObject thiz) {
1030193323Sed        ScriptFunction script = null;
1031193323Sed
1032204642Srdivacky        try {
1033193323Sed            script = compileScript(source, scope, new Context.ThrowErrorManager());
1034193323Sed        } catch (final ParserException e) {
1035193323Sed            e.throwAsEcmaException();
1036193323Sed        }
1037193323Sed
1038193323Sed        return ScriptRuntime.apply(script, thiz);
1039193323Sed    }
1040193323Sed
1041193323Sed    private static ScriptFunction getProgramFunction(final Class<?> script, final ScriptObject scope) {
1042193323Sed        if (script == null) {
1043201360Srdivacky            return null;
1044193323Sed        }
1045193323Sed        return invokeCreateProgramFunctionHandle(getCreateProgramFunctionHandle(script), scope);
1046201360Srdivacky    }
1047193323Sed
1048205407Srdivacky    private static MethodHandle getCreateProgramFunctionHandle(final Class<?> script) {
1049193323Sed        try {
1050193323Sed            return LOOKUP.findStatic(script, CREATE_PROGRAM_FUNCTION.symbolName(), CREATE_PROGRAM_FUNCTION_TYPE);
1051193323Sed        } catch (NoSuchMethodException | IllegalAccessException e) {
1052193323Sed            throw new AssertionError("Failed to retrieve a handle for the program function for " + script.getName(), e);
1053193323Sed        }
1054193323Sed    }
1055193323Sed
1056193323Sed    private static ScriptFunction invokeCreateProgramFunctionHandle(final MethodHandle createProgramFunctionHandle, final ScriptObject scope) {
1057193323Sed        try {
1058206124Srdivacky            return (ScriptFunction)createProgramFunctionHandle.invokeExact(scope);
1059193323Sed        } catch (final RuntimeException|Error e) {
1060193323Sed            throw e;
1061193323Sed        } catch (final Throwable t) {
1062193323Sed            throw new AssertionError("Failed to create a program function", t);
1063198090Srdivacky        }
1064204642Srdivacky    }
1065193323Sed
1066193323Sed    private ScriptFunction compileScript(final Source source, final ScriptObject scope, final ErrorManager errMan) {
1067208599Srdivacky        return getProgramFunction(compile(source, errMan, this._strict), scope);
1068193323Sed    }
1069249423Sdim
1070249423Sdim    private synchronized Class<?> compile(final Source source, final ErrorManager errMan, final boolean strict) {
1071208599Srdivacky        // start with no errors, no warnings.
1072208599Srdivacky        errMan.reset();
1073249423Sdim
1074208599Srdivacky        Class<?> script = findCachedClass(source);
1075208599Srdivacky        if (script != null) {
1076234353Sdim            final DebugLogger log = getLogger(Compiler.class);
1077234353Sdim            if (log.isEnabled()) {
1078193323Sed                log.fine(new RuntimeEvent<>(Level.INFO, source), "Code cache hit for ", source, " avoiding recompile.");
1079193323Sed            }
1080210299Sed            return script;
1081198090Srdivacky        }
1082195098Sed
1083195098Sed        CompiledScript compiledScript = null;
1084195098Sed        FunctionNode functionNode = null;
1085195098Sed
1086198090Srdivacky        if (!env._parse_only && codeStore != null) {
1087193323Sed            try {
1088243830Sdim                compiledScript = codeStore.getScript(source);
1089193323Sed            } catch (IOException | ClassNotFoundException e) {
1090243830Sdim                getLogger(Compiler.class).warning("Error loading ", source, " from cache: ", e);
1091193323Sed                // Fall back to normal compilation
1092193323Sed            }
1093193323Sed        }
1094193323Sed
1095193323Sed        if (compiledScript == null) {
1096193323Sed            functionNode = new Parser(env, source, errMan, strict, getLogger(Parser.class)).parse();
1097193323Sed
1098193323Sed            if (errors.hasErrors()) {
1099195098Sed                return null;
1100193323Sed            }
1101193323Sed
1102193323Sed            if (env._print_ast) {
1103193323Sed                getErr().println(new ASTWriter(functionNode));
1104193323Sed            }
1105193323Sed
1106193323Sed            if (env._print_parse) {
1107193323Sed                getErr().println(new PrintVisitor(functionNode, true, false));
1108193323Sed            }
1109195098Sed        }
1110239462Sdim
1111193323Sed        if (env._parse_only) {
1112201360Srdivacky            return null;
1113193323Sed        }
1114201360Srdivacky
1115210299Sed        final URL          url    = source.getURL();
1116205407Srdivacky        final ScriptLoader loader = env._loader_per_compile ? createNewLoader() : scriptLoader;
1117193323Sed        final CodeSource   cs     = new CodeSource(url, (CodeSigner[])null);
1118193323Sed        final CodeInstaller<ScriptEnvironment> installer = new ContextCodeInstaller(this, loader, cs);
1119193323Sed
1120193323Sed        if (functionNode != null) {
1121193323Sed            final CompilationPhases phases = Compiler.CompilationPhases.COMPILE_ALL;
1122198090Srdivacky
1123193323Sed            final Compiler compiler = new Compiler(
1124193323Sed                    this,
1125193323Sed                    env,
1126193323Sed                    installer,
1127193323Sed                    source,
1128201360Srdivacky                    functionNode.getSourceURL(),
1129193323Sed                    strict | functionNode.isStrict());
1130201360Srdivacky
1131205407Srdivacky            script = compiler.compile(functionNode, phases).getRootClass();
1132193323Sed        } else {
1133193323Sed            script = install(compiledScript, installer);
1134193323Sed        }
1135193323Sed
1136193323Sed        cacheClass(source, script);
1137198090Srdivacky        return script;
1138195098Sed    }
1139195098Sed
1140195098Sed    private ScriptLoader createNewLoader() {
1141193323Sed        return AccessController.doPrivileged(
1142193323Sed             new PrivilegedAction<ScriptLoader>() {
1143193323Sed                @Override
1144193323Sed                public ScriptLoader run() {
1145195098Sed                    return new ScriptLoader(appLoader, Context.this);
1146193323Sed                }
1147201360Srdivacky             }, CREATE_LOADER_ACC_CTXT);
1148193323Sed    }
1149201360Srdivacky
1150205407Srdivacky    private long getUniqueEvalId() {
1151205407Srdivacky        return uniqueEvalId.getAndIncrement();
1152193323Sed    }
1153193323Sed
1154193323Sed    private long getUniqueScriptId() {
1155193323Sed        return uniqueScriptId.getAndIncrement();
1156193323Sed    }
1157207618Srdivacky
1158193323Sed
1159198090Srdivacky    /**
1160195098Sed     * Install a previously compiled class from the code cache.
1161195098Sed     *
1162195098Sed     * @param compiledScript cached script containing class bytes and constants
1163193323Sed     * @return main script class
1164243830Sdim     */
1165193323Sed    private static Class<?> install(final CompiledScript compiledScript, final CodeInstaller<ScriptEnvironment> installer) {
1166193323Sed
1167193323Sed        final Map<String, Class<?>> installedClasses = new HashMap<>();
1168193323Sed        final Source   source        = compiledScript.getSource();
1169193323Sed        final Object[] constants     = compiledScript.getConstants();
1170193323Sed        final String   rootClassName = compiledScript.getMainClassName();
1171195098Sed        final byte[]   rootByteCode  = compiledScript.getClassBytes().get(rootClassName);
1172193323Sed        final Class<?> rootClass     = installer.install(rootClassName, rootByteCode);
1173201360Srdivacky
1174193323Sed        installedClasses.put(rootClassName, rootClass);
1175201360Srdivacky
1176205407Srdivacky        for (final Map.Entry<String, byte[]> entry : compiledScript.getClassBytes().entrySet()) {
1177205407Srdivacky            final String className = entry.getKey();
1178193323Sed            if (className.equals(rootClassName)) {
1179193323Sed                continue;
1180193323Sed            }
1181193323Sed            final byte[] code = entry.getValue();
1182193323Sed
1183193323Sed            installedClasses.put(className, installer.install(className, code));
1184198090Srdivacky        }
1185193323Sed
1186195098Sed        installer.initialize(installedClasses.values(), source, constants);
1187195098Sed
1188195098Sed        for (final Object constant : constants) {
1189195098Sed            if (constant instanceof RecompilableScriptFunctionData) {
1190193323Sed                ((RecompilableScriptFunctionData) constant).initTransients(source, installer);
1191243830Sdim            }
1192193323Sed        }
1193193323Sed
1194193323Sed        return rootClass;
1195193323Sed    }
1196193323Sed
1197226633Sdim    /**
1198195098Sed     * Cache for compiled script classes.
1199193323Sed     */
1200201360Srdivacky    @SuppressWarnings("serial")
1201193323Sed    private static class ClassCache extends LinkedHashMap<Source, ClassReference> {
1202201360Srdivacky        private final int size;
1203205407Srdivacky        private final ReferenceQueue<Class<?>> queue;
1204205407Srdivacky
1205193323Sed        ClassCache(final int size) {
1206193323Sed            super(size, 0.75f, true);
1207193323Sed            this.size = size;
1208193323Sed            this.queue = new ReferenceQueue<>();
1209193323Sed        }
1210239462Sdim
1211239462Sdim        void cache(final Source source, final Class<?> clazz) {
1212239462Sdim            put(source, new ClassReference(clazz, queue, source));
1213239462Sdim        }
1214239462Sdim
1215239462Sdim        @Override
1216239462Sdim        protected boolean removeEldestEntry(final Map.Entry<Source, ClassReference> eldest) {
1217239462Sdim            return size() > size;
1218239462Sdim        }
1219239462Sdim
1220239462Sdim        @Override
1221239462Sdim        public ClassReference get(final Object key) {
1222239462Sdim            for (ClassReference ref; (ref = (ClassReference)queue.poll()) != null; ) {
1223239462Sdim                remove(ref.source);
1224239462Sdim            }
1225239462Sdim            return super.get(key);
1226239462Sdim        }
1227239462Sdim
1228193323Sed    }
1229193323Sed
1230193323Sed    private static class ClassReference extends SoftReference<Class<?>> {
1231193323Sed        private final Source source;
1232193323Sed
1233201360Srdivacky        ClassReference(final Class<?> clazz, final ReferenceQueue<Class<?>> queue, final Source source) {
1234193323Sed            super(clazz, queue);
1235201360Srdivacky            this.source = source;
1236205407Srdivacky        }
1237193323Sed    }
1238193323Sed
1239193323Sed    // Class cache management
1240193323Sed    private Class<?> findCachedClass(final Source source) {
1241193323Sed        final ClassReference ref = classCache == null ? null : classCache.get(source);
1242198090Srdivacky        return ref != null ? ref.get() : null;
1243198090Srdivacky    }
1244198090Srdivacky
1245198090Srdivacky    private void cacheClass(final Source source, final Class<?> clazz) {
1246193323Sed        if (classCache != null) {
1247193323Sed            classCache.cache(source, clazz);
1248198090Srdivacky        }
1249193323Sed    }
1250193323Sed
1251205407Srdivacky    // logging
1252193323Sed    private final Map<String, DebugLogger> loggers = new HashMap<>();
1253193323Sed
1254193323Sed    private void initLoggers() {
1255193323Sed        ((Loggable)MethodHandleFactory.getFunctionality()).initLogger(this);
1256198090Srdivacky    }
1257193323Sed
1258193323Sed    /**
1259205407Srdivacky     * Get a logger, given a loggable class
1260193323Sed     * @param clazz a Loggable class
1261193323Sed     * @return debuglogger associated with that class
1262193323Sed     */
1263193323Sed    public DebugLogger getLogger(final Class<? extends Loggable> clazz) {
1264198090Srdivacky        return getLogger(clazz, null);
1265195098Sed    }
1266195098Sed
1267195098Sed    /**
1268195098Sed     * Get a logger, given a loggable class
1269193323Sed     * @param clazz a Loggable class
1270205407Srdivacky     * @param initHook an init hook - if this is the first time the logger is created in the context, run the init hook
1271193323Sed     * @return debuglogger associated with that class
1272193323Sed     */
1273193323Sed    public DebugLogger getLogger(final Class<? extends Loggable> clazz, final Consumer<DebugLogger> initHook) {
1274193323Sed        final String name = getLoggerName(clazz);
1275193323Sed        DebugLogger logger = loggers.get(name);
1276193323Sed        if (logger == null) {
1277193323Sed            if (!env.hasLogger(name)) {
1278193323Sed                return DebugLogger.DISABLED_LOGGER;
1279193323Sed            }
1280205407Srdivacky            final LoggerInfo info = env._loggers.get(name);
1281193323Sed            logger = new DebugLogger(name, info.getLevel(), info.isQuiet());
1282193323Sed            if (initHook != null) {
1283193323Sed                initHook.accept(logger);
1284201360Srdivacky            }
1285193323Sed            loggers.put(name, logger);
1286193323Sed        }
1287193323Sed        return logger;
1288193323Sed    }
1289193323Sed
1290193323Sed    /**
1291193323Sed     * Given a Loggable class, weave debug info info a method handle for that logger.
1292193323Sed     * Level.INFO is used
1293193323Sed     *
1294193323Sed     * @param clazz loggable
1295193323Sed     * @param mh    method handle
1296193323Sed     * @param text  debug printout to add
1297193323Sed     *
1298193323Sed     * @return instrumented method handle, or null if logger not enabled
1299193323Sed     */
1300193323Sed    public MethodHandle addLoggingToHandle(final Class<? extends Loggable> clazz, final MethodHandle mh, final Supplier<String> text) {
1301193323Sed        return addLoggingToHandle(clazz, Level.INFO, mh, Integer.MAX_VALUE, false, text);
1302198090Srdivacky    }
1303193323Sed
1304193323Sed    /**
1305198090Srdivacky     * Given a Loggable class, weave debug info info a method handle for that logger.
1306193323Sed     *
1307193323Sed     * @param clazz            loggable
1308193323Sed     * @param level            log level
1309193323Sed     * @param mh               method handle
1310193323Sed     * @param paramStart       first parameter to print
1311193323Sed     * @param printReturnValue should we print the return vaulue?
1312198090Srdivacky     * @param text             debug printout to add
1313193323Sed     *
1314198090Srdivacky     * @return instrumented method handle, or null if logger not enabled
1315193323Sed     */
1316193323Sed    public MethodHandle addLoggingToHandle(final Class<? extends Loggable> clazz, final Level level, final MethodHandle mh, final int paramStart, final boolean printReturnValue, final Supplier<String> text) {
1317193323Sed        final DebugLogger log = getLogger(clazz);
1318193323Sed        if (log.isEnabled()) {
1319193323Sed            return MethodHandleFactory.addDebugPrintout(log, level, mh, paramStart, printReturnValue, text.get());
1320193323Sed        }
1321193323Sed        return mh;
1322198090Srdivacky    }
1323193323Sed
1324193323Sed    private static String getLoggerName(final Class<?> clazz) {
1325193323Sed        Class<?> current = clazz;
1326193323Sed        while (current != null) {
1327193323Sed            final Logger log = current.getAnnotation(Logger.class);
1328193323Sed            if (log != null) {
1329198090Srdivacky                assert !"".equals(log.name());
1330193323Sed                return log.name();
1331193323Sed            }
1332193323Sed            current = current.getSuperclass();
1333198090Srdivacky        }
1334193323Sed        assert false;
1335193323Sed        return null;
1336193323Sed    }
1337193323Sed
1338193323Sed}
1339193323Sed