8141538: Make DynamicLinker specific to a Context in Nashorn
authorattila
Mon, 09 Nov 2015 14:04:43 +0100
changeset 33685 343aaf358c21
parent 33684 1bfe8ffd9baf
child 33686 1391474a6405
8141538: Make DynamicLinker specific to a Context in Nashorn Reviewed-by: hannesw, sundar
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java
nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/Bootstrap.java
nashorn/test/script/basic/JDK-8011578.js
nashorn/test/src/jdk/nashorn/internal/runtime/test/JDK_8078414_Test.java
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java	Mon Nov 09 14:03:37 2015 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/objects/Global.java	Mon Nov 09 14:04:43 2015 +0100
@@ -94,8 +94,8 @@
     // (__FILE__, __DIR__, __LINE__)
     private static final Object LAZY_SENTINEL = new Object();
 
-    private final InvokeByName TO_STRING = new InvokeByName("toString", ScriptObject.class);
-    private final InvokeByName VALUE_OF  = new InvokeByName("valueOf",  ScriptObject.class);
+    private InvokeByName TO_STRING;
+    private InvokeByName VALUE_OF;
 
     /**
      * Optimistic builtin names that require switchpoint invalidation
@@ -1073,6 +1073,9 @@
             return;
         }
 
+        TO_STRING = new InvokeByName("toString", ScriptObject.class);
+        VALUE_OF  = new InvokeByName("valueOf",  ScriptObject.class);
+
         this.engine = eng;
         if (this.engine != null) {
             this.scontext = new ThreadLocal<>();
@@ -1357,18 +1360,27 @@
         return desc;
     }
 
-    private static <T> T getLazilyCreatedValue(final Object key, final Callable<T> creator, final Map<Object, T> map) {
+    private <T> T getLazilyCreatedValue(final Object key, final Callable<T> creator, final Map<Object, T> map) {
         final T obj = map.get(key);
         if (obj != null) {
             return obj;
         }
 
+        final Global oldGlobal = Context.getGlobal();
+        final boolean differentGlobal = oldGlobal != this;
         try {
+            if (differentGlobal) {
+                Context.setGlobal(this);
+            }
             final T newObj = creator.call();
             final T existingObj = map.putIfAbsent(key, newObj);
             return existingObj != null ? existingObj : newObj;
         } catch (final Exception exp) {
             throw new RuntimeException(exp);
+        } finally {
+            if (differentGlobal) {
+                Context.setGlobal(oldGlobal);
+            }
         }
     }
 
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java	Mon Nov 09 14:03:37 2015 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/Context.java	Mon Nov 09 14:04:43 2015 +0100
@@ -73,6 +73,7 @@
 import java.util.function.Supplier;
 import java.util.logging.Level;
 import javax.script.ScriptEngine;
+import jdk.internal.dynalink.DynamicLinker;
 import jdk.internal.org.objectweb.asm.ClassReader;
 import jdk.internal.org.objectweb.asm.ClassWriter;
 import jdk.internal.org.objectweb.asm.Opcodes;
@@ -89,6 +90,7 @@
 import jdk.nashorn.internal.objects.Global;
 import jdk.nashorn.internal.parser.Parser;
 import jdk.nashorn.internal.runtime.events.RuntimeEvent;
+import jdk.nashorn.internal.runtime.linker.Bootstrap;
 import jdk.nashorn.internal.runtime.logging.DebugLogger;
 import jdk.nashorn.internal.runtime.logging.Loggable;
 import jdk.nashorn.internal.runtime.logging.Logger;
@@ -512,6 +514,9 @@
     /** Class loader to load classes compiled from scripts. */
     private final ScriptLoader scriptLoader;
 
+    /** Dynamic linker for linking call sites in script code loaded by this context */
+    private final DynamicLinker dynamicLinker;
+
     /** Current error manager. */
     private final ErrorManager errors;
 
@@ -644,6 +649,7 @@
         } else {
             this.appLoader = appLoader;
         }
+        this.dynamicLinker = Bootstrap.createDynamicLinker(this.appLoader);
 
         final int cacheSize = env._class_cache_size;
         if (cacheSize > 0) {
@@ -1295,6 +1301,26 @@
         return getContext(getGlobal());
     }
 
+    /**
+     * Gets the Nashorn dynamic linker for the specified class. If the class is
+     * a script class, the dynamic linker associated with its context is
+     * returned. Otherwise the dynamic linker associated with the current
+     * context is returned.
+     * @param clazz the class for which we want to retrieve a dynamic linker.
+     * @return the Nashorn dynamic linker for the specified class.
+     */
+    public static DynamicLinker getDynamicLinker(final Class<?> clazz) {
+        return fromClass(clazz).dynamicLinker;
+    }
+
+    /**
+     * Gets the Nashorn dynamic linker associated with the current context.
+     * @return the Nashorn dynamic linker for the current context.
+     */
+    public static DynamicLinker getDynamicLinker() {
+        return getContextTrusted().dynamicLinker;
+    }
+
     static Context getContextTrustedOrNull() {
         final Global global = Context.getGlobal();
         return global == null ? null : getContext(global);
@@ -1665,5 +1691,4 @@
     public SwitchPoint getBuiltinSwitchPoint(final String name) {
         return builtinSwitchPoints.get(name);
     }
-
 }
--- a/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/Bootstrap.java	Mon Nov 09 14:03:37 2015 +0100
+++ b/nashorn/src/jdk.scripting.nashorn/share/classes/jdk/nashorn/internal/runtime/linker/Bootstrap.java	Mon Nov 09 14:04:43 2015 +0100
@@ -49,6 +49,7 @@
 import jdk.nashorn.internal.codegen.CompilerConstants.Call;
 import jdk.nashorn.internal.lookup.MethodHandleFactory;
 import jdk.nashorn.internal.lookup.MethodHandleFunctionality;
+import jdk.nashorn.internal.runtime.Context;
 import jdk.nashorn.internal.runtime.ECMAException;
 import jdk.nashorn.internal.runtime.JSType;
 import jdk.nashorn.internal.runtime.OptimisticReturnFilters;
@@ -81,14 +82,22 @@
      * See for example octane.gbemu, run with --log=fields:warning to study
      * megamorphic behavior
      */
-    private static final int NASHORN_DEFAULT_UNSTABLE_RELINK_THRESHOLD = 16;
+    private static final int UNSTABLE_RELINK_THRESHOLD_DEFAULT = 16;
+    private static final int UNSTABLE_RELINK_THRESHOLD =
+            Options.getIntProperty("nashorn.unstable.relink.threshold",
+                    UNSTABLE_RELINK_THRESHOLD_DEFAULT);
 
     // do not create me!!
     private Bootstrap() {
     }
 
-    private static final DynamicLinker dynamicLinker;
-    static {
+    /**
+     * Creates a Nashorn dynamic linker with the given app class loader.
+     * @param appLoader the app class loader. It will be used to discover
+     * additional language runtime linkers (if any).
+     * @return a newly created dynamic linker.
+     */
+    public static DynamicLinker createDynamicLinker(final ClassLoader appLoader) {
         final DynamicLinkerFactory factory = new DynamicLinkerFactory();
         final NashornBeansLinker nashornBeansLinker = new NashornBeansLinker();
         factory.setPrioritizedLinkers(
@@ -116,15 +125,13 @@
             }
         });
         factory.setInternalObjectsFilter(NashornBeansLinker.createHiddenObjectFilter());
-        final int relinkThreshold = Options.getIntProperty("nashorn.unstable.relink.threshold", NASHORN_DEFAULT_UNSTABLE_RELINK_THRESHOLD);
-        if (relinkThreshold > -1) {
-            factory.setUnstableRelinkThreshold(relinkThreshold);
+        if (UNSTABLE_RELINK_THRESHOLD > -1) {
+            factory.setUnstableRelinkThreshold(UNSTABLE_RELINK_THRESHOLD);
         }
 
         // Linkers for any additional language runtimes deployed alongside Nashorn will be picked up by the factory.
-        factory.setClassLoader(Bootstrap.class.getClassLoader());
-
-        dynamicLinker = factory.createLinker();
+        factory.setClassLoader(appLoader);
+        return factory.createLinker();
     }
 
     /**
@@ -202,7 +209,7 @@
      * @return CallSite with MethodHandle to appropriate method or null if not found.
      */
     public static CallSite bootstrap(final Lookup lookup, final String opDesc, final MethodType type, final int flags) {
-        return dynamicLinker.link(LinkerCallSite.newLinkerCallSite(lookup, opDesc, type, flags));
+        return Context.getDynamicLinker(lookup.lookupClass()).link(LinkerCallSite.newLinkerCallSite(lookup, opDesc, type, flags));
     }
 
     /**
@@ -461,7 +468,7 @@
      * @return Nashorn's internal dynamic linker's services object.
      */
     public static LinkerServices getLinkerServices() {
-        return dynamicLinker.getLinkerServices();
+        return Context.getDynamicLinker().getLinkerServices();
     }
 
     /**
--- a/nashorn/test/script/basic/JDK-8011578.js	Mon Nov 09 14:03:37 2015 +0100
+++ b/nashorn/test/script/basic/JDK-8011578.js	Mon Nov 09 14:04:43 2015 +0100
@@ -26,6 +26,7 @@
  *
  * @test
  * @option -Dnashorn.unstable.relink.threshold=1
+ * @fork
  * @run
  */
 
--- a/nashorn/test/src/jdk/nashorn/internal/runtime/test/JDK_8078414_Test.java	Mon Nov 09 14:03:37 2015 +0100
+++ b/nashorn/test/src/jdk/nashorn/internal/runtime/test/JDK_8078414_Test.java	Mon Nov 09 14:04:43 2015 +0100
@@ -32,9 +32,15 @@
 import javax.script.Bindings;
 import jdk.nashorn.api.scripting.JSObject;
 import jdk.nashorn.api.scripting.ScriptObjectMirror;
+import jdk.nashorn.internal.objects.Global;
 import jdk.nashorn.internal.objects.NativeArray;
+import jdk.nashorn.internal.runtime.Context;
+import jdk.nashorn.internal.runtime.ErrorManager;
 import jdk.nashorn.internal.runtime.ScriptObject;
 import jdk.nashorn.internal.runtime.linker.Bootstrap;
+import jdk.nashorn.internal.runtime.options.Options;
+import org.testng.annotations.AfterClass;
+import org.testng.annotations.BeforeClass;
 import org.testng.annotations.Test;
 
 /**
@@ -44,6 +50,24 @@
  * @run testng jdk.nashorn.internal.runtime.test.JDK_8078414_Test
  */
 public class JDK_8078414_Test {
+    private static Context cx;
+    private static Global oldGlobal;
+
+    @BeforeClass
+    public static void beforeClass() {
+        // We must have a Context for the DynamicLinker that Bootstrap.getLinkerServices() will use
+        oldGlobal = Context.getGlobal();
+        cx = new Context(new Options(""), new ErrorManager(), null);
+        Context.setGlobal(cx.createGlobal());
+    }
+
+    @AfterClass
+    public static void afterClass() {
+        Context.setGlobal(oldGlobal);
+        oldGlobal = null;
+        cx = null;
+    }
+
     @Test
     public void testCanNotConvertArbitraryClassToMirror() {
         assertCanNotConvert(Double.class, Map.class);