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