src/jdk/nashorn/internal/runtime/Context.java

Fri, 25 Apr 2014 16:34:17 +0200

author
hannesw
date
Fri, 25 Apr 2014 16:34:17 +0200
changeset 845
cdf42b4b8226
parent 828
e0e2d72e6699
child 849
dea8e0de23b2
permissions
-rw-r--r--

8040078: Avoid repeated reading of source for cached loads
Reviewed-by: jlaskey, lagergren

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

mercurial