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