8163373: Rewrite GenerateJLIClassesPlugin to avoid reflective calls into java.lang.invoke
Reviewed-by: vlivanov, mchung
--- 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();