RevealDirectTest.java revision 8729:0242fce0f717
1/*
2 * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23
24/*
25 * @test
26 * @summary verify Lookup.revealDirect on a variety of input handles
27 * @compile -XDignore.symbol.file RevealDirectTest.java
28 * @run junit/othervm -ea -esa test.java.lang.invoke.RevealDirectTest
29 *
30 * @test
31 * @summary verify Lookup.revealDirect on a variety of input handles, with security manager
32 * @run main/othervm/policy=jtreg.security.policy/secure=java.lang.SecurityManager -ea -esa test.java.lang.invoke.RevealDirectTest
33 */
34
35/* To run manually:
36 * $ $JAVA8X_HOME/bin/javac -cp $JUNIT4_JAR -d ../../../.. -XDignore.symbol.file RevealDirectTest.java
37 * $ $JAVA8X_HOME/bin/java  -cp $JUNIT4_JAR:../../../.. -ea -esa org.junit.runner.JUnitCore test.java.lang.invoke.RevealDirectTest
38 * $ $JAVA8X_HOME/bin/java  -cp $JUNIT4_JAR:../../../.. -ea -esa    -Djava.security.manager test.java.lang.invoke.RevealDirectTest
39 */
40
41package test.java.lang.invoke;
42
43import java.lang.reflect.*;
44import java.lang.invoke.*;
45import static java.lang.invoke.MethodHandles.*;
46import static java.lang.invoke.MethodType.*;
47import static java.lang.invoke.MethodHandleInfo.*;
48import java.util.*;
49import static org.junit.Assert.*;
50import org.junit.*;
51
52public class RevealDirectTest {
53    public static void main(String... av) throws Throwable {
54        // Run the @Test methods explicitly, in case we don't want to use the JUnitCore driver.
55        // This appears to be necessary when running with a security manager.
56        Throwable fail = null;
57        for (Method test : RevealDirectTest.class.getDeclaredMethods()) {
58            if (!test.isAnnotationPresent(Test.class))  continue;
59            try {
60                test.invoke(new RevealDirectTest());
61            } catch (Throwable ex) {
62                if (ex instanceof InvocationTargetException)
63                    ex = ex.getCause();
64                if (fail == null)  fail = ex;
65                System.out.println("Testcase: "+test.getName()
66                                   +"("+test.getDeclaringClass().getName()
67                                   +"):\tCaused an ERROR");
68                System.out.println(ex);
69                ex.printStackTrace(System.out);
70            }
71        }
72        if (fail != null)  throw fail;
73    }
74
75    public interface SimpleSuperInterface {
76        public abstract int getInt();
77        public static void printAll(String... args) {
78            System.out.println(Arrays.toString(args));
79        }
80        public int NICE_CONSTANT = 42;
81    }
82    public interface SimpleInterface extends SimpleSuperInterface {
83        default float getFloat() { return getInt(); }
84        public static void printAll(String[] args) {
85            System.out.println(Arrays.toString(args));
86        }
87    }
88    public static class Simple implements SimpleInterface, Cloneable {
89        public int intField;
90        public final int finalField;
91        private static String stringField;
92        public int getInt() { return NICE_CONSTANT; }
93        private static Number getNum() { return 804; }
94        public Simple clone() {
95            try {
96                return (Simple) super.clone();
97            } catch (CloneNotSupportedException ex) {
98                throw new RuntimeException(ex);
99            }
100        }
101        Simple() { finalField = -NICE_CONSTANT; }
102        private static Lookup localLookup() { return lookup(); }
103        private static List<Member> members() { return getMembers(lookup().lookupClass()); };
104    }
105    static class Nestmate {
106        private static Lookup localLookup() { return lookup(); }
107    }
108
109    static boolean VERBOSE = false;
110
111    @Test public void testSimple() throws Throwable {
112        if (VERBOSE)  System.out.println("@Test testSimple");
113        testOnMembers("testSimple", Simple.members(), Simple.localLookup());
114    }
115    @Test public void testPublicLookup() throws Throwable {
116        if (VERBOSE)  System.out.println("@Test testPublicLookup");
117        List<Member> mems = publicOnly(Simple.members());
118        Lookup pubLookup = publicLookup(), privLookup = Simple.localLookup();
119        testOnMembers("testPublicLookup/1", mems, pubLookup);
120        // reveal using publicLookup:
121        testOnMembers("testPublicLookup/2", mems, privLookup, pubLookup);
122        // lookup using publicLookup, but reveal using private:
123        testOnMembers("testPublicLookup/3", mems, pubLookup, privLookup);
124    }
125    @Test public void testPublicLookupNegative() throws Throwable {
126        if (VERBOSE)  System.out.println("@Test testPublicLookupNegative");
127        List<Member> mems = nonPublicOnly(Simple.members());
128        Lookup pubLookup = publicLookup(), privLookup = Simple.localLookup();
129        testOnMembersNoLookup("testPublicLookupNegative/1", mems, pubLookup);
130        testOnMembersNoReveal("testPublicLookupNegative/2", mems, privLookup, pubLookup);
131        testOnMembersNoReflect("testPublicLookupNegative/3", mems, privLookup, pubLookup);
132    }
133    @Test public void testJavaLangClass() throws Throwable {
134        if (VERBOSE)  System.out.println("@Test testJavaLangClass");
135        List<Member> mems = callerSensitive(false, publicOnly(getMembers(Class.class)));
136        mems = limit(20, mems);
137        testOnMembers("testJavaLangClass", mems, Simple.localLookup());
138    }
139    @Test public void testCallerSensitive() throws Throwable {
140        if (VERBOSE)  System.out.println("@Test testCallerSensitive");
141        List<Member> mems = union(getMembers(MethodHandles.class, "lookup"),
142                                  getMembers(Method.class, "invoke"),
143                                  getMembers(Field.class, "get", "set", "getLong"),
144                                  getMembers(Class.class));
145        mems = callerSensitive(true, publicOnly(mems));
146        mems = limit(10, mems);
147        testOnMembers("testCallerSensitive", mems, Simple.localLookup());
148    }
149    @Test public void testCallerSensitiveNegative() throws Throwable {
150        if (VERBOSE)  System.out.println("@Test testCallerSensitiveNegative");
151        List<Member> mems = union(getMembers(MethodHandles.class, "lookup"),
152                                  getMembers(Class.class, "forName"),
153                                  getMembers(Method.class, "invoke"));
154        mems = callerSensitive(true, publicOnly(mems));
155        // CS methods cannot be looked up with publicLookup
156        testOnMembersNoLookup("testCallerSensitiveNegative/1", mems, publicLookup());
157        // CS methods have to be revealed with a matching lookupClass
158        testOnMembersNoReveal("testCallerSensitiveNegative/2", mems, Simple.localLookup(), publicLookup());
159        testOnMembersNoReveal("testCallerSensitiveNegative/3", mems, Simple.localLookup(), Nestmate.localLookup());
160    }
161    @Test public void testMethodHandleNatives() throws Throwable {
162        if (VERBOSE)  System.out.println("@Test testMethodHandleNatives");
163        List<Member> mems = getMembers(MethodHandle.class, "invoke", "invokeExact");
164        testOnMembers("testMethodHandleNatives", mems, Simple.localLookup());
165    }
166    @Test public void testMethodHandleInvokes() throws Throwable {
167        if (VERBOSE)  System.out.println("@Test testMethodHandleInvokes");
168        List<MethodType> types = new ArrayList<>();
169        Class<?>[] someParamTypes = { void.class, int.class, Object.class, Object[].class };
170        for (Class<?> rt : someParamTypes) {
171            for (Class<?> p0 : someParamTypes) {
172                if (p0 == void.class) { types.add(methodType(rt)); continue; }
173                for (Class<?> p1 : someParamTypes) {
174                    if (p1 == void.class) { types.add(methodType(rt, p0)); continue; }
175                    for (Class<?> p2 : someParamTypes) {
176                        if (p2 == void.class) { types.add(methodType(rt, p0, p1)); continue; }
177                        types.add(methodType(rt, p0, p1, p2));
178                    }
179                }
180            }
181        }
182        List<Member> mems = union(getPolyMembers(MethodHandle.class, "invoke", types),
183                                  getPolyMembers(MethodHandle.class, "invokeExact", types));
184        testOnMembers("testMethodHandleInvokes/1", mems, Simple.localLookup());
185        testOnMembers("testMethodHandleInvokes/2", mems, publicLookup());
186    }
187
188    static List<Member> getPolyMembers(Class<?> cls, String name, List<MethodType> types) {
189        assert(cls == MethodHandle.class);
190        ArrayList<Member> mems = new ArrayList<>();
191        for (MethodType type : types) {
192            mems.add(new SignaturePolymorphicMethod(name, type));
193        }
194        return mems;
195    }
196    static List<Member> getMembers(Class<?> cls) {
197        return getMembers(cls, (String[]) null);
198    }
199    static List<Member> getMembers(Class<?> cls, String... onlyNames) {
200        List<String> names = (onlyNames == null || onlyNames.length == 0 ? null : Arrays.asList(onlyNames));
201        ArrayList<Member> res = new ArrayList<>();
202        for (Class<?> sup : getSupers(cls)) {
203            res.addAll(getDeclaredMembers(sup, "getDeclaredFields"));
204            res.addAll(getDeclaredMembers(sup, "getDeclaredMethods"));
205            res.addAll(getDeclaredMembers(sup, "getDeclaredConstructors"));
206        }
207        res = new ArrayList<>(new LinkedHashSet<>(res));
208        for (int i = 0; i < res.size(); i++) {
209            Member mem = res.get(i);
210            if (!canBeReached(mem, cls) ||
211                res.indexOf(mem) != i ||
212                mem.isSynthetic() ||
213                (names != null && !names.contains(mem.getName()))
214                ) {
215                res.remove(i--);
216            }
217        }
218        return res;
219    }
220    static List<Class<?>> getSupers(Class<?> cls) {
221        ArrayList<Class<?>> res = new ArrayList<>();
222        ArrayList<Class<?>> intfs = new ArrayList<>();
223        for (Class<?> sup = cls; sup != null; sup = sup.getSuperclass()) {
224            res.add(sup);
225            for (Class<?> intf : cls.getInterfaces()) {
226                if (!intfs.contains(intf))
227                    intfs.add(intf);
228            }
229        }
230        for (int i = 0; i < intfs.size(); i++) {
231            for (Class<?> intf : intfs.get(i).getInterfaces()) {
232                if (!intfs.contains(intf))
233                    intfs.add(intf);
234            }
235        }
236        res.addAll(intfs);
237        //System.out.println("getSupers => "+res);
238        return res;
239    }
240    static boolean hasSM() {
241        return (System.getSecurityManager() != null);
242    }
243    static List<Member> getDeclaredMembers(Class<?> cls, String accessor) {
244        Member[] mems = {};
245        Method getter = getMethod(Class.class, accessor);
246        if (hasSM()) {
247            try {
248                mems = (Member[]) invokeMethod(getter, cls);
249            } catch (SecurityException ex) {
250                //if (VERBOSE)  ex.printStackTrace();
251                accessor = accessor.replace("Declared", "");
252                getter = getMethod(Class.class, accessor);
253                if (VERBOSE)  System.out.println("replaced accessor: "+getter);
254            }
255        }
256        if (mems.length == 0) {
257            try {
258                mems = (Member[]) invokeMethod(getter, cls);
259            } catch (SecurityException ex) {
260                ex.printStackTrace();
261            }
262        }
263        if (VERBOSE)  System.out.println(accessor+" "+cls.getName()+" => "+mems.length+" members");
264        return Arrays.asList(mems);
265    }
266    static Method getMethod(Class<?> cls, String name) {
267        try {
268            return cls.getMethod(name);
269        } catch (ReflectiveOperationException ex) {
270            throw new AssertionError(ex);
271        }
272    }
273    static Object invokeMethod(Method m, Object recv, Object... args) {
274        try {
275            return m.invoke(recv, args);
276        } catch (InvocationTargetException ex) {
277            Throwable ex2 = ex.getCause();
278            if (ex2 instanceof RuntimeException)  throw (RuntimeException) ex2;
279            if (ex2 instanceof Error)  throw (Error) ex2;
280            throw new AssertionError(ex);
281        } catch (ReflectiveOperationException ex) {
282            throw new AssertionError(ex);
283        }
284    }
285
286    static List<Member> limit(int len, List<Member> mems) {
287        if (mems.size() <= len)  return mems;
288        return mems.subList(0, len);
289    }
290    @SafeVarargs
291    static List<Member> union(List<Member> mems, List<Member>... mem2s) {
292        for (List<Member> mem2 : mem2s) {
293            for (Member m : mem2) {
294                if (!mems.contains(m))
295                    mems.add(m);
296            }
297        }
298        return mems;
299    }
300    static List<Member> callerSensitive(boolean cond, List<Member> members) {
301        for (Iterator<Member> i = members.iterator(); i.hasNext(); ) {
302            Member mem = i.next();
303            if (isCallerSensitive(mem) != cond)
304                i.remove();
305        }
306        if (members.isEmpty())  throw new AssertionError("trivial result");
307        return members;
308    }
309    static boolean isCallerSensitive(Member mem) {
310        if (!(mem instanceof AnnotatedElement))  return false;
311        AnnotatedElement ae = (AnnotatedElement) mem;
312        if (CS_CLASS != null)
313            return ae.isAnnotationPresent(sun.reflect.CallerSensitive.class);
314        for (java.lang.annotation.Annotation a : ae.getDeclaredAnnotations()) {
315            if (a.toString().contains(".CallerSensitive"))
316                return true;
317        }
318        return false;
319    }
320    static final Class<?> CS_CLASS;
321    static {
322        Class<?> c = null;
323        try {
324            c = sun.reflect.CallerSensitive.class;
325        } catch (SecurityException | LinkageError ex) {
326        }
327        CS_CLASS = c;
328    }
329    static List<Member> publicOnly(List<Member> members) {
330        return removeMods(members, Modifier.PUBLIC, 0);
331    }
332    static List<Member> nonPublicOnly(List<Member> members) {
333        return removeMods(members, Modifier.PUBLIC, -1);
334    }
335    static List<Member> removeMods(List<Member> members, int mask, int bits) {
336        int publicMods = (mask & Modifier.PUBLIC);
337        members = new ArrayList<>(members);
338        for (Iterator<Member> i = members.iterator(); i.hasNext(); ) {
339            Member mem = i.next();
340            int mods = mem.getModifiers();
341            if ((publicMods & mods) != 0 &&
342                (publicMods & mem.getDeclaringClass().getModifiers()) == 0)
343                mods -= publicMods;
344            if ((mods & mask) == (bits & mask))
345                i.remove();
346        }
347        return members;
348    }
349
350    void testOnMembers(String tname, List<Member> mems, Lookup lookup, Lookup... lookups) throws Throwable {
351        if (VERBOSE)  System.out.println("testOnMembers "+mems);
352        Lookup revLookup = (lookups.length > 0) ? lookups[0] : null;
353        if (revLookup == null)  revLookup = lookup;
354        Lookup refLookup = (lookups.length > 1) ? lookups[1] : null;
355        if (refLookup == null)  refLookup = lookup;
356        assert(lookups.length <= 2);
357        testOnMembersImpl(tname, mems, lookup, revLookup, refLookup, NO_FAIL);
358    }
359    void testOnMembersNoLookup(String tname, List<Member> mems, Lookup lookup) throws Throwable {
360        if (VERBOSE)  System.out.println("testOnMembersNoLookup "+mems);
361        testOnMembersImpl(tname, mems, lookup, null, null, FAIL_LOOKUP);
362    }
363    void testOnMembersNoReveal(String tname, List<Member> mems,
364                               Lookup lookup, Lookup negLookup) throws Throwable {
365        if (VERBOSE)  System.out.println("testOnMembersNoReveal "+mems);
366        testOnMembersImpl(tname, mems, lookup, negLookup, null, FAIL_REVEAL);
367    }
368    void testOnMembersNoReflect(String tname, List<Member> mems,
369                                Lookup lookup, Lookup negLookup) throws Throwable {
370        if (VERBOSE)  System.out.println("testOnMembersNoReflect "+mems);
371        testOnMembersImpl(tname, mems, lookup, lookup, negLookup, FAIL_REFLECT);
372    }
373    void testOnMembersImpl(String tname, List<Member> mems,
374                           Lookup lookup,
375                           Lookup revLookup,
376                           Lookup refLookup,
377                           int failureMode) throws Throwable {
378        Throwable fail = null;
379        int failCount = 0;
380        failureModeCounts = new int[FAIL_MODE_COUNT];
381        long tm0 = System.currentTimeMillis();
382        for (Member mem : mems) {
383            try {
384                testWithMember(mem, lookup, revLookup, refLookup, failureMode);
385            } catch (Throwable ex) {
386                if (fail == null)  fail = ex;
387                if (++failCount > 10) { System.out.println("*** FAIL: too many failures"); break; }
388                System.out.println("*** FAIL: "+mem+" => "+ex);
389                if (VERBOSE)  ex.printStackTrace(System.out);
390            }
391        }
392        long tm1 = System.currentTimeMillis();
393        System.out.printf("@Test %s executed %s tests in %d ms",
394                          tname, testKinds(failureModeCounts), (tm1-tm0)).println();
395        if (fail != null)  throw fail;
396    }
397    static String testKinds(int[] modes) {
398        int pos = modes[0], neg = -pos;
399        for (int n : modes)  neg += n;
400        if (neg == 0)  return pos + " positive";
401        String negs = "";
402        for (int n : modes)  negs += "/"+n;
403        negs = negs.replaceFirst("/"+pos+"/", "");
404        negs += " negative";
405        if (pos == 0)  return negs;
406        return pos + " positive, " + negs;
407    }
408    static class SignaturePolymorphicMethod implements Member {  // non-reflected instance of MH.invoke*
409        final String name;
410        final MethodType type;
411        SignaturePolymorphicMethod(String name, MethodType type) {
412            this.name = name;
413            this.type = type;
414        }
415        public String toString() {
416            String typeStr = type.toString();
417            if (isVarArgs())  typeStr = typeStr.replaceFirst("\\[\\])$", "...)");
418            return (Modifier.toString(getModifiers())
419                    +typeStr.substring(0, typeStr.indexOf('('))+" "
420                    +getDeclaringClass().getTypeName()+"."
421                    +getName()+typeStr.substring(typeStr.indexOf('(')));
422        }
423        public boolean equals(Object x) {
424            return (x instanceof SignaturePolymorphicMethod && equals((SignaturePolymorphicMethod)x));
425        }
426        public boolean equals(SignaturePolymorphicMethod that) {
427            return this.name.equals(that.name) && this.type.equals(that.type);
428        }
429        public int hashCode() {
430            return name.hashCode() * 31 + type.hashCode();
431        }
432        public Class<?> getDeclaringClass() { return MethodHandle.class; }
433        public String getName() { return name; }
434        public MethodType getMethodType() { return type; }
435        public int getModifiers() { return Modifier.PUBLIC | Modifier.FINAL | Modifier.NATIVE | SYNTHETIC; }
436        public boolean isVarArgs() { return Modifier.isTransient(getModifiers()); }
437        public boolean isSynthetic() { return true; }
438        public Class<?> getReturnType() { return type.returnType(); }
439        public Class<?>[] getParameterTypes() { return type.parameterArray(); }
440        static final int SYNTHETIC = 0x00001000;
441    }
442    static class UnreflectResult {  // a tuple
443        final MethodHandle mh;
444        final Throwable ex;
445        final byte kind;
446        final Member mem;
447        final int var;
448        UnreflectResult(MethodHandle mh, byte kind, Member mem, int var) {
449            this.mh = mh;
450            this.ex = null;
451            this.kind = kind;
452            this.mem = mem;
453            this.var = var;
454        }
455        UnreflectResult(Throwable ex, byte kind, Member mem, int var) {
456            this.mh = null;
457            this.ex = ex;
458            this.kind = kind;
459            this.mem = mem;
460            this.var = var;
461        }
462        public String toString() {
463            return toInfoString()+"/v"+var;
464        }
465        public String toInfoString() {
466            return String.format("%s %s.%s:%s", MethodHandleInfo.referenceKindToString(kind),
467                                 mem.getDeclaringClass().getName(), name(mem), type(mem, kind));
468        }
469        static String name(Member mem) {
470            if (mem instanceof Constructor)  return "<init>";
471            return mem.getName();
472        }
473        static MethodType type(Member mem, byte kind) {
474            if (mem instanceof Field) {
475                Class<?> type = ((Field)mem).getType();
476                if (kind == REF_putStatic || kind == REF_putField)
477                    return methodType(void.class, type);
478                return methodType(type);
479            } else if (mem instanceof SignaturePolymorphicMethod) {
480                return ((SignaturePolymorphicMethod)mem).getMethodType();
481            }
482            Class<?>[] params = ((Executable)mem).getParameterTypes();
483            if (mem instanceof Constructor)
484                return methodType(void.class, params);
485            Class<?> type = ((Method)mem).getReturnType();
486            return methodType(type, params);
487        }
488    }
489    static UnreflectResult unreflectMember(Lookup lookup, Member mem, int variation) {
490        byte[] refKind = {0};
491        try {
492            return unreflectMemberOrThrow(lookup, mem, variation, refKind);
493        } catch (ReflectiveOperationException|SecurityException ex) {
494            return new UnreflectResult(ex, refKind[0], mem, variation);
495        }
496    }
497    static UnreflectResult unreflectMemberOrThrow(Lookup lookup, Member mem, int variation,
498                                                  byte[] refKind) throws ReflectiveOperationException {
499        Class<?> cls = lookup.lookupClass();
500        Class<?> defc = mem.getDeclaringClass();
501        String   name = mem.getName();
502        int      mods = mem.getModifiers();
503        boolean isStatic = Modifier.isStatic(mods);
504        MethodHandle mh = null;
505        byte kind = 0;
506        if (mem instanceof Method) {
507            Method m = (Method) mem;
508            MethodType type = methodType(m.getReturnType(), m.getParameterTypes());
509            boolean canBeSpecial = (!isStatic &&
510                                    (lookup.lookupModes() & Modifier.PRIVATE) != 0 &&
511                                    defc.isAssignableFrom(cls) &&
512                                    (!defc.isInterface() || Arrays.asList(cls.getInterfaces()).contains(defc)));
513            if (variation >= 2)
514                kind = REF_invokeSpecial;
515            else if (isStatic)
516                kind = REF_invokeStatic;
517            else if (defc.isInterface())
518                kind = REF_invokeInterface;
519            else
520                kind = REF_invokeVirtual;
521            refKind[0] = kind;
522            switch (variation) {
523            case 0:
524                mh = lookup.unreflect(m);
525                break;
526            case 1:
527                if (defc == MethodHandle.class &&
528                    !isStatic &&
529                    m.isVarArgs() &&
530                    Modifier.isFinal(mods) &&
531                    Modifier.isNative(mods)) {
532                    break;
533                }
534                if (isStatic)
535                    mh = lookup.findStatic(defc, name, type);
536                else
537                    mh = lookup.findVirtual(defc, name, type);
538                break;
539            case 2:
540                if (!canBeSpecial)
541                    break;
542                mh = lookup.unreflectSpecial(m, lookup.lookupClass());
543                break;
544            case 3:
545                if (!canBeSpecial)
546                    break;
547                mh = lookup.findSpecial(defc, name, type, lookup.lookupClass());
548                break;
549            }
550        } else if (mem instanceof SignaturePolymorphicMethod) {
551            SignaturePolymorphicMethod m = (SignaturePolymorphicMethod) mem;
552            MethodType type = methodType(m.getReturnType(), m.getParameterTypes());
553            kind = REF_invokeVirtual;
554            refKind[0] = kind;
555            switch (variation) {
556            case 0:
557                mh = lookup.findVirtual(defc, name, type);
558                break;
559            }
560        } else if (mem instanceof Constructor) {
561            name = "<init>";  // not used
562            Constructor<?> m = (Constructor<?>) mem;
563            MethodType type = methodType(void.class, m.getParameterTypes());
564            kind = REF_newInvokeSpecial;
565            refKind[0] = kind;
566            switch (variation) {
567            case 0:
568                mh = lookup.unreflectConstructor(m);
569                break;
570            case 1:
571                mh = lookup.findConstructor(defc, type);
572                break;
573            }
574        } else if (mem instanceof Field) {
575            Field m = (Field) mem;
576            Class<?> type = m.getType();
577            boolean canHaveSetter = !Modifier.isFinal(mods);
578            if (variation >= 2)
579                kind = (byte)(isStatic ? REF_putStatic : REF_putField);
580            else
581                kind = (byte)(isStatic ? REF_getStatic : REF_getField);
582            refKind[0] = kind;
583            switch (variation) {
584            case 0:
585                mh = lookup.unreflectGetter(m);
586                break;
587            case 1:
588                if (isStatic)
589                    mh = lookup.findStaticGetter(defc, name, type);
590                else
591                    mh = lookup.findGetter(defc, name, type);
592                break;
593            case 3:
594                if (!canHaveSetter)
595                    break;
596                mh = lookup.unreflectSetter(m);
597                break;
598            case 2:
599                if (!canHaveSetter)
600                    break;
601                if (isStatic)
602                    mh = lookup.findStaticSetter(defc, name, type);
603                else
604                    mh = lookup.findSetter(defc, name, type);
605                break;
606            }
607        } else {
608            throw new IllegalArgumentException(String.valueOf(mem));
609        }
610        if (mh == null)
611            // ran out of valid variations; return null to caller
612            return null;
613        return new UnreflectResult(mh, kind, mem, variation);
614    }
615    static boolean canBeReached(Member mem, Class<?> cls) {
616        Class<?> defc = mem.getDeclaringClass();
617        String   name = mem.getName();
618        int      mods = mem.getModifiers();
619        if (mem instanceof Constructor) {
620            name = "<init>";  // according to 292 spec.
621        }
622        if (defc == cls)
623            return true;
624        if (name.startsWith("<"))
625            return false;  // only my own constructors
626        if (Modifier.isPrivate(mods))
627            return false;  // only my own constructors
628        if (defc.getPackage() == cls.getPackage())
629            return true;   // package access or greater OK
630        if (Modifier.isPublic(mods))
631            return true;   // publics always OK
632        if (Modifier.isProtected(mods) && defc.isAssignableFrom(cls))
633            return true;   // protected OK
634        return false;
635    }
636    static boolean consistent(UnreflectResult res, MethodHandleInfo info) {
637        assert(res.mh != null);
638        assertEquals(res.kind, info.getReferenceKind());
639        assertEquals(res.mem.getModifiers(), info.getModifiers());
640        assertEquals(res.mem.getDeclaringClass(), info.getDeclaringClass());
641        String expectName = res.mem.getName();
642        if (res.kind == REF_newInvokeSpecial)
643            expectName = "<init>";
644        assertEquals(expectName, info.getName());
645        MethodType expectType = res.mh.type();
646        if ((res.kind & 1) == (REF_getField & 1))
647            expectType = expectType.dropParameterTypes(0, 1);
648        if (res.kind == REF_newInvokeSpecial)
649            expectType = expectType.changeReturnType(void.class);
650        assertEquals(expectType, info.getMethodType());
651        assertEquals(res.mh.isVarargsCollector(), isVarArgs(info));
652        assertEquals(res.toInfoString(), info.toString());
653        assertEquals(res.toInfoString(), MethodHandleInfo.toString(info.getReferenceKind(), info.getDeclaringClass(), info.getName(), info.getMethodType()));
654        return true;
655    }
656    static boolean isVarArgs(MethodHandleInfo info) {
657        return info.isVarArgs();
658    }
659    static boolean consistent(Member mem, Member mem2) {
660        assertEquals(mem, mem2);
661        return true;
662    }
663    static boolean consistent(MethodHandleInfo info, MethodHandleInfo info2) {
664        assertEquals(info.getReferenceKind(), info2.getReferenceKind());
665        assertEquals(info.getModifiers(), info2.getModifiers());
666        assertEquals(info.getDeclaringClass(), info2.getDeclaringClass());
667        assertEquals(info.getName(), info2.getName());
668        assertEquals(info.getMethodType(), info2.getMethodType());
669        assertEquals(isVarArgs(info), isVarArgs(info));
670        return true;
671    }
672    static boolean consistent(MethodHandle mh, MethodHandle mh2) {
673        assertEquals(mh.type(), mh2.type());
674        assertEquals(mh.isVarargsCollector(), mh2.isVarargsCollector());
675        return true;
676    }
677    int[] failureModeCounts;
678    static final int NO_FAIL=0, FAIL_LOOKUP=1, FAIL_REVEAL=2, FAIL_REFLECT=3, FAIL_MODE_COUNT=4;
679    void testWithMember(Member mem,
680                        Lookup lookup,      // initial lookup of member => MH
681                        Lookup revLookup,   // reveal MH => info
682                        Lookup refLookup,   // reflect info => member
683                        int failureMode) throws Throwable {
684        boolean expectEx1 = (failureMode == FAIL_LOOKUP);   // testOnMembersNoLookup
685        boolean expectEx2 = (failureMode == FAIL_REVEAL);   // testOnMembersNoReveal
686        boolean expectEx3 = (failureMode == FAIL_REFLECT);  // testOnMembersNoReflect
687        for (int variation = 0; ; variation++) {
688            UnreflectResult res = unreflectMember(lookup, mem, variation);
689            failureModeCounts[failureMode] += 1;
690            if (variation == 0)  assert(res != null);
691            if (res == null)  break;
692            if (VERBOSE && variation == 0)
693                System.out.println("from "+mem.getDeclaringClass().getSimpleName());
694            MethodHandle mh = res.mh;
695            Throwable   ex1 = res.ex;
696            if (VERBOSE)  System.out.println("  "+variation+": "+res+"  << "+(mh != null ? mh : ex1));
697            if (expectEx1 && ex1 != null)
698                continue;  // this is OK; we expected that lookup to fail
699            if (expectEx1)
700                throw new AssertionError("unexpected lookup for negative test");
701            if (ex1 != null && !expectEx1) {
702                if (failureMode != NO_FAIL)
703                    throw new AssertionError("unexpected lookup failure for negative test", ex1);
704                throw ex1;
705            }
706            MethodHandleInfo info;
707            try {
708                info = revLookup.revealDirect(mh);
709                if (expectEx2)  throw new AssertionError("unexpected revelation for negative test");
710            } catch (IllegalArgumentException|SecurityException ex2) {
711                if (VERBOSE)  System.out.println("  "+variation+": "+res+" => "+mh.getClass().getName()+" => (EX2)"+ex2);
712                if (expectEx2)
713                    continue;  // this is OK; we expected the reflect to fail
714                if (failureMode != NO_FAIL)
715                    throw new AssertionError("unexpected revelation failure for negative test", ex2);
716                throw ex2;
717            }
718            assert(consistent(res, info));
719            Member mem2;
720            try {
721                mem2 = info.reflectAs(Member.class, refLookup);
722                if (expectEx3)  throw new AssertionError("unexpected reflection for negative test");
723                assert(!(mem instanceof SignaturePolymorphicMethod));
724            } catch (IllegalArgumentException ex3) {
725                if (VERBOSE)  System.out.println("  "+variation+": "+info+" => (EX3)"+ex3);
726                if (expectEx3)
727                    continue;  // this is OK; we expected the reflect to fail
728                if (mem instanceof SignaturePolymorphicMethod)
729                    continue;  // this is OK; we cannot reflect MH.invokeExact(a,b,c)
730                if (failureMode != NO_FAIL)
731                    throw new AssertionError("unexpected reflection failure for negative test", ex3);
732                throw ex3;
733            }
734            assert(consistent(mem, mem2));
735            UnreflectResult res2 = unreflectMember(lookup, mem2, variation);
736            MethodHandle mh2 = res2.mh;
737            assert(consistent(mh, mh2));
738            MethodHandleInfo info2 = lookup.revealDirect(mh2);
739            assert(consistent(info, info2));
740            assert(consistent(res, info2));
741            Member mem3;
742            if (hasSM())
743                mem3 = info2.reflectAs(Member.class, lookup);
744            else
745                mem3 = MethodHandles.reflectAs(Member.class, mh2);
746            assert(consistent(mem2, mem3));
747            if (hasSM()) {
748                try {
749                    MethodHandles.reflectAs(Member.class, mh2);
750                    throw new AssertionError("failed to throw on "+mem3);
751                } catch (SecurityException ex3) {
752                    // OK...
753                }
754            }
755        }
756    }
757}
758