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