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