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