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