8163373: Rewrite GenerateJLIClassesPlugin to avoid reflective calls into java.lang.invoke
authorredestad
Wed, 10 Aug 2016 21:55:25 +0200
changeset 40258 e6ba2e5c7a29
parent 40257 87b7b4c3306f
child 40259 439673b634a9
8163373: Rewrite GenerateJLIClassesPlugin to avoid reflective calls into java.lang.invoke Reviewed-by: vlivanov, mchung
jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java
jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java
jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java
jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java
jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangInvokeAccess.java
jdk/src/java.base/share/classes/jdk/internal/misc/SharedSecrets.java
jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java
--- a/jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java	Wed Aug 10 17:55:08 2016 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/BoundMethodHandle.java	Wed Aug 10 21:55:25 2016 +0200
@@ -587,26 +587,7 @@
             return bmhClass;
         }
 
-        /**
-         * @implNote this method is used by GenerateBMHClassesPlugin to enable
-         * ahead-of-time generation of BMH classes at link time. It does
-         * added validation since this string may be user provided.
-         */
-        static Map.Entry<String, byte[]> generateConcreteBMHClassBytes(
-                final String types) {
-            for (char c : types.toCharArray()) {
-                if ("LIJFD".indexOf(c) < 0) {
-                    throw new IllegalArgumentException("All characters must "
-                            + "correspond to a basic field type: LIJFD");
-                }
-            }
-            String shortTypes = LambdaForm.shortenSignature(types);
-            final String className  = speciesInternalClassName(shortTypes);
-            return Map.entry(className,
-                    generateConcreteBMHClassBytes(shortTypes, types, className));
-        }
-
-        private static String speciesInternalClassName(String shortTypes) {
+        static String speciesInternalClassName(String shortTypes) {
             return SPECIES_PREFIX_PATH + shortTypes;
         }
 
--- a/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java	Wed Aug 10 17:55:08 2016 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java	Wed Aug 10 21:55:25 2016 +0200
@@ -186,7 +186,7 @@
         return mtype.form().setCachedLambdaForm(which, lform);
     }
 
-    private static LambdaForm makePreparedLambdaForm(MethodType mtype, int which) {
+    static LambdaForm makePreparedLambdaForm(MethodType mtype, int which) {
         boolean needsInit = (which == LF_INVSTATIC_INIT);
         boolean doesAlloc = (which == LF_NEWINVSPECIAL);
         String linkerName, lambdaName;
@@ -248,20 +248,6 @@
         return lform;
     }
 
-    /*
-     * NOTE: This method acts as an API hook for use by the
-     * GenerateJLIClassesPlugin to generate a class wrapping DirectMethodHandle
-     * methods for an array of method types.
-     */
-    static byte[] generateDMHClassBytes(String className, MethodType[] methodTypes, int[] types) {
-        LambdaForm[] forms = new LambdaForm[methodTypes.length];
-        for (int i = 0; i < forms.length; i++) {
-            forms[i] = makePreparedLambdaForm(methodTypes[i], types[i]);
-            methodTypes[i] = forms[i].methodType();
-        }
-        return InvokerBytecodeGenerator.generateCodeBytesForMultiple(className, forms, methodTypes);
-    }
-
     static Object findDirectMethodHandle(Name name) {
         if (name.function == NF_internalMemberName ||
             name.function == NF_internalMemberNameEnsureInit ||
--- a/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Wed Aug 10 17:55:08 2016 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Wed Aug 10 21:55:25 2016 +0200
@@ -70,7 +70,7 @@
     private static final String LLV_SIG = "(L" + OBJ + ";L" + OBJ + ";)V";
 
     /** Name of its super class*/
-    private static final String INVOKER_SUPER_NAME = OBJ;
+    static final String INVOKER_SUPER_NAME = OBJ;
 
     /** Name of new class */
     private final String className;
@@ -124,7 +124,7 @@
     }
 
     /** For generating customized code for a single LambdaForm. */
-    private InvokerBytecodeGenerator(String className, LambdaForm form, MethodType invokerType) {
+    InvokerBytecodeGenerator(String className, LambdaForm form, MethodType invokerType) {
         this(form, form.names.length,
              className, form.debugName, invokerType);
         // Create an array to map name indexes to locals indexes.
@@ -655,35 +655,11 @@
         return classFile;
     }
 
-    /*
-     * NOTE: This is used from GenerateJLIClassesPlugin via
-     * DirectMethodHandle::generateDMHClassBytes.
-     *
-     * Generate customized code for a set of LambdaForms of specified types into
-     * a class with a specified name.
-     */
-    static byte[] generateCodeBytesForMultiple(String className,
-            LambdaForm[] forms, MethodType[] types) {
-        assert(forms.length == types.length);
-
-        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS + ClassWriter.COMPUTE_FRAMES);
-        cw.visit(Opcodes.V1_8, Opcodes.ACC_PRIVATE + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER,
-                className, null, INVOKER_SUPER_NAME, null);
-        cw.visitSource(className.substring(className.lastIndexOf('/') + 1), null);
-        for (int i = 0; i < forms.length; i++) {
-            InvokerBytecodeGenerator g
-                    = new InvokerBytecodeGenerator(className, forms[i], types[i]);
-            g.setClassWriter(cw);
-            g.addMethod();
-        }
-        return cw.toByteArray();
-    }
-
-    private void setClassWriter(ClassWriter cw) {
+    void setClassWriter(ClassWriter cw) {
         this.cw = cw;
     }
 
-    private void addMethod() {
+    void addMethod() {
         methodPrologue();
 
         // Suppress this method in backtraces displayed to the user.
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java	Wed Aug 10 17:55:08 2016 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MemberName.java	Wed Aug 10 21:55:25 2016 +0200
@@ -25,8 +25,6 @@
 
 package java.lang.invoke;
 
-import jdk.internal.misc.JavaLangInvokeAccess;
-import jdk.internal.misc.SharedSecrets;
 import sun.invoke.util.BytecodeDescriptor;
 import sun.invoke.util.VerifyAccess;
 
@@ -37,7 +35,6 @@
 import java.lang.reflect.Modifier;
 import java.lang.reflect.Module;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
@@ -1152,27 +1149,4 @@
             return buf;
         }
     }
-
-    static {
-        // StackFrameInfo stores Member and this provides the shared secrets
-        // for stack walker to access MemberName information.
-        SharedSecrets.setJavaLangInvokeAccess(new JavaLangInvokeAccess() {
-            @Override
-            public Object newMemberName() {
-                return new MemberName();
-            }
-
-            @Override
-            public String getName(Object mname) {
-                MemberName memberName = (MemberName)mname;
-                return memberName.getName();
-            }
-
-            @Override
-            public boolean isNative(Object mname) {
-                MemberName memberName = (MemberName)mname;
-                return memberName.isNative();
-            }
-        });
-    }
 }
--- a/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed Aug 10 17:55:08 2016 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/MethodHandleImpl.java	Wed Aug 10 21:55:25 2016 +0200
@@ -25,6 +25,8 @@
 
 package java.lang.invoke;
 
+import jdk.internal.misc.JavaLangInvokeAccess;
+import jdk.internal.misc.SharedSecrets;
 import jdk.internal.org.objectweb.asm.AnnotationVisitor;
 import jdk.internal.org.objectweb.asm.ClassWriter;
 import jdk.internal.org.objectweb.asm.MethodVisitor;
@@ -44,6 +46,7 @@
 import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
+import java.util.Map;
 import java.util.function.Function;
 import java.util.stream.Stream;
 
@@ -1710,6 +1713,39 @@
         } catch (ReflectiveOperationException ex) {
             throw newInternalError(ex);
         }
+
+        SharedSecrets.setJavaLangInvokeAccess(new JavaLangInvokeAccess() {
+            @Override
+            public Object newMemberName() {
+                return new MemberName();
+            }
+
+            @Override
+            public String getName(Object mname) {
+                MemberName memberName = (MemberName)mname;
+                return memberName.getName();
+            }
+
+            @Override
+            public boolean isNative(Object mname) {
+                MemberName memberName = (MemberName)mname;
+                return memberName.isNative();
+            }
+
+            @Override
+            public byte[] generateDMHClassBytes(String className,
+            MethodType[] methodTypes, int[] types) {
+                return GenerateJLIClassesHelper
+                        .generateDMHClassBytes(className, methodTypes, types);
+            }
+
+            @Override
+            public Map.Entry<String, byte[]> generateConcreteBMHClassBytes(
+                    final String types) {
+                return GenerateJLIClassesHelper
+                        .generateConcreteBMHClassBytes(types);
+            }
+        });
     }
 
     /** Result unboxing: ValueConversions.unbox() OR ValueConversions.identity() OR ValueConversions.ignore(). */
--- a/jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangInvokeAccess.java	Wed Aug 10 17:55:08 2016 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/misc/JavaLangInvokeAccess.java	Wed Aug 10 21:55:25 2016 +0200
@@ -25,19 +25,42 @@
 
 package jdk.internal.misc;
 
+import java.lang.invoke.MethodType;
+import java.util.Map;
+
 public interface JavaLangInvokeAccess {
     /**
-     * Create a new MemberName instance
+     * Create a new MemberName instance. Used by {@see StackFrameInfo}.
      */
     Object newMemberName();
 
     /**
-     * Returns the name for the given MemberName
+     * Returns the name for the given MemberName. Used by {@see StackFrameInfo}.
      */
     String getName(Object mname);
 
     /**
-     * Returns {@code true} if the given MemberName is a native method
+     * Returns {@code true} if the given MemberName is a native method. Used by
+     * {@see StackFrameInfo}.
      */
     boolean isNative(Object mname);
+
+    /**
+     * Returns a {@code byte[]} containing the bytecode for a class implementing
+     * DirectMethodHandle of each pairwise combination of {@code MethodType} and
+     * an {@code int} representing method type.  Used by
+     * GenerateJLIClassesPlugin to generate such a class during the jlink phase.
+     */
+    byte[] generateDMHClassBytes(String className, MethodType[] methodTypes,
+            int[] types);
+
+    /**
+     * Returns a {@code byte[]} containing the bytecode for a BoundMethodHandle
+     * species class implementing the signature defined by {@code types}. Used
+     * by GenerateBMHClassesPlugin to enable generation of such classes during
+     * the jlink phase. Should do some added validation since this string may be
+     * user provided.
+     */
+    Map.Entry<String, byte[]> generateConcreteBMHClassBytes(
+            final String types);
 }
--- a/jdk/src/java.base/share/classes/jdk/internal/misc/SharedSecrets.java	Wed Aug 10 17:55:08 2016 +0200
+++ b/jdk/src/java.base/share/classes/jdk/internal/misc/SharedSecrets.java	Wed Aug 10 21:55:25 2016 +0200
@@ -95,7 +95,7 @@
     public static JavaLangInvokeAccess getJavaLangInvokeAccess() {
         if (javaLangInvokeAccess == null) {
             try {
-                Class<?> c = Class.forName("java.lang.invoke.MemberName");
+                Class<?> c = Class.forName("java.lang.invoke.MethodHandleImpl");
                 unsafe.ensureClassInitialized(c);
             } catch (ClassNotFoundException e) {};
         }
--- a/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java	Wed Aug 10 17:55:08 2016 +0200
+++ b/jdk/src/jdk.jlink/share/classes/jdk/tools/jlink/internal/plugins/GenerateJLIClassesPlugin.java	Wed Aug 10 21:55:25 2016 +0200
@@ -25,7 +25,6 @@
 package jdk.tools.jlink.internal.plugins;
 
 import java.lang.invoke.MethodType;
-import java.lang.reflect.Method;
 import java.util.Arrays;
 import java.util.EnumSet;
 import java.util.HashMap;
@@ -33,6 +32,8 @@
 import java.util.Map;
 import java.util.Set;
 import java.util.stream.Collectors;
+import jdk.internal.misc.SharedSecrets;
+import jdk.internal.misc.JavaLangInvokeAccess;
 import jdk.tools.jlink.plugin.ResourcePoolEntry;
 import jdk.tools.jlink.plugin.PluginException;
 import jdk.tools.jlink.plugin.ResourcePool;
@@ -54,9 +55,6 @@
 
     private static final String DESCRIPTION = PluginsResourceBundle.getDescription(NAME);
 
-    private static final String BMH = "java/lang/invoke/BoundMethodHandle";
-    private static final Method BMH_FACTORY_METHOD;
-
     private static final String DMH = "java/lang/invoke/DirectMethodHandle$Holder";
     private static final String DMH_INVOKE_VIRTUAL = "invokeVirtual";
     private static final String DMH_INVOKE_STATIC = "invokeStatic";
@@ -64,7 +62,9 @@
     private static final String DMH_NEW_INVOKE_SPECIAL = "newInvokeSpecial";
     private static final String DMH_INVOKE_INTERFACE = "invokeInterface";
     private static final String DMH_INVOKE_STATIC_INIT = "invokeStaticInit";
-    private static final Method DMH_FACTORY_METHOD;
+
+    private static final JavaLangInvokeAccess JLIA
+            = SharedSecrets.getJavaLangInvokeAccess();
 
     List<String> speciesTypes;
 
@@ -232,8 +232,8 @@
     private void generateBMHClass(String types, ResourcePoolBuilder out) {
         try {
             // Generate class
-            Map.Entry<String, byte[]> result = (Map.Entry<String, byte[]>)
-                    BMH_FACTORY_METHOD.invoke(null, types);
+            Map.Entry<String, byte[]> result =
+                    JLIA.generateConcreteBMHClassBytes(types);
             String className = result.getKey();
             byte[] bytes = result.getValue();
 
@@ -264,11 +264,8 @@
             }
         }
         try {
-            byte[] bytes = (byte[])DMH_FACTORY_METHOD
-                    .invoke(null,
-                            DMH,
-                            methodTypes,
-                            dmhTypes);
+            byte[] bytes =
+                    JLIA.generateDMHClassBytes(DMH, methodTypes, dmhTypes);
             ResourcePoolEntry ndata = ResourcePoolEntry.create(DMH_ENTRY, bytes);
             out.add(ndata);
         } catch (Exception ex) {
@@ -277,22 +274,6 @@
     }
     private static final String DMH_ENTRY = "/java.base/" + DMH + ".class";
 
-    static {
-        try {
-            Class<?> BMHFactory = Class.forName("java.lang.invoke.BoundMethodHandle$Factory");
-            BMH_FACTORY_METHOD = BMHFactory.getDeclaredMethod("generateConcreteBMHClassBytes",
-                    String.class);
-            BMH_FACTORY_METHOD.setAccessible(true);
-
-            Class<?> DMHFactory = Class.forName("java.lang.invoke.DirectMethodHandle");
-            DMH_FACTORY_METHOD = DMHFactory.getDeclaredMethod("generateDMHClassBytes",
-                    String.class, MethodType[].class, int[].class);
-            DMH_FACTORY_METHOD.setAccessible(true);
-        } catch (Exception e) {
-            throw new PluginException(e);
-        }
-    }
-
     // Convert LL -> LL, L3 -> LLL
     private static String expandSignature(String signature) {
         StringBuilder sb = new StringBuilder();