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