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 
  41 package test.java.lang.invoke;
  42 
  43 import java.lang.reflect.*;
  44 import java.lang.invoke.*;
  45 import static java.lang.invoke.MethodHandles.*;
  46 import static java.lang.invoke.MethodType.*;
  47 import static java.lang.invoke.MethodHandleInfo.*;
  48 import java.util.*;
  49 import static org.junit.Assert.*;
  50 import org.junit.*;
  51 
  52 public 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 }