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