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