changeset 5971:ac725d3230ec

Security tightening and documentation
author Robert Field <Robert.Field@oracle.com>
date Wed, 05 Sep 2012 08:19:59 -0700
parents 6bd7ae62d2bd
children d7f2c76afeda 2e6170973d92
files src/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java src/share/classes/java/lang/invoke/LambdaMetafactory.java src/share/classes/java/lang/invoke/MethodHandleInfo.java src/share/classes/java/lang/invoke/MethodHandleProxyLambdaMetafactory.java
diffstat 5 files changed, 78 insertions(+), 60 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java	Tue Sep 04 10:54:35 2012 -0700
+++ b/src/share/classes/java/lang/invoke/AbstractValidatingLambdaMetafactory.java	Wed Sep 05 08:19:59 2012 -0700
@@ -32,25 +32,35 @@
 import java.util.Set;
 
 /**
- * Abstract implementation of a meta-factory which provides parameter unrolling
- * and input validation.
+ * Abstract implementation of a meta-factory which provides parameter unrolling and input validation.
  *
  * @author Robert Field
  */
-abstract class AbstractValidatingLambdaMetafactory {
+/*non-public*/ abstract class AbstractValidatingLambdaMetafactory {
 
-    final Class<?> targetClass;
-    final MethodType invokedType;
-    final Class<?> samBase;
-    final boolean isSerializable;
-    final MethodHandleInfo samInfo;
-    final Class<?> samClass;
-    final MethodType samMethodType;
-    final MethodHandleInfo implInfo;
-    final boolean implIsInstanceMethod;
-    final MethodType implMethodType;
-    final Class<?> implDefiningClass;
-    final MethodType functionDescType;
+    /*
+     * For context, the comments for the following fields are marked in quotes with their values, given this program:
+     * interface II<T> {  Object foo(T x); }
+     * interface JJ<R extends Number> extends II<R> { }
+     * class CC {  String impl(int i) { return "impl:"+i; }}
+     * class X {
+     *     public static void main(String[] args) {
+     *         JJ<Integer> iii = (new CC())::impl;
+     *         System.out.printf(">>> %s\n", iii.foo(44));
+     * }}
+     */
+    final Class<?> targetClass;           // The class calling the meta-factory via invokedynamic "class X"
+    final MethodType invokedType;         // The type of the invoked method "(CC)II"
+    final Class<?> samBase;               // The type of the returned instance "interface JJ"
+    final boolean isSerializable;         // Should the returned instance be serializable
+    final MethodHandleInfo samInfo;       // Info about the SAM method handle "MethodHandleInfo[9 II.foo(Object)Object]"
+    final Class<?> samClass;              // Interface containing the SAM method "interface II"
+    final MethodType samMethodType;       // Type of the SAM method "(Object)Object"
+    final MethodHandleInfo implInfo;      // Info about the implementation method handle "MethodHandleInfo[5 CC.impl(int)String]"
+    final boolean implIsInstanceMethod;   // Is the implementation an instance method "true"
+    final Class<?> implDefiningClass;     // Type defining the implementation "class CC"
+    final MethodType implMethodType;      // Type of the implementation method "(int)String"
+    final MethodType functionDescType;    // Type of the functional interface "(Integer)Object"
 
     /**
      * Meta-factory constructor.
@@ -155,9 +165,9 @@
         final int capturedArity = invokedType.parameterCount();
         final int samArity = samMethodType.parameterCount();
         if (implArity + receiverArity != capturedArity + samArity) {
-            throw new LambdaConversionException(String.format("Incorrect number of parameters for %s method; %d captured parameters, %d functional interface parameters, %d implementation parameters",
-                                                              implIsInstanceMethod ? "instance" : "static",
-                                                              capturedArity, samArity, implArity));
+            throw new LambdaConversionException(String.format("Incorrect number of parameters for %s method %s; %d captured parameters, %d functional interface parameters, %d implementation parameters",
+                    implIsInstanceMethod ? "instance" : "static", implInfo,
+                    capturedArity, samArity, implArity));
         }
 
         // If instance: first captured arg (receiver) must be subtype of class where impl method is defined
@@ -223,6 +233,12 @@
         }
     }
 
+    /**
+     * Check type adaptability
+     * @param fromType
+     * @param toType
+     * @return True if 'fromType' can be converted to 'toType'
+     */
     private boolean isAdaptableTo(Class<?> fromType, Class<?> toType) {
         if (fromType.equals(toType)) {
             return true;
@@ -261,6 +277,12 @@
         }
     }
 
+    /**
+     * Check type adaptability for return types (special handling of void type)
+     * @param fromType
+     * @param toType
+     * @return True if 'fromType' can be converted to 'toType'
+     */
     private boolean isAdaptableToAsReturn(Class<?> fromType, Class<?> toType) {
         return toType.equals(void.class)
                || !fromType.equals(void.class) && isAdaptableTo(fromType, toType);
--- a/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java	Tue Sep 04 10:54:35 2012 -0700
+++ b/src/share/classes/java/lang/invoke/InnerClassLambdaMetafactory.java	Wed Sep 05 08:19:59 2012 -0700
@@ -43,7 +43,7 @@
 /**
  * InnerClassLambdaMetafactory
  */
-class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
+/*non-public*/ final class InnerClassLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
     private static final int CLASSFILE_VERSION = 51;
     private static final String DESCR_METHOD_WRITE_REPLACE = "()Ljava/lang/Object;";
     private static final String NAME_METHOD_WRITE_REPLACE = "writeReplace";
@@ -55,29 +55,26 @@
     private static final String NAME_SERIALIZED_LAMBDA = "com/oracle/java/lang/invoke/SerializedLambdaImpl";
     private static final String NAME_CTOR = "<init>";
 
-    private static final Unsafe unsafe = Unsafe.getUnsafe();
-
     // Used to ensure that each spun class name is unique
     private static final AtomicInteger counter = new AtomicInteger(0);
 
-    private final int implKind;
-    private final String implMethodClassName;
-    private final String implMethodName;
-    private final String implMethodDesc;
-    private final Type[] implMethodArgumentTypes;
-    private final Type implMethodReturnType;
-    private final MethodType constructorType;
-    private final ClassWriter cw;
-    private final String[] argNames;
-    private final Type[] argTypes;
-    private final String lambdaClassName;
-    private final String constructorDesc;
-    private final Type[] functionalArgumentTypes;
+    // See context values in AbstractValidatingLambdaMetafactory
+    private final int implKind;                      // Invocation kind for implementation "5"=invokevirtual
+    private final String implMethodClassName;        // Name of type containing implementation "CC"
+    private final String implMethodName;             // Name of implementation method "impl"
+    private final String implMethodDesc;             // Type descriptor for implementation methods "(I)Ljava/lang/String;"
+    private final Type[] implMethodArgumentTypes;    // ASM types for implementaion method parameters
+    private final Type implMethodReturnType;         // ASM type for implementaion method return type "Ljava/lang/String;"
+    private final MethodType constructorType;        // Generated class constructor type "(CC)void"
+    private final String constructorDesc;            // Type descriptor for constructor "(LCC;)V"
+    private final ClassWriter cw;                    // ASM class writer
+    private final Type[] argTypes;                   // ASM types for the constructor arguments
+    private final String[] argNames;                 // Generated names for the constructor arguments
+    private final String lambdaClassName;            // Generated name for the generated class "X$$Lambda$1"
+    private final Type[] functionalArgumentTypes;    // ASM types for the functional interface arguments
 
     /**
      * Meta-factory constructor.
-    private final String constructorDesc;
-    private final          String functionalMethodD
      *
      * @param caller Stacked automatically by VM; represents a lookup context with the accessibility privileges
      *               of the caller.
@@ -149,9 +146,9 @@
      *
      * @return a Class which implements the functional interface
      */
-     private <T> Class<? extends T> spinInnerClass() {
+    private <T> Class<? extends T> spinInnerClass() {
         String samMethodName = samInfo.getName();
- 
+
         Method[] methods = samBase.getMethods();
         String samName = samBase.getName().replace('.', '/');
         Type samType = Type.getType(samBase);
@@ -159,7 +156,7 @@
         cw.visit(CLASSFILE_VERSION, ACC_PUBLIC + ACC_SUPER, lambdaClassName, null, NAME_MAGIC_ACCESSOR_IMPL,
                  isSerializable ? new String[]{samName, NAME_SERIALIZABLE} : new String[]{samName});
 
-        // Generate fields
+        // Generate final fields to be filled in by constructor
         for (int i = 0; i < argTypes.length; i++) {
             FieldVisitor fv = cw.visitField(ACC_PRIVATE + ACC_FINAL, argNames[i], argTypes[i].getDescriptor(), null, null);
             fv.visitEnd();
@@ -220,7 +217,6 @@
         if (!methodsToBridge.isEmpty() /* && !defaultMethodFound*/) {
             for (Method m : methodsToBridge) {
                 generateForwardingMethod(m, true);
-
             }
         }
 
@@ -230,17 +226,10 @@
 
         cw.visitEnd();
 
-        return defineGeneratedClass(cw.toByteArray());
-    }
+        // Define the generated class in this VM.
 
-     /**
-      * Define the generated class in this VM.
-      *
-      * @param <T>
-      * @param classBytes The bytes of the class file
-      * @return the defined class.
-      */
-    private <T> Class<? extends T> defineGeneratedClass(final byte[] classBytes) {
+        final byte[] classBytes = cw.toByteArray();
+
         if (System.getProperty("debug.dump.generated") != null) {
             System.out.printf("Loaded: %s (%d bytes) %n", lambdaClassName, classBytes.length);
             try (FileOutputStream fos = new FileOutputStream(lambdaClassName.replace('/', '.') + ".class")) {
@@ -249,10 +238,10 @@
                 Logger.getLogger(InnerClassLambdaMetafactory.class.getName()).log(Level.SEVERE, null, ex);
             }
         }
-        
+
         ClassLoader loader = targetClass.getClassLoader();
-        ProtectionDomain pd = (loader == null)? null : targetClass.getProtectionDomain();
-        return (Class <? extends T>) unsafe.defineClass(lambdaClassName, classBytes, 0, classBytes.length, loader, pd);
+        ProtectionDomain pd = (loader == null) ? null : targetClass.getProtectionDomain();
+        return (Class<? extends T>) Unsafe.getUnsafe().defineClass(lambdaClassName, classBytes, 0, classBytes.length, loader, pd);
     }
 
     /**
--- a/src/share/classes/java/lang/invoke/LambdaMetafactory.java	Tue Sep 04 10:54:35 2012 -0700
+++ b/src/share/classes/java/lang/invoke/LambdaMetafactory.java	Wed Sep 05 08:19:59 2012 -0700
@@ -43,14 +43,16 @@
  * method, and the static types of the captured lambda arguments, and link a call site which, when invoked,
  * produces the lambda object.
  *
- * <p>While functional interfaces only have a single non-Object method from the language perspective, at the
- * bytecode level they may actually have multiple methods because of the need for bridge methods.  Invoking any
- * of these methods on the lambda object will result in invoking the implementation method.
+ * <p>While functional interfaces only have a single abstract method from the language perspective (concrete
+ * methods in Object are and default methods may be present), at the bytecode level they may actually have multiple
+ * methods because of the need for bridge methods. Invoking any of these methods on the lambda object will result
+ * in invoking the implementation method.
  *
  * <p>The argument list of the implementation method and the argument list of the functional interface method(s)
  * may differ in several ways.  The implementation methods may have additional arguments to accommodate arguments
  * captured by the lambda expression; there may also be differences resulting from permitted adaptations of
- * arguments, such as casting, boxing, unboxing, and primitive widening.
+ * arguments, such as casting, boxing, unboxing, and primitive widening. They may also differ because of var-args,
+ * but this is handled in the compiler by adapter methods for the implementation method.
  *
  * <p>Invokedynamic call sites have two argument lists: a static argument list and a dynamic argument list.  The
  * static argument list lives in the constant pool; the dynamic argument list lives on the operand stack at
--- a/src/share/classes/java/lang/invoke/MethodHandleInfo.java	Tue Sep 04 10:54:35 2012 -0700
+++ b/src/share/classes/java/lang/invoke/MethodHandleInfo.java	Wed Sep 05 08:19:59 2012 -0700
@@ -30,7 +30,7 @@
  * Cracking (reflecting) method handles back into their constituent symbolic parts. 
  * 
  */
-public class MethodHandleInfo {
+public final class MethodHandleInfo {
    public static final int
        REF_NONE                    = Constants.REF_NONE,
        REF_getField                = Constants.REF_getField,
@@ -74,5 +74,10 @@
 
     public int getReferenceKind() {
         return referenceKind;
-    }    
+    }
+
+    @Override
+    public String toString() {
+        return String.format("MethodHandleInfo[%d %s.%s%s]", referenceKind, declaringClass.getName(), name, methodType);
+    }
 }
--- a/src/share/classes/java/lang/invoke/MethodHandleProxyLambdaMetafactory.java	Tue Sep 04 10:54:35 2012 -0700
+++ b/src/share/classes/java/lang/invoke/MethodHandleProxyLambdaMetafactory.java	Wed Sep 05 08:19:59 2012 -0700
@@ -33,7 +33,7 @@
  *
  * @author Brian Goetz
  */
-class MethodHandleProxyLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
+/*non-public*/ final class MethodHandleProxyLambdaMetafactory extends AbstractValidatingLambdaMetafactory {
 
     private final MethodHandle implMethod;