Fri, 04 Oct 2013 16:21:29 +0530
8025771: Enhance Nashorn Contexts
Reviewed-by: jlaskey, hannesw
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 */
26 package jdk.nashorn.internal.runtime;
28 import static jdk.nashorn.internal.codegen.CompilerConstants.RUN_SCRIPT;
29 import static jdk.nashorn.internal.codegen.CompilerConstants.STRICT_MODE;
30 import static jdk.nashorn.internal.lookup.Lookup.MH;
31 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
32 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
34 import java.io.File;
35 import java.io.IOException;
36 import java.io.PrintWriter;
37 import java.lang.invoke.MethodHandle;
38 import java.lang.invoke.MethodHandles;
39 import java.lang.reflect.Modifier;
40 import java.util.concurrent.atomic.AtomicLong;
41 import java.net.MalformedURLException;
42 import java.net.URL;
43 import java.security.AccessControlContext;
44 import java.security.AccessController;
45 import java.security.CodeSigner;
46 import java.security.CodeSource;
47 import java.security.Permissions;
48 import java.security.PrivilegedAction;
49 import java.security.ProtectionDomain;
50 import java.util.Map;
52 import jdk.internal.org.objectweb.asm.ClassReader;
53 import jdk.internal.org.objectweb.asm.util.CheckClassAdapter;
54 import jdk.nashorn.api.scripting.ScriptObjectMirror;
55 import jdk.nashorn.internal.codegen.Compiler;
56 import jdk.nashorn.internal.codegen.ObjectClassGenerator;
57 import jdk.nashorn.internal.ir.FunctionNode;
58 import jdk.nashorn.internal.ir.debug.ASTWriter;
59 import jdk.nashorn.internal.ir.debug.PrintVisitor;
60 import jdk.nashorn.internal.objects.Global;
61 import jdk.nashorn.internal.parser.Parser;
62 import jdk.nashorn.internal.runtime.options.Options;
64 /**
65 * This class manages the global state of execution. Context is immutable.
66 */
67 public final class Context {
68 // nashorn specific security runtime access permission names
69 /**
70 * Permission needed to pass arbitrary nashorn command line options when creating Context.
71 */
72 public static final String NASHORN_SET_CONFIG = "nashorn.setConfig";
74 /**
75 * Permission needed to create Nashorn Context instance.
76 */
77 public static final String NASHORN_CREATE_CONTEXT = "nashorn.createContext";
79 /**
80 * Permission needed to create Nashorn Global instance.
81 */
82 public static final String NASHORN_CREATE_GLOBAL = "nashorn.createGlobal";
84 /**
85 * Permission to get current Nashorn Context from thread local storage.
86 */
87 public static final String NASHORN_GET_CONTEXT = "nashorn.getContext";
89 /**
90 * Permission to use Java reflection/jsr292 from script code.
91 */
92 public static final String NASHORN_JAVA_REFLECTION = "nashorn.JavaReflection";
94 // nashorn load psuedo URL prefixes
95 private static final String LOAD_CLASSPATH = "classpath:";
96 private static final String LOAD_FX = "fx:";
97 private static final String LOAD_NASHORN = "nashorn:";
99 /* Force DebuggerSupport to be loaded. */
100 static {
101 DebuggerSupport.FORCELOAD = true;
102 }
104 /**
105 * ContextCodeInstaller that has the privilege of installing classes in the Context.
106 * Can only be instantiated from inside the context and is opaque to other classes
107 */
108 public static class ContextCodeInstaller implements CodeInstaller<ScriptEnvironment> {
109 private final Context context;
110 private final ScriptLoader loader;
111 private final CodeSource codeSource;
113 private ContextCodeInstaller(final Context context, final ScriptLoader loader, final CodeSource codeSource) {
114 this.context = context;
115 this.loader = loader;
116 this.codeSource = codeSource;
117 }
119 /**
120 * Return the context for this installer
121 * @return ScriptEnvironment
122 */
123 @Override
124 public ScriptEnvironment getOwner() {
125 return context.env;
126 }
128 @Override
129 public Class<?> install(final String className, final byte[] bytecode) {
130 return loader.installClass(className, bytecode, codeSource);
131 }
133 @Override
134 public void verify(final byte[] code) {
135 context.verify(code);
136 }
138 @Override
139 public long getUniqueScriptId() {
140 return context.getUniqueScriptId();
141 }
142 }
144 /** Is Context global debug mode enabled ? */
145 public static final boolean DEBUG = Options.getBooleanProperty("nashorn.debug");
147 private static final ThreadLocal<ScriptObject> currentGlobal = new ThreadLocal<>();
149 /**
150 * Get the current global scope
151 * @return the current global scope
152 */
153 public static ScriptObject getGlobal() {
154 // This class in a package.access protected package.
155 // Trusted code only can call this method.
156 return getGlobalTrusted();
157 }
159 /**
160 * Set the current global scope
161 * @param global the global scope
162 */
163 public static void setGlobal(final ScriptObject global) {
164 if (global != null && !(global instanceof Global)) {
165 throw new IllegalArgumentException("global is not an instance of Global!");
166 }
168 setGlobalTrusted(global);
169 }
171 /**
172 * Get context of the current global
173 * @return current global scope's context.
174 */
175 public static Context getContext() {
176 final SecurityManager sm = System.getSecurityManager();
177 if (sm != null) {
178 sm.checkPermission(new RuntimePermission(NASHORN_GET_CONTEXT));
179 }
180 return getContextTrusted();
181 }
183 /**
184 * Get current context's error writer
185 *
186 * @return error writer of the current context
187 */
188 public static PrintWriter getCurrentErr() {
189 final ScriptObject global = getGlobalTrusted();
190 return (global != null)? global.getContext().getErr() : new PrintWriter(System.err);
191 }
193 /**
194 * Output text to this Context's error stream
195 * @param str text to write
196 */
197 public static void err(final String str) {
198 err(str, true);
199 }
201 /**
202 * Output text to this Context's error stream, optionally with
203 * a newline afterwards
204 *
205 * @param str text to write
206 * @param crlf write a carriage return/new line after text
207 */
208 @SuppressWarnings("resource")
209 public static void err(final String str, final boolean crlf) {
210 final PrintWriter err = Context.getCurrentErr();
211 if (err != null) {
212 if (crlf) {
213 err.println(str);
214 } else {
215 err.print(str);
216 }
217 }
218 }
220 /** Current environment. */
221 private final ScriptEnvironment env;
223 /** is this context in strict mode? Cached from env. as this is used heavily. */
224 final boolean _strict;
226 /** class loader to resolve classes from script. */
227 private final ClassLoader appLoader;
229 /** Class loader to load classes from -classpath option, if set. */
230 private final ClassLoader classPathLoader;
232 /** Class loader to load classes compiled from scripts. */
233 private final ScriptLoader scriptLoader;
235 /** Current error manager. */
236 private final ErrorManager errors;
238 /** Unique id for script. Used only when --loader-per-compile=false */
239 private final AtomicLong uniqueScriptId;
241 private static final ClassLoader myLoader = Context.class.getClassLoader();
242 private static final StructureLoader sharedLoader;
244 /*package-private*/ ClassLoader getSharedLoader() {
245 return sharedLoader;
246 }
248 private static AccessControlContext createNoPermAccCtxt() {
249 return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, new Permissions()) });
250 }
252 private static AccessControlContext createPermAccCtxt(final String permName) {
253 final Permissions perms = new Permissions();
254 perms.add(new RuntimePermission(permName));
255 return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, perms) });
256 }
258 private static final AccessControlContext NO_PERMISSIONS_ACC_CTXT = createNoPermAccCtxt();
259 private static final AccessControlContext CREATE_LOADER_ACC_CTXT = createPermAccCtxt("createClassLoader");
260 private static final AccessControlContext CREATE_GLOBAL_ACC_CTXT = createPermAccCtxt(NASHORN_CREATE_GLOBAL);
262 static {
263 sharedLoader = AccessController.doPrivileged(new PrivilegedAction<StructureLoader>() {
264 @Override
265 public StructureLoader run() {
266 return new StructureLoader(myLoader);
267 }
268 }, CREATE_LOADER_ACC_CTXT);
269 }
271 /**
272 * ThrowErrorManager that throws ParserException upon error conditions.
273 */
274 public static class ThrowErrorManager extends ErrorManager {
275 @Override
276 public void error(final String message) {
277 throw new ParserException(message);
278 }
280 @Override
281 public void error(final ParserException e) {
282 throw e;
283 }
284 }
286 /**
287 * Constructor
288 *
289 * @param options options from command line or Context creator
290 * @param errors error manger
291 * @param appLoader application class loader
292 */
293 public Context(final Options options, final ErrorManager errors, final ClassLoader appLoader) {
294 this(options, errors, new PrintWriter(System.out, true), new PrintWriter(System.err, true), appLoader);
295 }
297 /**
298 * Constructor
299 *
300 * @param options options from command line or Context creator
301 * @param errors error manger
302 * @param out output writer for this Context
303 * @param err error writer for this Context
304 * @param appLoader application class loader
305 */
306 public Context(final Options options, final ErrorManager errors, final PrintWriter out, final PrintWriter err, final ClassLoader appLoader) {
307 final SecurityManager sm = System.getSecurityManager();
308 if (sm != null) {
309 sm.checkPermission(new RuntimePermission(NASHORN_CREATE_CONTEXT));
310 }
312 this.env = new ScriptEnvironment(options, out, err);
313 this._strict = env._strict;
314 this.appLoader = appLoader;
315 if (env._loader_per_compile) {
316 this.scriptLoader = null;
317 this.uniqueScriptId = null;
318 } else {
319 this.scriptLoader = createNewLoader();
320 this.uniqueScriptId = new AtomicLong();
321 }
322 this.errors = errors;
324 // if user passed -classpath option, make a class loader with that and set it as
325 // thread context class loader so that script can access classes from that path.
326 final String classPath = options.getString("classpath");
327 if (! env._compile_only && classPath != null && !classPath.isEmpty()) {
328 // make sure that caller can create a class loader.
329 if (sm != null) {
330 sm.checkPermission(new RuntimePermission("createClassLoader"));
331 }
332 this.classPathLoader = NashornLoader.createClassLoader(classPath);
333 } else {
334 this.classPathLoader = null;
335 }
337 // print version info if asked.
338 if (env._version) {
339 getErr().println("nashorn " + Version.version());
340 }
342 if (env._fullversion) {
343 getErr().println("nashorn full version " + Version.fullVersion());
344 }
345 }
347 /**
348 * Get the error manager for this context
349 * @return error manger
350 */
351 public ErrorManager getErrorManager() {
352 return errors;
353 }
355 /**
356 * Get the script environment for this context
357 * @return script environment
358 */
359 public ScriptEnvironment getEnv() {
360 return env;
361 }
363 /**
364 * Get the output stream for this context
365 * @return output print writer
366 */
367 public PrintWriter getOut() {
368 return env.getOut();
369 }
371 /**
372 * Get the error stream for this context
373 * @return error print writer
374 */
375 public PrintWriter getErr() {
376 return env.getErr();
377 }
379 /**
380 * Get the PropertyMap of the current global scope
381 * @return the property map of the current global scope
382 */
383 public static PropertyMap getGlobalMap() {
384 return Context.getGlobalTrusted().getMap();
385 }
387 /**
388 * Compile a top level script.
389 *
390 * @param source the source
391 * @param scope the scope
392 *
393 * @return top level function for script
394 */
395 public ScriptFunction compileScript(final Source source, final ScriptObject scope) {
396 return compileScript(source, scope, this.errors);
397 }
399 /**
400 * Entry point for {@code eval}
401 *
402 * @param initialScope The scope of this eval call
403 * @param string Evaluated code as a String
404 * @param callThis "this" to be passed to the evaluated code
405 * @param location location of the eval call
406 * @param strict is this {@code eval} call from a strict mode code?
407 *
408 * @return the return value of the {@code eval}
409 */
410 public Object eval(final ScriptObject initialScope, final String string, final Object callThis, final Object location, final boolean strict) {
411 final String file = (location == UNDEFINED || location == null) ? "<eval>" : location.toString();
412 final Source source = new Source(file, string);
413 final boolean directEval = location != UNDEFINED; // is this direct 'eval' call or indirectly invoked eval?
414 final ScriptObject global = Context.getGlobalTrusted();
416 ScriptObject scope = initialScope;
418 // ECMA section 10.1.1 point 2 says eval code is strict if it begins
419 // with "use strict" directive or eval direct call itself is made
420 // from from strict mode code. We are passed with caller's strict mode.
421 boolean strictFlag = directEval && strict;
423 Class<?> clazz = null;
424 try {
425 clazz = compile(source, new ThrowErrorManager(), strictFlag);
426 } catch (final ParserException e) {
427 e.throwAsEcmaException(global);
428 return null;
429 }
431 if (!strictFlag) {
432 // We need to get strict mode flag from compiled class. This is
433 // because eval code may start with "use strict" directive.
434 try {
435 strictFlag = clazz.getField(STRICT_MODE.symbolName()).getBoolean(null);
436 } catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
437 //ignored
438 strictFlag = false;
439 }
440 }
442 // In strict mode, eval does not instantiate variables and functions
443 // in the caller's environment. A new environment is created!
444 if (strictFlag) {
445 // Create a new scope object
446 final ScriptObject strictEvalScope = ((GlobalObject)global).newObject();
448 // bless it as a "scope"
449 strictEvalScope.setIsScope();
451 // set given scope to be it's proto so that eval can still
452 // access caller environment vars in the new environment.
453 strictEvalScope.setProto(scope);
454 scope = strictEvalScope;
455 }
457 ScriptFunction func = getRunScriptFunction(clazz, scope);
458 Object evalThis;
459 if (directEval) {
460 evalThis = (callThis instanceof ScriptObject || strictFlag) ? callThis : global;
461 } else {
462 evalThis = global;
463 }
465 return ScriptRuntime.apply(func, evalThis);
466 }
468 private static Source loadInternal(final String srcStr, final String prefix, final String resourcePath) {
469 if (srcStr.startsWith(prefix)) {
470 final String resource = resourcePath + srcStr.substring(prefix.length());
471 // NOTE: even sandbox scripts should be able to load scripts in nashorn: scheme
472 // These scripts are always available and are loaded from nashorn.jar's resources.
473 return AccessController.doPrivileged(
474 new PrivilegedAction<Source>() {
475 @Override
476 public Source run() {
477 try {
478 final URL resURL = Context.class.getResource(resource);
479 return (resURL != null)? new Source(srcStr, resURL) : null;
480 } catch (final IOException exp) {
481 return null;
482 }
483 }
484 });
485 }
487 return null;
488 }
490 /**
491 * Implementation of {@code load} Nashorn extension. Load a script file from a source
492 * expression
493 *
494 * @param scope the scope
495 * @param from source expression for script
496 *
497 * @return return value for load call (undefined)
498 *
499 * @throws IOException if source cannot be found or loaded
500 */
501 public Object load(final ScriptObject scope, final Object from) throws IOException {
502 final Object src = (from instanceof ConsString)? from.toString() : from;
503 Source source = null;
505 // load accepts a String (which could be a URL or a file name), a File, a URL
506 // or a ScriptObject that has "name" and "source" (string valued) properties.
507 if (src instanceof String) {
508 final String srcStr = (String)src;
509 if (srcStr.startsWith(LOAD_CLASSPATH)) {
510 URL url = getResourceURL(srcStr.substring(LOAD_CLASSPATH.length()));
511 source = (url != null)? new Source(url.toString(), url) : null;
512 } else {
513 final File file = new File(srcStr);
514 if (srcStr.indexOf(':') != -1) {
515 if ((source = loadInternal(srcStr, LOAD_NASHORN, "resources/")) == null &&
516 (source = loadInternal(srcStr, LOAD_FX, "resources/fx/")) == null) {
517 URL url;
518 try {
519 //check for malformed url. if malformed, it may still be a valid file
520 url = new URL(srcStr);
521 } catch (final MalformedURLException e) {
522 url = file.toURI().toURL();
523 }
524 source = new Source(url.toString(), url);
525 }
526 } else if (file.isFile()) {
527 source = new Source(srcStr, file);
528 }
529 }
530 } else if (src instanceof File && ((File)src).isFile()) {
531 final File file = (File)src;
532 source = new Source(file.getName(), file);
533 } else if (src instanceof URL) {
534 final URL url = (URL)src;
535 source = new Source(url.toString(), url);
536 } else if (src instanceof ScriptObject) {
537 final ScriptObject sobj = (ScriptObject)src;
538 if (sobj.has("script") && sobj.has("name")) {
539 final String script = JSType.toString(sobj.get("script"));
540 final String name = JSType.toString(sobj.get("name"));
541 source = new Source(name, script);
542 }
543 } else if (src instanceof Map) {
544 final Map<?,?> map = (Map<?,?>)src;
545 if (map.containsKey("script") && map.containsKey("name")) {
546 final String script = JSType.toString(map.get("script"));
547 final String name = JSType.toString(map.get("name"));
548 source = new Source(name, script);
549 }
550 }
552 if (source != null) {
553 return evaluateSource(source, scope, scope);
554 }
556 throw typeError("cant.load.script", ScriptRuntime.safeToString(from));
557 }
559 /**
560 * Implementation of {@code loadWithNewGlobal} Nashorn extension. Load a script file from a source
561 * expression, after creating a new global scope.
562 *
563 * @param from source expression for script
564 * @param args (optional) arguments to be passed to the loaded script
565 *
566 * @return return value for load call (undefined)
567 *
568 * @throws IOException if source cannot be found or loaded
569 */
570 public Object loadWithNewGlobal(final Object from, final Object...args) throws IOException {
571 final ScriptObject oldGlobal = getGlobalTrusted();
572 final ScriptObject newGlobal = AccessController.doPrivileged(new PrivilegedAction<ScriptObject>() {
573 @Override
574 public ScriptObject run() {
575 try {
576 return newGlobal();
577 } catch (final RuntimeException e) {
578 if (Context.DEBUG) {
579 e.printStackTrace();
580 }
581 throw e;
582 }
583 }
584 }, CREATE_GLOBAL_ACC_CTXT);
585 // initialize newly created Global instance
586 initGlobal(newGlobal);
587 setGlobalTrusted(newGlobal);
589 final Object[] wrapped = args == null? ScriptRuntime.EMPTY_ARRAY : ScriptObjectMirror.wrapArray(args, oldGlobal);
590 newGlobal.put("arguments", ((GlobalObject)newGlobal).wrapAsObject(wrapped), env._strict);
592 try {
593 // wrap objects from newGlobal's world as mirrors - but if result
594 // is from oldGlobal's world, unwrap it!
595 return ScriptObjectMirror.unwrap(ScriptObjectMirror.wrap(load(newGlobal, from), newGlobal), oldGlobal);
596 } finally {
597 setGlobalTrusted(oldGlobal);
598 }
599 }
601 /**
602 * Load or get a structure class. Structure class names are based on the number of parameter fields
603 * and {@link AccessorProperty} fields in them. Structure classes are used to represent ScriptObjects
604 *
605 * @see ObjectClassGenerator
606 * @see AccessorProperty
607 * @see ScriptObject
608 *
609 * @param fullName full name of class, e.g. jdk.nashorn.internal.objects.JO2P1 contains 2 fields and 1 parameter.
610 *
611 * @return the {@code Class<?>} for this structure
612 *
613 * @throws ClassNotFoundException if structure class cannot be resolved
614 */
615 public static Class<?> forStructureClass(final String fullName) throws ClassNotFoundException {
616 if (System.getSecurityManager() != null && !StructureLoader.isStructureClass(fullName)) {
617 throw new ClassNotFoundException(fullName);
618 }
619 return Class.forName(fullName, true, sharedLoader);
620 }
622 /**
623 * Checks that the given Class can be accessed from no permissions context.
624 *
625 * @param clazz Class object
626 * @throw SecurityException if not accessible
627 */
628 public static void checkPackageAccess(final Class clazz) {
629 final SecurityManager sm = System.getSecurityManager();
630 if (sm != null) {
631 Class bottomClazz = clazz;
632 while(bottomClazz.isArray()) {
633 bottomClazz = bottomClazz.getComponentType();
634 }
635 checkPackageAccess(sm, bottomClazz.getName());
636 }
637 }
639 /**
640 * Checks that the given package can be accessed from no permissions context.
641 *
642 * @param sm current security manager instance
643 * @param fullName fully qualified package name
644 * @throw SecurityException if not accessible
645 */
646 private static void checkPackageAccess(final SecurityManager sm, final String fullName) {
647 sm.getClass(); // null check
648 final int index = fullName.lastIndexOf('.');
649 if (index != -1) {
650 final String pkgName = fullName.substring(0, index);
651 AccessController.doPrivileged(new PrivilegedAction<Void>() {
652 @Override
653 public Void run() {
654 sm.checkPackageAccess(pkgName);
655 return null;
656 }
657 }, NO_PERMISSIONS_ACC_CTXT);
658 }
659 }
661 /**
662 * Checks that the given Class can be accessed from no permissions context.
663 *
664 * @param clazz Class object
665 * @return true if package is accessible, false otherwise
666 */
667 private static boolean isAccessiblePackage(final Class clazz) {
668 try {
669 checkPackageAccess(clazz);
670 return true;
671 } catch (final SecurityException se) {
672 return false;
673 }
674 }
676 /**
677 * Checks that the given Class is public and it can be accessed from no permissions context.
678 *
679 * @param clazz Class object to check
680 * @return true if Class is accessible, false otherwise
681 */
682 public static boolean isAccessibleClass(final Class<?> clazz) {
683 return Modifier.isPublic(clazz.getModifiers()) && Context.isAccessiblePackage(clazz);
684 }
686 /**
687 * Lookup a Java class. This is used for JSR-223 stuff linking in from
688 * {@code jdk.nashorn.internal.objects.NativeJava} and {@code jdk.nashorn.internal.runtime.NativeJavaPackage}
689 *
690 * @param fullName full name of class to load
691 *
692 * @return the {@code Class<?>} for the name
693 *
694 * @throws ClassNotFoundException if class cannot be resolved
695 */
696 public Class<?> findClass(final String fullName) throws ClassNotFoundException {
697 if (fullName.indexOf('[') != -1 || fullName.indexOf('/') != -1) {
698 // don't allow array class names or internal names.
699 throw new ClassNotFoundException(fullName);
700 }
702 // check package access as soon as possible!
703 final SecurityManager sm = System.getSecurityManager();
704 if (sm != null) {
705 checkPackageAccess(sm, fullName);
706 }
708 // try the script -classpath loader, if that is set
709 if (classPathLoader != null) {
710 try {
711 return Class.forName(fullName, true, classPathLoader);
712 } catch (final ClassNotFoundException ignored) {
713 // ignore, continue search
714 }
715 }
717 // Try finding using the "app" loader.
718 return Class.forName(fullName, true, appLoader);
719 }
721 /**
722 * Hook to print stack trace for a {@link Throwable} that occurred during
723 * execution
724 *
725 * @param t throwable for which to dump stack
726 */
727 public static void printStackTrace(final Throwable t) {
728 if (Context.DEBUG) {
729 t.printStackTrace(Context.getCurrentErr());
730 }
731 }
733 /**
734 * Verify generated bytecode before emission. This is called back from the
735 * {@link ObjectClassGenerator} or the {@link Compiler}. If the "--verify-code" parameter
736 * hasn't been given, this is a nop
737 *
738 * Note that verification may load classes -- we don't want to do that unless
739 * user specified verify option. We check it here even though caller
740 * may have already checked that flag
741 *
742 * @param bytecode bytecode to verify
743 */
744 public void verify(final byte[] bytecode) {
745 if (env._verify_code) {
746 // No verification when security manager is around as verifier
747 // may load further classes - which should be avoided.
748 if (System.getSecurityManager() == null) {
749 CheckClassAdapter.verify(new ClassReader(bytecode), sharedLoader, false, new PrintWriter(System.err, true));
750 }
751 }
752 }
754 /**
755 * Create and initialize a new global scope object.
756 *
757 * @return the initialized global scope object.
758 */
759 public ScriptObject createGlobal() {
760 return initGlobal(newGlobal());
761 }
763 /**
764 * Create a new uninitialized global scope object
765 * @return the global script object
766 */
767 public ScriptObject newGlobal() {
768 return new Global(this);
769 }
771 /**
772 * Initialize given global scope object.
773 *
774 * @param global the global
775 * @return the initialized global scope object.
776 */
777 public ScriptObject initGlobal(final ScriptObject global) {
778 if (! (global instanceof GlobalObject)) {
779 throw new IllegalArgumentException("not a global object!");
780 }
782 // Need only minimal global object, if we are just compiling.
783 if (!env._compile_only) {
784 final ScriptObject oldGlobal = Context.getGlobalTrusted();
785 try {
786 Context.setGlobalTrusted(global);
787 // initialize global scope with builtin global objects
788 ((GlobalObject)global).initBuiltinObjects();
789 } finally {
790 Context.setGlobalTrusted(oldGlobal);
791 }
792 }
794 return global;
795 }
797 /**
798 * Trusted variants - package-private
799 */
801 /**
802 * Return the current global scope
803 * @return current global scope
804 */
805 static ScriptObject getGlobalTrusted() {
806 return currentGlobal.get();
807 }
809 /**
810 * Set the current global scope
811 */
812 static void setGlobalTrusted(ScriptObject global) {
813 currentGlobal.set(global);
814 }
816 /**
817 * Return the current global's context
818 * @return current global's context
819 */
820 static Context getContextTrusted() {
821 return Context.getGlobalTrusted().getContext();
822 }
824 /**
825 * Try to infer Context instance from the Class. If we cannot,
826 * then get it from the thread local variable.
827 *
828 * @param clazz the class
829 * @return context
830 */
831 static Context fromClass(final Class<?> clazz) {
832 final ClassLoader loader = clazz.getClassLoader();
834 if (loader instanceof ScriptLoader) {
835 return ((ScriptLoader)loader).getContext();
836 }
838 return Context.getContextTrusted();
839 }
841 private URL getResourceURL(final String resName) throws IOException {
842 // try the classPathLoader if we have and then
843 // try the appLoader if non-null.
844 if (classPathLoader != null) {
845 return classPathLoader.getResource(resName);
846 } else if (appLoader != null) {
847 return appLoader.getResource(resName);
848 }
850 return null;
851 }
853 private Object evaluateSource(final Source source, final ScriptObject scope, final ScriptObject thiz) {
854 ScriptFunction script = null;
856 try {
857 script = compileScript(source, scope, new Context.ThrowErrorManager());
858 } catch (final ParserException e) {
859 e.throwAsEcmaException();
860 }
862 return ScriptRuntime.apply(script, thiz);
863 }
865 private static ScriptFunction getRunScriptFunction(final Class<?> script, final ScriptObject scope) {
866 if (script == null) {
867 return null;
868 }
870 // Get run method - the entry point to the script
871 final MethodHandle runMethodHandle =
872 MH.findStatic(
873 MethodHandles.lookup(),
874 script,
875 RUN_SCRIPT.symbolName(),
876 MH.type(
877 Object.class,
878 ScriptFunction.class,
879 Object.class));
881 boolean strict;
883 try {
884 strict = script.getField(STRICT_MODE.symbolName()).getBoolean(null);
885 } catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
886 strict = false;
887 }
889 // Package as a JavaScript function and pass function back to shell.
890 return ((GlobalObject)Context.getGlobalTrusted()).newScriptFunction(RUN_SCRIPT.symbolName(), runMethodHandle, scope, strict);
891 }
893 private ScriptFunction compileScript(final Source source, final ScriptObject scope, final ErrorManager errMan) {
894 return getRunScriptFunction(compile(source, errMan, this._strict), scope);
895 }
897 private synchronized Class<?> compile(final Source source, final ErrorManager errMan, final boolean strict) {
898 // start with no errors, no warnings.
899 errMan.reset();
901 GlobalObject global = null;
902 Class<?> script;
904 if (env._class_cache_size > 0) {
905 global = (GlobalObject)Context.getGlobalTrusted();
906 script = global.findCachedClass(source);
907 if (script != null) {
908 Compiler.LOG.fine("Code cache hit for ", source, " avoiding recompile.");
909 return script;
910 }
911 }
913 final FunctionNode functionNode = new Parser(env, source, errMan, strict).parse();
914 if (errors.hasErrors()) {
915 return null;
916 }
918 if (env._print_ast) {
919 getErr().println(new ASTWriter(functionNode));
920 }
922 if (env._print_parse) {
923 getErr().println(new PrintVisitor(functionNode));
924 }
926 if (env._parse_only) {
927 return null;
928 }
930 final URL url = source.getURL();
931 final ScriptLoader loader = env._loader_per_compile ? createNewLoader() : scriptLoader;
932 final CodeSource cs = url == null ? null : new CodeSource(url, (CodeSigner[])null);
933 final CodeInstaller<ScriptEnvironment> installer = new ContextCodeInstaller(this, loader, cs);
935 final Compiler compiler = new Compiler(installer, strict);
937 final FunctionNode newFunctionNode = compiler.compile(functionNode);
938 script = compiler.install(newFunctionNode);
940 if (global != null) {
941 global.cacheClass(source, script);
942 }
944 return script;
945 }
947 private ScriptLoader createNewLoader() {
948 return AccessController.doPrivileged(
949 new PrivilegedAction<ScriptLoader>() {
950 @Override
951 public ScriptLoader run() {
952 return new ScriptLoader(appLoader, Context.this);
953 }
954 }, CREATE_LOADER_ACC_CTXT);
955 }
957 private long getUniqueScriptId() {
958 return uniqueScriptId.getAndIncrement();
959 }
960 }