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