8164483: Generate field lambda forms at link time
authorredestad
Wed, 24 Aug 2016 16:09:34 +0200
changeset 40542 e7be26f852fa
parent 40541 8850531a2beb
child 40543 629f1f599595
8164483: Generate field lambda forms at link time Reviewed-by: vlivanov
jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java
jdk/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java
jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java
jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java
--- a/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java	Wed Aug 24 13:54:17 2016 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/DirectMethodHandle.java	Wed Aug 24 16:09:34 2016 +0200
@@ -492,7 +492,7 @@
     }
 
     // Caching machinery for field accessors:
-    private static final byte
+    static final byte
             AF_GETFIELD        = 0,
             AF_PUTFIELD        = 1,
             AF_GETSTATIC       = 2,
@@ -502,7 +502,7 @@
             AF_LIMIT           = 6;
     // Enumerate the different field kinds using Wrapper,
     // with an extra case added for checked references.
-    private static final int
+    static final int
             FT_LAST_WRAPPER    = Wrapper.COUNT-1,
             FT_UNCHECKED_REF   = Wrapper.OBJECT.ordinal(),
             FT_CHECKED_REF     = FT_LAST_WRAPPER+1,
@@ -515,7 +515,7 @@
     @Stable
     private static final LambdaForm[] ACCESSOR_FORMS
             = new LambdaForm[afIndex(AF_LIMIT, false, 0)];
-    private static int ftypeKind(Class<?> ftype) {
+    static int ftypeKind(Class<?> ftype) {
         if (ftype.isPrimitive())
             return Wrapper.forPrimitiveType(ftype).ordinal();
         else if (VerifyType.isNullReferenceConversion(Object.class, ftype))
@@ -566,7 +566,64 @@
 
     private static final Wrapper[] ALL_WRAPPERS = Wrapper.values();
 
-    private static LambdaForm makePreparedFieldLambdaForm(byte formOp, boolean isVolatile, int ftypeKind) {
+    private static Kind getFieldKind(boolean isGetter, boolean isVolatile, Wrapper wrapper) {
+        if (isGetter) {
+            if (isVolatile) {
+                switch (wrapper) {
+                    case BOOLEAN: return GET_BOOLEAN_VOLATILE;
+                    case BYTE:    return GET_BYTE_VOLATILE;
+                    case SHORT:   return GET_SHORT_VOLATILE;
+                    case CHAR:    return GET_CHAR_VOLATILE;
+                    case INT:     return GET_INT_VOLATILE;
+                    case LONG:    return GET_LONG_VOLATILE;
+                    case FLOAT:   return GET_FLOAT_VOLATILE;
+                    case DOUBLE:  return GET_DOUBLE_VOLATILE;
+                    case OBJECT:  return GET_OBJECT_VOLATILE;
+                }
+            } else {
+                switch (wrapper) {
+                    case BOOLEAN: return GET_BOOLEAN;
+                    case BYTE:    return GET_BYTE;
+                    case SHORT:   return GET_SHORT;
+                    case CHAR:    return GET_CHAR;
+                    case INT:     return GET_INT;
+                    case LONG:    return GET_LONG;
+                    case FLOAT:   return GET_FLOAT;
+                    case DOUBLE:  return GET_DOUBLE;
+                    case OBJECT:  return GET_OBJECT;
+                }
+            }
+        } else {
+            if (isVolatile) {
+                switch (wrapper) {
+                    case BOOLEAN: return PUT_BOOLEAN_VOLATILE;
+                    case BYTE:    return PUT_BYTE_VOLATILE;
+                    case SHORT:   return PUT_SHORT_VOLATILE;
+                    case CHAR:    return PUT_CHAR_VOLATILE;
+                    case INT:     return PUT_INT_VOLATILE;
+                    case LONG:    return PUT_LONG_VOLATILE;
+                    case FLOAT:   return PUT_FLOAT_VOLATILE;
+                    case DOUBLE:  return PUT_DOUBLE_VOLATILE;
+                    case OBJECT:  return PUT_OBJECT_VOLATILE;
+                }
+            } else {
+                switch (wrapper) {
+                    case BOOLEAN: return PUT_BOOLEAN;
+                    case BYTE:    return PUT_BYTE;
+                    case SHORT:   return PUT_SHORT;
+                    case CHAR:    return PUT_CHAR;
+                    case INT:     return PUT_INT;
+                    case LONG:    return PUT_LONG;
+                    case FLOAT:   return PUT_FLOAT;
+                    case DOUBLE:  return PUT_DOUBLE;
+                    case OBJECT:  return PUT_OBJECT;
+                }
+            }
+        }
+        throw new AssertionError("Invalid arguments");
+    }
+
+    static LambdaForm makePreparedFieldLambdaForm(byte formOp, boolean isVolatile, int ftypeKind) {
         boolean isGetter  = (formOp & 1) == (AF_GETFIELD & 1);
         boolean isStatic  = (formOp >= AF_GETSTATIC);
         boolean needsInit = (formOp >= AF_GETSTATIC_INIT);
@@ -576,24 +633,14 @@
         assert(ftypeKind(needsCast ? String.class : ft) == ftypeKind);
 
         // getObject, putIntVolatile, etc.
-        StringBuilder nameBuilder = new StringBuilder();
-        if (isGetter) {
-            nameBuilder.append("get");
-        } else {
-            nameBuilder.append("put");
-        }
-        nameBuilder.append(fw.primitiveSimpleName());
-        nameBuilder.setCharAt(3, Character.toUpperCase(nameBuilder.charAt(3)));
-        if (isVolatile) {
-            nameBuilder.append("Volatile");
-        }
+        Kind kind = getFieldKind(isGetter, isVolatile, fw);
 
         MethodType linkerType;
         if (isGetter)
             linkerType = MethodType.methodType(ft, Object.class, long.class);
         else
             linkerType = MethodType.methodType(void.class, Object.class, long.class, ft);
-        MemberName linker = new MemberName(Unsafe.class, nameBuilder.toString(), linkerType, REF_invokeVirtual);
+        MemberName linker = new MemberName(Unsafe.class, kind.methodName, linkerType, REF_invokeVirtual);
         try {
             linker = IMPL_NAMES.resolveOrFail(REF_invokeVirtual, linker, null, NoSuchMethodException.class);
         } catch (ReflectiveOperationException ex) {
@@ -620,6 +667,7 @@
         final int F_HOLDER  = (isStatic ? nameCursor++ : -1);  // static base if any
         final int F_OFFSET  = nameCursor++;  // Either static offset or field offset.
         final int OBJ_CHECK = (OBJ_BASE >= 0 ? nameCursor++ : -1);
+        final int U_HOLDER  = nameCursor++;  // UNSAFE holder
         final int INIT_BAR  = (needsInit ? nameCursor++ : -1);
         final int PRE_CAST  = (needsCast && !isGetter ? nameCursor++ : -1);
         final int LINKER_CALL = nameCursor++;
@@ -632,7 +680,7 @@
             names[PRE_CAST] = new Name(NF_checkCast, names[DMH_THIS], names[SET_VALUE]);
         Object[] outArgs = new Object[1 + linkerType.parameterCount()];
         assert(outArgs.length == (isGetter ? 3 : 4));
-        outArgs[0] = UNSAFE;
+        outArgs[0] = names[U_HOLDER] = new Name(NF_UNSAFE);
         if (isStatic) {
             outArgs[1] = names[F_HOLDER]  = new Name(NF_staticBase, names[DMH_THIS]);
             outArgs[2] = names[F_OFFSET]  = new Name(NF_staticOffset, names[DMH_THIS]);
@@ -650,6 +698,7 @@
         for (Name n : names)  assert(n != null);
         // add some detail to the lambdaForm debugname,
         // significant only for debugging
+        StringBuilder nameBuilder = new StringBuilder(kind.methodName);
         if (isStatic) {
             nameBuilder.append("Static");
         } else {
@@ -657,7 +706,12 @@
         }
         if (needsCast)  nameBuilder.append("Cast");
         if (needsInit)  nameBuilder.append("Init");
-        return new LambdaForm(nameBuilder.toString(), ARG_LIMIT, names, RESULT);
+        if (needsCast || needsInit) {
+            // can't use the pre-generated form when casting and/or initializing
+            return new LambdaForm(nameBuilder.toString(), ARG_LIMIT, names, RESULT);
+        } else {
+            return new LambdaForm(nameBuilder.toString(), ARG_LIMIT, names, RESULT, kind);
+        }
     }
 
     /**
@@ -674,7 +728,8 @@
             NF_staticOffset,
             NF_checkCast,
             NF_allocateInstance,
-            NF_constructorMethod;
+            NF_constructorMethod,
+            NF_UNSAFE;
     static {
         try {
             NamedFunction nfs[] = {
@@ -697,7 +752,9 @@
                     NF_allocateInstance = new NamedFunction(DirectMethodHandle.class
                             .getDeclaredMethod("allocateInstance", Object.class)),
                     NF_constructorMethod = new NamedFunction(DirectMethodHandle.class
-                            .getDeclaredMethod("constructorMethod", Object.class))
+                            .getDeclaredMethod("constructorMethod", Object.class)),
+                    NF_UNSAFE = new NamedFunction(new MemberName(MethodHandleStatics.class
+                            .getDeclaredField("UNSAFE")))
             };
             // Each nf must be statically invocable or we get tied up in our bootstraps.
             assert(InvokerBytecodeGenerator.isStaticallyInvocable(nfs));
--- a/jdk/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java	Wed Aug 24 13:54:17 2016 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/GenerateJLIClassesHelper.java	Wed Aug 24 16:09:34 2016 +0200
@@ -28,9 +28,11 @@
 import java.util.Map;
 import jdk.internal.org.objectweb.asm.ClassWriter;
 import jdk.internal.org.objectweb.asm.Opcodes;
-
 import java.util.ArrayList;
 import java.util.HashSet;
+import sun.invoke.util.Wrapper;
+
+import static java.lang.invoke.MethodHandleNatives.Constants.*;
 
 /**
  * Helper class to assist the GenerateJLIClassesPlugin to get access to
@@ -66,14 +68,38 @@
 
     static byte[] generateDirectMethodHandleHolderClassBytes(String className,
             MethodType[] methodTypes, int[] types) {
-        LambdaForm[] forms = new LambdaForm[methodTypes.length];
-        String[] names = new String[methodTypes.length];
-        for (int i = 0; i < forms.length; i++) {
-            forms[i] = DirectMethodHandle.makePreparedLambdaForm(methodTypes[i],
-                                                                 types[i]);
-            names[i] = forms[i].kind.defaultLambdaName;
+        ArrayList<LambdaForm> forms = new ArrayList<>();
+        ArrayList<String> names = new ArrayList<>();
+        for (int i = 0; i < methodTypes.length; i++) {
+            LambdaForm form = DirectMethodHandle
+                    .makePreparedLambdaForm(methodTypes[i], types[i]);
+            forms.add(form);
+            names.add(form.kind.defaultLambdaName);
         }
-        return generateCodeBytesForLFs(className, names, forms);
+        for (Wrapper wrapper : Wrapper.values()) {
+            if (wrapper == Wrapper.VOID) {
+                continue;
+            }
+            for (byte b = DirectMethodHandle.AF_GETFIELD; b < DirectMethodHandle.AF_LIMIT; b++) {
+                int ftype = DirectMethodHandle.ftypeKind(wrapper.primitiveType());
+                LambdaForm form = DirectMethodHandle
+                        .makePreparedFieldLambdaForm(b, /*isVolatile*/false, ftype);
+                if (form.kind != LambdaForm.Kind.GENERIC) {
+                    forms.add(form);
+                    names.add(form.kind.defaultLambdaName);
+                }
+                // volatile
+                form = DirectMethodHandle
+                        .makePreparedFieldLambdaForm(b, /*isVolatile*/true, ftype);
+                if (form.kind != LambdaForm.Kind.GENERIC) {
+                    forms.add(form);
+                    names.add(form.kind.defaultLambdaName);
+                }
+            }
+        }
+        return generateCodeBytesForLFs(className,
+                names.toArray(new String[0]),
+                forms.toArray(new LambdaForm[0]));
     }
 
     static byte[] generateDelegatingMethodHandleHolderClassBytes(String className,
@@ -166,4 +192,5 @@
                          BoundMethodHandle.Factory.generateConcreteBMHClassBytes(
                                  shortTypes, types, className));
     }
+
 }
--- a/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Wed Aug 24 13:54:17 2016 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/InvokerBytecodeGenerator.java	Wed Aug 24 16:09:34 2016 +0200
@@ -629,6 +629,24 @@
                 name = name + "_" + form.returnType().basicTypeChar();
                 return resolveFrom(name, invokerType, LambdaForm.Holder.class);
             }
+            case GET_OBJECT:                // fall-through
+            case GET_BOOLEAN:               // fall-through
+            case GET_BYTE:                  // fall-through
+            case GET_CHAR:                  // fall-through
+            case GET_SHORT:                 // fall-through
+            case GET_INT:                   // fall-through
+            case GET_LONG:                  // fall-through
+            case GET_FLOAT:                 // fall-through
+            case GET_DOUBLE:                // fall-through
+            case PUT_OBJECT:                // fall-through
+            case PUT_BOOLEAN:               // fall-through
+            case PUT_BYTE:                  // fall-through
+            case PUT_CHAR:                  // fall-through
+            case PUT_SHORT:                 // fall-through
+            case PUT_INT:                   // fall-through
+            case PUT_LONG:                  // fall-through
+            case PUT_FLOAT:                 // fall-through
+            case PUT_DOUBLE:                // fall-through
             case DIRECT_INVOKE_INTERFACE:   // fall-through
             case DIRECT_INVOKE_SPECIAL:     // fall-through
             case DIRECT_INVOKE_STATIC:      // fall-through
--- a/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java	Wed Aug 24 13:54:17 2016 +0200
+++ b/jdk/src/java.base/share/classes/java/lang/invoke/LambdaForm.java	Wed Aug 24 16:09:34 2016 +0200
@@ -280,7 +280,43 @@
         DIRECT_INVOKE_STATIC("DMH.invokeStatic"),
         DIRECT_NEW_INVOKE_SPECIAL("DMH.newInvokeSpecial"),
         DIRECT_INVOKE_INTERFACE("DMH.invokeInterface"),
-        DIRECT_INVOKE_STATIC_INIT("DMH.invokeStaticInit");
+        DIRECT_INVOKE_STATIC_INIT("DMH.invokeStaticInit"),
+        GET_OBJECT("getObject"),
+        PUT_OBJECT("putObject"),
+        GET_OBJECT_VOLATILE("getObjectVolatile"),
+        PUT_OBJECT_VOLATILE("putObjectVolatile"),
+        GET_INT("getInt"),
+        PUT_INT("putInt"),
+        GET_INT_VOLATILE("getIntVolatile"),
+        PUT_INT_VOLATILE("putIntVolatile"),
+        GET_BOOLEAN("getBoolean"),
+        PUT_BOOLEAN("putBoolean"),
+        GET_BOOLEAN_VOLATILE("getBooleanVolatile"),
+        PUT_BOOLEAN_VOLATILE("putBooleanVolatile"),
+        GET_BYTE("getByte"),
+        PUT_BYTE("putByte"),
+        GET_BYTE_VOLATILE("getByteVolatile"),
+        PUT_BYTE_VOLATILE("putByteVolatile"),
+        GET_CHAR("getChar"),
+        PUT_CHAR("putChar"),
+        GET_CHAR_VOLATILE("getCharVolatile"),
+        PUT_CHAR_VOLATILE("putCharVolatile"),
+        GET_SHORT("getShort"),
+        PUT_SHORT("putShort"),
+        GET_SHORT_VOLATILE("getShortVolatile"),
+        PUT_SHORT_VOLATILE("putShortVolatile"),
+        GET_LONG("getLong"),
+        PUT_LONG("putLong"),
+        GET_LONG_VOLATILE("getLongVolatile"),
+        PUT_LONG_VOLATILE("putLongVolatile"),
+        GET_FLOAT("getFloat"),
+        PUT_FLOAT("putFloat"),
+        GET_FLOAT_VOLATILE("getFloatVolatile"),
+        PUT_FLOAT_VOLATILE("putFloatVolatile"),
+        GET_DOUBLE("getDouble"),
+        PUT_DOUBLE("putDouble"),
+        GET_DOUBLE_VOLATILE("getDoubleVolatile"),
+        PUT_DOUBLE_VOLATILE("putDoubleVolatile");
 
         final String defaultLambdaName;
         final String methodName;