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