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