1 /*
2 * Copyright (c) 2010, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package jdk.nashorn.internal.runtime;
27
28 import static jdk.internal.org.objectweb.asm.Opcodes.V1_7;
29 import static jdk.nashorn.internal.codegen.CompilerConstants.CONSTANTS;
30 import static jdk.nashorn.internal.codegen.CompilerConstants.CREATE_PROGRAM_FUNCTION;
31 import static jdk.nashorn.internal.codegen.CompilerConstants.SOURCE;
32 import static jdk.nashorn.internal.codegen.CompilerConstants.STRICT_MODE;
33 import static jdk.nashorn.internal.runtime.CodeStore.newCodeStore;
34 import static jdk.nashorn.internal.runtime.ECMAErrors.typeError;
35 import static jdk.nashorn.internal.runtime.ScriptRuntime.UNDEFINED;
36 import static jdk.nashorn.internal.runtime.Source.sourceFor;
37
38 import java.io.File;
39 import java.io.IOException;
40 import java.io.PrintWriter;
41 import java.lang.invoke.MethodHandle;
42 import java.lang.invoke.MethodHandles;
43 import java.lang.invoke.MethodType;
44 import java.lang.invoke.SwitchPoint;
45 import java.lang.ref.Reference;
46 import java.lang.ref.ReferenceQueue;
47 import java.lang.ref.SoftReference;
48 import java.lang.ref.WeakReference;
49 import java.lang.reflect.Field;
50 import java.lang.reflect.Modifier;
51 import java.net.MalformedURLException;
52 import java.net.URL;
53 import java.security.AccessControlContext;
54 import java.security.AccessController;
55 import java.security.CodeSigner;
56 import java.security.CodeSource;
57 import java.security.Permissions;
58 import java.security.PrivilegedAction;
59 import java.security.PrivilegedActionException;
60 import java.security.PrivilegedExceptionAction;
61 import java.security.ProtectionDomain;
62 import java.util.Collection;
63 import java.util.HashMap;
64 import java.util.LinkedHashMap;
65 import java.util.Map;
66 import java.util.Objects;
67 import java.util.concurrent.atomic.AtomicLong;
68 import java.util.concurrent.atomic.AtomicReference;
69 import java.util.concurrent.atomic.LongAdder;
70 import java.util.function.Consumer;
71 import java.util.function.Supplier;
72 import java.util.logging.Level;
73 import javax.script.ScriptContext;
74 import javax.script.ScriptEngine;
75 import jdk.internal.org.objectweb.asm.ClassReader;
76 import jdk.internal.org.objectweb.asm.ClassWriter;
77 import jdk.internal.org.objectweb.asm.Opcodes;
78 import jdk.internal.org.objectweb.asm.util.CheckClassAdapter;
79 import jdk.nashorn.api.scripting.ClassFilter;
80 import jdk.nashorn.api.scripting.ScriptObjectMirror;
81 import jdk.nashorn.internal.codegen.Compiler;
82 import jdk.nashorn.internal.codegen.Compiler.CompilationPhases;
83 import jdk.nashorn.internal.codegen.ObjectClassGenerator;
84 import jdk.nashorn.internal.ir.FunctionNode;
85 import jdk.nashorn.internal.ir.debug.ASTWriter;
86 import jdk.nashorn.internal.ir.debug.PrintVisitor;
87 import jdk.nashorn.internal.lookup.MethodHandleFactory;
88 import jdk.nashorn.internal.objects.Global;
89 import jdk.nashorn.internal.parser.Parser;
90 import jdk.nashorn.internal.runtime.events.RuntimeEvent;
91 import jdk.nashorn.internal.runtime.logging.DebugLogger;
92 import jdk.nashorn.internal.runtime.logging.Loggable;
93 import jdk.nashorn.internal.runtime.logging.Logger;
94 import jdk.nashorn.internal.runtime.options.LoggingOption.LoggerInfo;
95 import jdk.nashorn.internal.runtime.options.Options;
96 import sun.misc.Unsafe;
97
98 /**
99 * This class manages the global state of execution. Context is immutable.
100 */
101 public final class Context {
102 // nashorn specific security runtime access permission names
103 /**
104 * Permission needed to pass arbitrary nashorn command line options when creating Context.
105 */
106 public static final String NASHORN_SET_CONFIG = "nashorn.setConfig";
107
108 /**
109 * Permission needed to create Nashorn Context instance.
110 */
111 public static final String NASHORN_CREATE_CONTEXT = "nashorn.createContext";
112
113 /**
114 * Permission needed to create Nashorn Global instance.
115 */
116 public static final String NASHORN_CREATE_GLOBAL = "nashorn.createGlobal";
117
118 /**
119 * Permission to get current Nashorn Context from thread local storage.
120 */
121 public static final String NASHORN_GET_CONTEXT = "nashorn.getContext";
122
123 /**
124 * Permission to use Java reflection/jsr292 from script code.
125 */
126 public static final String NASHORN_JAVA_REFLECTION = "nashorn.JavaReflection";
127
128 /**
129 * Permission to enable nashorn debug mode.
130 */
131 public static final String NASHORN_DEBUG_MODE = "nashorn.debugMode";
132
133 // nashorn load psuedo URL prefixes
134 private static final String LOAD_CLASSPATH = "classpath:";
135 private static final String LOAD_FX = "fx:";
136 private static final String LOAD_NASHORN = "nashorn:";
137
138 private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
139 private static final MethodType CREATE_PROGRAM_FUNCTION_TYPE = MethodType.methodType(ScriptFunction.class, ScriptObject.class);
140
141 private static final LongAdder NAMED_INSTALLED_SCRIPT_COUNT = new LongAdder();
142 private static final LongAdder ANONYMOUS_INSTALLED_SCRIPT_COUNT = new LongAdder();
143
144 /**
145 * Should scripts use only object slots for fields, or dual long/object slots? The default
146 * behaviour is to couple this to optimistic types, using dual representation if optimistic types are enabled
147 * and single field representation otherwise. This can be overridden by setting either the "nashorn.fields.objects"
148 * or "nashorn.fields.dual" system property.
149 */
150 private final FieldMode fieldMode;
151
152 private static enum FieldMode {
153 /** Value for automatic field representation depending on optimistic types setting */
154 AUTO,
155 /** Value for object field representation regardless of optimistic types setting */
156 OBJECTS,
157 /** Value for dual primitive/object field representation regardless of optimistic types setting */
158 DUAL
159 }
160
161 /**
162 * Keeps track of which builtin prototypes and properties have been relinked
163 * Currently we are conservative and associate the name of a builtin class with all
164 * its properties, so it's enough to invalidate a property to break all assumptions
165 * about a prototype. This can be changed to a more fine grained approach, but no one
166 * ever needs this, given the very rare occurrence of swapping out only parts of
167 * a builtin v.s. the entire builtin object
168 */
169 private final Map<String, SwitchPoint> builtinSwitchPoints = new HashMap<>();
170
171 /* Force DebuggerSupport to be loaded. */
172 static {
173 DebuggerSupport.FORCELOAD = true;
174 }
175
176 static long getNamedInstalledScriptCount() {
177 return NAMED_INSTALLED_SCRIPT_COUNT.sum();
178 }
179
180 static long getAnonymousInstalledScriptCount() {
181 return ANONYMOUS_INSTALLED_SCRIPT_COUNT.sum();
182 }
183
184 /**
185 * ContextCodeInstaller that has the privilege of installing classes in the Context.
186 * Can only be instantiated from inside the context and is opaque to other classes
187 */
188 private abstract static class ContextCodeInstaller implements CodeInstaller {
189 final Context context;
190 final CodeSource codeSource;
191
192 ContextCodeInstaller(final Context context, final CodeSource codeSource) {
193 this.context = context;
194 this.codeSource = codeSource;
195 }
196
197 @Override
198 public Context getContext() {
199 return context;
200 }
201
202 @Override
203 public void initialize(final Collection<Class<?>> classes, final Source source, final Object[] constants) {
204 try {
205 AccessController.doPrivileged(new PrivilegedExceptionAction<Void>() {
206 @Override
207 public Void run() throws Exception {
208 for (final Class<?> clazz : classes) {
209 //use reflection to write source and constants table to installed classes
210 final Field sourceField = clazz.getDeclaredField(SOURCE.symbolName());
211 sourceField.setAccessible(true);
212 sourceField.set(null, source);
213
214 final Field constantsField = clazz.getDeclaredField(CONSTANTS.symbolName());
215 constantsField.setAccessible(true);
216 constantsField.set(null, constants);
217 }
218 return null;
219 }
220 });
221 } catch (final PrivilegedActionException e) {
222 throw new RuntimeException(e);
223 }
224 }
225
226 @Override
227 public void verify(final byte[] code) {
228 context.verify(code);
229 }
230
231 @Override
232 public long getUniqueScriptId() {
233 return context.getUniqueScriptId();
234 }
235
236 @Override
237 public void storeScript(final String cacheKey, final Source source, final String mainClassName,
238 final Map<String,byte[]> classBytes, final Map<Integer, FunctionInitializer> initializers,
239 final Object[] constants, final int compilationId) {
240 if (context.codeStore != null) {
241 context.codeStore.store(cacheKey, source, mainClassName, classBytes, initializers, constants, compilationId);
242 }
243 }
244
245 @Override
246 public StoredScript loadScript(final Source source, final String functionKey) {
247 if (context.codeStore != null) {
248 return context.codeStore.load(source, functionKey);
249 }
250 return null;
251 }
252
253 @Override
254 public boolean isCompatibleWith(final CodeInstaller other) {
255 if (other instanceof ContextCodeInstaller) {
256 final ContextCodeInstaller cci = (ContextCodeInstaller)other;
257 return cci.context == context && cci.codeSource == codeSource;
258 }
259 return false;
260 }
261 }
262
263 private static class NamedContextCodeInstaller extends ContextCodeInstaller {
264 private final ScriptLoader loader;
265 private int usageCount = 0;
266 private int bytesDefined = 0;
267
268 // We reuse this installer for 10 compilations or 200000 defined bytes. Usually the first condition
269 // will occur much earlier, the second is a safety measure for very large scripts/functions.
270 private final static int MAX_USAGES = 10;
271 private final static int MAX_BYTES_DEFINED = 200_000;
272
273 private NamedContextCodeInstaller(final Context context, final CodeSource codeSource, final ScriptLoader loader) {
274 super(context, codeSource);
275 this.loader = loader;
276 }
277
278 @Override
279 public Class<?> install(final String className, final byte[] bytecode) {
280 usageCount++;
281 bytesDefined += bytecode.length;
282 NAMED_INSTALLED_SCRIPT_COUNT.increment();
283 return loader.installClass(Compiler.binaryName(className), bytecode, codeSource);
284 }
285
286 @Override
287 public CodeInstaller getOnDemandCompilationInstaller() {
288 // Reuse this installer if we're within our limits.
289 if (usageCount < MAX_USAGES && bytesDefined < MAX_BYTES_DEFINED) {
290 return this;
291 }
292 return new NamedContextCodeInstaller(context, codeSource, context.createNewLoader());
293 }
294
295 @Override
296 public CodeInstaller getMultiClassCodeInstaller() {
297 // This installer is perfectly suitable for installing multiple classes that reference each other
298 // as it produces classes with resolvable names, all defined in a single class loader.
299 return this;
300 }
301 }
302
303 private final Map<CodeSource, HostClassReference> anonymousHostClasses = new HashMap<>();
304 private final ReferenceQueue<Class<?>> anonymousHostClassesRefQueue = new ReferenceQueue<>();
305
306 private static class HostClassReference extends WeakReference<Class<?>> {
307 final CodeSource codeSource;
308
309 HostClassReference(final CodeSource codeSource, final Class<?> clazz, final ReferenceQueue<Class<?>> refQueue) {
310 super(clazz, refQueue);
311 this.codeSource = codeSource;
312 }
313 }
314
315 private synchronized Class<?> getAnonymousHostClass(final CodeSource codeSource) {
316 // Remove cleared entries
317 for(;;) {
318 final HostClassReference clearedRef = (HostClassReference)anonymousHostClassesRefQueue.poll();
319 if (clearedRef == null) {
320 break;
321 }
322 anonymousHostClasses.remove(clearedRef.codeSource, clearedRef);
323 }
324
325 // Try to find an existing host class
326 final Reference<Class<?>> ref = anonymousHostClasses.get(codeSource);
327 if (ref != null) {
328 final Class<?> existingHostClass = ref.get();
329 if (existingHostClass != null) {
330 return existingHostClass;
331 }
332 }
333
334 // Define a new host class if existing is not found
335 final Class<?> newHostClass = createNewLoader().installClass(
336 // NOTE: we're defining these constants in AnonymousContextCodeInstaller so they are not
337 // initialized if we don't use AnonymousContextCodeInstaller. As this method is only ever
338 // invoked from AnonymousContextCodeInstaller, this is okay.
339 AnonymousContextCodeInstaller.ANONYMOUS_HOST_CLASS_NAME,
340 AnonymousContextCodeInstaller.ANONYMOUS_HOST_CLASS_BYTES, codeSource);
341 anonymousHostClasses.put(codeSource, new HostClassReference(codeSource, newHostClass, anonymousHostClassesRefQueue));
342 return newHostClass;
343 }
344
345 private static final class AnonymousContextCodeInstaller extends ContextCodeInstaller {
346 private static final Unsafe UNSAFE = getUnsafe();
347 private static final String ANONYMOUS_HOST_CLASS_NAME = Compiler.SCRIPTS_PACKAGE.replace('/', '.') + ".AnonymousHost";
348 private static final byte[] ANONYMOUS_HOST_CLASS_BYTES = getAnonymousHostClassBytes();
349
350 private final Class<?> hostClass;
351
352 private AnonymousContextCodeInstaller(final Context context, final CodeSource codeSource, final Class<?> hostClass) {
353 super(context, codeSource);
354 this.hostClass = hostClass;
355 }
356
357 @Override
358 public Class<?> install(final String className, final byte[] bytecode) {
359 ANONYMOUS_INSTALLED_SCRIPT_COUNT.increment();
360 return UNSAFE.defineAnonymousClass(hostClass, bytecode, null);
361 }
362
363 @Override
364 public CodeInstaller getOnDemandCompilationInstaller() {
365 // This code loader can be indefinitely reused for on-demand recompilations for the same code source.
366 return this;
367 }
368
369 @Override
370 public CodeInstaller getMultiClassCodeInstaller() {
371 // This code loader can not be used to install multiple classes that reference each other, as they
372 // would have no resolvable names. Therefore, in such situation we must revert to an installer that
373 // produces named classes.
374 return new NamedContextCodeInstaller(context, codeSource, context.createNewLoader());
375 }
376
377 private static final byte[] getAnonymousHostClassBytes() {
378 final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
379 cw.visit(V1_7, Opcodes.ACC_INTERFACE | Opcodes.ACC_ABSTRACT, ANONYMOUS_HOST_CLASS_NAME.replace('.', '/'), null, "java/lang/Object", null);
380 cw.visitEnd();
381 return cw.toByteArray();
382 }
383
384 private static Unsafe getUnsafe() {
385 return AccessController.doPrivileged(new PrivilegedAction<Unsafe>() {
386 @Override
387 public Unsafe run() {
388 try {
389 final Field theUnsafeField = Unsafe.class.getDeclaredField("theUnsafe");
390 theUnsafeField.setAccessible(true);
391 return (Unsafe)theUnsafeField.get(null);
392 } catch (final ReflectiveOperationException e) {
393 throw new RuntimeException(e);
394 }
395 }
396 });
397 }
398 }
399
400 /** Is Context global debug mode enabled ? */
401 public static final boolean DEBUG = Options.getBooleanProperty("nashorn.debug");
402
403 private static final ThreadLocal<Global> currentGlobal = new ThreadLocal<>();
404
405 // in-memory cache for loaded classes
406 private ClassCache classCache;
407
408 // persistent code store
409 private CodeStore codeStore;
410
411 // A factory for linking global properties as constant method handles. It is created when the first Global
412 // is created, and invalidated forever once the second global is created.
413 private final AtomicReference<GlobalConstants> globalConstantsRef = new AtomicReference<>();
414
415 /**
416 * Get the current global scope
417 * @return the current global scope
418 */
419 public static Global getGlobal() {
420 // This class in a package.access protected package.
421 // Trusted code only can call this method.
422 return currentGlobal.get();
423 }
424
425 /**
426 * Set the current global scope
427 * @param global the global scope
428 */
429 public static void setGlobal(final ScriptObject global) {
430 if (global != null && !(global instanceof Global)) {
431 throw new IllegalArgumentException("not a global!");
432 }
433 setGlobal((Global)global);
434 }
435
436 /**
437 * Set the current global scope
438 * @param global the global scope
439 */
440 public static void setGlobal(final Global global) {
441 // This class in a package.access protected package.
442 // Trusted code only can call this method.
443 assert getGlobal() != global;
444 //same code can be cached between globals, then we need to invalidate method handle constants
445 if (global != null) {
446 final GlobalConstants globalConstants = getContext(global).getGlobalConstants();
447 if (globalConstants != null) {
448 globalConstants.invalidateAll();
449 }
450 }
451 currentGlobal.set(global);
452 }
453
454 /**
455 * Get context of the current global
456 * @return current global scope's context.
457 */
458 public static Context getContext() {
459 final SecurityManager sm = System.getSecurityManager();
460 if (sm != null) {
461 sm.checkPermission(new RuntimePermission(NASHORN_GET_CONTEXT));
462 }
463 return getContextTrusted();
464 }
465
466 /**
467 * Get current context's error writer
468 *
469 * @return error writer of the current context
470 */
471 public static PrintWriter getCurrentErr() {
472 final ScriptObject global = getGlobal();
473 return (global != null)? global.getContext().getErr() : new PrintWriter(System.err);
474 }
475
476 /**
477 * Output text to this Context's error stream
478 * @param str text to write
479 */
480 public static void err(final String str) {
481 err(str, true);
482 }
483
484 /**
485 * Output text to this Context's error stream, optionally with
486 * a newline afterwards
487 *
488 * @param str text to write
489 * @param crlf write a carriage return/new line after text
490 */
491 public static void err(final String str, final boolean crlf) {
492 final PrintWriter err = Context.getCurrentErr();
493 if (err != null) {
494 if (crlf) {
495 err.println(str);
496 } else {
497 err.print(str);
498 }
499 }
500 }
501
502 /** Current environment. */
503 private final ScriptEnvironment env;
504
505 /** is this context in strict mode? Cached from env. as this is used heavily. */
506 final boolean _strict;
507
508 /** class loader to resolve classes from script. */
509 private final ClassLoader appLoader;
510
511 /** Class loader to load classes from -classpath option, if set. */
512 private final ClassLoader classPathLoader;
513
514 /** Class loader to load classes compiled from scripts. */
515 private final ScriptLoader scriptLoader;
516
517 /** Current error manager. */
518 private final ErrorManager errors;
519
520 /** Unique id for script. Used only when --loader-per-compile=false */
521 private final AtomicLong uniqueScriptId;
522
523 /** Optional class filter to use for Java classes. Can be null. */
524 private final ClassFilter classFilter;
525
526 private static final ClassLoader myLoader = Context.class.getClassLoader();
527 private static final StructureLoader sharedLoader;
528
529 /*package-private*/ @SuppressWarnings("static-method")
530 ClassLoader getSharedLoader() {
531 return sharedLoader;
532 }
533
534 private static AccessControlContext createNoPermAccCtxt() {
535 return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, new Permissions()) });
536 }
537
538 private static AccessControlContext createPermAccCtxt(final String permName) {
539 final Permissions perms = new Permissions();
540 perms.add(new RuntimePermission(permName));
541 return new AccessControlContext(new ProtectionDomain[] { new ProtectionDomain(null, perms) });
542 }
543
544 private static final AccessControlContext NO_PERMISSIONS_ACC_CTXT = createNoPermAccCtxt();
545 private static final AccessControlContext CREATE_LOADER_ACC_CTXT = createPermAccCtxt("createClassLoader");
546 private static final AccessControlContext CREATE_GLOBAL_ACC_CTXT = createPermAccCtxt(NASHORN_CREATE_GLOBAL);
547
548 static {
549 sharedLoader = AccessController.doPrivileged(new PrivilegedAction<StructureLoader>() {
550 @Override
551 public StructureLoader run() {
552 return new StructureLoader(myLoader);
553 }
554 }, CREATE_LOADER_ACC_CTXT);
555 }
556
557 /**
558 * ThrowErrorManager that throws ParserException upon error conditions.
559 */
560 public static class ThrowErrorManager extends ErrorManager {
561 @Override
562 public void error(final String message) {
563 throw new ParserException(message);
564 }
565
566 @Override
567 public void error(final ParserException e) {
568 throw e;
569 }
570 }
571
572 /**
573 * Constructor
574 *
575 * @param options options from command line or Context creator
576 * @param errors error manger
577 * @param appLoader application class loader
578 */
579 public Context(final Options options, final ErrorManager errors, final ClassLoader appLoader) {
580 this(options, errors, appLoader, null);
581 }
582
583 /**
584 * Constructor
585 *
586 * @param options options from command line or Context creator
587 * @param errors error manger
588 * @param appLoader application class loader
589 * @param classFilter class filter to use
590 */
591 public Context(final Options options, final ErrorManager errors, final ClassLoader appLoader, final ClassFilter classFilter) {
592 this(options, errors, new PrintWriter(System.out, true), new PrintWriter(System.err, true), appLoader, classFilter);
593 }
594
595 /**
596 * Constructor
597 *
598 * @param options options from command line or Context creator
599 * @param errors error manger
600 * @param out output writer for this Context
601 * @param err error writer for this Context
602 * @param appLoader application class loader
603 */
604 public Context(final Options options, final ErrorManager errors, final PrintWriter out, final PrintWriter err, final ClassLoader appLoader) {
605 this(options, errors, out, err, appLoader, (ClassFilter)null);
606 }
607
608 /**
609 * Constructor
610 *
611 * @param options options from command line or Context creator
612 * @param errors error manger
613 * @param out output writer for this Context
614 * @param err error writer for this Context
615 * @param appLoader application class loader
616 * @param classFilter class filter to use
617 */
618 public Context(final Options options, final ErrorManager errors, final PrintWriter out, final PrintWriter err, final ClassLoader appLoader, final ClassFilter classFilter) {
619 final SecurityManager sm = System.getSecurityManager();
620 if (sm != null) {
621 sm.checkPermission(new RuntimePermission(NASHORN_CREATE_CONTEXT));
622 }
623
624 this.classFilter = classFilter;
625 this.env = new ScriptEnvironment(options, out, err);
626 this._strict = env._strict;
627 this.appLoader = appLoader;
628 if (env._loader_per_compile) {
629 this.scriptLoader = null;
630 this.uniqueScriptId = null;
631 } else {
632 this.scriptLoader = createNewLoader();
633 this.uniqueScriptId = new AtomicLong();
634 }
635 this.errors = errors;
636
637 // if user passed -classpath option, make a class loader with that and set it as
638 // thread context class loader so that script can access classes from that path.
639 final String classPath = options.getString("classpath");
640 if (!env._compile_only && classPath != null && !classPath.isEmpty()) {
641 // make sure that caller can create a class loader.
642 if (sm != null) {
643 sm.checkPermission(new RuntimePermission("createClassLoader"));
644 }
645 this.classPathLoader = NashornLoader.createClassLoader(classPath);
646 } else {
647 this.classPathLoader = null;
648 }
649
650 final int cacheSize = env._class_cache_size;
651 if (cacheSize > 0) {
652 classCache = new ClassCache(this, cacheSize);
653 }
654
655 if (env._persistent_cache) {
656 codeStore = newCodeStore(this);
657 }
658
659 // print version info if asked.
660 if (env._version) {
661 getErr().println("nashorn " + Version.version());
662 }
663
664 if (env._fullversion) {
665 getErr().println("nashorn full version " + Version.fullVersion());
666 }
667
668 if (Options.getBooleanProperty("nashorn.fields.dual")) {
669 fieldMode = FieldMode.DUAL;
670 } else if (Options.getBooleanProperty("nashorn.fields.objects")) {
671 fieldMode = FieldMode.OBJECTS;
672 } else {
673 fieldMode = FieldMode.AUTO;
674 }
675
676 initLoggers();
677 }
678
679
680 /**
681 * Get the class filter for this context
682 * @return class filter
683 */
684 public ClassFilter getClassFilter() {
685 return classFilter;
686 }
687
688 /**
689 * Returns the factory for constant method handles for global properties. The returned factory can be
690 * invalidated if this Context has more than one Global.
691 * @return the factory for constant method handles for global properties.
692 */
693 GlobalConstants getGlobalConstants() {
694 return globalConstantsRef.get();
695 }
696
697 /**
698 * Get the error manager for this context
699 * @return error manger
700 */
701 public ErrorManager getErrorManager() {
702 return errors;
703 }
704
705 /**
706 * Get the script environment for this context
707 * @return script environment
708 */
709 public ScriptEnvironment getEnv() {
710 return env;
711 }
712
713 /**
714 * Get the output stream for this context
715 * @return output print writer
716 */
717 public PrintWriter getOut() {
718 return env.getOut();
719 }
720
721 /**
722 * Get the error stream for this context
723 * @return error print writer
724 */
725 public PrintWriter getErr() {
726 return env.getErr();
727 }
728
729 /**
730 * Should scripts compiled by this context use dual field representation?
731 * @return true if using dual fields, false for object-only fields
732 */
733 public boolean useDualFields() {
734 return fieldMode == FieldMode.DUAL || (fieldMode == FieldMode.AUTO && env._optimistic_types);
735 }
736
737 /**
738 * Get the PropertyMap of the current global scope
739 * @return the property map of the current global scope
740 */
741 public static PropertyMap getGlobalMap() {
742 return Context.getGlobal().getMap();
743 }
744
745 /**
746 * Compile a top level script.
747 *
748 * @param source the source
749 * @param scope the scope
750 *
751 * @return top level function for script
752 */
753 public ScriptFunction compileScript(final Source source, final ScriptObject scope) {
754 return compileScript(source, scope, this.errors);
755 }
756
757 /**
758 * Interface to represent compiled code that can be re-used across many
759 * global scope instances
760 */
761 public static interface MultiGlobalCompiledScript {
762 /**
763 * Obtain script function object for a specific global scope object.
764 *
765 * @param newGlobal global scope for which function object is obtained
766 * @return script function for script level expressions
767 */
768 public ScriptFunction getFunction(final Global newGlobal);
769 }
770
771 /**
772 * Compile a top level script.
773 *
774 * @param source the script source
775 * @return reusable compiled script across many global scopes.
776 */
777 public MultiGlobalCompiledScript compileScript(final Source source) {
778 final Class<?> clazz = compile(source, this.errors, this._strict, false);
779 final MethodHandle createProgramFunctionHandle = getCreateProgramFunctionHandle(clazz);
780
781 return new MultiGlobalCompiledScript() {
782 @Override
783 public ScriptFunction getFunction(final Global newGlobal) {
784 return invokeCreateProgramFunctionHandle(createProgramFunctionHandle, newGlobal);
785 }
786 };
787 }
788
789 /**
790 * Entry point for {@code eval}
791 *
792 * @param initialScope The scope of this eval call
793 * @param string Evaluated code as a String
794 * @param callThis "this" to be passed to the evaluated code
795 * @param location location of the eval call
796 * @return the return value of the {@code eval}
797 */
798 public Object eval(final ScriptObject initialScope, final String string,
799 final Object callThis, final Object location) {
800 return eval(initialScope, string, callThis, location, false, false);
801 }
802
803 /**
804 * Entry point for {@code eval}
805 *
806 * @param initialScope The scope of this eval call
807 * @param string Evaluated code as a String
808 * @param callThis "this" to be passed to the evaluated code
809 * @param location location of the eval call
810 * @param strict is this {@code eval} call from a strict mode code?
811 * @param evalCall is this called from "eval" builtin?
812 *
813 * @return the return value of the {@code eval}
814 */
815 public Object eval(final ScriptObject initialScope, final String string,
816 final Object callThis, final Object location, final boolean strict, final boolean evalCall) {
817 final String file = location == UNDEFINED || location == null ? "<eval>" : location.toString();
818 final Source source = sourceFor(file, string, evalCall);
819 // is this direct 'eval' builtin call?
820 final boolean directEval = evalCall && (location != UNDEFINED);
821 final Global global = Context.getGlobal();
822 ScriptObject scope = initialScope;
823
824 // ECMA section 10.1.1 point 2 says eval code is strict if it begins
825 // with "use strict" directive or eval direct call itself is made
826 // from from strict mode code. We are passed with caller's strict mode.
827 // Nashorn extension: any 'eval' is unconditionally strict when -strict is specified.
828 boolean strictFlag = strict || this._strict;
829
830 Class<?> clazz = null;
831 try {
832 clazz = compile(source, new ThrowErrorManager(), strictFlag, true);
833 } catch (final ParserException e) {
834 e.throwAsEcmaException(global);
835 return null;
836 }
837
838 if (!strictFlag) {
839 // We need to get strict mode flag from compiled class. This is
840 // because eval code may start with "use strict" directive.
841 try {
842 strictFlag = clazz.getField(STRICT_MODE.symbolName()).getBoolean(null);
843 } catch (final NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
844 //ignored
845 strictFlag = false;
846 }
847 }
848
849 // In strict mode, eval does not instantiate variables and functions
850 // in the caller's environment. A new environment is created!
851 if (strictFlag) {
852 // Create a new scope object with given scope as its prototype
853 scope = newScope(scope);
854 }
855
856 final ScriptFunction func = getProgramFunction(clazz, scope);
857 Object evalThis;
858 if (directEval) {
859 evalThis = (callThis != UNDEFINED && callThis != null) || strictFlag ? callThis : global;
860 } else {
861 // either indirect evalCall or non-eval (Function, engine.eval, ScriptObjectMirror.eval..)
862 evalThis = callThis;
863 }
864
865 return ScriptRuntime.apply(func, evalThis);
866 }
867
868 private static ScriptObject newScope(final ScriptObject callerScope) {
869 return new Scope(callerScope, PropertyMap.newMap(Scope.class));
870 }
871
872 private static Source loadInternal(final String srcStr, final String prefix, final String resourcePath) {
873 if (srcStr.startsWith(prefix)) {
874 final String resource = resourcePath + srcStr.substring(prefix.length());
875 // NOTE: even sandbox scripts should be able to load scripts in nashorn: scheme
876 // These scripts are always available and are loaded from nashorn.jar's resources.
877 return AccessController.doPrivileged(
878 new PrivilegedAction<Source>() {
879 @Override
880 public Source run() {
881 try {
882 final URL resURL = Context.class.getResource(resource);
883 return resURL != null ? sourceFor(srcStr, resURL) : null;
884 } catch (final IOException exp) {
885 return null;
886 }
887 }
888 });
889 }
890
891 return null;
892 }
893
894 /**
895 * Implementation of {@code load} Nashorn extension. Load a script file from a source
896 * expression
897 *
898 * @param scope the scope
899 * @param from source expression for script
900 *
901 * @return return value for load call (undefined)
902 *
903 * @throws IOException if source cannot be found or loaded
904 */
905 public Object load(final Object scope, final Object from) throws IOException {
906 final Object src = from instanceof ConsString ? from.toString() : from;
907 Source source = null;
908
909 // load accepts a String (which could be a URL or a file name), a File, a URL
910 // or a ScriptObject that has "name" and "source" (string valued) properties.
911 if (src instanceof String) {
912 final String srcStr = (String)src;
913 if (srcStr.startsWith(LOAD_CLASSPATH)) {
914 final URL url = getResourceURL(srcStr.substring(LOAD_CLASSPATH.length()));
915 source = url != null ? sourceFor(url.toString(), url) : null;
916 } else {
917 final File file = new File(srcStr);
918 if (srcStr.indexOf(':') != -1) {
919 if ((source = loadInternal(srcStr, LOAD_NASHORN, "resources/")) == null &&
920 (source = loadInternal(srcStr, LOAD_FX, "resources/fx/")) == null) {
921 URL url;
922 try {
923 //check for malformed url. if malformed, it may still be a valid file
924 url = new URL(srcStr);
925 } catch (final MalformedURLException e) {
926 url = file.toURI().toURL();
927 }
928 source = sourceFor(url.toString(), url);
929 }
930 } else if (file.isFile()) {
931 source = sourceFor(srcStr, file);
932 }
933 }
934 } else if (src instanceof File && ((File)src).isFile()) {
935 final File file = (File)src;
936 source = sourceFor(file.getName(), file);
937 } else if (src instanceof URL) {
938 final URL url = (URL)src;
939 source = sourceFor(url.toString(), url);
940 } else if (src instanceof ScriptObject) {
941 final ScriptObject sobj = (ScriptObject)src;
942 if (sobj.has("script") && sobj.has("name")) {
943 final String script = JSType.toString(sobj.get("script"));
944 final String name = JSType.toString(sobj.get("name"));
945 source = sourceFor(name, script);
946 }
947 } else if (src instanceof Map) {
948 final Map<?,?> map = (Map<?,?>)src;
949 if (map.containsKey("script") && map.containsKey("name")) {
950 final String script = JSType.toString(map.get("script"));
951 final String name = JSType.toString(map.get("name"));
952 source = sourceFor(name, script);
953 }
954 }
955
956 if (source != null) {
957 if (scope instanceof ScriptObject && ((ScriptObject)scope).isScope()) {
958 final ScriptObject sobj = (ScriptObject)scope;
959 // passed object is a script object
960 // Global is the only user accessible scope ScriptObject
961 assert sobj.isGlobal() : "non-Global scope object!!";
962 return evaluateSource(source, sobj, sobj);
963 } else if (scope == null || scope == UNDEFINED) {
964 // undefined or null scope. Use current global instance.
965 final Global global = getGlobal();
966 return evaluateSource(source, global, global);
967 } else {
968 /*
969 * Arbitrary object passed for scope.
970 * Indirect load that is equivalent to:
971 *
972 * (function(scope, source) {
973 * with (scope) {
974 * eval(<script_from_source>);
975 * }
976 * })(scope, source);
977 */
978 final Global global = getGlobal();
979 // Create a new object. This is where all declarations
980 // (var, function) from the evaluated code go.
981 // make global to be its __proto__ so that global
982 // definitions are accessible to the evaluated code.
983 final ScriptObject evalScope = newScope(global);
984
985 // finally, make a WithObject around user supplied scope object
986 // so that it's properties are accessible as variables.
987 final ScriptObject withObj = ScriptRuntime.openWith(evalScope, scope);
988
989 // evaluate given source with 'withObj' as scope
990 // but use global object as "this".
991 return evaluateSource(source, withObj, global);
992 }
993 }
994
995 throw typeError("cant.load.script", ScriptRuntime.safeToString(from));
996 }
997
998 /**
999 * Implementation of {@code loadWithNewGlobal} Nashorn extension. Load a script file from a source
1000 * expression, after creating a new global scope.
1001 *
1002 * @param from source expression for script
1003 * @param args (optional) arguments to be passed to the loaded script
1004 *
1005 * @return return value for load call (undefined)
1006 *
1007 * @throws IOException if source cannot be found or loaded
1008 */
1009 public Object loadWithNewGlobal(final Object from, final Object...args) throws IOException {
1010 final Global oldGlobal = getGlobal();
1011 final Global newGlobal = AccessController.doPrivileged(new PrivilegedAction<Global>() {
1012 @Override
1013 public Global run() {
1014 try {
1015 return newGlobal();
1016 } catch (final RuntimeException e) {
1017 if (Context.DEBUG) {
1018 e.printStackTrace();
1019 }
1020 throw e;
1021 }
1022 }
1023 }, CREATE_GLOBAL_ACC_CTXT);
1024 // initialize newly created Global instance
1025 initGlobal(newGlobal);
1026 setGlobal(newGlobal);
1027
1028 final Object[] wrapped = args == null? ScriptRuntime.EMPTY_ARRAY : ScriptObjectMirror.wrapArray(args, oldGlobal);
1029 newGlobal.put("arguments", newGlobal.wrapAsObject(wrapped), env._strict);
1030
1031 try {
1032 // wrap objects from newGlobal's world as mirrors - but if result
1033 // is from oldGlobal's world, unwrap it!
1034 return ScriptObjectMirror.unwrap(ScriptObjectMirror.wrap(load(newGlobal, from), newGlobal), oldGlobal);
1035 } finally {
1036 setGlobal(oldGlobal);
1037 }
1038 }
1039
1040 /**
1041 * Load or get a structure class. Structure class names are based on the number of parameter fields
1042 * and {@link AccessorProperty} fields in them. Structure classes are used to represent ScriptObjects
1043 *
1044 * @see ObjectClassGenerator
1045 * @see AccessorProperty
1046 * @see ScriptObject
1047 *
1048 * @param fullName full name of class, e.g. jdk.nashorn.internal.objects.JO2P1 contains 2 fields and 1 parameter.
1049 *
1050 * @return the {@code Class<?>} for this structure
1051 *
1052 * @throws ClassNotFoundException if structure class cannot be resolved
1053 */
1054 @SuppressWarnings("unchecked")
1055 public static Class<? extends ScriptObject> forStructureClass(final String fullName) throws ClassNotFoundException {
1056 if (System.getSecurityManager() != null && !StructureLoader.isStructureClass(fullName)) {
1057 throw new ClassNotFoundException(fullName);
1058 }
1059 return (Class<? extends ScriptObject>)Class.forName(fullName, true, sharedLoader);
1060 }
1061
1062 /**
1063 * Is {@code className} the name of a structure class?
1064 *
1065 * @param className a class name
1066 * @return true if className is a structure class name
1067 */
1068 public static boolean isStructureClass(final String className) {
1069 return StructureLoader.isStructureClass(className);
1070 }
1071
1072 /**
1073 * Checks that the given Class can be accessed from no permissions context.
1074 *
1075 * @param clazz Class object
1076 * @throws SecurityException if not accessible
1077 */
1078 public static void checkPackageAccess(final Class<?> clazz) {
1079 final SecurityManager sm = System.getSecurityManager();
1080 if (sm != null) {
1081 Class<?> bottomClazz = clazz;
1082 while (bottomClazz.isArray()) {
1083 bottomClazz = bottomClazz.getComponentType();
1084 }
1085 checkPackageAccess(sm, bottomClazz.getName());
1086 }
1087 }
1088
1089 /**
1090 * Checks that the given package name can be accessed from no permissions context.
1091 *
1092 * @param pkgName package name
1093 * @throws SecurityException if not accessible
1094 */
1095 public static void checkPackageAccess(final String pkgName) {
1096 final SecurityManager sm = System.getSecurityManager();
1097 if (sm != null) {
1098 checkPackageAccess(sm, pkgName.endsWith(".") ? pkgName : pkgName + ".");
1099 }
1100 }
1101
1102 /**
1103 * Checks that the given package can be accessed from no permissions context.
1104 *
1105 * @param sm current security manager instance
1106 * @param fullName fully qualified package name
1107 * @throw SecurityException if not accessible
1108 */
1109 private static void checkPackageAccess(final SecurityManager sm, final String fullName) {
1110 Objects.requireNonNull(sm);
1111 final int index = fullName.lastIndexOf('.');
1112 if (index != -1) {
1113 final String pkgName = fullName.substring(0, index);
1114 AccessController.doPrivileged(new PrivilegedAction<Void>() {
1115 @Override
1116 public Void run() {
1117 sm.checkPackageAccess(pkgName);
1118 return null;
1119 }
1120 }, NO_PERMISSIONS_ACC_CTXT);
1121 }
1122 }
1123
1124 /**
1125 * Checks that the given Class can be accessed from no permissions context.
1126 *
1127 * @param clazz Class object
1128 * @return true if package is accessible, false otherwise
1129 */
1130 private static boolean isAccessiblePackage(final Class<?> clazz) {
1131 try {
1132 checkPackageAccess(clazz);
1133 return true;
1134 } catch (final SecurityException se) {
1135 return false;
1136 }
1137 }
1138
1139 /**
1140 * Checks that the given Class is public and it can be accessed from no permissions context.
1141 *
1142 * @param clazz Class object to check
1143 * @return true if Class is accessible, false otherwise
1144 */
1145 public static boolean isAccessibleClass(final Class<?> clazz) {
1146 return Modifier.isPublic(clazz.getModifiers()) && Context.isAccessiblePackage(clazz);
1147 }
1148
1149 /**
1150 * Lookup a Java class. This is used for JSR-223 stuff linking in from
1151 * {@code jdk.nashorn.internal.objects.NativeJava} and {@code jdk.nashorn.internal.runtime.NativeJavaPackage}
1152 *
1153 * @param fullName full name of class to load
1154 *
1155 * @return the {@code Class<?>} for the name
1156 *
1157 * @throws ClassNotFoundException if class cannot be resolved
1158 */
1159 public Class<?> findClass(final String fullName) throws ClassNotFoundException {
1160 if (fullName.indexOf('[') != -1 || fullName.indexOf('/') != -1) {
1161 // don't allow array class names or internal names.
1162 throw new ClassNotFoundException(fullName);
1163 }
1164
1165 // give chance to ClassFilter to filter out, if present
1166 if (classFilter != null && !classFilter.exposeToScripts(fullName)) {
1167 throw new ClassNotFoundException(fullName);
1168 }
1169
1170 // check package access as soon as possible!
1171 final SecurityManager sm = System.getSecurityManager();
1172 if (sm != null) {
1173 checkPackageAccess(sm, fullName);
1174 }
1175
1176 // try the script -classpath loader, if that is set
1177 if (classPathLoader != null) {
1178 try {
1179 return Class.forName(fullName, true, classPathLoader);
1180 } catch (final ClassNotFoundException ignored) {
1181 // ignore, continue search
1182 }
1183 }
1184
1185 // Try finding using the "app" loader.
1186 return Class.forName(fullName, true, appLoader);
1187 }
1188
1189 /**
1190 * Hook to print stack trace for a {@link Throwable} that occurred during
1191 * execution
1192 *
1193 * @param t throwable for which to dump stack
1194 */
1195 public static void printStackTrace(final Throwable t) {
1196 if (Context.DEBUG) {
1197 t.printStackTrace(Context.getCurrentErr());
1198 }
1199 }
1200
1201 /**
1202 * Verify generated bytecode before emission. This is called back from the
1203 * {@link ObjectClassGenerator} or the {@link Compiler}. If the "--verify-code" parameter
1204 * hasn't been given, this is a nop
1205 *
1206 * Note that verification may load classes -- we don't want to do that unless
1207 * user specified verify option. We check it here even though caller
1208 * may have already checked that flag
1209 *
1210 * @param bytecode bytecode to verify
1211 */
1212 public void verify(final byte[] bytecode) {
1213 if (env._verify_code) {
1214 // No verification when security manager is around as verifier
1215 // may load further classes - which should be avoided.
1216 if (System.getSecurityManager() == null) {
1217 CheckClassAdapter.verify(new ClassReader(bytecode), sharedLoader, false, new PrintWriter(System.err, true));
1218 }
1219 }
1220 }
1221
1222 /**
1223 * Create and initialize a new global scope object.
1224 *
1225 * @return the initialized global scope object.
1226 */
1227 public Global createGlobal() {
1228 return initGlobal(newGlobal());
1229 }
1230
1231 /**
1232 * Create a new uninitialized global scope object
1233 * @return the global script object
1234 */
1235 public Global newGlobal() {
1236 createOrInvalidateGlobalConstants();
1237 return new Global(this);
1238 }
1239
1240 private void createOrInvalidateGlobalConstants() {
1241 for (;;) {
1242 final GlobalConstants currentGlobalConstants = getGlobalConstants();
1243 if (currentGlobalConstants != null) {
1244 // Subsequent invocation; we're creating our second or later Global. GlobalConstants is not safe to use
1245 // with more than one Global, as the constant method handle linkages it creates create a coupling
1246 // between the Global and the call sites in the compiled code.
1247 currentGlobalConstants.invalidateForever();
1248 return;
1249 }
1250 final GlobalConstants newGlobalConstants = new GlobalConstants(getLogger(GlobalConstants.class));
1251 if (globalConstantsRef.compareAndSet(null, newGlobalConstants)) {
1252 // First invocation; we're creating the first Global in this Context. Create the GlobalConstants object
1253 // for this Context.
1254 return;
1255 }
1256
1257 // If we reach here, then we started out as the first invocation, but another concurrent invocation won the
1258 // CAS race. We'll just let the loop repeat and invalidate the CAS race winner.
1259 }
1260 }
1261
1262 /**
1263 * Initialize given global scope object.
1264 *
1265 * @param global the global
1266 * @param engine the associated ScriptEngine instance, can be null
1267 * @return the initialized global scope object.
1268 */
1269 public Global initGlobal(final Global global, final ScriptEngine engine) {
1270 // Need only minimal global object, if we are just compiling.
1271 if (!env._compile_only) {
1272 final Global oldGlobal = Context.getGlobal();
1273 try {
1274 Context.setGlobal(global);
1275 // initialize global scope with builtin global objects
1276 global.initBuiltinObjects(engine);
1277 } finally {
1278 Context.setGlobal(oldGlobal);
1279 }
1280 }
1281
1282 return global;
1283 }
1284
1285 /**
1286 * Initialize given global scope object.
1287 *
1288 * @param global the global
1289 * @return the initialized global scope object.
1290 */
1291 public Global initGlobal(final Global global) {
1292 return initGlobal(global, null);
1293 }
1294
1295 /**
1296 * Return the current global's context
1297 * @return current global's context
1298 */
1299 static Context getContextTrusted() {
1300 return getContext(getGlobal());
1301 }
1302
1303 static Context getContextTrustedOrNull() {
1304 final Global global = Context.getGlobal();
1305 return global == null ? null : getContext(global);
1306 }
1307
1308 private static Context getContext(final Global global) {
1309 // We can't invoke Global.getContext() directly, as it's a protected override, and Global isn't in our package.
1310 // In order to access the method, we must cast it to ScriptObject first (which is in our package) and then let
1311 // virtual invocation do its thing.
1312 return ((ScriptObject)global).getContext();
1313 }
1314
1315 /**
1316 * Try to infer Context instance from the Class. If we cannot,
1317 * then get it from the thread local variable.
1318 *
1319 * @param clazz the class
1320 * @return context
1321 */
1322 static Context fromClass(final Class<?> clazz) {
1323 final ClassLoader loader = clazz.getClassLoader();
1324
1325 if (loader instanceof ScriptLoader) {
1326 return ((ScriptLoader)loader).getContext();
1327 }
1328
1329 return Context.getContextTrusted();
1330 }
1331
1332 private URL getResourceURL(final String resName) {
1333 // try the classPathLoader if we have and then
1334 // try the appLoader if non-null.
1335 if (classPathLoader != null) {
1336 return classPathLoader.getResource(resName);
1337 } else if (appLoader != null) {
1338 return appLoader.getResource(resName);
1339 }
1340
1341 return null;
1342 }
1343
1344 private Object evaluateSource(final Source source, final ScriptObject scope, final ScriptObject thiz) {
1345 ScriptFunction script = null;
1346
1347 try {
1348 script = compileScript(source, scope, new Context.ThrowErrorManager());
1349 } catch (final ParserException e) {
1350 e.throwAsEcmaException();
1351 }
1352
1353 return ScriptRuntime.apply(script, thiz);
1354 }
1355
1356 private static ScriptFunction getProgramFunction(final Class<?> script, final ScriptObject scope) {
1357 if (script == null) {
1358 return null;
1359 }
1360 return invokeCreateProgramFunctionHandle(getCreateProgramFunctionHandle(script), scope);
1361 }
1362
1363 private static MethodHandle getCreateProgramFunctionHandle(final Class<?> script) {
1364 try {
1365 return LOOKUP.findStatic(script, CREATE_PROGRAM_FUNCTION.symbolName(), CREATE_PROGRAM_FUNCTION_TYPE);
1366 } catch (NoSuchMethodException | IllegalAccessException e) {
1367 throw new AssertionError("Failed to retrieve a handle for the program function for " + script.getName(), e);
1368 }
1369 }
1370
1371 private static ScriptFunction invokeCreateProgramFunctionHandle(final MethodHandle createProgramFunctionHandle, final ScriptObject scope) {
1372 try {
1373 return (ScriptFunction)createProgramFunctionHandle.invokeExact(scope);
1374 } catch (final RuntimeException|Error e) {
1375 throw e;
1376 } catch (final Throwable t) {
1377 throw new AssertionError("Failed to create a program function", t);
1378 }
1379 }
1380
1381 private ScriptFunction compileScript(final Source source, final ScriptObject scope, final ErrorManager errMan) {
1382 return getProgramFunction(compile(source, errMan, this._strict, false), scope);
1383 }
1384
1385 private synchronized Class<?> compile(final Source source, final ErrorManager errMan, final boolean strict, final boolean isEval) {
1386 // start with no errors, no warnings.
1387 errMan.reset();
1388
1389 Class<?> script = findCachedClass(source);
1390 if (script != null) {
1391 final DebugLogger log = getLogger(Compiler.class);
1392 if (log.isEnabled()) {
1393 log.fine(new RuntimeEvent<>(Level.INFO, source), "Code cache hit for ", source, " avoiding recompile.");
1394 }
1395 return script;
1396 }
1397
1398 StoredScript storedScript = null;
1399 FunctionNode functionNode = null;
1400 // Don't use code store if optimistic types is enabled but lazy compilation is not.
1401 // This would store a full script compilation with many wrong optimistic assumptions that would
1402 // do more harm than good on later runs with both optimistic types and lazy compilation enabled.
1403 final boolean useCodeStore = codeStore != null && !env._parse_only && (!env._optimistic_types || env._lazy_compilation);
1404 final String cacheKey = useCodeStore ? CodeStore.getCacheKey("script", null) : null;
1405
1406 if (useCodeStore) {
1407 storedScript = codeStore.load(source, cacheKey);
1408 }
1409
1410 if (storedScript == null) {
1411 if (env._dest_dir != null) {
1412 source.dump(env._dest_dir);
1413 }
1414
1415 functionNode = new Parser(env, source, errMan, strict, getLogger(Parser.class)).parse();
1416
1417 if (errMan.hasErrors()) {
1418 return null;
1419 }
1420
1421 if (env._print_ast || functionNode.getFlag(FunctionNode.IS_PRINT_AST)) {
1422 getErr().println(new ASTWriter(functionNode));
1423 }
1424
1425 if (env._print_parse || functionNode.getFlag(FunctionNode.IS_PRINT_PARSE)) {
1426 getErr().println(new PrintVisitor(functionNode, true, false));
1427 }
1428 }
1429
1430 if (env._parse_only) {
1431 return null;
1432 }
1433
1434 final URL url = source.getURL();
1435 final CodeSource cs = new CodeSource(url, (CodeSigner[])null);
1436 final CodeInstaller installer;
1437 if (!env.useAnonymousClasses(isEval) || env._persistent_cache || !env._lazy_compilation) {
1438 // Persistent code cache and eager compilation preclude use of VM anonymous classes
1439 final ScriptLoader loader = env._loader_per_compile ? createNewLoader() : scriptLoader;
1440 installer = new NamedContextCodeInstaller(this, cs, loader);
1441 } else {
1442 installer = new AnonymousContextCodeInstaller(this, cs, getAnonymousHostClass(cs));
1443 }
1444
1445 if (storedScript == null) {
1446 final CompilationPhases phases = Compiler.CompilationPhases.COMPILE_ALL;
1447
1448 final Compiler compiler = Compiler.forInitialCompilation(
1449 installer,
1450 source,
1451 errMan,
1452 strict | functionNode.isStrict());
1453
1454 final FunctionNode compiledFunction = compiler.compile(functionNode, phases);
1455 if (errMan.hasErrors()) {
1456 return null;
1457 }
1458 script = compiledFunction.getRootClass();
1459 compiler.persistClassInfo(cacheKey, compiledFunction);
1460 } else {
1461 Compiler.updateCompilationId(storedScript.getCompilationId());
1462 script = storedScript.installScript(source, installer);
1463 }
1464
1465 cacheClass(source, script);
1466 return script;
1467 }
1468
1469 private ScriptLoader createNewLoader() {
1470 return AccessController.doPrivileged(
1471 new PrivilegedAction<ScriptLoader>() {
1472 @Override
1473 public ScriptLoader run() {
1474 return new ScriptLoader(appLoader, Context.this);
1475 }
1476 }, CREATE_LOADER_ACC_CTXT);
1477 }
1478
1479 private long getUniqueScriptId() {
1480 return uniqueScriptId.getAndIncrement();
1481 }
1482
1483 /**
1484 * Cache for compiled script classes.
1485 */
1486 @SuppressWarnings("serial")
1487 @Logger(name="classcache")
1488 private static class ClassCache extends LinkedHashMap<Source, ClassReference> implements Loggable {
1489 private final int size;
1490 private final ReferenceQueue<Class<?>> queue;
1491 private final DebugLogger log;
1492
1493 ClassCache(final Context context, final int size) {
1494 super(size, 0.75f, true);
1495 this.size = size;
1496 this.queue = new ReferenceQueue<>();
1497 this.log = initLogger(context);
1498 }
1499
1500 void cache(final Source source, final Class<?> clazz) {
1501 if (log.isEnabled()) {
1502 log.info("Caching ", source, " in class cache");
1503 }
1504 put(source, new ClassReference(clazz, queue, source));
1505 }
1506
1507 @Override
1508 protected boolean removeEldestEntry(final Map.Entry<Source, ClassReference> eldest) {
1509 return size() > size;
1510 }
1511
1512 @Override
1513 public ClassReference get(final Object key) {
1514 for (ClassReference ref; (ref = (ClassReference)queue.poll()) != null; ) {
1515 final Source source = ref.source;
1516 if (log.isEnabled()) {
1517 log.info("Evicting ", source, " from class cache.");
1518 }
1519 remove(source);
1520 }
1521
1522 final ClassReference ref = super.get(key);
1523 if (ref != null && log.isEnabled()) {
1524 log.info("Retrieved class reference for ", ref.source, " from class cache");
1525 }
1526 return ref;
1527 }
1528
1529 @Override
1530 public DebugLogger initLogger(final Context context) {
1531 return context.getLogger(getClass());
1532 }
1533
1534 @Override
1535 public DebugLogger getLogger() {
1536 return log;
1537 }
1538
1539 }
1540
1541 private static class ClassReference extends SoftReference<Class<?>> {
1542 private final Source source;
1543
1544 ClassReference(final Class<?> clazz, final ReferenceQueue<Class<?>> queue, final Source source) {
1545 super(clazz, queue);
1546 this.source = source;
1547 }
1548 }
1549
1550 // Class cache management
1551 private Class<?> findCachedClass(final Source source) {
1552 final ClassReference ref = classCache == null ? null : classCache.get(source);
1553 return ref != null ? ref.get() : null;
1554 }
1555
1556 private void cacheClass(final Source source, final Class<?> clazz) {
1557 if (classCache != null) {
1558 classCache.cache(source, clazz);
1559 }
1560 }
1561
1562 // logging
1563 private final Map<String, DebugLogger> loggers = new HashMap<>();
1564
1565 private void initLoggers() {
1566 ((Loggable)MethodHandleFactory.getFunctionality()).initLogger(this);
1567 }
1568
1569 /**
1570 * Get a logger, given a loggable class
1571 * @param clazz a Loggable class
1572 * @return debuglogger associated with that class
1573 */
1574 public DebugLogger getLogger(final Class<? extends Loggable> clazz) {
1575 return getLogger(clazz, null);
1576 }
1577
1578 /**
1579 * Get a logger, given a loggable class
1580 * @param clazz a Loggable class
1581 * @param initHook an init hook - if this is the first time the logger is created in the context, run the init hook
1582 * @return debuglogger associated with that class
1583 */
1584 public DebugLogger getLogger(final Class<? extends Loggable> clazz, final Consumer<DebugLogger> initHook) {
1585 final String name = getLoggerName(clazz);
1586 DebugLogger logger = loggers.get(name);
1587 if (logger == null) {
1588 if (!env.hasLogger(name)) {
1589 return DebugLogger.DISABLED_LOGGER;
1590 }
1591 final LoggerInfo info = env._loggers.get(name);
1592 logger = new DebugLogger(name, info.getLevel(), info.isQuiet());
1593 if (initHook != null) {
1594 initHook.accept(logger);
1595 }
1596 loggers.put(name, logger);
1597 }
1598 return logger;
1599 }
1600
1601 /**
1602 * Given a Loggable class, weave debug info info a method handle for that logger.
1603 * Level.INFO is used
1604 *
1605 * @param clazz loggable
1606 * @param mh method handle
1607 * @param text debug printout to add
1608 *
1609 * @return instrumented method handle, or null if logger not enabled
1610 */
1611 public MethodHandle addLoggingToHandle(final Class<? extends Loggable> clazz, final MethodHandle mh, final Supplier<String> text) {
1612 return addLoggingToHandle(clazz, Level.INFO, mh, Integer.MAX_VALUE, false, text);
1613 }
1614
1615 /**
1616 * Given a Loggable class, weave debug info info a method handle for that logger.
1617 *
1618 * @param clazz loggable
1619 * @param level log level
1620 * @param mh method handle
1621 * @param paramStart first parameter to print
1622 * @param printReturnValue should we print the return value?
1623 * @param text debug printout to add
1624 *
1625 * @return instrumented method handle, or null if logger not enabled
1626 */
1627 public MethodHandle addLoggingToHandle(final Class<? extends Loggable> clazz, final Level level, final MethodHandle mh, final int paramStart, final boolean printReturnValue, final Supplier<String> text) {
1628 final DebugLogger log = getLogger(clazz);
1629 if (log.isEnabled()) {
1630 return MethodHandleFactory.addDebugPrintout(log, level, mh, paramStart, printReturnValue, text.get());
1631 }
1632 return mh;
1633 }
1634
1635 private static String getLoggerName(final Class<?> clazz) {
1636 Class<?> current = clazz;
1637 while (current != null) {
1638 final Logger log = current.getAnnotation(Logger.class);
1639 if (log != null) {
1640 assert !"".equals(log.name());
1641 return log.name();
1642 }
1643 current = current.getSuperclass();
1644 }
1645 assert false;
1646 return null;
1647 }
1648
1649 /**
1650 * This is a special kind of switchpoint used to guard builtin
1651 * properties and prototypes. In the future it might contain
1652 * logic to e.g. multiple switchpoint classes.
1653 */
1654 public static final class BuiltinSwitchPoint extends SwitchPoint {
1655 //empty
1656 }
1657
1658 /**
1659 * Create a new builtin switchpoint and return it
1660 * @param name key name
1661 * @return new builtin switchpoint
1662 */
1663 public SwitchPoint newBuiltinSwitchPoint(final String name) {
1664 assert builtinSwitchPoints.get(name) == null;
1665 final SwitchPoint sp = new BuiltinSwitchPoint();
1666 builtinSwitchPoints.put(name, sp);
1667 return sp;
1668 }
1669
1670 /**
1671 * Return the builtin switchpoint for a particular key name
1672 * @param name key name
1673 * @return builtin switchpoint or null if none
1674 */
1675 public SwitchPoint getBuiltinSwitchPoint(final String name) {
1676 return builtinSwitchPoints.get(name);
1677 }
1678
1679 }
--- EOF ---