rfield | 099afec | 2013-09-03 21:42:56 -0700 | [diff] [blame] | 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 | } |
jrose | 8347af8 | 2013-10-05 05:30:40 -0700 | [diff] [blame] | 107 | static class Nestmate { |
| 108 | private static Lookup localLookup() { return lookup(); } |
| 109 | } |
rfield | 099afec | 2013-09-03 21:42:56 -0700 | [diff] [blame] | 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 |
jrose | 8347af8 | 2013-10-05 05:30:40 -0700 | [diff] [blame] | 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()); |
rfield | 099afec | 2013-09-03 21:42:56 -0700 | [diff] [blame] | 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"); |
jrose | 8347af8 | 2013-10-05 05:30:40 -0700 | [diff] [blame] | 712 | } catch (IllegalArgumentException|SecurityException ex2) { |
rfield | 099afec | 2013-09-03 21:42:56 -0700 | [diff] [blame] | 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 | } |