Context.java revision 953:221a84ef44c0
1193323Sed/* 2193323Sed * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved. 3193323Sed * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4193323Sed * 5193323Sed * This code is free software; you can redistribute it and/or modify it 6193323Sed * under the terms of the GNU General Public License version 2 only, as 7193323Sed * published by the Free Software Foundation. Oracle designates this 8193323Sed * particular file as subject to the "Classpath" exception as provided 9193323Sed * by Oracle in the LICENSE file that accompanied this code. 10193323Sed * 11193323Sed * This code is distributed in the hope that it will be useful, but WITHOUT 12193323Sed * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13201360Srdivacky * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14193323Sed * version 2 for more details (a copy is included in the LICENSE file that 15249423Sdim * accompanied this code). 16201360Srdivacky * 17249423Sdim * You should have received a copy of the GNU General Public License version 18249423Sdim * 2 along with this work; if not, write to the Free Software Foundation, 19249423Sdim * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20249423Sdim * 21249423Sdim * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22249423Sdim * or visit www.oracle.com if you need additional information or have any 23239462Sdim * questions. 24193323Sed */ 25193323Sed 26193323Sedpackage jdk.nashorn.internal.runtime; 27193323Sed 28193323Sedimport static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS; 29249423Sdimimport static jdk.nashorn.internal.codegen.CompilerConstants.CREATE_PROGRAM_FUNCTION; 30249423Sdimimport static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE; 31249423Sdimimport static jdk.nashorn.internal.codegen.CompilerConstants.STRICT_MODE; 32249423Sdimimport static jdk.nashorn.internal.runtime.ECMAErrors.typeError; 33249423Sdimimport static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED; 34249423Sdimimport static jdk.nashorn.internal.runtime.Source.sourceFor; 35249423Sdim 36249423Sdimimport java.io.File; 37249423Sdimimport java.io.IOException; 38193323Sedimport java.io.PrintWriter; 39202375Srdivackyimport java.lang.invoke.MethodHandle; 40198090Srdivackyimport java.lang.invoke.MethodHandles; 41195098Sedimport java.lang.invoke.MethodType; 42193323Sedimport java.lang.ref.ReferenceQueue; 43249423Sdimimport java.lang.ref.SoftReference; 44193323Sedimport java.lang.reflect.Field; 45249423Sdimimport java.lang.reflect.Modifier; 46249423Sdimimport java.net.MalformedURLException; 47249423Sdimimport java.net.URL; 48249423Sdimimport java.security.AccessControlContext; 49249423Sdimimport java.security.AccessController; 50249423Sdimimport java.security.CodeSigner; 51249423Sdimimport java.security.CodeSource; 52193323Sedimport java.security.Permissions; 53193323Sedimport java.security.PrivilegedAction; 54193323Sedimport java.security.PrivilegedActionException; 55193323Sedimport java.security.PrivilegedExceptionAction; 56193323Sedimport java.security.ProtectionDomain; 57193323Sedimport java.util.Collection; 58198090Srdivackyimport java.util.HashMap; 59193323Sedimport java.util.LinkedHashMap; 60193323Sedimport java.util.Map; 61193323Sedimport java.util.concurrent.atomic.AtomicLong; 62193323Sedimport java.util.function.Consumer; 63239462Sdimimport java.util.function.Supplier; 64239462Sdimimport java.util.logging.Level; 65239462Sdimimport javax.script.ScriptEngine; 66193323Sedimport jdk.internal.org.objectweb.asm.ClassReader; 67193323Sedimport jdk.internal.org.objectweb.asm.util.CheckClassAdapter; 68193323Sedimport jdk.nashorn.api.scripting.ScriptObjectMirror; 69193323Sedimport jdk.nashorn.internal.codegen.Compiler; 70193323Sedimport jdk.nashorn.internal.codegen.Compiler.CompilationPhases; 71193323Sedimport jdk.nashorn.internal.codegen.ObjectClassGenerator; 72193323Sedimport jdk.nashorn.internal.ir.FunctionNode; 73193323Sedimport jdk.nashorn.internal.ir.debug.ASTWriter; 74193323Sedimport jdk.nashorn.internal.ir.debug.PrintVisitor; 75193323Sedimport jdk.nashorn.internal.lookup.MethodHandleFactory; 76193323Sedimport jdk.nashorn.internal.objects.Global; 77193323Sedimport jdk.nashorn.internal.parser.Parser; 78193323Sedimport jdk.nashorn.internal.runtime.events.RuntimeEvent; 79198090Srdivackyimport jdk.nashorn.internal.runtime.logging.DebugLogger; 80193323Sedimport jdk.nashorn.internal.runtime.logging.Loggable; 81193323Sedimport jdk.nashorn.internal.runtime.logging.Logger; 82193323Sedimport jdk.nashorn.internal.runtime.options.LoggingOption.LoggerInfo; 83193323Sedimport jdk.nashorn.internal.runtime.options.Options; 84193323Sed 85193323Sed/** 86249423Sdim * This class manages the global state of execution. Context is immutable. 87249423Sdim */ 88193323Sedpublic final class Context { 89193323Sed // nashorn specific security runtime access permission names 90193323Sed /** 91193323Sed * Permission needed to pass arbitrary nashorn command line options when creating Context. 92193323Sed */ 93193323Sed public static final String NASHORN_SET_CONFIG = "nashorn.setConfig"; 94193323Sed 95193323Sed /** 96193323Sed * Permission needed to create Nashorn Context instance. 97193323Sed */ 98193323Sed public static final String NASHORN_CREATE_CONTEXT = "nashorn.createContext"; 99193323Sed 100218893Sdim /** 101193323Sed * Permission needed to create Nashorn Global instance. 102193323Sed */ 103193323Sed public static final String NASHORN_CREATE_GLOBAL = "nashorn.createGlobal"; 104193323Sed 105193323Sed /** 106193323Sed * Permission to get current Nashorn Context from thread local storage. 107193323Sed */ 108193323Sed public static final String NASHORN_GET_CONTEXT = "nashorn.getContext"; 109193323Sed 110193323Sed /** 111193323Sed * Permission to use Java reflection/jsr292 from script code. 112193323Sed */ 113193323Sed public static final String NASHORN_JAVA_REFLECTION = "nashorn.JavaReflection"; 114193323Sed 115234353Sdim /** 116234353Sdim * Permission to enable nashorn debug mode. 117234353Sdim */ 118234353Sdim public static final String NASHORN_DEBUG_MODE = "nashorn.debugMode"; 119234353Sdim 120234353Sdim // nashorn load psuedo URL prefixes 121234353Sdim private static final String LOAD_CLASSPATH = "classpath:"; 122193323Sed private static final String LOAD_FX = "fx:"; 123234353Sdim private static final String LOAD_NASHORN = "nashorn:"; 124243830Sdim 125243830Sdim private static MethodHandles.Lookup LOOKUP = MethodHandles.lookup(); 126193323Sed private static MethodType CREATE_PROGRAM_FUNCTION_TYPE = MethodType.methodType(ScriptFunction.class, ScriptObject.class); 127243830Sdim 128243830Sdim /* Force DebuggerSupport to be loaded. */ 129193323Sed static { 130193323Sed DebuggerSupport.FORCELOAD = true; 131193323Sed } 132193323Sed 133193323Sed /** 134234353Sdim * ContextCodeInstaller that has the privilege of installing classes in the Context. 135234353Sdim * Can only be instantiated from inside the context and is opaque to other classes 136193323Sed */ 137193323Sed public static class ContextCodeInstaller implements CodeInstaller<ScriptEnvironment> { 138193323Sed private final Context context; 139193323Sed private final ScriptLoader loader; 140193323Sed private final CodeSource codeSource; 141193323Sed 142193323Sed private ContextCodeInstaller(final Context context, final ScriptLoader loader, final CodeSource codeSource) { 143193323Sed this.context = context; 144193323Sed this.loader = loader; 145193323Sed this.codeSource = codeSource; 146193323Sed } 147193323Sed 148218893Sdim /** 149193323Sed * Return the context for this installer 150193323Sed * @return ScriptEnvironment 151193323Sed */ 152193323Sed @Override 153193323Sed public ScriptEnvironment getOwner() { 154193323Sed return context.env; 155193323Sed } 156193323Sed 157193323Sed @Override 158193323Sed public Class<?> install(final String className, final byte[] bytecode) { 159193323Sed final String binaryName = Compiler.binaryName(className); 160193323Sed return loader.installClass(binaryName, bytecode, codeSource); 161193323Sed } 162193574Sed 163193323Sed @Override 164193323Sed public void initialize(final Collection<Class<?>> classes, final Source source, final Object[] constants) { 165243830Sdim // do these in parallel, this significantly reduces class installation overhead 166243830Sdim // however - it still means that every thread needs a separate doPrivileged 167193323Sed classes.parallelStream().forEach( 168243830Sdim new Consumer<Class<?>>() { 169243830Sdim @Override 170193323Sed public void accept(final Class<?> clazz) { 171193323Sed try { 172193323Sed AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() { 173193323Sed @Override 174193574Sed public Void run() { 175193323Sed try { 176193323Sed //use reflection to write source and constants table to installed classes 177193323Sed final Field sourceField = clazz.getDeclaredField(SOURCE.symbolName()); 178193323Sed sourceField.setAccessible(true); 179193323Sed sourceField.set(null, source); 180193323Sed 181193323Sed final Field constantsField = clazz.getDeclaredField(CONSTANTS.symbolName()); 182193323Sed constantsField.setAccessible(true); 183193323Sed constantsField.set(null, constants); 184193323Sed } catch (final IllegalAccessException | NoSuchFieldException e) { 185193323Sed throw new RuntimeException(e); 186193323Sed } 187193323Sed return null; 188193323Sed } 189193323Sed }); 190193323Sed } catch (final PrivilegedActionException e) { 191193323Sed throw new RuntimeException(e); 192193323Sed } 193193323Sed } 194193323Sed }); 195218893Sdim } 196218893Sdim 197193323Sed @Override 198193323Sed public void verify(final byte[] code) { 199193323Sed context.verify(code); 200193323Sed } 201193323Sed 202193323Sed @Override 203193323Sed public long getUniqueScriptId() { 204193323Sed return context.getUniqueScriptId(); 205239462Sdim } 206239462Sdim 207239462Sdim @Override 208239462Sdim public long getUniqueEvalId() { 209239462Sdim return context.getUniqueEvalId(); 210239462Sdim } 211239462Sdim 212239462Sdim @Override 213239462Sdim public void storeCompiledScript(final Source source, final String mainClassName, 214239462Sdim final Map<String, byte[]> classBytes, final Object[] constants) { 215239462Sdim if (context.codeStore != null) { 216239462Sdim try { 217239462Sdim context.codeStore.putScript(source, mainClassName, classBytes, constants); 218239462Sdim } catch (final IOException e) { 219239462Sdim throw new RuntimeException(e); 220239462Sdim } 221193323Sed } 222193323Sed } 223193323Sed } 224193323Sed 225193323Sed /** Is Context global debug mode enabled ? */ 226193323Sed public static final boolean DEBUG = Options.getBooleanProperty("nashorn.debug"); 227193323Sed 228193323Sed private static final ThreadLocal<Global> currentGlobal = new ThreadLocal<>(); 229193323Sed 230193323Sed // in-memory cache for loaded classes 231193323Sed private ClassCache classCache; 232193323Sed 233193323Sed // persistent code store 234193323Sed private CodeStore codeStore; 235193323Sed 236193323Sed /** 237193323Sed * Get the current global scope 238193323Sed * @return the current global scope 239193323Sed */ 240193323Sed public static Global getGlobal() { 241193323Sed // This class in a package.access protected package. 242193323Sed // Trusted code only can call this method. 243193323Sed return currentGlobal.get(); 244193323Sed } 245193323Sed 246193323Sed /** 247193323Sed * Set the current global scope 248193323Sed * @param global the global scope 249193323Sed */ 250193323Sed public static void setGlobal(final ScriptObject global) { 251193323Sed if (global != null && !(global instanceof Global)) { 252193323Sed throw new IllegalArgumentException("not a global!"); 253193323Sed } 254198090Srdivacky setGlobal((Global)global); 255193323Sed } 256193323Sed 257193323Sed /** 258193323Sed * Set the current global scope 259193323Sed * @param global the global scope 260193323Sed */ 261193323Sed public static void setGlobal(final Global global) { 262193323Sed // This class in a package.access protected package. 263193323Sed // Trusted code only can call this method. 264193323Sed assert getGlobal() != global; 265193323Sed //same code can be cached between globals, then we need to invalidate method handle constants 266193323Sed if (global != null) { 267193323Sed Global.getConstants().invalidateAll(); 268193323Sed } 269193323Sed currentGlobal.set(global); 270193323Sed } 271193323Sed 272193323Sed /** 273193323Sed * Get context of the current global 274193323Sed * @return current global scope's context. 275193323Sed */ 276193323Sed public static Context getContext() { 277193323Sed final SecurityManager sm = System.getSecurityManager(); 278193323Sed if (sm != null) { 279193323Sed sm.checkPermission(new RuntimePermission(NASHORN_GET_CONTEXT)); 280193323Sed } 281193323Sed return getContextTrusted(); 282193323Sed } 283193323Sed 284193323Sed /** 285193323Sed * Get current context's error writer 286193323Sed * 287193323Sed * @return error writer of the current context 288193323Sed */ 289193323Sed public static PrintWriter getCurrentErr() { 290193323Sed final ScriptObject global = getGlobal(); 291193323Sed return (global != null)? global.getContext().getErr() : new PrintWriter(System.err); 292193323Sed } 293193323Sed 294193323Sed /** 295193323Sed * Output text to this Context's error stream 296193323Sed * @param str text to write 297193323Sed */ 298193323Sed public static void err(final String str) { 299193323Sed err(str, true); 300193323Sed } 301193323Sed 302193323Sed /** 303193323Sed * Output text to this Context's error stream, optionally with 304193323Sed * a newline afterwards 305193323Sed * 306193323Sed * @param str text to write 307193323Sed * @param crlf write a carriage return/new line after text 308193323Sed */ 309193323Sed public static void err(final String str, final boolean crlf) { 310193323Sed final PrintWriter err = Context.getCurrentErr(); 311193323Sed if (err != null) { 312193323Sed if (crlf) { 313193323Sed err.println(str); 314193323Sed } else { 315193323Sed err.print(str); 316193323Sed } 317193323Sed } 318193323Sed } 319193323Sed 320193323Sed /** Current environment. */ 321193323Sed private final ScriptEnvironment env; 322193323Sed 323193323Sed /** is this context in strict mode? Cached from env. as this is used heavily. */ 324193323Sed final boolean _strict; 325193323Sed 326193323Sed /** class loader to resolve classes from script. */ 327193323Sed private final ClassLoader appLoader; 328193323Sed 329193323Sed /** Class loader to load classes from -classpath option, if set. */ 330193323Sed private final ClassLoader classPathLoader; 331193323Sed 332193323Sed /** Class loader to load classes compiled from scripts. */ 333193323Sed private final ScriptLoader scriptLoader; 334193323Sed 335193323Sed /** Current error manager. */ 336193323Sed private final ErrorManager errors; 337193323Sed 338193323Sed /** Unique id for script. Used only when --loader-per-compile=false */ 339193323Sed private final AtomicLong uniqueScriptId; 340193323Sed 341193323Sed /** Unique id for 'eval' */ 342193323Sed private final AtomicLong uniqueEvalId; 343193323Sed 344193323Sed private static final ClassLoader myLoader = Context.class.getClassLoader(); 345193323Sed private static final StructureLoader sharedLoader; 346193323Sed 347193323Sed /*package-private*/ @SuppressWarnings("static-method") 348193323Sed ClassLoader getSharedLoader() { 349193323Sed return sharedLoader; 350193323Sed } 351193323Sed 352193323Sed private static AccessControlContext createNoPermAccCtxt() { 353193323Sed return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, new Permissions()) }); 354193323Sed } 355193323Sed 356193323Sed private static AccessControlContext createPermAccCtxt(final String permName) { 357193323Sed final Permissions perms = new Permissions(); 358193323Sed perms.add(new RuntimePermission(permName)); 359193323Sed return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, perms) }); 360193323Sed } 361193323Sed 362193323Sed private static final AccessControlContext NO_PERMISSIONS_ACC_CTXT = createNoPermAccCtxt(); 363193323Sed private static final AccessControlContext CREATE_LOADER_ACC_CTXT = createPermAccCtxt("createClassLoader"); 364193323Sed private static final AccessControlContext CREATE_GLOBAL_ACC_CTXT = createPermAccCtxt(NASHORN_CREATE_GLOBAL); 365193323Sed 366193323Sed static { 367193323Sed sharedLoader = AccessController.doPrivileged(new PrivilegedAction<StructureLoader>() { 368195098Sed @Override 369195098Sed public StructureLoader run() { 370198090Srdivacky return new StructureLoader(myLoader); 371193323Sed } 372193323Sed }, CREATE_LOADER_ACC_CTXT); 373193323Sed } 374193323Sed 375193323Sed /** 376193323Sed * ThrowErrorManager that throws ParserException upon error conditions. 377193323Sed */ 378193323Sed public static class ThrowErrorManager extends ErrorManager { 379193323Sed @Override 380193323Sed public void error(final String message) { 381193323Sed throw new ParserException(message); 382193323Sed } 383193323Sed 384193323Sed @Override 385193323Sed public void error(final ParserException e) { 386193323Sed throw e; 387193323Sed } 388195098Sed } 389239462Sdim 390193323Sed /** 391193323Sed * Constructor 392193323Sed * 393193323Sed * @param options options from command line or Context creator 394193323Sed * @param errors error manger 395193323Sed * @param appLoader application class loader 396193323Sed */ 397193323Sed public Context(final Options options, final ErrorManager errors, final ClassLoader appLoader) { 398234353Sdim this(options, errors, new PrintWriter(System.out, true), new PrintWriter(System.err, true), appLoader); 399234353Sdim } 400234353Sdim 401193323Sed /** 402193323Sed * Constructor 403193323Sed * 404193323Sed * @param options options from command line or Context creator 405193323Sed * @param errors error manger 406193323Sed * @param out output writer for this Context 407193323Sed * @param err error writer for this Context 408193323Sed * @param appLoader application class loader 409193323Sed */ 410193323Sed public Context(final Options options, final ErrorManager errors, final PrintWriter out, final PrintWriter err, final ClassLoader appLoader) { 411195098Sed final SecurityManager sm = System.getSecurityManager(); 412193323Sed if (sm != null) { 413193323Sed sm.checkPermission(new RuntimePermission(NASHORN_CREATE_CONTEXT)); 414193323Sed } 415193323Sed 416193323Sed this.env = new ScriptEnvironment(options, out, err); 417193323Sed this._strict = env._strict; 418193323Sed this.appLoader = appLoader; 419226633Sdim if (env._loader_per_compile) { 420193323Sed this.scriptLoader = null; 421193323Sed this.uniqueScriptId = null; 422195098Sed } else { 423193323Sed this.scriptLoader = createNewLoader(); 424193323Sed this.uniqueScriptId = new AtomicLong(); 425239462Sdim } 426239462Sdim this.errors = errors; 427239462Sdim this.uniqueEvalId = new AtomicLong(); 428239462Sdim 429239462Sdim // if user passed -classpath option, make a class loader with that and set it as 430239462Sdim // thread context class loader so that script can access classes from that path. 431239462Sdim final String classPath = options.getString("classpath"); 432193323Sed if (!env._compile_only && classPath != null && !classPath.isEmpty()) { 433193323Sed // make sure that caller can create a class loader. 434193323Sed if (sm != null) { 435193323Sed sm.checkPermission(new RuntimePermission("createClassLoader")); 436239462Sdim } 437193323Sed this.classPathLoader = NashornLoader.createClassLoader(classPath); 438193323Sed } else { 439193323Sed this.classPathLoader = null; 440193323Sed } 441193323Sed 442193323Sed final int cacheSize = env._class_cache_size; 443239462Sdim if (cacheSize > 0) { 444193323Sed classCache = new ClassCache(cacheSize); 445193323Sed } 446193323Sed 447193323Sed if (env._persistent_cache) { 448193323Sed try { 449193323Sed final String cacheDir = Options.getStringProperty("nashorn.persistent.code.cache", "nashorn_code_cache"); 450193323Sed codeStore = new CodeStore(cacheDir); 451193323Sed } catch (final IOException e) { 452193323Sed throw new RuntimeException("Error initializing code cache", e); 453193323Sed } 454193323Sed } 455193323Sed 456193323Sed // print version info if asked. 457226633Sdim if (env._version) { 458226633Sdim getErr().println("nashorn " + Version.version()); 459226633Sdim } 460193323Sed 461193323Sed if (env._fullversion) { 462193323Sed getErr().println("nashorn full version " + Version.fullVersion()); 463239462Sdim } 464193323Sed 465193323Sed initLoggers(); 466239462Sdim } 467239462Sdim 468239462Sdim /** 469239462Sdim * Get the error manager for this context 470239462Sdim * @return error manger 471193323Sed */ 472193323Sed public ErrorManager getErrorManager() { 473198090Srdivacky return errors; 474193323Sed } 475193323Sed 476193323Sed /** 477193323Sed * Get the script environment for this context 478198892Srdivacky * @return script environment 479198892Srdivacky */ 480243830Sdim public ScriptEnvironment getEnv() { 481243830Sdim return env; 482243830Sdim } 483243830Sdim 484198892Srdivacky /** 485198892Srdivacky * Get the output stream for this context 486193323Sed * @return output print writer 487239462Sdim */ 488239462Sdim public PrintWriter getOut() { 489239462Sdim return env.getOut(); 490239462Sdim } 491193323Sed 492193323Sed /** 493193323Sed * Get the error stream for this context 494193323Sed * @return error print writer 495193323Sed */ 496193323Sed public PrintWriter getErr() { 497193323Sed return env.getErr(); 498193323Sed } 499193323Sed 500193323Sed /** 501193323Sed * Get the PropertyMap of the current global scope 502193323Sed * @return the property map of the current global scope 503193323Sed */ 504193323Sed public static PropertyMap getGlobalMap() { 505193323Sed return Context.getGlobal().getMap(); 506193323Sed } 507204642Srdivacky 508193323Sed /** 509193323Sed * Compile a top level script. 510193323Sed * 511204642Srdivacky * @param source the source 512234353Sdim * @param scope the scope 513193323Sed * 514193323Sed * @return top level function for script 515193323Sed */ 516193323Sed public ScriptFunction compileScript(final Source source, final ScriptObject scope) { 517193323Sed return compileScript(source, scope, this.errors); 518193323Sed } 519204642Srdivacky 520234353Sdim /** 521234353Sdim * Interface to represent compiled code that can be re-used across many 522193323Sed * global scope instances 523193323Sed */ 524193323Sed public static interface MultiGlobalCompiledScript { 525193323Sed /** 526193323Sed * Obtain script function object for a specific global scope object. 527193323Sed * 528193323Sed * @param newGlobal global scope for which function object is obtained 529193323Sed * @return script function for script level expressions 530218893Sdim */ 531193323Sed public ScriptFunction getFunction(final Global newGlobal); 532193323Sed } 533193323Sed 534193323Sed /** 535193323Sed * Compile a top level script. 536193323Sed * 537193323Sed * @param source the script source 538193323Sed * @return reusable compiled script across many global scopes. 539193323Sed */ 540193323Sed public MultiGlobalCompiledScript compileScript(final Source source) { 541193323Sed final Class<?> clazz = compile(source, this.errors, this._strict); 542218893Sdim final MethodHandle createProgramFunctionHandle = getCreateProgramFunctionHandle(clazz); 543193323Sed 544193323Sed return new MultiGlobalCompiledScript() { 545193323Sed @Override 546193323Sed public ScriptFunction getFunction(final Global newGlobal) { 547193323Sed return invokeCreateProgramFunctionHandle(createProgramFunctionHandle, newGlobal); 548193323Sed } 549193323Sed }; 550193323Sed } 551193323Sed 552193323Sed /** 553193323Sed * Entry point for {@code eval} 554193323Sed * 555193323Sed * @param initialScope The scope of this eval call 556193323Sed * @param string Evaluated code as a String 557193323Sed * @param callThis "this" to be passed to the evaluated code 558193323Sed * @param location location of the eval call 559193323Sed * @param strict is this {@code eval} call from a strict mode code? 560193323Sed * @return the return value of the {@code eval} 561193323Sed */ 562193323Sed public Object eval(final ScriptObject initialScope, final String string, 563193323Sed final Object callThis, final Object location, final boolean strict) { 564193323Sed return eval(initialScope, string, callThis, location, strict, false); 565193323Sed } 566193323Sed 567193323Sed /** 568193323Sed * Entry point for {@code eval} 569193323Sed * 570239462Sdim * @param initialScope The scope of this eval call 571193323Sed * @param string Evaluated code as a String 572193323Sed * @param callThis "this" to be passed to the evaluated code 573193323Sed * @param location location of the eval call 574193323Sed * @param strict is this {@code eval} call from a strict mode code? 575193323Sed * @param evalCall is this called from "eval" builtin? 576193323Sed * 577239462Sdim * @return the return value of the {@code eval} 578239462Sdim */ 579193323Sed public Object eval(final ScriptObject initialScope, final String string, 580193323Sed final Object callThis, final Object location, final boolean strict, final boolean evalCall) { 581193323Sed final String file = location == UNDEFINED || location == null ? "<eval>" : location.toString(); 582193323Sed final Source source = sourceFor(file, string, evalCall); 583193323Sed final boolean directEval = location != UNDEFINED; // is this direct 'eval' call or indirectly invoked eval? 584193323Sed final Global global = Context.getGlobal(); 585193323Sed ScriptObject scope = initialScope; 586193323Sed 587193323Sed // ECMA section 10.1.1 point 2 says eval code is strict if it begins 588193323Sed // with "use strict" directive or eval direct call itself is made 589193323Sed // from from strict mode code. We are passed with caller's strict mode. 590193323Sed boolean strictFlag = directEval && strict; 591193323Sed 592193323Sed Class<?> clazz = null; 593193323Sed try { 594193323Sed clazz = compile(source, new ThrowErrorManager(), strictFlag); 595193323Sed } catch (final ParserException e) { 596193323Sed e.throwAsEcmaException(global); 597193323Sed return null; 598193323Sed } 599239462Sdim 600193323Sed if (!strictFlag) { 601234353Sdim // We need to get strict mode flag from compiled class. This is 602234353Sdim // because eval code may start with "use strict" directive. 603234353Sdim try { 604234353Sdim strictFlag = clazz.getField(STRICT_MODE.symbolName()).getBoolean(null); 605234353Sdim } catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) { 606234353Sdim //ignored 607239462Sdim strictFlag = false; 608193323Sed } 609193323Sed } 610193323Sed 611193323Sed // In strict mode, eval does not instantiate variables and functions 612193323Sed // in the caller's environment. A new environment is created! 613193323Sed if (strictFlag) { 614193323Sed // Create a new scope object 615193323Sed final ScriptObject strictEvalScope = global.newObject(); 616193323Sed 617193323Sed // bless it as a "scope" 618193323Sed strictEvalScope.setIsScope(); 619193323Sed 620193323Sed // set given scope to be it's proto so that eval can still 621193323Sed // access caller environment vars in the new environment. 622193323Sed strictEvalScope.setProto(scope); 623193323Sed scope = strictEvalScope; 624193323Sed } 625193323Sed 626193323Sed final ScriptFunction func = getProgramFunction(clazz, scope); 627193323Sed Object evalThis; 628193323Sed if (directEval) { 629193323Sed evalThis = callThis instanceof ScriptObject || strictFlag ? callThis : global; 630193323Sed } else { 631193323Sed evalThis = global; 632193323Sed } 633193323Sed 634193323Sed return ScriptRuntime.apply(func, evalThis); 635193323Sed } 636193323Sed 637193323Sed private static Source loadInternal(final String srcStr, final String prefix, final String resourcePath) { 638200581Srdivacky if (srcStr.startsWith(prefix)) { 639200581Srdivacky final String resource = resourcePath + srcStr.substring(prefix.length()); 640202878Srdivacky // NOTE: even sandbox scripts should be able to load scripts in nashorn: scheme 641205218Srdivacky // These scripts are always available and are loaded from nashorn.jar's resources. 642206083Srdivacky return AccessController.doPrivileged( 643224145Sdim new PrivilegedAction<Source>() { 644206083Srdivacky @Override 645206083Srdivacky public Source run() { 646193323Sed try { 647193323Sed final URL resURL = Context.class.getResource(resource); 648193323Sed return resURL != null ? sourceFor(srcStr, resURL) : null; 649193323Sed } catch (final IOException exp) { 650193323Sed return null; 651193323Sed } 652193323Sed } 653193323Sed }); 654193323Sed } 655193323Sed 656193323Sed return null; 657193323Sed } 658193323Sed 659193323Sed /** 660193323Sed * Implementation of {@code load} Nashorn extension. Load a script file from a source 661193323Sed * expression 662193323Sed * 663193323Sed * @param scope the scope 664193323Sed * @param from source expression for script 665195098Sed * 666195098Sed * @return return value for load call (undefined) 667195098Sed * 668195098Sed * @throws IOException if source cannot be found or loaded 669195098Sed */ 670193323Sed public Object load(final ScriptObject scope, final Object from) throws IOException { 671195098Sed final Object src = from instanceof ConsString ? from.toString() : from; 672193323Sed Source source = null; 673198090Srdivacky 674193323Sed // load accepts a String (which could be a URL or a file name), a File, a URL 675193323Sed // or a ScriptObject that has "name" and "source" (string valued) properties. 676193323Sed if (src instanceof String) { 677198090Srdivacky final String srcStr = (String)src; 678198090Srdivacky if (srcStr.startsWith(LOAD_CLASSPATH)) { 679193323Sed final URL url = getResourceURL(srcStr.substring(LOAD_CLASSPATH.length())); 680193323Sed source = url != null ? sourceFor(url.toString(), url) : null; 681193323Sed } else { 682193323Sed final File file = new File(srcStr); 683193323Sed if (srcStr.indexOf(':') != -1) { 684218893Sdim if ((source = loadInternal(srcStr, LOAD_NASHORN, "resources/")) == null && 685218893Sdim (source = loadInternal(srcStr, LOAD_FX, "resources/fx/")) == null) { 686193323Sed URL url; 687193323Sed try { 688193323Sed //check for malformed url. if malformed, it may still be a valid file 689193323Sed url = new URL(srcStr); 690193323Sed } catch (final MalformedURLException e) { 691193323Sed url = file.toURI().toURL(); 692193323Sed } 693218893Sdim source = sourceFor(url.toString(), url); 694193323Sed } 695193323Sed } else if (file.isFile()) { 696202375Srdivacky source = sourceFor(srcStr, file); 697198090Srdivacky } 698193323Sed } 699193323Sed } else if (src instanceof File && ((File)src).isFile()) { 700193323Sed final File file = (File)src; 701193323Sed source = sourceFor(file.getName(), file); 702193323Sed } else if (src instanceof URL) { 703193323Sed final URL url = (URL)src; 704193323Sed source = sourceFor(url.toString(), url); 705193323Sed } else if (src instanceof ScriptObject) { 706193323Sed final ScriptObject sobj = (ScriptObject)src; 707193323Sed if (sobj.has("script") && sobj.has("name")) { 708193323Sed final String script = JSType.toString(sobj.get("script")); 709239462Sdim final String name = JSType.toString(sobj.get("name")); 710193323Sed source = sourceFor(name, script); 711193323Sed } 712193323Sed } else if (src instanceof Map) { 713193323Sed final Map<?,?> map = (Map<?,?>)src; 714193323Sed if (map.containsKey("script") && map.containsKey("name")) { 715193323Sed final String script = JSType.toString(map.get("script")); 716193323Sed final String name = JSType.toString(map.get("name")); 717193323Sed source = sourceFor(name, script); 718239462Sdim } 719193323Sed } 720239462Sdim 721239462Sdim if (source != null) { 722239462Sdim return evaluateSource(source, scope, scope); 723193323Sed } 724193323Sed 725193323Sed throw typeError("cant.load.script", ScriptRuntime.safeToString(from)); 726193323Sed } 727193323Sed 728239462Sdim /** 729239462Sdim * Implementation of {@code loadWithNewGlobal} Nashorn extension. Load a script file from a source 730239462Sdim * expression, after creating a new global scope. 731193323Sed * 732193323Sed * @param from source expression for script 733193323Sed * @param args (optional) arguments to be passed to the loaded script 734193323Sed * 735193323Sed * @return return value for load call (undefined) 736193323Sed * 737193323Sed * @throws IOException if source cannot be found or loaded 738193323Sed */ 739193323Sed public Object loadWithNewGlobal(final Object from, final Object...args) throws IOException { 740193323Sed final Global oldGlobal = getGlobal(); 741193323Sed final Global newGlobal = AccessController.doPrivileged(new PrivilegedAction<Global>() { 742193323Sed @Override 743193323Sed public Global run() { 744193323Sed try { 745193323Sed return newGlobal(); 746200581Srdivacky } catch (final RuntimeException e) { 747200581Srdivacky if (Context.DEBUG) { 748193323Sed e.printStackTrace(); 749193323Sed } 750193323Sed throw e; 751193323Sed } 752193323Sed } 753193323Sed }, CREATE_GLOBAL_ACC_CTXT); 754193323Sed // initialize newly created Global instance 755193323Sed initGlobal(newGlobal); 756193323Sed setGlobal(newGlobal); 757193323Sed 758193323Sed final Object[] wrapped = args == null? ScriptRuntime.EMPTY_ARRAY : ScriptObjectMirror.wrapArray(args, oldGlobal); 759193323Sed newGlobal.put("arguments", newGlobal.wrapAsObject(wrapped), env._strict); 760193323Sed 761193323Sed try { 762193323Sed // wrap objects from newGlobal's world as mirrors - but if result 763193323Sed // is from oldGlobal's world, unwrap it! 764200581Srdivacky return ScriptObjectMirror.unwrap(ScriptObjectMirror.wrap(load(newGlobal, from), newGlobal), oldGlobal); 765200581Srdivacky } finally { 766193323Sed setGlobal(oldGlobal); 767193323Sed } 768193323Sed } 769193323Sed 770193323Sed /** 771193323Sed * Load or get a structure class. Structure class names are based on the number of parameter fields 772193323Sed * and {@link AccessorProperty} fields in them. Structure classes are used to represent ScriptObjects 773193323Sed * 774193323Sed * @see ObjectClassGenerator 775193323Sed * @see AccessorProperty 776193323Sed * @see ScriptObject 777193323Sed * 778193323Sed * @param fullName full name of class, e.g. jdk.nashorn.internal.objects.JO2P1 contains 2 fields and 1 parameter. 779193323Sed * 780193323Sed * @return the {@code Class<?>} for this structure 781193323Sed * 782200581Srdivacky * @throws ClassNotFoundException if structure class cannot be resolved 783200581Srdivacky */ 784193323Sed @SuppressWarnings("unchecked") 785193323Sed public static Class<? extends ScriptObject> forStructureClass(final String fullName) throws ClassNotFoundException { 786218893Sdim if (System.getSecurityManager() != null && !StructureLoader.isStructureClass(fullName)) { 787218893Sdim throw new ClassNotFoundException(fullName); 788218893Sdim } 789193323Sed return (Class<? extends ScriptObject>)Class.forName(fullName, true, sharedLoader); 790193323Sed } 791193323Sed 792193323Sed /** 793198090Srdivacky * Checks that the given Class can be accessed from no permissions context. 794193323Sed * 795193323Sed * @param clazz Class object 796193323Sed * @throws SecurityException if not accessible 797193323Sed */ 798193323Sed public static void checkPackageAccess(final Class<?> clazz) { 799193323Sed final SecurityManager sm = System.getSecurityManager(); 800193323Sed if (sm != null) { 801193323Sed Class<?> bottomClazz = clazz; 802193323Sed while (bottomClazz.isArray()) { 803193323Sed bottomClazz = bottomClazz.getComponentType(); 804193323Sed } 805193323Sed checkPackageAccess(sm, bottomClazz.getName()); 806193323Sed } 807193323Sed } 808193323Sed 809193323Sed /** 810193323Sed * Checks that the given package name can be accessed from no permissions context. 811198090Srdivacky * 812226633Sdim * @param pkgName package name 813193323Sed * @throws SecurityException if not accessible 814193323Sed */ 815193323Sed public static void checkPackageAccess(final String pkgName) { 816193323Sed final SecurityManager sm = System.getSecurityManager(); 817226633Sdim if (sm != null) { 818226633Sdim checkPackageAccess(sm, pkgName.endsWith(".") ? pkgName : pkgName + "."); 819226633Sdim } 820193323Sed } 821193323Sed 822193323Sed /** 823193323Sed * Checks that the given package can be accessed from no permissions context. 824193323Sed * 825218893Sdim * @param sm current security manager instance 826218893Sdim * @param fullName fully qualified package name 827218893Sdim * @throw SecurityException if not accessible 828218893Sdim */ 829218893Sdim private static void checkPackageAccess(final SecurityManager sm, final String fullName) { 830218893Sdim sm.getClass(); // null check 831218893Sdim final int index = fullName.lastIndexOf('.'); 832218893Sdim if (index != -1) { 833218893Sdim final String pkgName = fullName.substring(0, index); 834218893Sdim AccessController.doPrivileged(new PrivilegedAction<Void>() { 835218893Sdim @Override 836218893Sdim public Void run() { 837218893Sdim sm.checkPackageAccess(pkgName); 838218893Sdim return null; 839218893Sdim } 840218893Sdim }, NO_PERMISSIONS_ACC_CTXT); 841218893Sdim } 842218893Sdim } 843218893Sdim 844218893Sdim /** 845218893Sdim * Checks that the given Class can be accessed from no permissions context. 846218893Sdim * 847218893Sdim * @param clazz Class object 848218893Sdim * @return true if package is accessible, false otherwise 849218893Sdim */ 850218893Sdim private static boolean isAccessiblePackage(final Class<?> clazz) { 851218893Sdim try { 852218893Sdim checkPackageAccess(clazz); 853218893Sdim return true; 854218893Sdim } catch (final SecurityException se) { 855218893Sdim return false; 856218893Sdim } 857218893Sdim } 858218893Sdim 859218893Sdim /** 860218893Sdim * Checks that the given Class is public and it can be accessed from no permissions context. 861218893Sdim * 862218893Sdim * @param clazz Class object to check 863198090Srdivacky * @return true if Class is accessible, false otherwise 864193323Sed */ 865193323Sed public static boolean isAccessibleClass(final Class<?> clazz) { 866198090Srdivacky return Modifier.isPublic(clazz.getModifiers()) && Context.isAccessiblePackage(clazz); 867226633Sdim } 868198090Srdivacky 869198090Srdivacky /** 870193323Sed * Lookup a Java class. This is used for JSR-223 stuff linking in from 871243830Sdim * {@code jdk.nashorn.internal.objects.NativeJava} and {@code jdk.nashorn.internal.runtime.NativeJavaPackage} 872193323Sed * 873193323Sed * @param fullName full name of class to load 874193323Sed * 875234353Sdim * @return the {@code Class<?>} for the name 876208599Srdivacky * 877249423Sdim * @throws ClassNotFoundException if class cannot be resolved 878249423Sdim */ 879239462Sdim public Class<?> findClass(final String fullName) throws ClassNotFoundException { 880193323Sed if (fullName.indexOf('[') != -1 || fullName.indexOf('/') != -1) { 881202878Srdivacky // don't allow array class names or internal names. 882205218Srdivacky throw new ClassNotFoundException(fullName); 883193323Sed } 884193323Sed 885249423Sdim // check package access as soon as possible! 886193323Sed final SecurityManager sm = System.getSecurityManager(); 887249423Sdim if (sm != null) { 888198090Srdivacky checkPackageAccess(sm, fullName); 889193323Sed } 890193323Sed 891193323Sed // try the script -classpath loader, if that is set 892239462Sdim if (classPathLoader != null) { 893193323Sed try { 894200581Srdivacky return Class.forName(fullName, true, classPathLoader); 895205218Srdivacky } catch (final ClassNotFoundException ignored) { 896193323Sed // ignore, continue search 897193323Sed } 898193323Sed } 899193323Sed 900193323Sed // Try finding using the "app" loader. 901193323Sed return Class.forName(fullName, true, appLoader); 902193323Sed } 903193323Sed 904193323Sed /** 905193323Sed * Hook to print stack trace for a {@link Throwable} that occurred during 906193323Sed * execution 907193323Sed * 908193323Sed * @param t throwable for which to dump stack 909193323Sed */ 910193323Sed public static void printStackTrace(final Throwable t) { 911193323Sed if (Context.DEBUG) { 912193323Sed t.printStackTrace(Context.getCurrentErr()); 913193323Sed } 914193323Sed } 915193323Sed 916193323Sed /** 917193323Sed * Verify generated bytecode before emission. This is called back from the 918193323Sed * {@link ObjectClassGenerator} or the {@link Compiler}. If the "--verify-code" parameter 919193323Sed * hasn't been given, this is a nop 920193323Sed * 921210299Sed * Note that verification may load classes -- we don't want to do that unless 922206083Srdivacky * user specified verify option. We check it here even though caller 923193323Sed * may have already checked that flag 924193323Sed * 925226633Sdim * @param bytecode bytecode to verify 926226633Sdim */ 927226633Sdim public void verify(final byte[] bytecode) { 928226633Sdim if (env._verify_code) { 929226633Sdim // No verification when security manager is around as verifier 930226633Sdim // may load further classes - which should be avoided. 931198090Srdivacky if (System.getSecurityManager() == null) { 932198090Srdivacky CheckClassAdapter.verify(new ClassReader(bytecode), sharedLoader, false, new PrintWriter(System.err, true)); 933198090Srdivacky } 934198090Srdivacky } 935198090Srdivacky } 936198090Srdivacky 937198090Srdivacky /** 938198090Srdivacky * Create and initialize a new global scope object. 939198090Srdivacky * 940198090Srdivacky * @return the initialized global scope object. 941198090Srdivacky */ 942198090Srdivacky public Global createGlobal() { 943198090Srdivacky return initGlobal(newGlobal()); 944200581Srdivacky } 945200581Srdivacky 946200581Srdivacky /** 947193323Sed * Create a new uninitialized global scope object 948200581Srdivacky * @return the global script object 949200581Srdivacky */ 950193323Sed public Global newGlobal() { 951193323Sed return new Global(this); 952193323Sed } 953193323Sed 954193323Sed /** 955193323Sed * Initialize given global scope object. 956193323Sed * 957198090Srdivacky * @param global the global 958204642Srdivacky * @param engine the associated ScriptEngine instance, can be null 959193323Sed * @return the initialized global scope object. 960193323Sed */ 961193323Sed public Global initGlobal(final Global global, final ScriptEngine engine) { 962193323Sed // Need only minimal global object, if we are just compiling. 963193323Sed if (!env._compile_only) { 964198090Srdivacky final Global oldGlobal = Context.getGlobal(); 965204642Srdivacky try { 966193323Sed Context.setGlobal(global); 967193323Sed // initialize global scope with builtin global objects 968193323Sed global.initBuiltinObjects(engine); 969193323Sed } finally { 970193323Sed Context.setGlobal(oldGlobal); 971193323Sed } 972198090Srdivacky } 973198090Srdivacky 974193323Sed return global; 975193323Sed } 976198090Srdivacky 977193323Sed /** 978193323Sed * Initialize given global scope object. 979204642Srdivacky * 980226633Sdim * @param global the global 981226633Sdim * @return the initialized global scope object. 982226633Sdim */ 983226633Sdim public Global initGlobal(final Global global) { 984226633Sdim return initGlobal(global, null); 985226633Sdim } 986226633Sdim 987226633Sdim /** 988226633Sdim * Return the current global's context 989226633Sdim * @return current global's context 990226633Sdim */ 991226633Sdim static Context getContextTrusted() { 992226633Sdim return ((ScriptObject)Context.getGlobal()).getContext(); 993226633Sdim } 994193323Sed 995193323Sed static Context getContextTrustedOrNull() { 996193323Sed final Global global = Context.getGlobal(); 997193323Sed return global == null ? null : ((ScriptObject)global).getContext(); 998226633Sdim } 999193323Sed 1000193323Sed /** 1001201360Srdivacky * Try to infer Context instance from the Class. If we cannot, 1002193323Sed * then get it from the thread local variable. 1003193323Sed * 1004201360Srdivacky * @param clazz the class 1005193323Sed * @return context 1006226633Sdim */ 1007193323Sed static Context fromClass(final Class<?> clazz) { 1008193323Sed final ClassLoader loader = clazz.getClassLoader(); 1009193323Sed 1010193323Sed if (loader instanceof ScriptLoader) { 1011193323Sed return ((ScriptLoader)loader).getContext(); 1012193323Sed } 1013193323Sed 1014193323Sed return Context.getContextTrusted(); 1015206124Srdivacky } 1016193323Sed 1017193323Sed private URL getResourceURL(final String resName) { 1018193323Sed // try the classPathLoader if we have and then 1019193323Sed // try the appLoader if non-null. 1020193323Sed if (classPathLoader != null) { 1021193323Sed return classPathLoader.getResource(resName); 1022193323Sed } else if (appLoader != null) { 1023193323Sed return appLoader.getResource(resName); 1024193323Sed } 1025198090Srdivacky 1026198090Srdivacky return null; 1027193323Sed } 1028193323Sed 1029198090Srdivacky private Object evaluateSource(final Source source, final ScriptObject scope, final ScriptObject thiz) { 1030193323Sed ScriptFunction script = null; 1031193323Sed 1032204642Srdivacky try { 1033193323Sed script = compileScript(source, scope, new Context.ThrowErrorManager()); 1034193323Sed } catch (final ParserException e) { 1035193323Sed e.throwAsEcmaException(); 1036193323Sed } 1037193323Sed 1038193323Sed return ScriptRuntime.apply(script, thiz); 1039193323Sed } 1040193323Sed 1041193323Sed private static ScriptFunction getProgramFunction(final Class<?> script, final ScriptObject scope) { 1042193323Sed if (script == null) { 1043201360Srdivacky return null; 1044193323Sed } 1045193323Sed return invokeCreateProgramFunctionHandle(getCreateProgramFunctionHandle(script), scope); 1046201360Srdivacky } 1047193323Sed 1048205407Srdivacky private static MethodHandle getCreateProgramFunctionHandle(final Class<?> script) { 1049193323Sed try { 1050193323Sed return LOOKUP.findStatic(script, CREATE_PROGRAM_FUNCTION.symbolName(), CREATE_PROGRAM_FUNCTION_TYPE); 1051193323Sed } catch (NoSuchMethodException | IllegalAccessException e) { 1052193323Sed throw new AssertionError("Failed to retrieve a handle for the program function for " + script.getName(), e); 1053193323Sed } 1054193323Sed } 1055193323Sed 1056193323Sed private static ScriptFunction invokeCreateProgramFunctionHandle(final MethodHandle createProgramFunctionHandle, final ScriptObject scope) { 1057193323Sed try { 1058206124Srdivacky return (ScriptFunction)createProgramFunctionHandle.invokeExact(scope); 1059193323Sed } catch (final RuntimeException|Error e) { 1060193323Sed throw e; 1061193323Sed } catch (final Throwable t) { 1062193323Sed throw new AssertionError("Failed to create a program function", t); 1063198090Srdivacky } 1064204642Srdivacky } 1065193323Sed 1066193323Sed private ScriptFunction compileScript(final Source source, final ScriptObject scope, final ErrorManager errMan) { 1067208599Srdivacky return getProgramFunction(compile(source, errMan, this._strict), scope); 1068193323Sed } 1069249423Sdim 1070249423Sdim private synchronized Class<?> compile(final Source source, final ErrorManager errMan, final boolean strict) { 1071208599Srdivacky // start with no errors, no warnings. 1072208599Srdivacky errMan.reset(); 1073249423Sdim 1074208599Srdivacky Class<?> script = findCachedClass(source); 1075208599Srdivacky if (script != null) { 1076234353Sdim final DebugLogger log = getLogger(Compiler.class); 1077234353Sdim if (log.isEnabled()) { 1078193323Sed log.fine(new RuntimeEvent<>(Level.INFO, source), "Code cache hit for ", source, " avoiding recompile."); 1079193323Sed } 1080210299Sed return script; 1081198090Srdivacky } 1082195098Sed 1083195098Sed CompiledScript compiledScript = null; 1084195098Sed FunctionNode functionNode = null; 1085195098Sed 1086198090Srdivacky if (!env._parse_only && codeStore != null) { 1087193323Sed try { 1088243830Sdim compiledScript = codeStore.getScript(source); 1089193323Sed } catch (IOException | ClassNotFoundException e) { 1090243830Sdim getLogger(Compiler.class).warning("Error loading ", source, " from cache: ", e); 1091193323Sed // Fall back to normal compilation 1092193323Sed } 1093193323Sed } 1094193323Sed 1095193323Sed if (compiledScript == null) { 1096193323Sed functionNode = new Parser(env, source, errMan, strict, getLogger(Parser.class)).parse(); 1097193323Sed 1098193323Sed if (errors.hasErrors()) { 1099195098Sed return null; 1100193323Sed } 1101193323Sed 1102193323Sed if (env._print_ast) { 1103193323Sed getErr().println(new ASTWriter(functionNode)); 1104193323Sed } 1105193323Sed 1106193323Sed if (env._print_parse) { 1107193323Sed getErr().println(new PrintVisitor(functionNode, true, false)); 1108193323Sed } 1109195098Sed } 1110239462Sdim 1111193323Sed if (env._parse_only) { 1112201360Srdivacky return null; 1113193323Sed } 1114201360Srdivacky 1115210299Sed final URL url = source.getURL(); 1116205407Srdivacky final ScriptLoader loader = env._loader_per_compile ? createNewLoader() : scriptLoader; 1117193323Sed final CodeSource cs = new CodeSource(url, (CodeSigner[])null); 1118193323Sed final CodeInstaller<ScriptEnvironment> installer = new ContextCodeInstaller(this, loader, cs); 1119193323Sed 1120193323Sed if (functionNode != null) { 1121193323Sed final CompilationPhases phases = Compiler.CompilationPhases.COMPILE_ALL; 1122198090Srdivacky 1123193323Sed final Compiler compiler = new Compiler( 1124193323Sed this, 1125193323Sed env, 1126193323Sed installer, 1127193323Sed source, 1128201360Srdivacky functionNode.getSourceURL(), 1129193323Sed strict | functionNode.isStrict()); 1130201360Srdivacky 1131205407Srdivacky script = compiler.compile(functionNode, phases).getRootClass(); 1132193323Sed } else { 1133193323Sed script = install(compiledScript, installer); 1134193323Sed } 1135193323Sed 1136193323Sed cacheClass(source, script); 1137198090Srdivacky return script; 1138195098Sed } 1139195098Sed 1140195098Sed private ScriptLoader createNewLoader() { 1141193323Sed return AccessController.doPrivileged( 1142193323Sed new PrivilegedAction<ScriptLoader>() { 1143193323Sed @Override 1144193323Sed public ScriptLoader run() { 1145195098Sed return new ScriptLoader(appLoader, Context.this); 1146193323Sed } 1147201360Srdivacky }, CREATE_LOADER_ACC_CTXT); 1148193323Sed } 1149201360Srdivacky 1150205407Srdivacky private long getUniqueEvalId() { 1151205407Srdivacky return uniqueEvalId.getAndIncrement(); 1152193323Sed } 1153193323Sed 1154193323Sed private long getUniqueScriptId() { 1155193323Sed return uniqueScriptId.getAndIncrement(); 1156193323Sed } 1157207618Srdivacky 1158193323Sed 1159198090Srdivacky /** 1160195098Sed * Install a previously compiled class from the code cache. 1161195098Sed * 1162195098Sed * @param compiledScript cached script containing class bytes and constants 1163193323Sed * @return main script class 1164243830Sdim */ 1165193323Sed private static Class<?> install(final CompiledScript compiledScript, final CodeInstaller<ScriptEnvironment> installer) { 1166193323Sed 1167193323Sed final Map<String, Class<?>> installedClasses = new HashMap<>(); 1168193323Sed final Source source = compiledScript.getSource(); 1169193323Sed final Object[] constants = compiledScript.getConstants(); 1170193323Sed final String rootClassName = compiledScript.getMainClassName(); 1171195098Sed final byte[] rootByteCode = compiledScript.getClassBytes().get(rootClassName); 1172193323Sed final Class<?> rootClass = installer.install(rootClassName, rootByteCode); 1173201360Srdivacky 1174193323Sed installedClasses.put(rootClassName, rootClass); 1175201360Srdivacky 1176205407Srdivacky for (final Map.Entry<String, byte[]> entry : compiledScript.getClassBytes().entrySet()) { 1177205407Srdivacky final String className = entry.getKey(); 1178193323Sed if (className.equals(rootClassName)) { 1179193323Sed continue; 1180193323Sed } 1181193323Sed final byte[] code = entry.getValue(); 1182193323Sed 1183193323Sed installedClasses.put(className, installer.install(className, code)); 1184198090Srdivacky } 1185193323Sed 1186195098Sed installer.initialize(installedClasses.values(), source, constants); 1187195098Sed 1188195098Sed for (final Object constant : constants) { 1189195098Sed if (constant instanceof RecompilableScriptFunctionData) { 1190193323Sed ((RecompilableScriptFunctionData) constant).initTransients(source, installer); 1191243830Sdim } 1192193323Sed } 1193193323Sed 1194193323Sed return rootClass; 1195193323Sed } 1196193323Sed 1197226633Sdim /** 1198195098Sed * Cache for compiled script classes. 1199193323Sed */ 1200201360Srdivacky @SuppressWarnings("serial") 1201193323Sed private static class ClassCache extends LinkedHashMap<Source, ClassReference> { 1202201360Srdivacky private final int size; 1203205407Srdivacky private final ReferenceQueue<Class<?>> queue; 1204205407Srdivacky 1205193323Sed ClassCache(final int size) { 1206193323Sed super(size, 0.75f, true); 1207193323Sed this.size = size; 1208193323Sed this.queue = new ReferenceQueue<>(); 1209193323Sed } 1210239462Sdim 1211239462Sdim void cache(final Source source, final Class<?> clazz) { 1212239462Sdim put(source, new ClassReference(clazz, queue, source)); 1213239462Sdim } 1214239462Sdim 1215239462Sdim @Override 1216239462Sdim protected boolean removeEldestEntry(final Map.Entry<Source, ClassReference> eldest) { 1217239462Sdim return size() > size; 1218239462Sdim } 1219239462Sdim 1220239462Sdim @Override 1221239462Sdim public ClassReference get(final Object key) { 1222239462Sdim for (ClassReference ref; (ref = (ClassReference)queue.poll()) != null; ) { 1223239462Sdim remove(ref.source); 1224239462Sdim } 1225239462Sdim return super.get(key); 1226239462Sdim } 1227239462Sdim 1228193323Sed } 1229193323Sed 1230193323Sed private static class ClassReference extends SoftReference<Class<?>> { 1231193323Sed private final Source source; 1232193323Sed 1233201360Srdivacky ClassReference(final Class<?> clazz, final ReferenceQueue<Class<?>> queue, final Source source) { 1234193323Sed super(clazz, queue); 1235201360Srdivacky this.source = source; 1236205407Srdivacky } 1237193323Sed } 1238193323Sed 1239193323Sed // Class cache management 1240193323Sed private Class<?> findCachedClass(final Source source) { 1241193323Sed final ClassReference ref = classCache == null ? null : classCache.get(source); 1242198090Srdivacky return ref != null ? ref.get() : null; 1243198090Srdivacky } 1244198090Srdivacky 1245198090Srdivacky private void cacheClass(final Source source, final Class<?> clazz) { 1246193323Sed if (classCache != null) { 1247193323Sed classCache.cache(source, clazz); 1248198090Srdivacky } 1249193323Sed } 1250193323Sed 1251205407Srdivacky // logging 1252193323Sed private final Map<String, DebugLogger> loggers = new HashMap<>(); 1253193323Sed 1254193323Sed private void initLoggers() { 1255193323Sed ((Loggable)MethodHandleFactory.getFunctionality()).initLogger(this); 1256198090Srdivacky } 1257193323Sed 1258193323Sed /** 1259205407Srdivacky * Get a logger, given a loggable class 1260193323Sed * @param clazz a Loggable class 1261193323Sed * @return debuglogger associated with that class 1262193323Sed */ 1263193323Sed public DebugLogger getLogger(final Class<? extends Loggable> clazz) { 1264198090Srdivacky return getLogger(clazz, null); 1265195098Sed } 1266195098Sed 1267195098Sed /** 1268195098Sed * Get a logger, given a loggable class 1269193323Sed * @param clazz a Loggable class 1270205407Srdivacky * @param initHook an init hook - if this is the first time the logger is created in the context, run the init hook 1271193323Sed * @return debuglogger associated with that class 1272193323Sed */ 1273193323Sed public DebugLogger getLogger(final Class<? extends Loggable> clazz, final Consumer<DebugLogger> initHook) { 1274193323Sed final String name = getLoggerName(clazz); 1275193323Sed DebugLogger logger = loggers.get(name); 1276193323Sed if (logger == null) { 1277193323Sed if (!env.hasLogger(name)) { 1278193323Sed return DebugLogger.DISABLED_LOGGER; 1279193323Sed } 1280205407Srdivacky final LoggerInfo info = env._loggers.get(name); 1281193323Sed logger = new DebugLogger(name, info.getLevel(), info.isQuiet()); 1282193323Sed if (initHook != null) { 1283193323Sed initHook.accept(logger); 1284201360Srdivacky } 1285193323Sed loggers.put(name, logger); 1286193323Sed } 1287193323Sed return logger; 1288193323Sed } 1289193323Sed 1290193323Sed /** 1291193323Sed * Given a Loggable class, weave debug info info a method handle for that logger. 1292193323Sed * Level.INFO is used 1293193323Sed * 1294193323Sed * @param clazz loggable 1295193323Sed * @param mh method handle 1296193323Sed * @param text debug printout to add 1297193323Sed * 1298193323Sed * @return instrumented method handle, or null if logger not enabled 1299193323Sed */ 1300193323Sed public MethodHandle addLoggingToHandle(final Class<? extends Loggable> clazz, final MethodHandle mh, final Supplier<String> text) { 1301193323Sed return addLoggingToHandle(clazz, Level.INFO, mh, Integer.MAX_VALUE, false, text); 1302198090Srdivacky } 1303193323Sed 1304193323Sed /** 1305198090Srdivacky * Given a Loggable class, weave debug info info a method handle for that logger. 1306193323Sed * 1307193323Sed * @param clazz loggable 1308193323Sed * @param level log level 1309193323Sed * @param mh method handle 1310193323Sed * @param paramStart first parameter to print 1311193323Sed * @param printReturnValue should we print the return vaulue? 1312198090Srdivacky * @param text debug printout to add 1313193323Sed * 1314198090Srdivacky * @return instrumented method handle, or null if logger not enabled 1315193323Sed */ 1316193323Sed public MethodHandle addLoggingToHandle(final Class<? extends Loggable> clazz, final Level level, final MethodHandle mh, final int paramStart, final boolean printReturnValue, final Supplier<String> text) { 1317193323Sed final DebugLogger log = getLogger(clazz); 1318193323Sed if (log.isEnabled()) { 1319193323Sed return MethodHandleFactory.addDebugPrintout(log, level, mh, paramStart, printReturnValue, text.get()); 1320193323Sed } 1321193323Sed return mh; 1322198090Srdivacky } 1323193323Sed 1324193323Sed private static String getLoggerName(final Class<?> clazz) { 1325193323Sed Class<?> current = clazz; 1326193323Sed while (current != null) { 1327193323Sed final Logger log = current.getAnnotation(Logger.class); 1328193323Sed if (log != null) { 1329198090Srdivacky assert !"".equals(log.name()); 1330193323Sed return log.name(); 1331193323Sed } 1332193323Sed current = current.getSuperclass(); 1333198090Srdivacky } 1334193323Sed assert false; 1335193323Sed return null; 1336193323Sed } 1337193323Sed 1338193323Sed} 1339193323Sed