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