Context.java revision 1015:8a4af0397070
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.nashorn.internal.codegen.CompilerConstants.CONSTANTS;
29import static jdk.nashorn.internal.codegen.CompilerConstants.CREATE_PROGRAM_FUNCTION;
30import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE;
31import static jdk.nashorn.internal.codegen.CompilerConstants.STRICT_MODE;
32import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
33import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
34import static jdk.nashorn.internal.runtime.Source.sourceFor;
35
36import java.io.File;
37import java.io.IOException;
38import java.io.PrintWriter;
39import java.lang.invoke.MethodHandle;
40import java.lang.invoke.MethodHandles;
41import java.lang.invoke.MethodType;
42import java.lang.ref.ReferenceQueue;
43import java.lang.ref.SoftReference;
44import java.lang.reflect.Field;
45import java.lang.reflect.Modifier;
46import java.net.MalformedURLException;
47import java.net.URL;
48import java.security.AccessControlContext;
49import java.security.AccessController;
50import java.security.CodeSigner;
51import java.security.CodeSource;
52import java.security.Permissions;
53import java.security.PrivilegedAction;
54import java.security.PrivilegedActionException;
55import java.security.PrivilegedExceptionAction;
56import java.security.ProtectionDomain;
57import java.util.Collection;
58import java.util.HashMap;
59import java.util.LinkedHashMap;
60import java.util.Map;
61import java.util.concurrent.atomic.AtomicLong;
62import java.util.function.Consumer;
63import java.util.function.Supplier;
64import java.util.logging.Level;
65import javax.script.ScriptEngine;
66import jdk.internal.org.objectweb.asm.ClassReader;
67import jdk.internal.org.objectweb.asm.util.CheckClassAdapter;
68import jdk.nashorn.api.scripting.ClassFilter;
69import jdk.nashorn.api.scripting.ScriptObjectMirror;
70import jdk.nashorn.internal.codegen.Compiler;
71import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
72import jdk.nashorn.internal.codegen.ObjectClassGenerator;
73import jdk.nashorn.internal.ir.FunctionNode;
74import jdk.nashorn.internal.ir.debug.ASTWriter;
75import jdk.nashorn.internal.ir.debug.PrintVisitor;
76import jdk.nashorn.internal.lookup.MethodHandleFactory;
77import jdk.nashorn.internal.objects.Global;
78import jdk.nashorn.internal.parser.Parser;
79import jdk.nashorn.internal.runtime.events.RuntimeEvent;
80import jdk.nashorn.internal.runtime.logging.DebugLogger;
81import jdk.nashorn.internal.runtime.logging.Loggable;
82import jdk.nashorn.internal.runtime.logging.Logger;
83import jdk.nashorn.internal.runtime.options.LoggingOption.LoggerInfo;
84import jdk.nashorn.internal.runtime.options.Options;
85
86/**
87 * This class manages the global state of execution. Context is immutable.
88 */
89public final class Context {
90    // nashorn specific security runtime access permission names
91    /**
92     * Permission needed to pass arbitrary nashorn command line options when creating Context.
93     */
94    public static final String NASHORN_SET_CONFIG      = "nashorn.setConfig";
95
96    /**
97     * Permission needed to create Nashorn Context instance.
98     */
99    public static final String NASHORN_CREATE_CONTEXT  = "nashorn.createContext";
100
101    /**
102     * Permission needed to create Nashorn Global instance.
103     */
104    public static final String NASHORN_CREATE_GLOBAL   = "nashorn.createGlobal";
105
106    /**
107     * Permission to get current Nashorn Context from thread local storage.
108     */
109    public static final String NASHORN_GET_CONTEXT     = "nashorn.getContext";
110
111    /**
112     * Permission to use Java reflection/jsr292 from script code.
113     */
114    public static final String NASHORN_JAVA_REFLECTION = "nashorn.JavaReflection";
115
116    /**
117     * Permission to enable nashorn debug mode.
118     */
119    public static final String NASHORN_DEBUG_MODE = "nashorn.debugMode";
120
121    // nashorn load psuedo URL prefixes
122    private static final String LOAD_CLASSPATH = "classpath:";
123    private static final String LOAD_FX = "fx:";
124    private static final String LOAD_NASHORN = "nashorn:";
125
126    private static MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
127    private static MethodType CREATE_PROGRAM_FUNCTION_TYPE = MethodType.methodType(ScriptFunction.class, ScriptObject.class);
128
129    /* Force DebuggerSupport to be loaded. */
130    static {
131        DebuggerSupport.FORCELOAD = true;
132    }
133
134    /**
135     * ContextCodeInstaller that has the privilege of installing classes in the Context.
136     * Can only be instantiated from inside the context and is opaque to other classes
137     */
138    public static class ContextCodeInstaller implements CodeInstaller<ScriptEnvironment> {
139        private final Context      context;
140        private final ScriptLoader loader;
141        private final CodeSource   codeSource;
142
143        private ContextCodeInstaller(final Context context, final ScriptLoader loader, final CodeSource codeSource) {
144            this.context    = context;
145            this.loader     = loader;
146            this.codeSource = codeSource;
147        }
148
149        /**
150         * Return the context for this installer
151         * @return ScriptEnvironment
152         */
153        @Override
154        public ScriptEnvironment getOwner() {
155            return context.env;
156        }
157
158        @Override
159        public Class<?> install(final String className, final byte[] bytecode) {
160            final String   binaryName = Compiler.binaryName(className);
161            return loader.installClass(binaryName, bytecode, codeSource);
162        }
163
164        @Override
165        public void initialize(final Collection<Class<?>> classes, final Source source, final Object[] constants) {
166            try {
167                AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
168                    @Override
169                    public Void run() throws Exception {
170                        for (final Class<?> clazz : classes) {
171                            //use reflection to write source and constants table to installed classes
172                            final Field sourceField = clazz.getDeclaredField(SOURCE.symbolName());
173                            sourceField.setAccessible(true);
174                            sourceField.set(null, source);
175
176                            final Field constantsField = clazz.getDeclaredField(CONSTANTS.symbolName());
177                            constantsField.setAccessible(true);
178                            constantsField.set(null, constants);
179                        }
180                        return null;
181                    }
182                });
183            } catch (final PrivilegedActionException e) {
184                throw new RuntimeException(e);
185            }
186        }
187
188        @Override
189        public void verify(final byte[] code) {
190            context.verify(code);
191        }
192
193        @Override
194        public long getUniqueScriptId() {
195            return context.getUniqueScriptId();
196        }
197
198        @Override
199        public void storeScript(final String cacheKey, final Source source, final String mainClassName,
200                                final Map<String,byte[]> classBytes, final Map<Integer, FunctionInitializer> initializers,
201                                final Object[] constants, final int compilationId) {
202            if (context.codeStore != null) {
203                context.codeStore.storeScript(cacheKey, source, mainClassName, classBytes, initializers, constants, compilationId);
204            }
205        }
206
207        @Override
208        public StoredScript loadScript(final Source source, final String functionKey) {
209            if (context.codeStore != null) {
210                return context.codeStore.loadScript(source, functionKey);
211            }
212            return null;
213        }
214    }
215
216    /** Is Context global debug mode enabled ? */
217    public static final boolean DEBUG = Options.getBooleanProperty("nashorn.debug");
218
219    private static final ThreadLocal<Global> currentGlobal = new ThreadLocal<>();
220
221    // in-memory cache for loaded classes
222    private ClassCache classCache;
223
224    // persistent code store
225    private CodeStore codeStore;
226
227    /**
228     * Get the current global scope
229     * @return the current global scope
230     */
231    public static Global getGlobal() {
232        // This class in a package.access protected package.
233        // Trusted code only can call this method.
234        return currentGlobal.get();
235    }
236
237    /**
238     * Set the current global scope
239     * @param global the global scope
240     */
241    public static void setGlobal(final ScriptObject global) {
242        if (global != null && !(global instanceof Global)) {
243            throw new IllegalArgumentException("not a global!");
244        }
245        setGlobal((Global)global);
246    }
247
248    /**
249     * Set the current global scope
250     * @param global the global scope
251     */
252    public static void setGlobal(final Global global) {
253        // This class in a package.access protected package.
254        // Trusted code only can call this method.
255        assert getGlobal() != global;
256        //same code can be cached between globals, then we need to invalidate method handle constants
257        if (global != null) {
258            Global.getConstants().invalidateAll();
259        }
260        currentGlobal.set(global);
261    }
262
263    /**
264     * Get context of the current global
265     * @return current global scope's context.
266     */
267    public static Context getContext() {
268        final SecurityManager sm = System.getSecurityManager();
269        if (sm != null) {
270            sm.checkPermission(new RuntimePermission(NASHORN_GET_CONTEXT));
271        }
272        return getContextTrusted();
273    }
274
275    /**
276     * Get current context's error writer
277     *
278     * @return error writer of the current context
279     */
280    public static PrintWriter getCurrentErr() {
281        final ScriptObject global = getGlobal();
282        return (global != null)? global.getContext().getErr() : new PrintWriter(System.err);
283    }
284
285    /**
286     * Output text to this Context's error stream
287     * @param str text to write
288     */
289    public static void err(final String str) {
290        err(str, true);
291    }
292
293    /**
294     * Output text to this Context's error stream, optionally with
295     * a newline afterwards
296     *
297     * @param str  text to write
298     * @param crlf write a carriage return/new line after text
299     */
300    public static void err(final String str, final boolean crlf) {
301        final PrintWriter err = Context.getCurrentErr();
302        if (err != null) {
303            if (crlf) {
304                err.println(str);
305            } else {
306                err.print(str);
307            }
308        }
309    }
310
311    /** Current environment. */
312    private final ScriptEnvironment env;
313
314    /** is this context in strict mode? Cached from env. as this is used heavily. */
315    final boolean _strict;
316
317    /** class loader to resolve classes from script. */
318    private final ClassLoader  appLoader;
319
320    /** Class loader to load classes from -classpath option, if set. */
321    private final ClassLoader  classPathLoader;
322
323    /** Class loader to load classes compiled from scripts. */
324    private final ScriptLoader scriptLoader;
325
326    /** Current error manager. */
327    private final ErrorManager errors;
328
329    /** Unique id for script. Used only when --loader-per-compile=false */
330    private final AtomicLong uniqueScriptId;
331
332    /** Optional class filter to use for Java classes. Can be null. */
333    private final ClassFilter classFilter;
334
335    private static final ClassLoader myLoader = Context.class.getClassLoader();
336    private static final StructureLoader sharedLoader;
337
338    /*package-private*/ @SuppressWarnings("static-method")
339    ClassLoader getSharedLoader() {
340        return sharedLoader;
341    }
342
343    private static AccessControlContext createNoPermAccCtxt() {
344        return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, new Permissions()) });
345    }
346
347    private static AccessControlContext createPermAccCtxt(final String permName) {
348        final Permissions perms = new Permissions();
349        perms.add(new RuntimePermission(permName));
350        return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, perms) });
351    }
352
353    private static final AccessControlContext NO_PERMISSIONS_ACC_CTXT = createNoPermAccCtxt();
354    private static final AccessControlContext CREATE_LOADER_ACC_CTXT  = createPermAccCtxt("createClassLoader");
355    private static final AccessControlContext CREATE_GLOBAL_ACC_CTXT  = createPermAccCtxt(NASHORN_CREATE_GLOBAL);
356
357    static {
358        sharedLoader = AccessController.doPrivileged(new PrivilegedAction<StructureLoader>() {
359            @Override
360            public StructureLoader run() {
361                return new StructureLoader(myLoader);
362            }
363        }, CREATE_LOADER_ACC_CTXT);
364    }
365
366    /**
367     * ThrowErrorManager that throws ParserException upon error conditions.
368     */
369    public static class ThrowErrorManager extends ErrorManager {
370        @Override
371        public void error(final String message) {
372            throw new ParserException(message);
373        }
374
375        @Override
376        public void error(final ParserException e) {
377            throw e;
378        }
379    }
380
381    /**
382     * Constructor
383     *
384     * @param options options from command line or Context creator
385     * @param errors  error manger
386     * @param appLoader application class loader
387     */
388    public Context(final Options options, final ErrorManager errors, final ClassLoader appLoader) {
389        this(options, errors, appLoader, (ClassFilter)null);
390    }
391
392    /**
393     * Constructor
394     *
395     * @param options options from command line or Context creator
396     * @param errors  error manger
397     * @param appLoader application class loader
398     * @param classFilter class filter to use
399     */
400    public Context(final Options options, final ErrorManager errors, final ClassLoader appLoader, final ClassFilter classFilter) {
401        this(options, errors, new PrintWriter(System.out, true), new PrintWriter(System.err, true), appLoader, classFilter);
402    }
403
404    /**
405     * Constructor
406     *
407     * @param options options from command line or Context creator
408     * @param errors  error manger
409     * @param out     output writer for this Context
410     * @param err     error writer for this Context
411     * @param appLoader application class loader
412     */
413    public Context(final Options options, final ErrorManager errors, final PrintWriter out, final PrintWriter err, final ClassLoader appLoader) {
414        this(options, errors, out, err, appLoader, (ClassFilter)null);
415    }
416
417    /**
418     * Constructor
419     *
420     * @param options options from command line or Context creator
421     * @param errors  error manger
422     * @param out     output writer for this Context
423     * @param err     error writer for this Context
424     * @param appLoader application class loader
425     * @param classFilter class filter to use
426     */
427    public Context(final Options options, final ErrorManager errors, final PrintWriter out, final PrintWriter err, final ClassLoader appLoader, final ClassFilter classFilter) {
428        final SecurityManager sm = System.getSecurityManager();
429        if (sm != null) {
430            sm.checkPermission(new RuntimePermission(NASHORN_CREATE_CONTEXT));
431        }
432
433        this.classFilter = classFilter;
434        this.env       = new ScriptEnvironment(options, out, err);
435        this._strict   = env._strict;
436        this.appLoader = appLoader;
437        if (env._loader_per_compile) {
438            this.scriptLoader = null;
439            this.uniqueScriptId = null;
440        } else {
441            this.scriptLoader = createNewLoader();
442            this.uniqueScriptId = new AtomicLong();
443        }
444        this.errors    = errors;
445
446        // if user passed -classpath option, make a class loader with that and set it as
447        // thread context class loader so that script can access classes from that path.
448        final String classPath = options.getString("classpath");
449        if (!env._compile_only && classPath != null && !classPath.isEmpty()) {
450            // make sure that caller can create a class loader.
451            if (sm != null) {
452                sm.checkPermission(new RuntimePermission("createClassLoader"));
453            }
454            this.classPathLoader = NashornLoader.createClassLoader(classPath);
455        } else {
456            this.classPathLoader = null;
457        }
458
459        final int cacheSize = env._class_cache_size;
460        if (cacheSize > 0) {
461            classCache = new ClassCache(cacheSize);
462        }
463
464        if (env._persistent_cache) {
465            try {
466                final String cacheDir = Options.getStringProperty("nashorn.persistent.code.cache", "nashorn_code_cache");
467                codeStore = new CodeStore(this, cacheDir);
468            } catch (final IOException e) {
469                throw new RuntimeException("Error initializing code cache", e);
470            }
471        }
472
473        // print version info if asked.
474        if (env._version) {
475            getErr().println("nashorn " + Version.version());
476        }
477
478        if (env._fullversion) {
479            getErr().println("nashorn full version " + Version.fullVersion());
480        }
481
482        initLoggers();
483    }
484
485
486    /**
487     * Get the class filter for this context
488     * @return class filter
489     */
490    public ClassFilter getClassFilter() {
491        return classFilter;
492    }
493
494    /**
495     * Get the error manager for this context
496     * @return error manger
497     */
498    public ErrorManager getErrorManager() {
499        return errors;
500    }
501
502    /**
503     * Get the script environment for this context
504     * @return script environment
505     */
506    public ScriptEnvironment getEnv() {
507        return env;
508    }
509
510    /**
511     * Get the output stream for this context
512     * @return output print writer
513     */
514    public PrintWriter getOut() {
515        return env.getOut();
516    }
517
518    /**
519     * Get the error stream for this context
520     * @return error print writer
521     */
522    public PrintWriter getErr() {
523        return env.getErr();
524    }
525
526    /**
527     * Get the PropertyMap of the current global scope
528     * @return the property map of the current global scope
529     */
530    public static PropertyMap getGlobalMap() {
531        return Context.getGlobal().getMap();
532    }
533
534    /**
535     * Compile a top level script.
536     *
537     * @param source the source
538     * @param scope  the scope
539     *
540     * @return top level function for script
541     */
542    public ScriptFunction compileScript(final Source source, final ScriptObject scope) {
543        return compileScript(source, scope, this.errors);
544    }
545
546    /**
547     * Interface to represent compiled code that can be re-used across many
548     * global scope instances
549     */
550    public static interface MultiGlobalCompiledScript {
551        /**
552         * Obtain script function object for a specific global scope object.
553         *
554         * @param newGlobal global scope for which function object is obtained
555         * @return script function for script level expressions
556         */
557        public ScriptFunction getFunction(final Global newGlobal);
558    }
559
560    /**
561     * Compile a top level script.
562     *
563     * @param source the script source
564     * @return reusable compiled script across many global scopes.
565     */
566    public MultiGlobalCompiledScript compileScript(final Source source) {
567        final Class<?> clazz = compile(source, this.errors, this._strict);
568        final MethodHandle createProgramFunctionHandle = getCreateProgramFunctionHandle(clazz);
569
570        return new MultiGlobalCompiledScript() {
571            @Override
572            public ScriptFunction getFunction(final Global newGlobal) {
573                return invokeCreateProgramFunctionHandle(createProgramFunctionHandle, newGlobal);
574            }
575        };
576    }
577
578    /**
579     * Entry point for {@code eval}
580     *
581     * @param initialScope The scope of this eval call
582     * @param string       Evaluated code as a String
583     * @param callThis     "this" to be passed to the evaluated code
584     * @param location     location of the eval call
585     * @param strict       is this {@code eval} call from a strict mode code?
586     * @return the return value of the {@code eval}
587     */
588    public Object eval(final ScriptObject initialScope, final String string,
589            final Object callThis, final Object location, final boolean strict) {
590        return eval(initialScope, string, callThis, location, strict, false);
591    }
592
593    /**
594     * Entry point for {@code eval}
595     *
596     * @param initialScope The scope of this eval call
597     * @param string       Evaluated code as a String
598     * @param callThis     "this" to be passed to the evaluated code
599     * @param location     location of the eval call
600     * @param strict       is this {@code eval} call from a strict mode code?
601     * @param evalCall     is this called from "eval" builtin?
602     *
603     * @return the return value of the {@code eval}
604     */
605    public Object eval(final ScriptObject initialScope, final String string,
606            final Object callThis, final Object location, final boolean strict, final boolean evalCall) {
607        final String  file       = location == UNDEFINED || location == null ? "<eval>" : location.toString();
608        final Source  source     = sourceFor(file, string, evalCall);
609        final boolean directEval = location != UNDEFINED; // is this direct 'eval' call or indirectly invoked eval?
610        final Global  global = Context.getGlobal();
611        ScriptObject scope = initialScope;
612
613        // ECMA section 10.1.1 point 2 says eval code is strict if it begins
614        // with "use strict" directive or eval direct call itself is made
615        // from from strict mode code. We are passed with caller's strict mode.
616        boolean strictFlag = directEval && strict;
617
618        Class<?> clazz = null;
619        try {
620            clazz = compile(source, new ThrowErrorManager(), strictFlag);
621        } catch (final ParserException e) {
622            e.throwAsEcmaException(global);
623            return null;
624        }
625
626        if (!strictFlag) {
627            // We need to get strict mode flag from compiled class. This is
628            // because eval code may start with "use strict" directive.
629            try {
630                strictFlag = clazz.getField(STRICT_MODE.symbolName()).getBoolean(null);
631            } catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
632                //ignored
633                strictFlag = false;
634            }
635        }
636
637        // In strict mode, eval does not instantiate variables and functions
638        // in the caller's environment. A new environment is created!
639        if (strictFlag) {
640            // Create a new scope object
641            final ScriptObject strictEvalScope = global.newObject();
642
643            // bless it as a "scope"
644            strictEvalScope.setIsScope();
645
646            // set given scope to be it's proto so that eval can still
647            // access caller environment vars in the new environment.
648            strictEvalScope.setProto(scope);
649            scope = strictEvalScope;
650        }
651
652        final ScriptFunction func = getProgramFunction(clazz, scope);
653        Object evalThis;
654        if (directEval) {
655            evalThis = callThis instanceof ScriptObject || strictFlag ? callThis : global;
656        } else {
657            evalThis = global;
658        }
659
660        return ScriptRuntime.apply(func, evalThis);
661    }
662
663    private static Source loadInternal(final String srcStr, final String prefix, final String resourcePath) {
664        if (srcStr.startsWith(prefix)) {
665            final String resource = resourcePath + srcStr.substring(prefix.length());
666            // NOTE: even sandbox scripts should be able to load scripts in nashorn: scheme
667            // These scripts are always available and are loaded from nashorn.jar's resources.
668            return AccessController.doPrivileged(
669                    new PrivilegedAction<Source>() {
670                        @Override
671                        public Source run() {
672                            try {
673                                final URL resURL = Context.class.getResource(resource);
674                                return resURL != null ? sourceFor(srcStr, resURL) : null;
675                            } catch (final IOException exp) {
676                                return null;
677                            }
678                        }
679                    });
680        }
681
682        return null;
683    }
684
685    /**
686     * Implementation of {@code load} Nashorn extension. Load a script file from a source
687     * expression
688     *
689     * @param scope  the scope
690     * @param from   source expression for script
691     *
692     * @return return value for load call (undefined)
693     *
694     * @throws IOException if source cannot be found or loaded
695     */
696    public Object load(final ScriptObject scope, final Object from) throws IOException {
697        final Object src = from instanceof ConsString ? from.toString() : from;
698        Source source = null;
699
700        // load accepts a String (which could be a URL or a file name), a File, a URL
701        // or a ScriptObject that has "name" and "source" (string valued) properties.
702        if (src instanceof String) {
703            final String srcStr = (String)src;
704            if (srcStr.startsWith(LOAD_CLASSPATH)) {
705                final URL url = getResourceURL(srcStr.substring(LOAD_CLASSPATH.length()));
706                source = url != null ? sourceFor(url.toString(), url) : null;
707            } else {
708                final File file = new File(srcStr);
709                if (srcStr.indexOf(':') != -1) {
710                    if ((source = loadInternal(srcStr, LOAD_NASHORN, "resources/")) == null &&
711                        (source = loadInternal(srcStr, LOAD_FX, "resources/fx/")) == null) {
712                        URL url;
713                        try {
714                            //check for malformed url. if malformed, it may still be a valid file
715                            url = new URL(srcStr);
716                        } catch (final MalformedURLException e) {
717                            url = file.toURI().toURL();
718                        }
719                        source = sourceFor(url.toString(), url);
720                    }
721                } else if (file.isFile()) {
722                    source = sourceFor(srcStr, file);
723                }
724            }
725        } else if (src instanceof File && ((File)src).isFile()) {
726            final File file = (File)src;
727            source = sourceFor(file.getName(), file);
728        } else if (src instanceof URL) {
729            final URL url = (URL)src;
730            source = sourceFor(url.toString(), url);
731        } else if (src instanceof ScriptObject) {
732            final ScriptObject sobj = (ScriptObject)src;
733            if (sobj.has("script") && sobj.has("name")) {
734                final String script = JSType.toString(sobj.get("script"));
735                final String name   = JSType.toString(sobj.get("name"));
736                source = sourceFor(name, script);
737            }
738        } else if (src instanceof Map) {
739            final Map<?,?> map = (Map<?,?>)src;
740            if (map.containsKey("script") && map.containsKey("name")) {
741                final String script = JSType.toString(map.get("script"));
742                final String name   = JSType.toString(map.get("name"));
743                source = sourceFor(name, script);
744            }
745        }
746
747        if (source != null) {
748            return evaluateSource(source, scope, scope);
749        }
750
751        throw typeError("cant.load.script", ScriptRuntime.safeToString(from));
752    }
753
754    /**
755     * Implementation of {@code loadWithNewGlobal} Nashorn extension. Load a script file from a source
756     * expression, after creating a new global scope.
757     *
758     * @param from source expression for script
759     * @param args (optional) arguments to be passed to the loaded script
760     *
761     * @return return value for load call (undefined)
762     *
763     * @throws IOException if source cannot be found or loaded
764     */
765    public Object loadWithNewGlobal(final Object from, final Object...args) throws IOException {
766        final Global oldGlobal = getGlobal();
767        final Global newGlobal = AccessController.doPrivileged(new PrivilegedAction<Global>() {
768           @Override
769           public Global run() {
770               try {
771                   return newGlobal();
772               } catch (final RuntimeException e) {
773                   if (Context.DEBUG) {
774                       e.printStackTrace();
775                   }
776                   throw e;
777               }
778           }
779        }, CREATE_GLOBAL_ACC_CTXT);
780        // initialize newly created Global instance
781        initGlobal(newGlobal);
782        setGlobal(newGlobal);
783
784        final Object[] wrapped = args == null? ScriptRuntime.EMPTY_ARRAY :  ScriptObjectMirror.wrapArray(args, oldGlobal);
785        newGlobal.put("arguments", newGlobal.wrapAsObject(wrapped), env._strict);
786
787        try {
788            // wrap objects from newGlobal's world as mirrors - but if result
789            // is from oldGlobal's world, unwrap it!
790            return ScriptObjectMirror.unwrap(ScriptObjectMirror.wrap(load(newGlobal, from), newGlobal), oldGlobal);
791        } finally {
792            setGlobal(oldGlobal);
793        }
794    }
795
796    /**
797     * Load or get a structure class. Structure class names are based on the number of parameter fields
798     * and {@link AccessorProperty} fields in them. Structure classes are used to represent ScriptObjects
799     *
800     * @see ObjectClassGenerator
801     * @see AccessorProperty
802     * @see ScriptObject
803     *
804     * @param fullName  full name of class, e.g. jdk.nashorn.internal.objects.JO2P1 contains 2 fields and 1 parameter.
805     *
806     * @return the {@code Class<?>} for this structure
807     *
808     * @throws ClassNotFoundException if structure class cannot be resolved
809     */
810    @SuppressWarnings("unchecked")
811    public static Class<? extends ScriptObject> forStructureClass(final String fullName) throws ClassNotFoundException {
812        if (System.getSecurityManager() != null && !StructureLoader.isStructureClass(fullName)) {
813            throw new ClassNotFoundException(fullName);
814        }
815        return (Class<? extends ScriptObject>)Class.forName(fullName, true, sharedLoader);
816    }
817
818    /**
819     * Checks that the given Class can be accessed from no permissions context.
820     *
821     * @param clazz Class object
822     * @throws SecurityException if not accessible
823     */
824    public static void checkPackageAccess(final Class<?> clazz) {
825        final SecurityManager sm = System.getSecurityManager();
826        if (sm != null) {
827            Class<?> bottomClazz = clazz;
828            while (bottomClazz.isArray()) {
829                bottomClazz = bottomClazz.getComponentType();
830            }
831            checkPackageAccess(sm, bottomClazz.getName());
832        }
833    }
834
835    /**
836     * Checks that the given package name can be accessed from no permissions context.
837     *
838     * @param pkgName package name
839     * @throws SecurityException if not accessible
840     */
841    public static void checkPackageAccess(final String pkgName) {
842        final SecurityManager sm = System.getSecurityManager();
843        if (sm != null) {
844            checkPackageAccess(sm, pkgName.endsWith(".") ? pkgName : pkgName + ".");
845        }
846    }
847
848    /**
849     * Checks that the given package can be accessed from no permissions context.
850     *
851     * @param sm current security manager instance
852     * @param fullName fully qualified package name
853     * @throw SecurityException if not accessible
854     */
855    private static void checkPackageAccess(final SecurityManager sm, final String fullName) {
856        sm.getClass(); // null check
857        final int index = fullName.lastIndexOf('.');
858        if (index != -1) {
859            final String pkgName = fullName.substring(0, index);
860            AccessController.doPrivileged(new PrivilegedAction<Void>() {
861                @Override
862                public Void run() {
863                    sm.checkPackageAccess(pkgName);
864                    return null;
865                }
866            }, NO_PERMISSIONS_ACC_CTXT);
867        }
868    }
869
870    /**
871     * Checks that the given Class can be accessed from no permissions context.
872     *
873     * @param clazz Class object
874     * @return true if package is accessible, false otherwise
875     */
876    private static boolean isAccessiblePackage(final Class<?> clazz) {
877        try {
878            checkPackageAccess(clazz);
879            return true;
880        } catch (final SecurityException se) {
881            return false;
882        }
883    }
884
885    /**
886     * Checks that the given Class is public and it can be accessed from no permissions context.
887     *
888     * @param clazz Class object to check
889     * @return true if Class is accessible, false otherwise
890     */
891    public static boolean isAccessibleClass(final Class<?> clazz) {
892        return Modifier.isPublic(clazz.getModifiers()) && Context.isAccessiblePackage(clazz);
893    }
894
895    /**
896     * Lookup a Java class. This is used for JSR-223 stuff linking in from
897     * {@code jdk.nashorn.internal.objects.NativeJava} and {@code jdk.nashorn.internal.runtime.NativeJavaPackage}
898     *
899     * @param fullName full name of class to load
900     *
901     * @return the {@code Class<?>} for the name
902     *
903     * @throws ClassNotFoundException if class cannot be resolved
904     */
905    public Class<?> findClass(final String fullName) throws ClassNotFoundException {
906        if (fullName.indexOf('[') != -1 || fullName.indexOf('/') != -1) {
907            // don't allow array class names or internal names.
908            throw new ClassNotFoundException(fullName);
909        }
910
911        // give chance to ClassFilter to filter out, if present
912        if (classFilter != null && !classFilter.exposeToScripts(fullName)) {
913            throw new ClassNotFoundException(fullName);
914        }
915
916        // check package access as soon as possible!
917        final SecurityManager sm = System.getSecurityManager();
918        if (sm != null) {
919            checkPackageAccess(sm, fullName);
920        }
921
922        // try the script -classpath loader, if that is set
923        if (classPathLoader != null) {
924            try {
925                return Class.forName(fullName, true, classPathLoader);
926            } catch (final ClassNotFoundException ignored) {
927                // ignore, continue search
928            }
929        }
930
931        // Try finding using the "app" loader.
932        return Class.forName(fullName, true, appLoader);
933    }
934
935    /**
936     * Hook to print stack trace for a {@link Throwable} that occurred during
937     * execution
938     *
939     * @param t throwable for which to dump stack
940     */
941    public static void printStackTrace(final Throwable t) {
942        if (Context.DEBUG) {
943            t.printStackTrace(Context.getCurrentErr());
944        }
945    }
946
947    /**
948     * Verify generated bytecode before emission. This is called back from the
949     * {@link ObjectClassGenerator} or the {@link Compiler}. If the "--verify-code" parameter
950     * hasn't been given, this is a nop
951     *
952     * Note that verification may load classes -- we don't want to do that unless
953     * user specified verify option. We check it here even though caller
954     * may have already checked that flag
955     *
956     * @param bytecode bytecode to verify
957     */
958    public void verify(final byte[] bytecode) {
959        if (env._verify_code) {
960            // No verification when security manager is around as verifier
961            // may load further classes - which should be avoided.
962            if (System.getSecurityManager() == null) {
963                CheckClassAdapter.verify(new ClassReader(bytecode), sharedLoader, false, new PrintWriter(System.err, true));
964            }
965        }
966    }
967
968    /**
969     * Create and initialize a new global scope object.
970     *
971     * @return the initialized global scope object.
972     */
973    public Global createGlobal() {
974        return initGlobal(newGlobal());
975    }
976
977    /**
978     * Create a new uninitialized global scope object
979     * @return the global script object
980     */
981    public Global newGlobal() {
982        return new Global(this);
983    }
984
985    /**
986     * Initialize given global scope object.
987     *
988     * @param global the global
989     * @param engine the associated ScriptEngine instance, can be null
990     * @return the initialized global scope object.
991     */
992    public Global initGlobal(final Global global, final ScriptEngine engine) {
993        // Need only minimal global object, if we are just compiling.
994        if (!env._compile_only) {
995            final Global oldGlobal = Context.getGlobal();
996            try {
997                Context.setGlobal(global);
998                // initialize global scope with builtin global objects
999                global.initBuiltinObjects(engine);
1000            } finally {
1001                Context.setGlobal(oldGlobal);
1002            }
1003        }
1004
1005        return global;
1006    }
1007
1008    /**
1009     * Initialize given global scope object.
1010     *
1011     * @param global the global
1012     * @return the initialized global scope object.
1013     */
1014    public Global initGlobal(final Global global) {
1015        return initGlobal(global, null);
1016    }
1017
1018    /**
1019     * Return the current global's context
1020     * @return current global's context
1021     */
1022    static Context getContextTrusted() {
1023        return ((ScriptObject)Context.getGlobal()).getContext();
1024    }
1025
1026    static Context getContextTrustedOrNull() {
1027        final Global global = Context.getGlobal();
1028        return global == null ? null : ((ScriptObject)global).getContext();
1029    }
1030
1031    /**
1032     * Try to infer Context instance from the Class. If we cannot,
1033     * then get it from the thread local variable.
1034     *
1035     * @param clazz the class
1036     * @return context
1037     */
1038    static Context fromClass(final Class<?> clazz) {
1039        final ClassLoader loader = clazz.getClassLoader();
1040
1041        if (loader instanceof ScriptLoader) {
1042            return ((ScriptLoader)loader).getContext();
1043        }
1044
1045        return Context.getContextTrusted();
1046    }
1047
1048    private URL getResourceURL(final String resName) {
1049        // try the classPathLoader if we have and then
1050        // try the appLoader if non-null.
1051        if (classPathLoader != null) {
1052            return classPathLoader.getResource(resName);
1053        } else if (appLoader != null) {
1054            return appLoader.getResource(resName);
1055        }
1056
1057        return null;
1058    }
1059
1060    private Object evaluateSource(final Source source, final ScriptObject scope, final ScriptObject thiz) {
1061        ScriptFunction script = null;
1062
1063        try {
1064            script = compileScript(source, scope, new Context.ThrowErrorManager());
1065        } catch (final ParserException e) {
1066            e.throwAsEcmaException();
1067        }
1068
1069        return ScriptRuntime.apply(script, thiz);
1070    }
1071
1072    private static ScriptFunction getProgramFunction(final Class<?> script, final ScriptObject scope) {
1073        if (script == null) {
1074            return null;
1075        }
1076        return invokeCreateProgramFunctionHandle(getCreateProgramFunctionHandle(script), scope);
1077    }
1078
1079    private static MethodHandle getCreateProgramFunctionHandle(final Class<?> script) {
1080        try {
1081            return LOOKUP.findStatic(script, CREATE_PROGRAM_FUNCTION.symbolName(), CREATE_PROGRAM_FUNCTION_TYPE);
1082        } catch (NoSuchMethodException | IllegalAccessException e) {
1083            throw new AssertionError("Failed to retrieve a handle for the program function for " + script.getName(), e);
1084        }
1085    }
1086
1087    private static ScriptFunction invokeCreateProgramFunctionHandle(final MethodHandle createProgramFunctionHandle, final ScriptObject scope) {
1088        try {
1089            return (ScriptFunction)createProgramFunctionHandle.invokeExact(scope);
1090        } catch (final RuntimeException|Error e) {
1091            throw e;
1092        } catch (final Throwable t) {
1093            throw new AssertionError("Failed to create a program function", t);
1094        }
1095    }
1096
1097    private ScriptFunction compileScript(final Source source, final ScriptObject scope, final ErrorManager errMan) {
1098        return getProgramFunction(compile(source, errMan, this._strict), scope);
1099    }
1100
1101    private synchronized Class<?> compile(final Source source, final ErrorManager errMan, final boolean strict) {
1102        // start with no errors, no warnings.
1103        errMan.reset();
1104
1105        Class<?> script = findCachedClass(source);
1106        if (script != null) {
1107            final DebugLogger log = getLogger(Compiler.class);
1108            if (log.isEnabled()) {
1109                log.fine(new RuntimeEvent<>(Level.INFO, source), "Code cache hit for ", source, " avoiding recompile.");
1110            }
1111            return script;
1112        }
1113
1114        StoredScript storedScript = null;
1115        FunctionNode functionNode = null;
1116        final boolean useCodeStore = env._persistent_cache && !env._parse_only && !env._optimistic_types;
1117        final String cacheKey = useCodeStore ? CodeStore.getCacheKey(0, null) : null;
1118
1119        if (useCodeStore) {
1120            storedScript = codeStore.loadScript(source, cacheKey);
1121        }
1122
1123        if (storedScript == null) {
1124            functionNode = new Parser(env, source, errMan, strict, getLogger(Parser.class)).parse();
1125
1126            if (errMan.hasErrors()) {
1127                return null;
1128            }
1129
1130            if (env._print_ast || functionNode.getFlag(FunctionNode.IS_PRINT_AST)) {
1131                getErr().println(new ASTWriter(functionNode));
1132            }
1133
1134            if (env._print_parse || functionNode.getFlag(FunctionNode.IS_PRINT_PARSE)) {
1135                getErr().println(new PrintVisitor(functionNode, true, false));
1136            }
1137        }
1138
1139        if (env._parse_only) {
1140            return null;
1141        }
1142
1143        final URL          url    = source.getURL();
1144        final ScriptLoader loader = env._loader_per_compile ? createNewLoader() : scriptLoader;
1145        final CodeSource   cs     = new CodeSource(url, (CodeSigner[])null);
1146        final CodeInstaller<ScriptEnvironment> installer = new ContextCodeInstaller(this, loader, cs);
1147
1148        if (storedScript == null) {
1149            final CompilationPhases phases = Compiler.CompilationPhases.COMPILE_ALL;
1150
1151            final Compiler compiler = new Compiler(
1152                    this,
1153                    env,
1154                    installer,
1155                    source,
1156                    errMan,
1157                    strict | functionNode.isStrict());
1158
1159            final FunctionNode compiledFunction = compiler.compile(functionNode, phases);
1160            if (errMan.hasErrors()) {
1161                return null;
1162            }
1163            script = compiledFunction.getRootClass();
1164            compiler.persistClassInfo(cacheKey, compiledFunction);
1165        } else {
1166            Compiler.updateCompilationId(storedScript.getCompilationId());
1167            script = install(storedScript, source, installer);
1168        }
1169
1170        cacheClass(source, script);
1171        return script;
1172    }
1173
1174    private ScriptLoader createNewLoader() {
1175        return AccessController.doPrivileged(
1176             new PrivilegedAction<ScriptLoader>() {
1177                @Override
1178                public ScriptLoader run() {
1179                    return new ScriptLoader(appLoader, Context.this);
1180                }
1181             }, CREATE_LOADER_ACC_CTXT);
1182    }
1183
1184    private long getUniqueScriptId() {
1185        return uniqueScriptId.getAndIncrement();
1186    }
1187
1188    /**
1189     * Install a previously compiled class from the code cache.
1190     *
1191     * @param storedScript cached script containing class bytes and constants
1192     * @return main script class
1193     */
1194    private static Class<?> install(final StoredScript storedScript, final Source source, final CodeInstaller<ScriptEnvironment> installer) {
1195
1196        final Map<String, Class<?>> installedClasses = new HashMap<>();
1197        final Object[] constants       = storedScript.getConstants();
1198        final String   mainClassName   = storedScript.getMainClassName();
1199        final byte[]   mainClassBytes  = storedScript.getClassBytes().get(mainClassName);
1200        final Class<?> mainClass       = installer.install(mainClassName, mainClassBytes);
1201        final Map<Integer, FunctionInitializer> initialzers = storedScript.getInitializers();
1202
1203        installedClasses.put(mainClassName, mainClass);
1204
1205        for (final Map.Entry<String, byte[]> entry : storedScript.getClassBytes().entrySet()) {
1206            final String className = entry.getKey();
1207            if (className.equals(mainClassName)) {
1208                continue;
1209            }
1210            final byte[] code = entry.getValue();
1211
1212            installedClasses.put(className, installer.install(className, code));
1213        }
1214
1215        installer.initialize(installedClasses.values(), source, constants);
1216
1217        for (final Object constant : constants) {
1218            if (constant instanceof RecompilableScriptFunctionData) {
1219                final RecompilableScriptFunctionData data = (RecompilableScriptFunctionData) constant;
1220                data.initTransients(source, installer);
1221                if (initialzers != null) {
1222                    final FunctionInitializer initializer = initialzers.get(data.getFunctionNodeId());
1223                    initializer.setCode(installedClasses.get(initializer.getClassName()));
1224                    data.initializeCode(initializer);
1225                }
1226            }
1227        }
1228
1229        return mainClass;
1230    }
1231
1232    /**
1233     * Cache for compiled script classes.
1234     */
1235    @SuppressWarnings("serial")
1236    private static class ClassCache extends LinkedHashMap<Source, ClassReference> {
1237        private final int size;
1238        private final ReferenceQueue<Class<?>> queue;
1239
1240        ClassCache(final int size) {
1241            super(size, 0.75f, true);
1242            this.size = size;
1243            this.queue = new ReferenceQueue<>();
1244        }
1245
1246        void cache(final Source source, final Class<?> clazz) {
1247            put(source, new ClassReference(clazz, queue, source));
1248        }
1249
1250        @Override
1251        protected boolean removeEldestEntry(final Map.Entry<Source, ClassReference> eldest) {
1252            return size() > size;
1253        }
1254
1255        @Override
1256        public ClassReference get(final Object key) {
1257            for (ClassReference ref; (ref = (ClassReference)queue.poll()) != null; ) {
1258                remove(ref.source);
1259            }
1260            return super.get(key);
1261        }
1262
1263    }
1264
1265    private static class ClassReference extends SoftReference<Class<?>> {
1266        private final Source source;
1267
1268        ClassReference(final Class<?> clazz, final ReferenceQueue<Class<?>> queue, final Source source) {
1269            super(clazz, queue);
1270            this.source = source;
1271        }
1272    }
1273
1274    // Class cache management
1275    private Class<?> findCachedClass(final Source source) {
1276        final ClassReference ref = classCache == null ? null : classCache.get(source);
1277        return ref != null ? ref.get() : null;
1278    }
1279
1280    private void cacheClass(final Source source, final Class<?> clazz) {
1281        if (classCache != null) {
1282            classCache.cache(source, clazz);
1283        }
1284    }
1285
1286    // logging
1287    private final Map<String, DebugLogger> loggers = new HashMap<>();
1288
1289    private void initLoggers() {
1290        ((Loggable)MethodHandleFactory.getFunctionality()).initLogger(this);
1291    }
1292
1293    /**
1294     * Get a logger, given a loggable class
1295     * @param clazz a Loggable class
1296     * @return debuglogger associated with that class
1297     */
1298    public DebugLogger getLogger(final Class<? extends Loggable> clazz) {
1299        return getLogger(clazz, null);
1300    }
1301
1302    /**
1303     * Get a logger, given a loggable class
1304     * @param clazz a Loggable class
1305     * @param initHook an init hook - if this is the first time the logger is created in the context, run the init hook
1306     * @return debuglogger associated with that class
1307     */
1308    public DebugLogger getLogger(final Class<? extends Loggable> clazz, final Consumer<DebugLogger> initHook) {
1309        final String name = getLoggerName(clazz);
1310        DebugLogger logger = loggers.get(name);
1311        if (logger == null) {
1312            if (!env.hasLogger(name)) {
1313                return DebugLogger.DISABLED_LOGGER;
1314            }
1315            final LoggerInfo info = env._loggers.get(name);
1316            logger = new DebugLogger(name, info.getLevel(), info.isQuiet());
1317            if (initHook != null) {
1318                initHook.accept(logger);
1319            }
1320            loggers.put(name, logger);
1321        }
1322        return logger;
1323    }
1324
1325    /**
1326     * Given a Loggable class, weave debug info info a method handle for that logger.
1327     * Level.INFO is used
1328     *
1329     * @param clazz loggable
1330     * @param mh    method handle
1331     * @param text  debug printout to add
1332     *
1333     * @return instrumented method handle, or null if logger not enabled
1334     */
1335    public MethodHandle addLoggingToHandle(final Class<? extends Loggable> clazz, final MethodHandle mh, final Supplier<String> text) {
1336        return addLoggingToHandle(clazz, Level.INFO, mh, Integer.MAX_VALUE, false, text);
1337    }
1338
1339    /**
1340     * Given a Loggable class, weave debug info info a method handle for that logger.
1341     *
1342     * @param clazz            loggable
1343     * @param level            log level
1344     * @param mh               method handle
1345     * @param paramStart       first parameter to print
1346     * @param printReturnValue should we print the return vaulue?
1347     * @param text             debug printout to add
1348     *
1349     * @return instrumented method handle, or null if logger not enabled
1350     */
1351    public MethodHandle addLoggingToHandle(final Class<? extends Loggable> clazz, final Level level, final MethodHandle mh, final int paramStart, final boolean printReturnValue, final Supplier<String> text) {
1352        final DebugLogger log = getLogger(clazz);
1353        if (log.isEnabled()) {
1354            return MethodHandleFactory.addDebugPrintout(log, level, mh, paramStart, printReturnValue, text.get());
1355        }
1356        return mh;
1357    }
1358
1359    private static String getLoggerName(final Class<?> clazz) {
1360        Class<?> current = clazz;
1361        while (current != null) {
1362            final Logger log = current.getAnnotation(Logger.class);
1363            if (log != null) {
1364                assert !"".equals(log.name());
1365                return log.name();
1366            }
1367            current = current.getSuperclass();
1368        }
1369        assert false;
1370        return null;
1371    }
1372
1373}
1374