Context.java revision 1015:8a4af0397070
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 26package jdk.nashorn.internal.runtime; 27 28import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS; 29import static jdk.nashorn.internal.codegen.CompilerConstants.CREATE_PROGRAM_FUNCTION; 30import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE; 31import static jdk.nashorn.internal.codegen.CompilerConstants.STRICT_MODE; 32import static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 33import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; 34import static jdk.nashorn.internal.runtime.Source.sourceFor; 35 36import java.io.File; 37import java.io.IOException; 38import java.io.PrintWriter; 39import java.lang.invoke.MethodHandle; 40import java.lang.invoke.MethodHandles; 41import java.lang.invoke.MethodType; 42import java.lang.ref.ReferenceQueue; 43import java.lang.ref.SoftReference; 44import java.lang.reflect.Field; 45import java.lang.reflect.Modifier; 46import java.net.MalformedURLException; 47import java.net.URL; 48import java.security.AccessControlContext; 49import java.security.AccessController; 50import java.security.CodeSigner; 51import java.security.CodeSource; 52import java.security.Permissions; 53import java.security.PrivilegedAction; 54import java.security.PrivilegedActionException; 55import java.security.PrivilegedExceptionAction; 56import java.security.ProtectionDomain; 57import java.util.Collection; 58import java.util.HashMap; 59import java.util.LinkedHashMap; 60import java.util.Map; 61import java.util.concurrent.atomic.AtomicLong; 62import java.util.function.Consumer; 63import java.util.function.Supplier; 64import java.util.logging.Level; 65import javax.script.ScriptEngine; 66import jdk.internal.org.objectweb.asm.ClassReader; 67import jdk.internal.org.objectweb.asm.util.CheckClassAdapter; 68import jdk.nashorn.api.scripting.ClassFilter; 69import jdk.nashorn.api.scripting.ScriptObjectMirror; 70import jdk.nashorn.internal.codegen.Compiler; 71import jdk.nashorn.internal.codegen.Compiler.CompilationPhases; 72import jdk.nashorn.internal.codegen.ObjectClassGenerator; 73import jdk.nashorn.internal.ir.FunctionNode; 74import jdk.nashorn.internal.ir.debug.ASTWriter; 75import jdk.nashorn.internal.ir.debug.PrintVisitor; 76import jdk.nashorn.internal.lookup.MethodHandleFactory; 77import jdk.nashorn.internal.objects.Global; 78import jdk.nashorn.internal.parser.Parser; 79import jdk.nashorn.internal.runtime.events.RuntimeEvent; 80import jdk.nashorn.internal.runtime.logging.DebugLogger; 81import jdk.nashorn.internal.runtime.logging.Loggable; 82import jdk.nashorn.internal.runtime.logging.Logger; 83import jdk.nashorn.internal.runtime.options.LoggingOption.LoggerInfo; 84import jdk.nashorn.internal.runtime.options.Options; 85 86/** 87 * This class manages the global state of execution. Context is immutable. 88 */ 89public final class Context { 90 // nashorn specific security runtime access permission names 91 /** 92 * Permission needed to pass arbitrary nashorn command line options when creating Context. 93 */ 94 public static final String NASHORN_SET_CONFIG = "nashorn.setConfig"; 95 96 /** 97 * Permission needed to create Nashorn Context instance. 98 */ 99 public static final String NASHORN_CREATE_CONTEXT = "nashorn.createContext"; 100 101 /** 102 * Permission needed to create Nashorn Global instance. 103 */ 104 public static final String NASHORN_CREATE_GLOBAL = "nashorn.createGlobal"; 105 106 /** 107 * Permission to get current Nashorn Context from thread local storage. 108 */ 109 public static final String NASHORN_GET_CONTEXT = "nashorn.getContext"; 110 111 /** 112 * Permission to use Java reflection/jsr292 from script code. 113 */ 114 public static final String NASHORN_JAVA_REFLECTION = "nashorn.JavaReflection"; 115 116 /** 117 * Permission to enable nashorn debug mode. 118 */ 119 public static final String NASHORN_DEBUG_MODE = "nashorn.debugMode"; 120 121 // nashorn load psuedo URL prefixes 122 private static final String LOAD_CLASSPATH = "classpath:"; 123 private static final String LOAD_FX = "fx:"; 124 private static final String LOAD_NASHORN = "nashorn:"; 125 126 private static MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); 127 private static MethodType CREATE_PROGRAM_FUNCTION_TYPE = MethodType.methodType(ScriptFunction.class, ScriptObject.class); 128 129 /* Force DebuggerSupport to be loaded. */ 130 static { 131 DebuggerSupport.FORCELOAD = true; 132 } 133 134 /** 135 * ContextCodeInstaller that has the privilege of installing classes in the Context. 136 * Can only be instantiated from inside the context and is opaque to other classes 137 */ 138 public static class ContextCodeInstaller implements CodeInstaller<ScriptEnvironment> { 139 private final Context context; 140 private final ScriptLoader loader; 141 private final CodeSource codeSource; 142 143 private ContextCodeInstaller(final Context context, final ScriptLoader loader, final CodeSource codeSource) { 144 this.context = context; 145 this.loader = loader; 146 this.codeSource = codeSource; 147 } 148 149 /** 150 * Return the context for this installer 151 * @return ScriptEnvironment 152 */ 153 @Override 154 public ScriptEnvironment getOwner() { 155 return context.env; 156 } 157 158 @Override 159 public Class<?> install(final String className, final byte[] bytecode) { 160 final String binaryName = Compiler.binaryName(className); 161 return loader.installClass(binaryName, bytecode, codeSource); 162 } 163 164 @Override 165 public void initialize(final Collection<Class<?>> classes, final Source source, final Object[] constants) { 166 try { 167 AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() { 168 @Override 169 public Void run() throws Exception { 170 for (final Class<?> clazz : classes) { 171 //use reflection to write source and constants table to installed classes 172 final Field sourceField = clazz.getDeclaredField(SOURCE.symbolName()); 173 sourceField.setAccessible(true); 174 sourceField.set(null, source); 175 176 final Field constantsField = clazz.getDeclaredField(CONSTANTS.symbolName()); 177 constantsField.setAccessible(true); 178 constantsField.set(null, constants); 179 } 180 return null; 181 } 182 }); 183 } catch (final PrivilegedActionException e) { 184 throw new RuntimeException(e); 185 } 186 } 187 188 @Override 189 public void verify(final byte[] code) { 190 context.verify(code); 191 } 192 193 @Override 194 public long getUniqueScriptId() { 195 return context.getUniqueScriptId(); 196 } 197 198 @Override 199 public void storeScript(final String cacheKey, final Source source, final String mainClassName, 200 final Map<String,byte[]> classBytes, final Map<Integer, FunctionInitializer> initializers, 201 final Object[] constants, final int compilationId) { 202 if (context.codeStore != null) { 203 context.codeStore.storeScript(cacheKey, source, mainClassName, classBytes, initializers, constants, compilationId); 204 } 205 } 206 207 @Override 208 public StoredScript loadScript(final Source source, final String functionKey) { 209 if (context.codeStore != null) { 210 return context.codeStore.loadScript(source, functionKey); 211 } 212 return null; 213 } 214 } 215 216 /** Is Context global debug mode enabled ? */ 217 public static final boolean DEBUG = Options.getBooleanProperty("nashorn.debug"); 218 219 private static final ThreadLocal<Global> currentGlobal = new ThreadLocal<>(); 220 221 // in-memory cache for loaded classes 222 private ClassCache classCache; 223 224 // persistent code store 225 private CodeStore codeStore; 226 227 /** 228 * Get the current global scope 229 * @return the current global scope 230 */ 231 public static Global getGlobal() { 232 // This class in a package.access protected package. 233 // Trusted code only can call this method. 234 return currentGlobal.get(); 235 } 236 237 /** 238 * Set the current global scope 239 * @param global the global scope 240 */ 241 public static void setGlobal(final ScriptObject global) { 242 if (global != null && !(global instanceof Global)) { 243 throw new IllegalArgumentException("not a global!"); 244 } 245 setGlobal((Global)global); 246 } 247 248 /** 249 * Set the current global scope 250 * @param global the global scope 251 */ 252 public static void setGlobal(final Global global) { 253 // This class in a package.access protected package. 254 // Trusted code only can call this method. 255 assert getGlobal() != global; 256 //same code can be cached between globals, then we need to invalidate method handle constants 257 if (global != null) { 258 Global.getConstants().invalidateAll(); 259 } 260 currentGlobal.set(global); 261 } 262 263 /** 264 * Get context of the current global 265 * @return current global scope's context. 266 */ 267 public static Context getContext() { 268 final SecurityManager sm = System.getSecurityManager(); 269 if (sm != null) { 270 sm.checkPermission(new RuntimePermission(NASHORN_GET_CONTEXT)); 271 } 272 return getContextTrusted(); 273 } 274 275 /** 276 * Get current context's error writer 277 * 278 * @return error writer of the current context 279 */ 280 public static PrintWriter getCurrentErr() { 281 final ScriptObject global = getGlobal(); 282 return (global != null)? global.getContext().getErr() : new PrintWriter(System.err); 283 } 284 285 /** 286 * Output text to this Context's error stream 287 * @param str text to write 288 */ 289 public static void err(final String str) { 290 err(str, true); 291 } 292 293 /** 294 * Output text to this Context's error stream, optionally with 295 * a newline afterwards 296 * 297 * @param str text to write 298 * @param crlf write a carriage return/new line after text 299 */ 300 public static void err(final String str, final boolean crlf) { 301 final PrintWriter err = Context.getCurrentErr(); 302 if (err != null) { 303 if (crlf) { 304 err.println(str); 305 } else { 306 err.print(str); 307 } 308 } 309 } 310 311 /** Current environment. */ 312 private final ScriptEnvironment env; 313 314 /** is this context in strict mode? Cached from env. as this is used heavily. */ 315 final boolean _strict; 316 317 /** class loader to resolve classes from script. */ 318 private final ClassLoader appLoader; 319 320 /** Class loader to load classes from -classpath option, if set. */ 321 private final ClassLoader classPathLoader; 322 323 /** Class loader to load classes compiled from scripts. */ 324 private final ScriptLoader scriptLoader; 325 326 /** Current error manager. */ 327 private final ErrorManager errors; 328 329 /** Unique id for script. Used only when --loader-per-compile=false */ 330 private final AtomicLong uniqueScriptId; 331 332 /** Optional class filter to use for Java classes. Can be null. */ 333 private final ClassFilter classFilter; 334 335 private static final ClassLoader myLoader = Context.class.getClassLoader(); 336 private static final StructureLoader sharedLoader; 337 338 /*package-private*/ @SuppressWarnings("static-method") 339 ClassLoader getSharedLoader() { 340 return sharedLoader; 341 } 342 343 private static AccessControlContext createNoPermAccCtxt() { 344 return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, new Permissions()) }); 345 } 346 347 private static AccessControlContext createPermAccCtxt(final String permName) { 348 final Permissions perms = new Permissions(); 349 perms.add(new RuntimePermission(permName)); 350 return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, perms) }); 351 } 352 353 private static final AccessControlContext NO_PERMISSIONS_ACC_CTXT = createNoPermAccCtxt(); 354 private static final AccessControlContext CREATE_LOADER_ACC_CTXT = createPermAccCtxt("createClassLoader"); 355 private static final AccessControlContext CREATE_GLOBAL_ACC_CTXT = createPermAccCtxt(NASHORN_CREATE_GLOBAL); 356 357 static { 358 sharedLoader = AccessController.doPrivileged(new PrivilegedAction<StructureLoader>() { 359 @Override 360 public StructureLoader run() { 361 return new StructureLoader(myLoader); 362 } 363 }, CREATE_LOADER_ACC_CTXT); 364 } 365 366 /** 367 * ThrowErrorManager that throws ParserException upon error conditions. 368 */ 369 public static class ThrowErrorManager extends ErrorManager { 370 @Override 371 public void error(final String message) { 372 throw new ParserException(message); 373 } 374 375 @Override 376 public void error(final ParserException e) { 377 throw e; 378 } 379 } 380 381 /** 382 * Constructor 383 * 384 * @param options options from command line or Context creator 385 * @param errors error manger 386 * @param appLoader application class loader 387 */ 388 public Context(final Options options, final ErrorManager errors, final ClassLoader appLoader) { 389 this(options, errors, appLoader, (ClassFilter)null); 390 } 391 392 /** 393 * Constructor 394 * 395 * @param options options from command line or Context creator 396 * @param errors error manger 397 * @param appLoader application class loader 398 * @param classFilter class filter to use 399 */ 400 public Context(final Options options, final ErrorManager errors, final ClassLoader appLoader, final ClassFilter classFilter) { 401 this(options, errors, new PrintWriter(System.out, true), new PrintWriter(System.err, true), appLoader, classFilter); 402 } 403 404 /** 405 * Constructor 406 * 407 * @param options options from command line or Context creator 408 * @param errors error manger 409 * @param out output writer for this Context 410 * @param err error writer for this Context 411 * @param appLoader application class loader 412 */ 413 public Context(final Options options, final ErrorManager errors, final PrintWriter out, final PrintWriter err, final ClassLoader appLoader) { 414 this(options, errors, out, err, appLoader, (ClassFilter)null); 415 } 416 417 /** 418 * Constructor 419 * 420 * @param options options from command line or Context creator 421 * @param errors error manger 422 * @param out output writer for this Context 423 * @param err error writer for this Context 424 * @param appLoader application class loader 425 * @param classFilter class filter to use 426 */ 427 public Context(final Options options, final ErrorManager errors, final PrintWriter out, final PrintWriter err, final ClassLoader appLoader, final ClassFilter classFilter) { 428 final SecurityManager sm = System.getSecurityManager(); 429 if (sm != null) { 430 sm.checkPermission(new RuntimePermission(NASHORN_CREATE_CONTEXT)); 431 } 432 433 this.classFilter = classFilter; 434 this.env = new ScriptEnvironment(options, out, err); 435 this._strict = env._strict; 436 this.appLoader = appLoader; 437 if (env._loader_per_compile) { 438 this.scriptLoader = null; 439 this.uniqueScriptId = null; 440 } else { 441 this.scriptLoader = createNewLoader(); 442 this.uniqueScriptId = new AtomicLong(); 443 } 444 this.errors = errors; 445 446 // if user passed -classpath option, make a class loader with that and set it as 447 // thread context class loader so that script can access classes from that path. 448 final String classPath = options.getString("classpath"); 449 if (!env._compile_only && classPath != null && !classPath.isEmpty()) { 450 // make sure that caller can create a class loader. 451 if (sm != null) { 452 sm.checkPermission(new RuntimePermission("createClassLoader")); 453 } 454 this.classPathLoader = NashornLoader.createClassLoader(classPath); 455 } else { 456 this.classPathLoader = null; 457 } 458 459 final int cacheSize = env._class_cache_size; 460 if (cacheSize > 0) { 461 classCache = new ClassCache(cacheSize); 462 } 463 464 if (env._persistent_cache) { 465 try { 466 final String cacheDir = Options.getStringProperty("nashorn.persistent.code.cache", "nashorn_code_cache"); 467 codeStore = new CodeStore(this, cacheDir); 468 } catch (final IOException e) { 469 throw new RuntimeException("Error initializing code cache", e); 470 } 471 } 472 473 // print version info if asked. 474 if (env._version) { 475 getErr().println("nashorn " + Version.version()); 476 } 477 478 if (env._fullversion) { 479 getErr().println("nashorn full version " + Version.fullVersion()); 480 } 481 482 initLoggers(); 483 } 484 485 486 /** 487 * Get the class filter for this context 488 * @return class filter 489 */ 490 public ClassFilter getClassFilter() { 491 return classFilter; 492 } 493 494 /** 495 * Get the error manager for this context 496 * @return error manger 497 */ 498 public ErrorManager getErrorManager() { 499 return errors; 500 } 501 502 /** 503 * Get the script environment for this context 504 * @return script environment 505 */ 506 public ScriptEnvironment getEnv() { 507 return env; 508 } 509 510 /** 511 * Get the output stream for this context 512 * @return output print writer 513 */ 514 public PrintWriter getOut() { 515 return env.getOut(); 516 } 517 518 /** 519 * Get the error stream for this context 520 * @return error print writer 521 */ 522 public PrintWriter getErr() { 523 return env.getErr(); 524 } 525 526 /** 527 * Get the PropertyMap of the current global scope 528 * @return the property map of the current global scope 529 */ 530 public static PropertyMap getGlobalMap() { 531 return Context.getGlobal().getMap(); 532 } 533 534 /** 535 * Compile a top level script. 536 * 537 * @param source the source 538 * @param scope the scope 539 * 540 * @return top level function for script 541 */ 542 public ScriptFunction compileScript(final Source source, final ScriptObject scope) { 543 return compileScript(source, scope, this.errors); 544 } 545 546 /** 547 * Interface to represent compiled code that can be re-used across many 548 * global scope instances 549 */ 550 public static interface MultiGlobalCompiledScript { 551 /** 552 * Obtain script function object for a specific global scope object. 553 * 554 * @param newGlobal global scope for which function object is obtained 555 * @return script function for script level expressions 556 */ 557 public ScriptFunction getFunction(final Global newGlobal); 558 } 559 560 /** 561 * Compile a top level script. 562 * 563 * @param source the script source 564 * @return reusable compiled script across many global scopes. 565 */ 566 public MultiGlobalCompiledScript compileScript(final Source source) { 567 final Class<?> clazz = compile(source, this.errors, this._strict); 568 final MethodHandle createProgramFunctionHandle = getCreateProgramFunctionHandle(clazz); 569 570 return new MultiGlobalCompiledScript() { 571 @Override 572 public ScriptFunction getFunction(final Global newGlobal) { 573 return invokeCreateProgramFunctionHandle(createProgramFunctionHandle, newGlobal); 574 } 575 }; 576 } 577 578 /** 579 * Entry point for {@code eval} 580 * 581 * @param initialScope The scope of this eval call 582 * @param string Evaluated code as a String 583 * @param callThis "this" to be passed to the evaluated code 584 * @param location location of the eval call 585 * @param strict is this {@code eval} call from a strict mode code? 586 * @return the return value of the {@code eval} 587 */ 588 public Object eval(final ScriptObject initialScope, final String string, 589 final Object callThis, final Object location, final boolean strict) { 590 return eval(initialScope, string, callThis, location, strict, false); 591 } 592 593 /** 594 * Entry point for {@code eval} 595 * 596 * @param initialScope The scope of this eval call 597 * @param string Evaluated code as a String 598 * @param callThis "this" to be passed to the evaluated code 599 * @param location location of the eval call 600 * @param strict is this {@code eval} call from a strict mode code? 601 * @param evalCall is this called from "eval" builtin? 602 * 603 * @return the return value of the {@code eval} 604 */ 605 public Object eval(final ScriptObject initialScope, final String string, 606 final Object callThis, final Object location, final boolean strict, final boolean evalCall) { 607 final String file = location == UNDEFINED || location == null ? "<eval>" : location.toString(); 608 final Source source = sourceFor(file, string, evalCall); 609 final boolean directEval = location != UNDEFINED; // is this direct 'eval' call or indirectly invoked eval? 610 final Global global = Context.getGlobal(); 611 ScriptObject scope = initialScope; 612 613 // ECMA section 10.1.1 point 2 says eval code is strict if it begins 614 // with "use strict" directive or eval direct call itself is made 615 // from from strict mode code. We are passed with caller's strict mode. 616 boolean strictFlag = directEval && strict; 617 618 Class<?> clazz = null; 619 try { 620 clazz = compile(source, new ThrowErrorManager(), strictFlag); 621 } catch (final ParserException e) { 622 e.throwAsEcmaException(global); 623 return null; 624 } 625 626 if (!strictFlag) { 627 // We need to get strict mode flag from compiled class. This is 628 // because eval code may start with "use strict" directive. 629 try { 630 strictFlag = clazz.getField(STRICT_MODE.symbolName()).getBoolean(null); 631 } catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { 632 //ignored 633 strictFlag = false; 634 } 635 } 636 637 // In strict mode, eval does not instantiate variables and functions 638 // in the caller's environment. A new environment is created! 639 if (strictFlag) { 640 // Create a new scope object 641 final ScriptObject strictEvalScope = global.newObject(); 642 643 // bless it as a "scope" 644 strictEvalScope.setIsScope(); 645 646 // set given scope to be it's proto so that eval can still 647 // access caller environment vars in the new environment. 648 strictEvalScope.setProto(scope); 649 scope = strictEvalScope; 650 } 651 652 final ScriptFunction func = getProgramFunction(clazz, scope); 653 Object evalThis; 654 if (directEval) { 655 evalThis = callThis instanceof ScriptObject || strictFlag ? callThis : global; 656 } else { 657 evalThis = global; 658 } 659 660 return ScriptRuntime.apply(func, evalThis); 661 } 662 663 private static Source loadInternal(final String srcStr, final String prefix, final String resourcePath) { 664 if (srcStr.startsWith(prefix)) { 665 final String resource = resourcePath + srcStr.substring(prefix.length()); 666 // NOTE: even sandbox scripts should be able to load scripts in nashorn: scheme 667 // These scripts are always available and are loaded from nashorn.jar's resources. 668 return AccessController.doPrivileged( 669 new PrivilegedAction<Source>() { 670 @Override 671 public Source run() { 672 try { 673 final URL resURL = Context.class.getResource(resource); 674 return resURL != null ? sourceFor(srcStr, resURL) : null; 675 } catch (final IOException exp) { 676 return null; 677 } 678 } 679 }); 680 } 681 682 return null; 683 } 684 685 /** 686 * Implementation of {@code load} Nashorn extension. Load a script file from a source 687 * expression 688 * 689 * @param scope the scope 690 * @param from source expression for script 691 * 692 * @return return value for load call (undefined) 693 * 694 * @throws IOException if source cannot be found or loaded 695 */ 696 public Object load(final ScriptObject scope, final Object from) throws IOException { 697 final Object src = from instanceof ConsString ? from.toString() : from; 698 Source source = null; 699 700 // load accepts a String (which could be a URL or a file name), a File, a URL 701 // or a ScriptObject that has "name" and "source" (string valued) properties. 702 if (src instanceof String) { 703 final String srcStr = (String)src; 704 if (srcStr.startsWith(LOAD_CLASSPATH)) { 705 final URL url = getResourceURL(srcStr.substring(LOAD_CLASSPATH.length())); 706 source = url != null ? sourceFor(url.toString(), url) : null; 707 } else { 708 final File file = new File(srcStr); 709 if (srcStr.indexOf(':') != -1) { 710 if ((source = loadInternal(srcStr, LOAD_NASHORN, "resources/")) == null && 711 (source = loadInternal(srcStr, LOAD_FX, "resources/fx/")) == null) { 712 URL url; 713 try { 714 //check for malformed url. if malformed, it may still be a valid file 715 url = new URL(srcStr); 716 } catch (final MalformedURLException e) { 717 url = file.toURI().toURL(); 718 } 719 source = sourceFor(url.toString(), url); 720 } 721 } else if (file.isFile()) { 722 source = sourceFor(srcStr, file); 723 } 724 } 725 } else if (src instanceof File && ((File)src).isFile()) { 726 final File file = (File)src; 727 source = sourceFor(file.getName(), file); 728 } else if (src instanceof URL) { 729 final URL url = (URL)src; 730 source = sourceFor(url.toString(), url); 731 } else if (src instanceof ScriptObject) { 732 final ScriptObject sobj = (ScriptObject)src; 733 if (sobj.has("script") && sobj.has("name")) { 734 final String script = JSType.toString(sobj.get("script")); 735 final String name = JSType.toString(sobj.get("name")); 736 source = sourceFor(name, script); 737 } 738 } else if (src instanceof Map) { 739 final Map<?,?> map = (Map<?,?>)src; 740 if (map.containsKey("script") && map.containsKey("name")) { 741 final String script = JSType.toString(map.get("script")); 742 final String name = JSType.toString(map.get("name")); 743 source = sourceFor(name, script); 744 } 745 } 746 747 if (source != null) { 748 return evaluateSource(source, scope, scope); 749 } 750 751 throw typeError("cant.load.script", ScriptRuntime.safeToString(from)); 752 } 753 754 /** 755 * Implementation of {@code loadWithNewGlobal} Nashorn extension. Load a script file from a source 756 * expression, after creating a new global scope. 757 * 758 * @param from source expression for script 759 * @param args (optional) arguments to be passed to the loaded script 760 * 761 * @return return value for load call (undefined) 762 * 763 * @throws IOException if source cannot be found or loaded 764 */ 765 public Object loadWithNewGlobal(final Object from, final Object...args) throws IOException { 766 final Global oldGlobal = getGlobal(); 767 final Global newGlobal = AccessController.doPrivileged(new PrivilegedAction<Global>() { 768 @Override 769 public Global run() { 770 try { 771 return newGlobal(); 772 } catch (final RuntimeException e) { 773 if (Context.DEBUG) { 774 e.printStackTrace(); 775 } 776 throw e; 777 } 778 } 779 }, CREATE_GLOBAL_ACC_CTXT); 780 // initialize newly created Global instance 781 initGlobal(newGlobal); 782 setGlobal(newGlobal); 783 784 final Object[] wrapped = args == null? ScriptRuntime.EMPTY_ARRAY : ScriptObjectMirror.wrapArray(args, oldGlobal); 785 newGlobal.put("arguments", newGlobal.wrapAsObject(wrapped), env._strict); 786 787 try { 788 // wrap objects from newGlobal's world as mirrors - but if result 789 // is from oldGlobal's world, unwrap it! 790 return ScriptObjectMirror.unwrap(ScriptObjectMirror.wrap(load(newGlobal, from), newGlobal), oldGlobal); 791 } finally { 792 setGlobal(oldGlobal); 793 } 794 } 795 796 /** 797 * Load or get a structure class. Structure class names are based on the number of parameter fields 798 * and {@link AccessorProperty} fields in them. Structure classes are used to represent ScriptObjects 799 * 800 * @see ObjectClassGenerator 801 * @see AccessorProperty 802 * @see ScriptObject 803 * 804 * @param fullName full name of class, e.g. jdk.nashorn.internal.objects.JO2P1 contains 2 fields and 1 parameter. 805 * 806 * @return the {@code Class<?>} for this structure 807 * 808 * @throws ClassNotFoundException if structure class cannot be resolved 809 */ 810 @SuppressWarnings("unchecked") 811 public static Class<? extends ScriptObject> forStructureClass(final String fullName) throws ClassNotFoundException { 812 if (System.getSecurityManager() != null && !StructureLoader.isStructureClass(fullName)) { 813 throw new ClassNotFoundException(fullName); 814 } 815 return (Class<? extends ScriptObject>)Class.forName(fullName, true, sharedLoader); 816 } 817 818 /** 819 * Checks that the given Class can be accessed from no permissions context. 820 * 821 * @param clazz Class object 822 * @throws SecurityException if not accessible 823 */ 824 public static void checkPackageAccess(final Class<?> clazz) { 825 final SecurityManager sm = System.getSecurityManager(); 826 if (sm != null) { 827 Class<?> bottomClazz = clazz; 828 while (bottomClazz.isArray()) { 829 bottomClazz = bottomClazz.getComponentType(); 830 } 831 checkPackageAccess(sm, bottomClazz.getName()); 832 } 833 } 834 835 /** 836 * Checks that the given package name can be accessed from no permissions context. 837 * 838 * @param pkgName package name 839 * @throws SecurityException if not accessible 840 */ 841 public static void checkPackageAccess(final String pkgName) { 842 final SecurityManager sm = System.getSecurityManager(); 843 if (sm != null) { 844 checkPackageAccess(sm, pkgName.endsWith(".") ? pkgName : pkgName + "."); 845 } 846 } 847 848 /** 849 * Checks that the given package can be accessed from no permissions context. 850 * 851 * @param sm current security manager instance 852 * @param fullName fully qualified package name 853 * @throw SecurityException if not accessible 854 */ 855 private static void checkPackageAccess(final SecurityManager sm, final String fullName) { 856 sm.getClass(); // null check 857 final int index = fullName.lastIndexOf('.'); 858 if (index != -1) { 859 final String pkgName = fullName.substring(0, index); 860 AccessController.doPrivileged(new PrivilegedAction<Void>() { 861 @Override 862 public Void run() { 863 sm.checkPackageAccess(pkgName); 864 return null; 865 } 866 }, NO_PERMISSIONS_ACC_CTXT); 867 } 868 } 869 870 /** 871 * Checks that the given Class can be accessed from no permissions context. 872 * 873 * @param clazz Class object 874 * @return true if package is accessible, false otherwise 875 */ 876 private static boolean isAccessiblePackage(final Class<?> clazz) { 877 try { 878 checkPackageAccess(clazz); 879 return true; 880 } catch (final SecurityException se) { 881 return false; 882 } 883 } 884 885 /** 886 * Checks that the given Class is public and it can be accessed from no permissions context. 887 * 888 * @param clazz Class object to check 889 * @return true if Class is accessible, false otherwise 890 */ 891 public static boolean isAccessibleClass(final Class<?> clazz) { 892 return Modifier.isPublic(clazz.getModifiers()) && Context.isAccessiblePackage(clazz); 893 } 894 895 /** 896 * Lookup a Java class. This is used for JSR-223 stuff linking in from 897 * {@code jdk.nashorn.internal.objects.NativeJava} and {@code jdk.nashorn.internal.runtime.NativeJavaPackage} 898 * 899 * @param fullName full name of class to load 900 * 901 * @return the {@code Class<?>} for the name 902 * 903 * @throws ClassNotFoundException if class cannot be resolved 904 */ 905 public Class<?> findClass(final String fullName) throws ClassNotFoundException { 906 if (fullName.indexOf('[') != -1 || fullName.indexOf('/') != -1) { 907 // don't allow array class names or internal names. 908 throw new ClassNotFoundException(fullName); 909 } 910 911 // give chance to ClassFilter to filter out, if present 912 if (classFilter != null && !classFilter.exposeToScripts(fullName)) { 913 throw new ClassNotFoundException(fullName); 914 } 915 916 // check package access as soon as possible! 917 final SecurityManager sm = System.getSecurityManager(); 918 if (sm != null) { 919 checkPackageAccess(sm, fullName); 920 } 921 922 // try the script -classpath loader, if that is set 923 if (classPathLoader != null) { 924 try { 925 return Class.forName(fullName, true, classPathLoader); 926 } catch (final ClassNotFoundException ignored) { 927 // ignore, continue search 928 } 929 } 930 931 // Try finding using the "app" loader. 932 return Class.forName(fullName, true, appLoader); 933 } 934 935 /** 936 * Hook to print stack trace for a {@link Throwable} that occurred during 937 * execution 938 * 939 * @param t throwable for which to dump stack 940 */ 941 public static void printStackTrace(final Throwable t) { 942 if (Context.DEBUG) { 943 t.printStackTrace(Context.getCurrentErr()); 944 } 945 } 946 947 /** 948 * Verify generated bytecode before emission. This is called back from the 949 * {@link ObjectClassGenerator} or the {@link Compiler}. If the "--verify-code" parameter 950 * hasn't been given, this is a nop 951 * 952 * Note that verification may load classes -- we don't want to do that unless 953 * user specified verify option. We check it here even though caller 954 * may have already checked that flag 955 * 956 * @param bytecode bytecode to verify 957 */ 958 public void verify(final byte[] bytecode) { 959 if (env._verify_code) { 960 // No verification when security manager is around as verifier 961 // may load further classes - which should be avoided. 962 if (System.getSecurityManager() == null) { 963 CheckClassAdapter.verify(new ClassReader(bytecode), sharedLoader, false, new PrintWriter(System.err, true)); 964 } 965 } 966 } 967 968 /** 969 * Create and initialize a new global scope object. 970 * 971 * @return the initialized global scope object. 972 */ 973 public Global createGlobal() { 974 return initGlobal(newGlobal()); 975 } 976 977 /** 978 * Create a new uninitialized global scope object 979 * @return the global script object 980 */ 981 public Global newGlobal() { 982 return new Global(this); 983 } 984 985 /** 986 * Initialize given global scope object. 987 * 988 * @param global the global 989 * @param engine the associated ScriptEngine instance, can be null 990 * @return the initialized global scope object. 991 */ 992 public Global initGlobal(final Global global, final ScriptEngine engine) { 993 // Need only minimal global object, if we are just compiling. 994 if (!env._compile_only) { 995 final Global oldGlobal = Context.getGlobal(); 996 try { 997 Context.setGlobal(global); 998 // initialize global scope with builtin global objects 999 global.initBuiltinObjects(engine); 1000 } finally { 1001 Context.setGlobal(oldGlobal); 1002 } 1003 } 1004 1005 return global; 1006 } 1007 1008 /** 1009 * Initialize given global scope object. 1010 * 1011 * @param global the global 1012 * @return the initialized global scope object. 1013 */ 1014 public Global initGlobal(final Global global) { 1015 return initGlobal(global, null); 1016 } 1017 1018 /** 1019 * Return the current global's context 1020 * @return current global's context 1021 */ 1022 static Context getContextTrusted() { 1023 return ((ScriptObject)Context.getGlobal()).getContext(); 1024 } 1025 1026 static Context getContextTrustedOrNull() { 1027 final Global global = Context.getGlobal(); 1028 return global == null ? null : ((ScriptObject)global).getContext(); 1029 } 1030 1031 /** 1032 * Try to infer Context instance from the Class. If we cannot, 1033 * then get it from the thread local variable. 1034 * 1035 * @param clazz the class 1036 * @return context 1037 */ 1038 static Context fromClass(final Class<?> clazz) { 1039 final ClassLoader loader = clazz.getClassLoader(); 1040 1041 if (loader instanceof ScriptLoader) { 1042 return ((ScriptLoader)loader).getContext(); 1043 } 1044 1045 return Context.getContextTrusted(); 1046 } 1047 1048 private URL getResourceURL(final String resName) { 1049 // try the classPathLoader if we have and then 1050 // try the appLoader if non-null. 1051 if (classPathLoader != null) { 1052 return classPathLoader.getResource(resName); 1053 } else if (appLoader != null) { 1054 return appLoader.getResource(resName); 1055 } 1056 1057 return null; 1058 } 1059 1060 private Object evaluateSource(final Source source, final ScriptObject scope, final ScriptObject thiz) { 1061 ScriptFunction script = null; 1062 1063 try { 1064 script = compileScript(source, scope, new Context.ThrowErrorManager()); 1065 } catch (final ParserException e) { 1066 e.throwAsEcmaException(); 1067 } 1068 1069 return ScriptRuntime.apply(script, thiz); 1070 } 1071 1072 private static ScriptFunction getProgramFunction(final Class<?> script, final ScriptObject scope) { 1073 if (script == null) { 1074 return null; 1075 } 1076 return invokeCreateProgramFunctionHandle(getCreateProgramFunctionHandle(script), scope); 1077 } 1078 1079 private static MethodHandle getCreateProgramFunctionHandle(final Class<?> script) { 1080 try { 1081 return LOOKUP.findStatic(script, CREATE_PROGRAM_FUNCTION.symbolName(), CREATE_PROGRAM_FUNCTION_TYPE); 1082 } catch (NoSuchMethodException | IllegalAccessException e) { 1083 throw new AssertionError("Failed to retrieve a handle for the program function for " + script.getName(), e); 1084 } 1085 } 1086 1087 private static ScriptFunction invokeCreateProgramFunctionHandle(final MethodHandle createProgramFunctionHandle, final ScriptObject scope) { 1088 try { 1089 return (ScriptFunction)createProgramFunctionHandle.invokeExact(scope); 1090 } catch (final RuntimeException|Error e) { 1091 throw e; 1092 } catch (final Throwable t) { 1093 throw new AssertionError("Failed to create a program function", t); 1094 } 1095 } 1096 1097 private ScriptFunction compileScript(final Source source, final ScriptObject scope, final ErrorManager errMan) { 1098 return getProgramFunction(compile(source, errMan, this._strict), scope); 1099 } 1100 1101 private synchronized Class<?> compile(final Source source, final ErrorManager errMan, final boolean strict) { 1102 // start with no errors, no warnings. 1103 errMan.reset(); 1104 1105 Class<?> script = findCachedClass(source); 1106 if (script != null) { 1107 final DebugLogger log = getLogger(Compiler.class); 1108 if (log.isEnabled()) { 1109 log.fine(new RuntimeEvent<>(Level.INFO, source), "Code cache hit for ", source, " avoiding recompile."); 1110 } 1111 return script; 1112 } 1113 1114 StoredScript storedScript = null; 1115 FunctionNode functionNode = null; 1116 final boolean useCodeStore = env._persistent_cache && !env._parse_only && !env._optimistic_types; 1117 final String cacheKey = useCodeStore ? CodeStore.getCacheKey(0, null) : null; 1118 1119 if (useCodeStore) { 1120 storedScript = codeStore.loadScript(source, cacheKey); 1121 } 1122 1123 if (storedScript == null) { 1124 functionNode = new Parser(env, source, errMan, strict, getLogger(Parser.class)).parse(); 1125 1126 if (errMan.hasErrors()) { 1127 return null; 1128 } 1129 1130 if (env._print_ast || functionNode.getFlag(FunctionNode.IS_PRINT_AST)) { 1131 getErr().println(new ASTWriter(functionNode)); 1132 } 1133 1134 if (env._print_parse || functionNode.getFlag(FunctionNode.IS_PRINT_PARSE)) { 1135 getErr().println(new PrintVisitor(functionNode, true, false)); 1136 } 1137 } 1138 1139 if (env._parse_only) { 1140 return null; 1141 } 1142 1143 final URL url = source.getURL(); 1144 final ScriptLoader loader = env._loader_per_compile ? createNewLoader() : scriptLoader; 1145 final CodeSource cs = new CodeSource(url, (CodeSigner[])null); 1146 final CodeInstaller<ScriptEnvironment> installer = new ContextCodeInstaller(this, loader, cs); 1147 1148 if (storedScript == null) { 1149 final CompilationPhases phases = Compiler.CompilationPhases.COMPILE_ALL; 1150 1151 final Compiler compiler = new Compiler( 1152 this, 1153 env, 1154 installer, 1155 source, 1156 errMan, 1157 strict | functionNode.isStrict()); 1158 1159 final FunctionNode compiledFunction = compiler.compile(functionNode, phases); 1160 if (errMan.hasErrors()) { 1161 return null; 1162 } 1163 script = compiledFunction.getRootClass(); 1164 compiler.persistClassInfo(cacheKey, compiledFunction); 1165 } else { 1166 Compiler.updateCompilationId(storedScript.getCompilationId()); 1167 script = install(storedScript, source, installer); 1168 } 1169 1170 cacheClass(source, script); 1171 return script; 1172 } 1173 1174 private ScriptLoader createNewLoader() { 1175 return AccessController.doPrivileged( 1176 new PrivilegedAction<ScriptLoader>() { 1177 @Override 1178 public ScriptLoader run() { 1179 return new ScriptLoader(appLoader, Context.this); 1180 } 1181 }, CREATE_LOADER_ACC_CTXT); 1182 } 1183 1184 private long getUniqueScriptId() { 1185 return uniqueScriptId.getAndIncrement(); 1186 } 1187 1188 /** 1189 * Install a previously compiled class from the code cache. 1190 * 1191 * @param storedScript cached script containing class bytes and constants 1192 * @return main script class 1193 */ 1194 private static Class<?> install(final StoredScript storedScript, final Source source, final CodeInstaller<ScriptEnvironment> installer) { 1195 1196 final Map<String, Class<?>> installedClasses = new HashMap<>(); 1197 final Object[] constants = storedScript.getConstants(); 1198 final String mainClassName = storedScript.getMainClassName(); 1199 final byte[] mainClassBytes = storedScript.getClassBytes().get(mainClassName); 1200 final Class<?> mainClass = installer.install(mainClassName, mainClassBytes); 1201 final Map<Integer, FunctionInitializer> initialzers = storedScript.getInitializers(); 1202 1203 installedClasses.put(mainClassName, mainClass); 1204 1205 for (final Map.Entry<String, byte[]> entry : storedScript.getClassBytes().entrySet()) { 1206 final String className = entry.getKey(); 1207 if (className.equals(mainClassName)) { 1208 continue; 1209 } 1210 final byte[] code = entry.getValue(); 1211 1212 installedClasses.put(className, installer.install(className, code)); 1213 } 1214 1215 installer.initialize(installedClasses.values(), source, constants); 1216 1217 for (final Object constant : constants) { 1218 if (constant instanceof RecompilableScriptFunctionData) { 1219 final RecompilableScriptFunctionData data = (RecompilableScriptFunctionData) constant; 1220 data.initTransients(source, installer); 1221 if (initialzers != null) { 1222 final FunctionInitializer initializer = initialzers.get(data.getFunctionNodeId()); 1223 initializer.setCode(installedClasses.get(initializer.getClassName())); 1224 data.initializeCode(initializer); 1225 } 1226 } 1227 } 1228 1229 return mainClass; 1230 } 1231 1232 /** 1233 * Cache for compiled script classes. 1234 */ 1235 @SuppressWarnings("serial") 1236 private static class ClassCache extends LinkedHashMap<Source, ClassReference> { 1237 private final int size; 1238 private final ReferenceQueue<Class<?>> queue; 1239 1240 ClassCache(final int size) { 1241 super(size, 0.75f, true); 1242 this.size = size; 1243 this.queue = new ReferenceQueue<>(); 1244 } 1245 1246 void cache(final Source source, final Class<?> clazz) { 1247 put(source, new ClassReference(clazz, queue, source)); 1248 } 1249 1250 @Override 1251 protected boolean removeEldestEntry(final Map.Entry<Source, ClassReference> eldest) { 1252 return size() > size; 1253 } 1254 1255 @Override 1256 public ClassReference get(final Object key) { 1257 for (ClassReference ref; (ref = (ClassReference)queue.poll()) != null; ) { 1258 remove(ref.source); 1259 } 1260 return super.get(key); 1261 } 1262 1263 } 1264 1265 private static class ClassReference extends SoftReference<Class<?>> { 1266 private final Source source; 1267 1268 ClassReference(final Class<?> clazz, final ReferenceQueue<Class<?>> queue, final Source source) { 1269 super(clazz, queue); 1270 this.source = source; 1271 } 1272 } 1273 1274 // Class cache management 1275 private Class<?> findCachedClass(final Source source) { 1276 final ClassReference ref = classCache == null ? null : classCache.get(source); 1277 return ref != null ? ref.get() : null; 1278 } 1279 1280 private void cacheClass(final Source source, final Class<?> clazz) { 1281 if (classCache != null) { 1282 classCache.cache(source, clazz); 1283 } 1284 } 1285 1286 // logging 1287 private final Map<String, DebugLogger> loggers = new HashMap<>(); 1288 1289 private void initLoggers() { 1290 ((Loggable)MethodHandleFactory.getFunctionality()).initLogger(this); 1291 } 1292 1293 /** 1294 * Get a logger, given a loggable class 1295 * @param clazz a Loggable class 1296 * @return debuglogger associated with that class 1297 */ 1298 public DebugLogger getLogger(final Class<? extends Loggable> clazz) { 1299 return getLogger(clazz, null); 1300 } 1301 1302 /** 1303 * Get a logger, given a loggable class 1304 * @param clazz a Loggable class 1305 * @param initHook an init hook - if this is the first time the logger is created in the context, run the init hook 1306 * @return debuglogger associated with that class 1307 */ 1308 public DebugLogger getLogger(final Class<? extends Loggable> clazz, final Consumer<DebugLogger> initHook) { 1309 final String name = getLoggerName(clazz); 1310 DebugLogger logger = loggers.get(name); 1311 if (logger == null) { 1312 if (!env.hasLogger(name)) { 1313 return DebugLogger.DISABLED_LOGGER; 1314 } 1315 final LoggerInfo info = env._loggers.get(name); 1316 logger = new DebugLogger(name, info.getLevel(), info.isQuiet()); 1317 if (initHook != null) { 1318 initHook.accept(logger); 1319 } 1320 loggers.put(name, logger); 1321 } 1322 return logger; 1323 } 1324 1325 /** 1326 * Given a Loggable class, weave debug info info a method handle for that logger. 1327 * Level.INFO is used 1328 * 1329 * @param clazz loggable 1330 * @param mh method handle 1331 * @param text debug printout to add 1332 * 1333 * @return instrumented method handle, or null if logger not enabled 1334 */ 1335 public MethodHandle addLoggingToHandle(final Class<? extends Loggable> clazz, final MethodHandle mh, final Supplier<String> text) { 1336 return addLoggingToHandle(clazz, Level.INFO, mh, Integer.MAX_VALUE, false, text); 1337 } 1338 1339 /** 1340 * Given a Loggable class, weave debug info info a method handle for that logger. 1341 * 1342 * @param clazz loggable 1343 * @param level log level 1344 * @param mh method handle 1345 * @param paramStart first parameter to print 1346 * @param printReturnValue should we print the return vaulue? 1347 * @param text debug printout to add 1348 * 1349 * @return instrumented method handle, or null if logger not enabled 1350 */ 1351 public MethodHandle addLoggingToHandle(final Class<? extends Loggable> clazz, final Level level, final MethodHandle mh, final int paramStart, final boolean printReturnValue, final Supplier<String> text) { 1352 final DebugLogger log = getLogger(clazz); 1353 if (log.isEnabled()) { 1354 return MethodHandleFactory.addDebugPrintout(log, level, mh, paramStart, printReturnValue, text.get()); 1355 } 1356 return mh; 1357 } 1358 1359 private static String getLoggerName(final Class<?> clazz) { 1360 Class<?> current = clazz; 1361 while (current != null) { 1362 final Logger log = current.getAnnotation(Logger.class); 1363 if (log != null) { 1364 assert !"".equals(log.name()); 1365 return log.name(); 1366 } 1367 current = current.getSuperclass(); 1368 } 1369 assert false; 1370 return null; 1371 } 1372 1373} 1374