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