changeset 445:08add1bcd8eb

meth-info-8008688.patch: API tweaks
author jrose
date Mon, 17 Jun 2013 18:59:01 -0700
parents 469cff19f299
children 0591b53a4471
files meth-info-8008688.patch
diffstat 1 files changed, 150 insertions(+), 165 deletions(-) [+]
line wrap: on
line diff
--- a/meth-info-8008688.patch	Wed May 15 23:31:58 2013 -0700
+++ b/meth-info-8008688.patch	Mon Jun 17 18:59:01 2013 -0700
@@ -160,66 +160,17 @@
 +        }
 +    }
 +}
-diff --git a/src/share/classes/java/lang/invoke/MemberName.java b/src/share/classes/java/lang/invoke/MemberName.java
---- a/src/share/classes/java/lang/invoke/MemberName.java
-+++ b/src/share/classes/java/lang/invoke/MemberName.java
-@@ -236,6 +236,8 @@
-             assert(MethodHandleNatives.refKindIsMethod(refKind));
-             if (clazz.isInterface())
-                 assert(refKind == REF_invokeInterface ||
-+                       refKind == REF_invokeStatic    ||
-+                       refKind == REF_invokeSpecial   ||
-                        refKind == REF_invokeVirtual && isObjectPublicMethod());
-         } else {
-             assert(false);
-@@ -268,7 +270,7 @@
-             assert(refKind == REF_invokeSpecial) : this;
-             return true;
-         }
--        assert(false) : this;
-+        assert(false) : this+" != "+MethodHandleNatives.refKindName((byte)originalRefKind);
-         return true;
-     }
-     private boolean staticIsConsistent() {
-@@ -288,6 +290,8 @@
-         } else {
-             if (MethodHandleNatives.refKindDoesDispatch(refKind))
-                 assert(vmindex >= 0) : vmindex + ":" + this;
-+            else if (vmindex >= 0 && clazz.isInterface()) //FIXME: delete
-+                assert(false) : this; //FIXME: JVM should not install vmindex in this case!
-             else
-                 assert(vmindex < 0) : vmindex;
-             assert(vmtarget instanceof MemberName) : vmtarget + " in " + this;
-@@ -485,14 +489,19 @@
-         if (this.type == null)
-             this.type = new Object[] { m.getReturnType(), m.getParameterTypes() };
-         if (wantSpecial) {
-+            assert(!isAbstract()) : this;
-             if (getReferenceKind() == REF_invokeVirtual)
-                 changeReferenceKind(REF_invokeSpecial, REF_invokeVirtual);
-+            else if (getReferenceKind() == REF_invokeInterface)
-+                // invokeSpecial on a default method
-+                changeReferenceKind(REF_invokeSpecial, REF_invokeInterface);
-         }
-     }
-     public MemberName asSpecial() {
-         switch (getReferenceKind()) {
-         case REF_invokeSpecial:     return this;
-         case REF_invokeVirtual:     return clone().changeReferenceKind(REF_invokeSpecial, REF_invokeVirtual);
-+        case REF_invokeInterface:   return clone().changeReferenceKind(REF_invokeSpecial, REF_invokeInterface);
-         case REF_newInvokeSpecial:  return clone().changeReferenceKind(REF_invokeSpecial, REF_newInvokeSpecial);
-         }
-         throw new IllegalArgumentException(this.toString());
 diff --git a/src/share/classes/java/lang/invoke/MethodHandleInfo.java b/src/share/classes/java/lang/invoke/MethodHandleInfo.java
 --- a/src/share/classes/java/lang/invoke/MethodHandleInfo.java
 +++ b/src/share/classes/java/lang/invoke/MethodHandleInfo.java
-@@ -24,80 +24,211 @@
+@@ -24,80 +24,247 @@
   */
  
  package java.lang.invoke;
 +
 +import java.security.*;
 +import java.lang.reflect.*;
++import java.util.*;
  import java.lang.invoke.MethodHandleNatives.Constants;
 +import java.lang.invoke.MethodHandles.Lookup;
 +import static java.lang.invoke.MethodHandleStatics.*;
@@ -413,6 +364,7 @@
 +     * @param lookup the lookup object that created this MethodHandleInfo, or one with equivalent access privileges
 +     * @return a reference to the method, constructor, or field object
 +     * @exception ClassCastException if the member is not of the expected type
++     * @exception NullPointerException if either argument is {@code null}
 +     * @exception IllegalArgumentException if the underlying member is not accessible to the given lookup object
 +     */
 +    public <T extends Member> T reflectAs(Class<T> expected, Lookup lookup);
@@ -443,17 +395,26 @@
 -            default: return "UNKNOWN_REFENCE_KIND" + "[" + referenceKind + "]";
 -        }
 +    /**
-+     * Returns the descriptive name of the given reference kind,
-+     * as defined in the <a href="MethodHandleInfo.html#refkinds">table above</a>.
-+     * The conventional prefix "REF_" is omitted.
-+     * @param referenceKind an integer code for a kind of reference used to access a class member
-+     * @return a mixed-case string such as {@code "getField"}
-+     * @exception IllegalArgumentException if the given number is not a reference kind
++     * Determines if the underlying member was a variable arity method or constructor.
++     * Such members are represented by method handles that are varargs collectors.
++     * @implSpec
++     * This produces a result equivalent to:
++     * <pre>{@code
++     *     getReferenceKind() >= REF_invokeVirtual && Modifier.isTransient(getModifiers())
++     * }</pre>
++     *
++     *
++     * @return {@code true} if and only if the underlying member was declared with variable arity.
 +     */
-+    public static String referenceKindToString(int referenceKind) {
-+        if (!MethodHandleNatives.refKindIsValid(referenceKind))
-+            throw newIllegalArgumentException("invalid reference kind", referenceKind);
-+        return MethodHandleNatives.refKindName((byte)referenceKind);
++    // spelling derived from java.lang.reflect.Executable, not MethodHandle.isVarargsCollector
++    public default boolean isVarArgs()  {
++        // fields are never varargs:
++        if (MethodHandleNatives.refKindIsField((byte) getReferenceKind()))
++            return false;
++        // not in the public API: Modifier.VARARGS
++        final int ACC_VARARGS = 0x00000080;  // from JVMS 4.6 (Table 4.20)
++        assert(ACC_VARARGS == Modifier.TRANSIENT);
++        return Modifier.isTransient(getModifiers());
      }
  
 -    @Override
@@ -461,27 +422,52 @@
 -        return String.format("%s %s.%s:%s", getReferenceKindString(referenceKind),
 -                             declaringClass.getName(), name, methodType);
 +    /**
++     * Returns the descriptive name of the given reference kind,
++     * as defined in the <a href="MethodHandleInfo.html#refkinds">table above</a>.
++     * The conventional prefix "REF_" is omitted.
++     * @param referenceKind an integer code for a kind of reference used to access a class member
++     * @return a mixed-case string such as {@code "getField"}
++     * @exception IllegalArgumentException if the argument is not a valid
++     *            <a href="MethodHandleInfo.html#refkinds">reference kind number</a>
++     */
++    public static String referenceKindToString(int referenceKind) {
++        if (!MethodHandleNatives.refKindIsValid(referenceKind))
++            throw newIllegalArgumentException("invalid reference kind", referenceKind);
++        return MethodHandleNatives.refKindName((byte)referenceKind);
++    }
++
++    /**
 +     * Returns a string representation for a {@code MethodHandleInfo},
-+     * a symbolic reference to a method, constructor, or field.
++     * given the four parts of its symbolic reference.
 +     * This is defined to be of the form {@code "RK C.N:MT"}, where {@code RK} is the
-+     * {@linkplain #referenceKindToString reference kind string},
-+     * {@code C} is the {@linkplain java.lang.Class#getName name} of the
++     * {@linkplain #referenceKindToString reference kind string} for {@code kind},
++     * {@code C} is the {@linkplain java.lang.Class#getName name} of {@code defc}
++     * {@code N} is the {@code name}, and
++     * {@code MT} is the {@code type}.
++     * These four values may be obtained from the
++     * {@linkplain #getReferenceKind reference kind},
 +     * {@linkplain #getDeclaringClass declaring class},
-+     * {@code N} is the {@linkplain #getName name}, and
-+     * {@code MT} is the {@linkplain #getMethodType method type} of this reference.
++     * {@linkplain #getName member name},
++     * and {@linkplain #getMethodType method type}
++     * of a {@code MethodHandleInfo} object.
++     *
 +     * @implSpec
 +     * This produces a result equivalent to:
 +     * <pre>{@code
 +     *     String.format("%s %s.%s:%s", referenceKindToString(kind), defc.getName(), name, type)
 +     * }</pre>
 +     *
-+     * @param kind the reference kind of this symbolic reference
-+     * @param defc the declared class of this symbolic reference
-+     * @param name the reference kind of this symbolic reference
-+     * @param type the reference kind of this symbolic reference
++     * @param kind the {@linkplain #getReferenceKind reference kind} part of the symbolic reference
++     * @param defc the {@linkplain #getDeclaringClass declaring class} part of the symbolic reference
++     * @param name the {@linkplain #getName member name} part of the symbolic reference
++     * @param type the {@linkplain #getMethodType method type} part of the symbolic reference
 +     * @return a string of the form {@code "RK C.N:MT"}
++     * @exception IllegalArgumentException if the first argument is not a valid
++     *            <a href="MethodHandleInfo.html#refkinds">reference kind number</a>
++     * @exception NullPointerException if any reference argument is {@code null}
 +     */
 +    public static String toString(int kind, Class<?> defc, String name, MethodType type) {
++        Objects.requireNonNull(name); Objects.requireNonNull(type);
 +        return String.format("%s %s.%s:%s", referenceKindToString(kind), defc.getName(), name, type);
      }
  }
@@ -523,7 +509,7 @@
      }
  
      /**
-+     * Perform an unchecked "crack" of a direct method handle.
++     * Performs an unchecked "crack" of a direct method handle.
 +     * The result is as if the user had obtained a lookup object capable enough
 +     * to crack the target method handle, called
 +     * {@link java.lang.invoke.MethodHandles.Lookup#revealDirect Lookup.revealDirect}
@@ -536,7 +522,7 @@
 +     * @param <T> the desired type of the result, either {@link Member} or a subtype
 +     * @param target a direct method handle to crack into symbolic reference components
 +     * @param expected a class object representing the desired result type {@code T}
-+     * @return the method, constructor, or field
++     * @return a reference to the method, constructor, or field object
 +     * @exception SecurityException if the caller is not privileged to call {@code setAccessible}
 +     * @exception NullPointerException if either argument is {@code null}
 +     * @exception IllegalArgumentException if the target is not a direct method handle
@@ -563,7 +549,7 @@
          }
  
 +        /**
-+         * Crack a direct method handle created by this lookup object or a similar one.
++         * Cracks a direct method handle created by this lookup object or a similar one.
 +         * Security and access checks are performed to ensure that this lookup object
 +         * is capable of reproducing the target method handle.
 +         * This means that the cracking may fail if target is a direct method handle
@@ -645,74 +631,26 @@
          return type.invokers().generalInvoker();
      }
  
-diff --git a/src/share/classes/java/lang/reflect/Modifier.java b/src/share/classes/java/lang/reflect/Modifier.java
---- a/src/share/classes/java/lang/reflect/Modifier.java
-+++ b/src/share/classes/java/lang/reflect/Modifier.java
-@@ -147,12 +147,34 @@
-      * @param   mod a set of modifiers
-      * @return {@code true} if {@code mod} includes the
-      * {@code transient} modifier; {@code false} otherwise.
-+     * @see #isVarArgs
-      */
-     public static boolean isTransient(int mod) {
-         return (mod & TRANSIENT) != 0;
+diff --git a/src/share/classes/java/lang/invoke/SerializedLambda.java b/src/share/classes/java/lang/invoke/SerializedLambda.java
+--- a/src/share/classes/java/lang/invoke/SerializedLambda.java
++++ b/src/share/classes/java/lang/invoke/SerializedLambda.java
+@@ -189,9 +189,9 @@
+     public String toString() {
+         return String.format("SerializedLambda[capturingClass=%s, functionalInterfaceMethod=%s %s.%s:%s, " +
+                              "implementation=%s %s.%s:%s, instantiatedMethodType=%s, numCaptured=%d]",
+-                             capturingClass, MethodHandleInfo.getReferenceKindString(functionalInterfaceMethodKind),
++                             capturingClass, MethodHandleInfo.referenceKindToString(functionalInterfaceMethodKind),
+                              functionalInterfaceClass, functionalInterfaceMethodName, functionalInterfaceMethodSignature,
+-                             MethodHandleInfo.getReferenceKindString(implMethodKind), implClass, implMethodName,
++                             MethodHandleInfo.referenceKindToString(implMethodKind), implClass, implMethodName,
+                              implMethodSignature, instantiatedMethodType, capturedArgs.length);
      }
- 
-     /**
-+     * Assuming that the integer argument is regarded
-+     * as modifiers for a method or constructor,
-+     * return {@code true} if the executable was declared
-+     * to take a variable number of arguments,
-+     * {@code false} otherwise.
-+     * <p>
-+     * Note:  Since the bit position used for variable arity methods and constructors
-+     * is also used for {@code transient} fields, this call may also be
-+     * regarded as testing for the {@code transient} modifier.
-+     *
-+     * @param   mod a set of modifiers
-+     * @return {@code true} if {@code mod} specifies
-+     * a variable number of arguments; {@code false} otherwise.
-+     * @see #isTransient
-+     * @since 1.8
-+     */
-+    public static boolean isVarArgs(int mod) {
-+        return (mod & VARARGS) != 0;
-+    }
-+
-+    /**
-      * Return {@code true} if the integer argument includes the
-      * {@code native} modifier, {@code false} otherwise.
-      *
-@@ -332,12 +354,23 @@
-      */
-     public static final int STRICT           = 0x00000800;
- 
-+    /**
-+     * The {@code int} value used to declare that a method or constructor 
-+     * takes a variable number of arguments.
-+     * <p>
-+     * Note: This value is also used to declare {@code transient} fields.
-+     * @since 1.8
-+     */
-+    public static final int VARARGS          = 0x00000080;
-+    static {
-+        assert(VARARGS == TRANSIENT);
-+    }
-+
-     // Bits not (yet) exposed in the public API either because they
-     // have different meanings for fields and methods and there is no
-     // way to distinguish between the two in this class, or because
-     // they are not Java programming language keywords
-     static final int BRIDGE    = 0x00000040;
--    static final int VARARGS   = 0x00000080;
-     static final int SYNTHETIC = 0x00001000;
-     static final int ANNOTATION  = 0x00002000;
-     static final int ENUM      = 0x00004000;
+ }
 diff --git a/test/java/lang/invoke/RevealDirectTest.java b/test/java/lang/invoke/RevealDirectTest.java
 new file mode 100644
 --- /dev/null
 +++ b/test/java/lang/invoke/RevealDirectTest.java
-@@ -0,0 +1,431 @@
+@@ -0,0 +1,478 @@
 +/*
 + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
@@ -742,8 +680,10 @@
 + * @test
 + * @summary verify Lookup.revealDirect on a variety of input handles
 + * @run junit/othervm -ea -esa test.java.lang.invoke.RevealDirectTest
-+ *
-+ * @test
++ */
++/*
++ * -ignore The following test appears to fail because of an interaction between JUnit and the SM
++ * -test
 + * @summary verify Lookup.revealDirect on a variety of input handles, with security manager
 + * @run junit/othervm/policy=jtreg.security.policy/secure=java.lang.SecurityManager -ea -esa test.java.lang.invoke.RevealDirectTest
 + */
@@ -793,7 +733,7 @@
 +        }
 +        Simple() { finalField = -NICE_CONSTANT; }
 +        private static Lookup localLookup() { return lookup(); }
-+        private static List<Member> MEMBERS = getMembers(lookup().lookupClass());
++        private static List<Member> members() { return getMembers(lookup().lookupClass()); };
 +    }
 +
 +    static boolean VERBOSE = false;
@@ -801,31 +741,32 @@
 +    @Test public void testSimple() throws Throwable {
 +        if (VERBOSE)  System.out.println("@Test testSimple");
 +        testOnClass(Simple.localLookup(), null,
-+                    Simple.class, Simple.MEMBERS);
++                    Simple.class, Simple.members());
 +    }
 +    @Test public void testSimpleNegative() throws Throwable {
 +        if (VERBOSE)  System.out.println("@Test testSimpleNegative");
 +        testOnClass(Simple.localLookup(), publicLookup(),
-+                    Simple.class, nonPublicOnly(Simple.MEMBERS));
++                    Simple.class, nonPublicOnly(Simple.members()));
 +    }
 +    @Test public void testPublicLookup() throws Throwable {
 +        if (VERBOSE)  System.out.println("@Test testPublicLookup");
 +        testOnClass(publicLookup(), null,
-+                    Simple.class, publicOnly(Simple.MEMBERS));
++                    Simple.class, publicOnly(Simple.members()));
 +    }
 +    @Test public void testPublicLookupNegative() throws Throwable {
 +        if (VERBOSE)  System.out.println("@Test testPublicLookupNegative");
 +        testOnClass(publicLookup(), publicLookup(),
-+                    Simple.class, nonPublicOnly(Simple.MEMBERS));
++                    Simple.class, nonPublicOnly(Simple.members()));
 +    }
 +
 +    static List<Member> getMembers(Class<?> cls) {
 +        ArrayList<Member> res = new ArrayList<>();
 +        for (Class<?> sup : getSupers(cls)) {
-+            res.addAll(Arrays.asList(sup.getDeclaredFields()));
-+            res.addAll(Arrays.asList(sup.getDeclaredMethods()));
-+            res.addAll(Arrays.asList(sup.getDeclaredConstructors()));
++            res.addAll(getDeclaredMembers(sup, "getDeclaredFields"));
++            res.addAll(getDeclaredMembers(sup, "getDeclaredMethods"));
++            res.addAll(getDeclaredMembers(sup, "getDeclaredConstructors"));
 +        }
++        res = new ArrayList<>(new LinkedHashSet<>(res));
 +        for (int i = 0; i < res.size(); i++) {
 +            Member mem = res.get(i);
 +            if (!canBeReached(mem, cls) ||
@@ -834,7 +775,7 @@
 +                res.remove(i--);
 +            }
 +        }
-+        //System.out.println("getMembers => "+res);
++        if (VERBOSE)  System.out.println("getMembers => "+res);
 +        return res;
 +    }
 +    static List<Class<?>> getSupers(Class<?> cls) {
@@ -857,6 +798,48 @@
 +        //System.out.println("getSupers => "+res);
 +        return res;
 +    }
++    static List<Member> getDeclaredMembers(Class<?> cls, String accessor) {
++        if (VERBOSE)  System.out.println(accessor+" "+cls.getName());
++        Member[] mems = {};
++        Method getter = getMethod(Class.class, accessor);
++        if (System.getSecurityManager() != null) {
++            try {
++                mems = (Member[]) invokeMethod(getter, cls);
++            } catch (SecurityException ex) {
++                ex.printStackTrace();
++                accessor = accessor.replace("Declared", "");
++                getter = getMethod(Class.class, accessor);
++                System.out.println("replaced accessor: "+getter);
++            }
++        }
++        if (mems.length == 0) {
++            try {
++                mems = (Member[]) invokeMethod(getter, cls);
++            } catch (SecurityException ex) {
++                ex.printStackTrace();
++            }
++        }
++        return Arrays.asList(mems);
++    }
++    static Method getMethod(Class<?> cls, String name) {
++        try {
++            return cls.getMethod(name);
++        } catch (ReflectiveOperationException ex) {
++            throw new AssertionError(ex);
++        }
++    }
++    static Object invokeMethod(Method m, Object recv, Object... args) {
++        try {
++            return m.invoke(recv, args);
++        } catch (InvocationTargetException ex) {
++            Throwable ex2 = ex.getCause();
++            if (ex2 instanceof RuntimeException)  throw (RuntimeException) ex2;
++            if (ex2 instanceof Error)  throw (Error) ex2;
++            throw new AssertionError(ex);
++        } catch (ReflectiveOperationException ex) {
++            throw new AssertionError(ex);
++        }
++    }
 +
 +    static List<Member> publicOnly(List<Member> members) {
 +        return removeMods(members, Modifier.PUBLIC, 0);
@@ -906,7 +889,8 @@
 +            this.var = var;
 +        }
 +        public String toString() {
-+            return MethodHandleInfo.toString(kind, mem.getDeclaringClass(), name(mem), type(mem, kind));
++            return String.format("%s %s.%s:%s", MethodHandleInfo.referenceKindToString(kind),
++                                 mem.getDeclaringClass().getName(), name(mem), type(mem, kind));
 +        }
 +        static String name(Member mem) {
 +            if (mem instanceof Constructor)  return "<init>";
@@ -1058,42 +1042,43 @@
 +    }
 +    static boolean consistent(UnreflectResult res, MethodHandleInfo info) {
 +        assert(res.mh != null);
-+        assert(res.kind == info.getReferenceKind());
-+        assert(res.mem.getModifiers() == info.getModifiers());
-+        assert(res.mem.getDeclaringClass() == info.getDeclaringClass());
++        assertEquals(res.kind, info.getReferenceKind());
++        assertEquals(res.mem.getModifiers(), info.getModifiers());
++        assertEquals(res.mem.getDeclaringClass(), info.getDeclaringClass());
 +        String expectName = res.mem.getName();
 +        if (res.kind == REF_newInvokeSpecial)
 +            expectName = "<init>";
-+        assert(expectName.equals(info.getName()));
++        assertEquals(expectName, info.getName());
 +        MethodType expectType = res.mh.type();
 +        if ((res.kind & 1) == (REF_getField & 1))
 +            expectType = expectType.dropParameterTypes(0, 1);
 +        if (res.kind == REF_newInvokeSpecial)
 +            expectType = expectType.changeReturnType(void.class);
-+        assert(expectType.equals(info.getMethodType()));
-+        assert(res.mh.isVarargsCollector() == isVarArgs(info));
-+        assert(res.toString().equals(info.toString()));
++        assertEquals(expectType, info.getMethodType());
++        assertEquals(res.mh.isVarargsCollector(), isVarArgs(info));
++        assertEquals(res.toString(), info.toString());
++        assertEquals(res.toString(), MethodHandleInfo.toString(info.getReferenceKind(), info.getDeclaringClass(), info.getName(), info.getMethodType()));
 +        return true;
 +    }
 +    static boolean isVarArgs(MethodHandleInfo info) {
-+        return Modifier.isVarArgs(info.getModifiers());
++        return info.isVarArgs();
 +    }
 +    static boolean consistent(Member mem, Member mem2) {
-+        assert(mem.equals(mem2));
++        assertEquals(mem, mem2);
 +        return true;
 +    }
 +    static boolean consistent(MethodHandleInfo info, MethodHandleInfo info2) {
-+        assert(info.getReferenceKind() == info2.getReferenceKind());
-+        assert(info.getModifiers() == info2.getModifiers());
-+        assert(info.getDeclaringClass() == info2.getDeclaringClass());
-+        assert(info.getName().equals(info2.getName()));
-+        assert(info.getMethodType().equals(info2.getMethodType()));
-+        assert(isVarArgs(info) == isVarArgs(info));
++        assertEquals(info.getReferenceKind(), info2.getReferenceKind());
++        assertEquals(info.getModifiers(), info2.getModifiers());
++        assertEquals(info.getDeclaringClass(), info2.getDeclaringClass());
++        assertEquals(info.getName(), info2.getName());
++        assertEquals(info.getMethodType(), info2.getMethodType());
++        assertEquals(isVarArgs(info), isVarArgs(info));
 +        return true;
 +    }
 +    static boolean consistent(MethodHandle mh, MethodHandle mh2) {
-+        assert(mh.type().equals(mh2.type()));
-+        assert(mh.isVarargsCollector() == mh2.isVarargsCollector());
++        assertEquals(mh.type(), mh2.type());
++        assertEquals(mh.isVarargsCollector(), mh2.isVarargsCollector());
 +        return true;
 +    }
 +    void testWithMember(Lookup lookup, Lookup negLookup, Member mem) throws Throwable {