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