|
1 /* |
|
2 * Copyright (c) 2009, 2011, 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 /* @test |
|
27 * @summary unit tests for java.lang.invoke.MethodHandles |
|
28 * @compile -source 7 -target 7 -XDallowTransitionalJSR292=no MethodHandlesTest.java |
|
29 * @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableMethodHandles test.java.lang.invoke.MethodHandlesTest |
|
30 */ |
|
31 |
|
32 package test.java.lang.invoke; |
|
33 |
|
34 import java.lang.invoke.*; |
|
35 import java.lang.invoke.MethodHandles.Lookup; |
|
36 import java.lang.reflect.*; |
|
37 import java.util.*; |
|
38 import org.junit.*; |
|
39 import static org.junit.Assert.*; |
|
40 import static org.junit.Assume.*; |
|
41 |
|
42 |
|
43 /** |
|
44 * |
|
45 * @author jrose |
|
46 */ |
|
47 public class MethodHandlesTest { |
|
48 // How much output? |
|
49 static int verbosity = 0; |
|
50 static { |
|
51 String vstr = System.getProperty("test.java.lang.invoke.MethodHandlesTest.verbosity"); |
|
52 if (vstr != null) verbosity = Integer.parseInt(vstr); |
|
53 } |
|
54 |
|
55 // Set this true during development if you want to fast-forward to |
|
56 // a particular new, non-working test. Tests which are known to |
|
57 // work (or have recently worked) test this flag and return on true. |
|
58 static boolean CAN_SKIP_WORKING = false; |
|
59 //static { CAN_SKIP_WORKING = true; } |
|
60 |
|
61 // Set true to test more calls. If false, some tests are just |
|
62 // lookups, without exercising the actual method handle. |
|
63 static boolean DO_MORE_CALLS = true; |
|
64 |
|
65 @Test |
|
66 public void testFirst() throws Throwable { |
|
67 verbosity += 9; try { |
|
68 // left blank for debugging |
|
69 } finally { printCounts(); verbosity -= 9; } |
|
70 } |
|
71 |
|
72 // current failures |
|
73 @Test @Ignore("failure in call to makeRawRetypeOnly in ToGeneric") |
|
74 public void testFail_1() throws Throwable { |
|
75 // AMH.<init>: IllegalArgumentException: bad adapter (conversion=0xfffab300): adapter pushes too many parameters |
|
76 testSpreadArguments(int.class, 0, 6); |
|
77 } |
|
78 @Test @Ignore("failure in JVM when expanding the stack using asm stub for _adapter_spread_args") |
|
79 public void testFail_2() throws Throwable { |
|
80 // if CONV_OP_IMPLEMENTED_MASK includes OP_SPREAD_ARGS, this crashes: |
|
81 testSpreadArguments(Object.class, 0, 2); |
|
82 } |
|
83 @Test @Ignore("IllArgEx failure in call to ToGeneric.make") |
|
84 public void testFail_3() throws Throwable { |
|
85 // ToGeneric.<init>: UnsupportedOperationException: NYI: primitive parameters must follow references; entryType = (int,java.lang.Object)java.lang.Object |
|
86 testSpreadArguments(int.class, 1, 2); |
|
87 } |
|
88 @Test @Ignore("IllArgEx failure in call to ToGeneric.make") |
|
89 public void testFail_4() throws Throwable { |
|
90 // ToGeneric.<init>: UnsupportedOperationException: NYI: primitive parameters must follow references; entryType = (int,java.lang.Object)java.lang.Object |
|
91 testCollectArguments(int.class, 1, 2); |
|
92 } |
|
93 @Test @Ignore("cannot collect leading primitive types") |
|
94 public void testFail_5() throws Throwable { |
|
95 // ToGeneric.<init>: UnsupportedOperationException: NYI: primitive parameters must follow references; entryType = (int,java.lang.Object)java.lang.Object |
|
96 testInvokers(MethodType.genericMethodType(2).changeParameterType(0, int.class)); |
|
97 } |
|
98 @Test @Ignore("should not insert arguments beyond MethodHandlePushLimit") |
|
99 public void testFail_6() throws Throwable { |
|
100 // ValueConversions.varargsArray: UnsupportedOperationException: NYI: cannot form a varargs array of length 13 |
|
101 testInsertArguments(0, 0, MAX_ARG_INCREASE+10); |
|
102 } |
|
103 static final int MAX_ARG_INCREASE = 3; |
|
104 |
|
105 public MethodHandlesTest() { |
|
106 } |
|
107 |
|
108 @Before |
|
109 public void checkImplementedPlatform() { |
|
110 boolean platformOK = false; |
|
111 Properties properties = System.getProperties(); |
|
112 String vers = properties.getProperty("java.vm.version"); |
|
113 String name = properties.getProperty("java.vm.name"); |
|
114 String arch = properties.getProperty("os.arch"); |
|
115 if ((arch.equals("amd64") || arch.equals("i386") || arch.equals("x86") || |
|
116 arch.equals("sparc") || arch.equals("sparcv9")) && |
|
117 (name.contains("Client") || name.contains("Server")) |
|
118 ) { |
|
119 platformOK = true; |
|
120 } else { |
|
121 System.err.println("Skipping tests for unsupported platform: "+Arrays.asList(vers, name, arch)); |
|
122 } |
|
123 assumeTrue(platformOK); |
|
124 } |
|
125 |
|
126 String testName; |
|
127 static int allPosTests, allNegTests; |
|
128 int posTests, negTests; |
|
129 @After |
|
130 public void printCounts() { |
|
131 if (verbosity >= 2 && (posTests | negTests) != 0) { |
|
132 System.out.println(); |
|
133 if (posTests != 0) System.out.println("=== "+testName+": "+posTests+" positive test cases run"); |
|
134 if (negTests != 0) System.out.println("=== "+testName+": "+negTests+" negative test cases run"); |
|
135 allPosTests += posTests; |
|
136 allNegTests += negTests; |
|
137 posTests = negTests = 0; |
|
138 } |
|
139 } |
|
140 void countTest(boolean positive) { |
|
141 if (positive) ++posTests; |
|
142 else ++negTests; |
|
143 } |
|
144 void countTest() { countTest(true); } |
|
145 void startTest(String name) { |
|
146 if (testName != null) printCounts(); |
|
147 if (verbosity >= 1) |
|
148 System.out.println(name); |
|
149 posTests = negTests = 0; |
|
150 testName = name; |
|
151 } |
|
152 |
|
153 @BeforeClass |
|
154 public static void setUpClass() throws Exception { |
|
155 calledLog.clear(); |
|
156 calledLog.add(null); |
|
157 nextArgVal = INITIAL_ARG_VAL; |
|
158 } |
|
159 |
|
160 @AfterClass |
|
161 public static void tearDownClass() throws Exception { |
|
162 int posTests = allPosTests, negTests = allNegTests; |
|
163 if (verbosity >= 2 && (posTests | negTests) != 0) { |
|
164 System.out.println(); |
|
165 if (posTests != 0) System.out.println("=== "+posTests+" total positive test cases"); |
|
166 if (negTests != 0) System.out.println("=== "+negTests+" total negative test cases"); |
|
167 } |
|
168 } |
|
169 |
|
170 static List<Object> calledLog = new ArrayList<Object>(); |
|
171 static Object logEntry(String name, Object... args) { |
|
172 return Arrays.asList(name, Arrays.asList(args)); |
|
173 } |
|
174 static Object called(String name, Object... args) { |
|
175 Object entry = logEntry(name, args); |
|
176 calledLog.add(entry); |
|
177 return entry; |
|
178 } |
|
179 static void assertCalled(String name, Object... args) { |
|
180 Object expected = logEntry(name, args); |
|
181 Object actual = calledLog.get(calledLog.size() - 1); |
|
182 if (expected.equals(actual) && verbosity < 9) return; |
|
183 System.out.println("assertCalled "+name+":"); |
|
184 System.out.println("expected: "+expected); |
|
185 System.out.println("actual: "+actual); |
|
186 System.out.println("ex. types: "+getClasses(expected)); |
|
187 System.out.println("act. types: "+getClasses(actual)); |
|
188 assertEquals("previous method call", expected, actual); |
|
189 } |
|
190 static void printCalled(MethodHandle target, String name, Object... args) { |
|
191 if (verbosity >= 3) |
|
192 System.out.println("calling MH="+target+" to "+name+Arrays.toString(args)); |
|
193 } |
|
194 |
|
195 static Object castToWrapper(Object value, Class<?> dst) { |
|
196 Object wrap = null; |
|
197 if (value instanceof Number) |
|
198 wrap = castToWrapperOrNull(((Number)value).longValue(), dst); |
|
199 if (value instanceof Character) |
|
200 wrap = castToWrapperOrNull((char)(Character)value, dst); |
|
201 if (wrap != null) return wrap; |
|
202 return dst.cast(value); |
|
203 } |
|
204 |
|
205 static Object castToWrapperOrNull(long value, Class<?> dst) { |
|
206 if (dst == int.class || dst == Integer.class) |
|
207 return (int)(value); |
|
208 if (dst == long.class || dst == Long.class) |
|
209 return (long)(value); |
|
210 if (dst == char.class || dst == Character.class) |
|
211 return (char)(value); |
|
212 if (dst == short.class || dst == Short.class) |
|
213 return (short)(value); |
|
214 if (dst == float.class || dst == Float.class) |
|
215 return (float)(value); |
|
216 if (dst == double.class || dst == Double.class) |
|
217 return (double)(value); |
|
218 if (dst == byte.class || dst == Byte.class) |
|
219 return (byte)(value); |
|
220 if (dst == boolean.class || dst == boolean.class) |
|
221 return ((value % 29) & 1) == 0; |
|
222 return null; |
|
223 } |
|
224 |
|
225 static final int ONE_MILLION = (1000*1000), // first int value |
|
226 TEN_BILLION = (10*1000*1000*1000), // scale factor to reach upper 32 bits |
|
227 INITIAL_ARG_VAL = ONE_MILLION << 1; // <<1 makes space for sign bit; |
|
228 static long nextArgVal; |
|
229 static long nextArg(boolean moreBits) { |
|
230 long val = nextArgVal++; |
|
231 long sign = -(val & 1); // alternate signs |
|
232 val >>= 1; |
|
233 if (moreBits) |
|
234 // Guarantee some bits in the high word. |
|
235 // In any case keep the decimal representation simple-looking, |
|
236 // with lots of zeroes, so as not to make the printed decimal |
|
237 // strings unnecessarily noisy. |
|
238 val += (val % ONE_MILLION) * TEN_BILLION; |
|
239 return val ^ sign; |
|
240 } |
|
241 static int nextArg() { |
|
242 // Produce a 32-bit result something like ONE_MILLION+(smallint). |
|
243 // Example: 1_000_042. |
|
244 return (int) nextArg(false); |
|
245 } |
|
246 static long nextArg(Class<?> kind) { |
|
247 if (kind == long.class || kind == Long.class || |
|
248 kind == double.class || kind == Double.class) |
|
249 // produce a 64-bit result something like |
|
250 // ((TEN_BILLION+1) * (ONE_MILLION+(smallint))) |
|
251 // Example: 10_000_420_001_000_042. |
|
252 return nextArg(true); |
|
253 return (long) nextArg(); |
|
254 } |
|
255 |
|
256 static Object randomArg(Class<?> param) { |
|
257 Object wrap = castToWrapperOrNull(nextArg(param), param); |
|
258 if (wrap != null) { |
|
259 return wrap; |
|
260 } |
|
261 // import sun.invoke.util.Wrapper; |
|
262 // Wrapper wrap = Wrapper.forBasicType(dst); |
|
263 // if (wrap == Wrapper.OBJECT && Wrapper.isWrapperType(dst)) |
|
264 // wrap = Wrapper.forWrapperType(dst); |
|
265 // if (wrap != Wrapper.OBJECT) |
|
266 // return wrap.wrap(nextArg++); |
|
267 if (param.isInterface()) { |
|
268 for (Class<?> c : param.getClasses()) { |
|
269 if (param.isAssignableFrom(c) && !c.isInterface()) |
|
270 { param = c; break; } |
|
271 } |
|
272 } |
|
273 if (param.isInterface() || param.isAssignableFrom(String.class)) |
|
274 return "#"+nextArg(); |
|
275 else |
|
276 try { |
|
277 return param.newInstance(); |
|
278 } catch (InstantiationException ex) { |
|
279 } catch (IllegalAccessException ex) { |
|
280 } |
|
281 return null; // random class not Object, String, Integer, etc. |
|
282 } |
|
283 static Object[] randomArgs(Class<?>... params) { |
|
284 Object[] args = new Object[params.length]; |
|
285 for (int i = 0; i < args.length; i++) |
|
286 args[i] = randomArg(params[i]); |
|
287 return args; |
|
288 } |
|
289 static Object[] randomArgs(int nargs, Class<?> param) { |
|
290 Object[] args = new Object[nargs]; |
|
291 for (int i = 0; i < args.length; i++) |
|
292 args[i] = randomArg(param); |
|
293 return args; |
|
294 } |
|
295 |
|
296 static <T, E extends T> T[] array(Class<T[]> atype, E... a) { |
|
297 return Arrays.copyOf(a, a.length, atype); |
|
298 } |
|
299 static <T> T[] cat(T[] a, T... b) { |
|
300 int alen = a.length, blen = b.length; |
|
301 if (blen == 0) return a; |
|
302 T[] c = Arrays.copyOf(a, alen + blen); |
|
303 System.arraycopy(b, 0, c, alen, blen); |
|
304 return c; |
|
305 } |
|
306 static Integer[] boxAll(int... vx) { |
|
307 Integer[] res = new Integer[vx.length]; |
|
308 for (int i = 0; i < res.length; i++) { |
|
309 res[i] = vx[i]; |
|
310 } |
|
311 return res; |
|
312 } |
|
313 static Object getClasses(Object x) { |
|
314 if (x == null) return x; |
|
315 if (x instanceof String) return x; // keep the name |
|
316 if (x instanceof List) { |
|
317 // recursively report classes of the list elements |
|
318 Object[] xa = ((List)x).toArray(); |
|
319 for (int i = 0; i < xa.length; i++) |
|
320 xa[i] = getClasses(xa[i]); |
|
321 return Arrays.asList(xa); |
|
322 } |
|
323 return x.getClass().getSimpleName(); |
|
324 } |
|
325 |
|
326 /** Return lambda(arg...[arity]) { new Object[]{ arg... } } */ |
|
327 static MethodHandle varargsList(int arity) { |
|
328 return ValueConversions.varargsList(arity); |
|
329 } |
|
330 /** Return lambda(arg...[arity]) { Arrays.asList(arg...) } */ |
|
331 static MethodHandle varargsArray(int arity) { |
|
332 return ValueConversions.varargsArray(arity); |
|
333 } |
|
334 /** Variation of varargsList, but with the given rtype. */ |
|
335 static MethodHandle varargsList(int arity, Class<?> rtype) { |
|
336 MethodHandle list = varargsList(arity); |
|
337 MethodType listType = list.type().changeReturnType(rtype); |
|
338 if (List.class.isAssignableFrom(rtype) || rtype == void.class || rtype == Object.class) { |
|
339 // OK |
|
340 } else if (rtype.isAssignableFrom(String.class)) { |
|
341 if (LIST_TO_STRING == null) |
|
342 try { |
|
343 LIST_TO_STRING = PRIVATE.findStatic(PRIVATE.lookupClass(), "listToString", |
|
344 MethodType.methodType(String.class, List.class)); |
|
345 } catch (Exception ex) { throw new RuntimeException(ex); } |
|
346 list = MethodHandles.filterReturnValue(list, LIST_TO_STRING); |
|
347 } else if (rtype.isPrimitive()) { |
|
348 if (LIST_TO_INT == null) |
|
349 try { |
|
350 LIST_TO_INT = PRIVATE.findStatic(PRIVATE.lookupClass(), "listToInt", |
|
351 MethodType.methodType(int.class, List.class)); |
|
352 } catch (Exception ex) { throw new RuntimeException(ex); } |
|
353 list = MethodHandles.filterReturnValue(list, LIST_TO_INT); |
|
354 list = MethodHandles.explicitCastArguments(list, listType); |
|
355 } else { |
|
356 throw new RuntimeException("varargsList: "+rtype); |
|
357 } |
|
358 return list.asType(listType); |
|
359 } |
|
360 private static MethodHandle LIST_TO_STRING, LIST_TO_INT; |
|
361 private static String listToString(List x) { return x.toString(); } |
|
362 private static int listToInt(List x) { return x.toString().hashCode(); } |
|
363 |
|
364 static MethodHandle changeArgTypes(MethodHandle target, Class<?> argType) { |
|
365 return changeArgTypes(target, 0, 999, argType); |
|
366 } |
|
367 static MethodHandle changeArgTypes(MethodHandle target, |
|
368 int beg, int end, Class<?> argType) { |
|
369 MethodType targetType = target.type(); |
|
370 end = Math.min(end, targetType.parameterCount()); |
|
371 ArrayList<Class<?>> argTypes = new ArrayList<Class<?>>(targetType.parameterList()); |
|
372 Collections.fill(argTypes.subList(beg, end), argType); |
|
373 MethodType ttype2 = MethodType.methodType(targetType.returnType(), argTypes); |
|
374 return MethodHandles.convertArguments(target, ttype2); |
|
375 } |
|
376 |
|
377 // This lookup is good for all members in and under MethodHandlesTest. |
|
378 static final Lookup PRIVATE = MethodHandles.lookup(); |
|
379 // This lookup is good for package-private members but not private ones. |
|
380 static final Lookup PACKAGE = PackageSibling.lookup(); |
|
381 // This lookup is good only for public members. |
|
382 static final Lookup PUBLIC = MethodHandles.publicLookup(); |
|
383 |
|
384 // Subject methods... |
|
385 static class Example implements IntExample { |
|
386 final String name; |
|
387 public Example() { name = "Example#"+nextArg(); } |
|
388 protected Example(String name) { this.name = name; } |
|
389 protected Example(int x) { this(); called("protected <init>", this, x); } |
|
390 @Override public String toString() { return name; } |
|
391 |
|
392 public void v0() { called("v0", this); } |
|
393 void pkg_v0() { called("pkg_v0", this); } |
|
394 private void pri_v0() { called("pri_v0", this); } |
|
395 public static void s0() { called("s0"); } |
|
396 static void pkg_s0() { called("pkg_s0"); } |
|
397 private static void pri_s0() { called("pri_s0"); } |
|
398 |
|
399 public Object v1(Object x) { return called("v1", this, x); } |
|
400 public Object v2(Object x, Object y) { return called("v2", this, x, y); } |
|
401 public Object v2(Object x, int y) { return called("v2", this, x, y); } |
|
402 public Object v2(int x, Object y) { return called("v2", this, x, y); } |
|
403 public Object v2(int x, int y) { return called("v2", this, x, y); } |
|
404 public static Object s1(Object x) { return called("s1", x); } |
|
405 public static Object s2(int x) { return called("s2", x); } |
|
406 public static Object s3(long x) { return called("s3", x); } |
|
407 public static Object s4(int x, int y) { return called("s4", x, y); } |
|
408 public static Object s5(long x, int y) { return called("s5", x, y); } |
|
409 public static Object s6(int x, long y) { return called("s6", x, y); } |
|
410 public static Object s7(float x, double y) { return called("s7", x, y); } |
|
411 |
|
412 static final Lookup EXAMPLE = MethodHandles.lookup(); // for testing findSpecial |
|
413 } |
|
414 static final Lookup EXAMPLE = Example.EXAMPLE; |
|
415 public static class PubExample extends Example { |
|
416 public PubExample() { super("PubExample#"+nextArg()); } |
|
417 } |
|
418 static class SubExample extends Example { |
|
419 @Override public void v0() { called("Sub/v0", this); } |
|
420 @Override void pkg_v0() { called("Sub/pkg_v0", this); } |
|
421 private SubExample(int x) { called("<init>", this, x); } |
|
422 public SubExample() { super("SubExample#"+nextArg()); } |
|
423 } |
|
424 public static interface IntExample { |
|
425 public void v0(); |
|
426 public static class Impl implements IntExample { |
|
427 public void v0() { called("Int/v0", this); } |
|
428 final String name; |
|
429 public Impl() { name = "Impl#"+nextArg(); } |
|
430 @Override public String toString() { return name; } |
|
431 } |
|
432 } |
|
433 |
|
434 static final Object[][][] ACCESS_CASES = { |
|
435 { { false, PUBLIC }, { false, PACKAGE }, { false, PRIVATE }, { false, EXAMPLE } }, //[0]: all false |
|
436 { { false, PUBLIC }, { false, PACKAGE }, { true, PRIVATE }, { true, EXAMPLE } }, //[1]: only PRIVATE |
|
437 { { false, PUBLIC }, { true, PACKAGE }, { true, PRIVATE }, { true, EXAMPLE } }, //[2]: PUBLIC false |
|
438 { { true, PUBLIC }, { true, PACKAGE }, { true, PRIVATE }, { true, EXAMPLE } }, //[3]: all true |
|
439 }; |
|
440 |
|
441 static Object[][] accessCases(Class<?> defc, String name, boolean isSpecial) { |
|
442 Object[][] cases; |
|
443 if (name.contains("pri_") || isSpecial) { |
|
444 cases = ACCESS_CASES[1]; // PRIVATE only |
|
445 } else if (name.contains("pkg_") || !Modifier.isPublic(defc.getModifiers())) { |
|
446 cases = ACCESS_CASES[2]; // not PUBLIC |
|
447 } else { |
|
448 assertTrue(name.indexOf('_') < 0); |
|
449 boolean pubc = Modifier.isPublic(defc.getModifiers()); |
|
450 if (pubc) |
|
451 cases = ACCESS_CASES[3]; // all access levels |
|
452 else |
|
453 cases = ACCESS_CASES[2]; // PACKAGE but not PUBLIC |
|
454 } |
|
455 if (defc != Example.class && cases[cases.length-1][1] == EXAMPLE) |
|
456 cases = Arrays.copyOfRange(cases, 0, cases.length-1); |
|
457 return cases; |
|
458 } |
|
459 static Object[][] accessCases(Class<?> defc, String name) { |
|
460 return accessCases(defc, name, false); |
|
461 } |
|
462 |
|
463 @Test |
|
464 public void testFindStatic() throws Throwable { |
|
465 if (CAN_SKIP_WORKING) return; |
|
466 startTest("findStatic"); |
|
467 testFindStatic(PubExample.class, void.class, "s0"); |
|
468 testFindStatic(Example.class, void.class, "s0"); |
|
469 testFindStatic(Example.class, void.class, "pkg_s0"); |
|
470 testFindStatic(Example.class, void.class, "pri_s0"); |
|
471 |
|
472 testFindStatic(Example.class, Object.class, "s1", Object.class); |
|
473 testFindStatic(Example.class, Object.class, "s2", int.class); |
|
474 testFindStatic(Example.class, Object.class, "s3", long.class); |
|
475 testFindStatic(Example.class, Object.class, "s4", int.class, int.class); |
|
476 testFindStatic(Example.class, Object.class, "s5", long.class, int.class); |
|
477 testFindStatic(Example.class, Object.class, "s6", int.class, long.class); |
|
478 testFindStatic(Example.class, Object.class, "s7", float.class, double.class); |
|
479 |
|
480 testFindStatic(false, PRIVATE, Example.class, void.class, "bogus"); |
|
481 } |
|
482 |
|
483 void testFindStatic(Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable { |
|
484 for (Object[] ac : accessCases(defc, name)) { |
|
485 testFindStatic((Boolean)ac[0], (Lookup)ac[1], defc, ret, name, params); |
|
486 } |
|
487 } |
|
488 void testFindStatic(Lookup lookup, Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable { |
|
489 testFindStatic(true, lookup, defc, ret, name, params); |
|
490 } |
|
491 void testFindStatic(boolean positive, Lookup lookup, Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable { |
|
492 countTest(positive); |
|
493 MethodType type = MethodType.methodType(ret, params); |
|
494 MethodHandle target = null; |
|
495 Exception noAccess = null; |
|
496 try { |
|
497 if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type); |
|
498 target = lookup.in(defc).findStatic(defc, name, type); |
|
499 } catch (ReflectiveOperationException ex) { |
|
500 noAccess = ex; |
|
501 if (name.contains("bogus")) |
|
502 assertTrue(noAccess instanceof NoSuchMethodException); |
|
503 else |
|
504 assertTrue(noAccess instanceof IllegalAccessException); |
|
505 } |
|
506 if (verbosity >= 3) |
|
507 System.out.println("findStatic "+lookup+": "+defc.getName()+"."+name+"/"+type+" => "+target |
|
508 +(noAccess == null ? "" : " !! "+noAccess)); |
|
509 if (positive && noAccess != null) throw noAccess; |
|
510 assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null); |
|
511 if (!positive) return; // negative test failed as expected |
|
512 assertEquals(type, target.type()); |
|
513 assertNameStringContains(target, name); |
|
514 if (!DO_MORE_CALLS && lookup != PRIVATE) return; |
|
515 Object[] args = randomArgs(params); |
|
516 printCalled(target, name, args); |
|
517 target.invokeWithArguments(args); |
|
518 assertCalled(name, args); |
|
519 if (verbosity >= 1) |
|
520 System.out.print(':'); |
|
521 } |
|
522 |
|
523 // rough check of name string |
|
524 static void assertNameStringContains(Object x, String s) { |
|
525 if (x.toString().contains(s)) return; |
|
526 assertEquals(s, x); |
|
527 } |
|
528 |
|
529 @Test |
|
530 public void testFindVirtual() throws Throwable { |
|
531 if (CAN_SKIP_WORKING) return; |
|
532 startTest("findVirtual"); |
|
533 testFindVirtual(Example.class, void.class, "v0"); |
|
534 testFindVirtual(Example.class, void.class, "pkg_v0"); |
|
535 testFindVirtual(Example.class, void.class, "pri_v0"); |
|
536 testFindVirtual(Example.class, Object.class, "v1", Object.class); |
|
537 testFindVirtual(Example.class, Object.class, "v2", Object.class, Object.class); |
|
538 testFindVirtual(Example.class, Object.class, "v2", Object.class, int.class); |
|
539 testFindVirtual(Example.class, Object.class, "v2", int.class, Object.class); |
|
540 testFindVirtual(Example.class, Object.class, "v2", int.class, int.class); |
|
541 testFindVirtual(false, PRIVATE, Example.class, Example.class, void.class, "bogus"); |
|
542 // test dispatch |
|
543 testFindVirtual(SubExample.class, SubExample.class, void.class, "Sub/v0"); |
|
544 testFindVirtual(SubExample.class, Example.class, void.class, "Sub/v0"); |
|
545 testFindVirtual(SubExample.class, IntExample.class, void.class, "Sub/v0"); |
|
546 testFindVirtual(SubExample.class, SubExample.class, void.class, "Sub/pkg_v0"); |
|
547 testFindVirtual(SubExample.class, Example.class, void.class, "Sub/pkg_v0"); |
|
548 testFindVirtual(Example.class, IntExample.class, void.class, "v0"); |
|
549 testFindVirtual(IntExample.Impl.class, IntExample.class, void.class, "Int/v0"); |
|
550 } |
|
551 |
|
552 void testFindVirtual(Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable { |
|
553 Class<?> rcvc = defc; |
|
554 testFindVirtual(rcvc, defc, ret, name, params); |
|
555 } |
|
556 void testFindVirtual(Class<?> rcvc, Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable { |
|
557 for (Object[] ac : accessCases(defc, name)) { |
|
558 testFindVirtual((Boolean)ac[0], (Lookup)ac[1], rcvc, defc, ret, name, params); |
|
559 } |
|
560 } |
|
561 void testFindVirtual(Lookup lookup, Class<?> rcvc, Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable { |
|
562 testFindVirtual(true, lookup, rcvc, defc, ret, name, params); |
|
563 } |
|
564 void testFindVirtual(boolean positive, Lookup lookup, Class<?> rcvc, Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable { |
|
565 countTest(positive); |
|
566 String methodName = name.substring(1 + name.indexOf('/')); // foo/bar => foo |
|
567 MethodType type = MethodType.methodType(ret, params); |
|
568 MethodHandle target = null; |
|
569 Exception noAccess = null; |
|
570 try { |
|
571 if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type); |
|
572 target = lookup.in(defc).findVirtual(defc, methodName, type); |
|
573 } catch (ReflectiveOperationException ex) { |
|
574 noAccess = ex; |
|
575 if (name.contains("bogus")) |
|
576 assertTrue(noAccess instanceof NoSuchMethodException); |
|
577 else |
|
578 assertTrue(noAccess instanceof IllegalAccessException); |
|
579 } |
|
580 if (verbosity >= 3) |
|
581 System.out.println("findVirtual "+lookup+": "+defc.getName()+"."+name+"/"+type+" => "+target |
|
582 +(noAccess == null ? "" : " !! "+noAccess)); |
|
583 if (positive && noAccess != null) throw noAccess; |
|
584 assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null); |
|
585 if (!positive) return; // negative test failed as expected |
|
586 Class<?>[] paramsWithSelf = cat(array(Class[].class, (Class)defc), params); |
|
587 MethodType typeWithSelf = MethodType.methodType(ret, paramsWithSelf); |
|
588 assertEquals(typeWithSelf, target.type()); |
|
589 assertNameStringContains(target, methodName); |
|
590 if (!DO_MORE_CALLS && lookup != PRIVATE) return; |
|
591 Object[] argsWithSelf = randomArgs(paramsWithSelf); |
|
592 if (rcvc != defc) argsWithSelf[0] = randomArg(rcvc); |
|
593 printCalled(target, name, argsWithSelf); |
|
594 target.invokeWithArguments(argsWithSelf); |
|
595 assertCalled(name, argsWithSelf); |
|
596 if (verbosity >= 1) |
|
597 System.out.print(':'); |
|
598 } |
|
599 |
|
600 @Test |
|
601 public void testFindSpecial() throws Throwable { |
|
602 if (CAN_SKIP_WORKING) return; |
|
603 startTest("findSpecial"); |
|
604 testFindSpecial(SubExample.class, Example.class, void.class, "v0"); |
|
605 testFindSpecial(SubExample.class, Example.class, void.class, "pkg_v0"); |
|
606 // Do some negative testing: |
|
607 testFindSpecial(false, EXAMPLE, SubExample.class, Example.class, void.class, "bogus"); |
|
608 testFindSpecial(false, PRIVATE, SubExample.class, Example.class, void.class, "bogus"); |
|
609 for (Lookup lookup : new Lookup[]{ PRIVATE, EXAMPLE, PACKAGE, PUBLIC }) { |
|
610 testFindSpecial(false, lookup, Object.class, Example.class, void.class, "v0"); |
|
611 testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "<init>", int.class); |
|
612 testFindSpecial(false, lookup, SubExample.class, Example.class, void.class, "s0"); |
|
613 } |
|
614 } |
|
615 |
|
616 void testFindSpecial(Class<?> specialCaller, |
|
617 Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable { |
|
618 testFindSpecial(true, EXAMPLE, specialCaller, defc, ret, name, params); |
|
619 testFindSpecial(true, PRIVATE, specialCaller, defc, ret, name, params); |
|
620 testFindSpecial(false, PACKAGE, specialCaller, defc, ret, name, params); |
|
621 testFindSpecial(false, PUBLIC, specialCaller, defc, ret, name, params); |
|
622 } |
|
623 void testFindSpecial(boolean positive, Lookup lookup, Class<?> specialCaller, |
|
624 Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable { |
|
625 countTest(positive); |
|
626 MethodType type = MethodType.methodType(ret, params); |
|
627 MethodHandle target = null; |
|
628 Exception noAccess = null; |
|
629 try { |
|
630 if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type); |
|
631 if (verbosity >= 5) System.out.println(" lookup => "+lookup.in(specialCaller)); |
|
632 target = lookup.in(specialCaller).findSpecial(defc, name, type, specialCaller); |
|
633 } catch (ReflectiveOperationException ex) { |
|
634 noAccess = ex; |
|
635 if (name.contains("bogus")) |
|
636 assertTrue(noAccess instanceof NoSuchMethodException); |
|
637 else |
|
638 assertTrue(noAccess instanceof IllegalAccessException); |
|
639 } |
|
640 if (verbosity >= 3) |
|
641 System.out.println("findSpecial from "+specialCaller.getName()+" to "+defc.getName()+"."+name+"/"+type+" => "+target |
|
642 +(target == null ? "" : target.type()) |
|
643 +(noAccess == null ? "" : " !! "+noAccess)); |
|
644 if (positive && noAccess != null) throw noAccess; |
|
645 assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null); |
|
646 if (!positive) return; // negative test failed as expected |
|
647 assertEquals(specialCaller, target.type().parameterType(0)); |
|
648 assertEquals(type, target.type().dropParameterTypes(0,1)); |
|
649 Class<?>[] paramsWithSelf = cat(array(Class[].class, (Class)specialCaller), params); |
|
650 MethodType typeWithSelf = MethodType.methodType(ret, paramsWithSelf); |
|
651 assertNameStringContains(target, name); |
|
652 if (!DO_MORE_CALLS && lookup != PRIVATE && lookup != EXAMPLE) return; |
|
653 Object[] args = randomArgs(paramsWithSelf); |
|
654 printCalled(target, name, args); |
|
655 target.invokeWithArguments(args); |
|
656 assertCalled(name, args); |
|
657 } |
|
658 |
|
659 @Test |
|
660 public void testBind() throws Throwable { |
|
661 if (CAN_SKIP_WORKING) return; |
|
662 startTest("bind"); |
|
663 testBind(Example.class, void.class, "v0"); |
|
664 testBind(Example.class, void.class, "pkg_v0"); |
|
665 testBind(Example.class, void.class, "pri_v0"); |
|
666 testBind(Example.class, Object.class, "v1", Object.class); |
|
667 testBind(Example.class, Object.class, "v2", Object.class, Object.class); |
|
668 testBind(Example.class, Object.class, "v2", Object.class, int.class); |
|
669 testBind(Example.class, Object.class, "v2", int.class, Object.class); |
|
670 testBind(Example.class, Object.class, "v2", int.class, int.class); |
|
671 testBind(false, PRIVATE, Example.class, void.class, "bogus"); |
|
672 testBind(SubExample.class, void.class, "Sub/v0"); |
|
673 testBind(SubExample.class, void.class, "Sub/pkg_v0"); |
|
674 testBind(IntExample.Impl.class, void.class, "Int/v0"); |
|
675 } |
|
676 |
|
677 void testBind(Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable { |
|
678 for (Object[] ac : accessCases(defc, name)) { |
|
679 testBind((Boolean)ac[0], (Lookup)ac[1], defc, ret, name, params); |
|
680 } |
|
681 } |
|
682 |
|
683 void testBind(boolean positive, Lookup lookup, Class<?> defc, Class<?> ret, String name, Class<?>... params) throws Throwable { |
|
684 countTest(positive); |
|
685 String methodName = name.substring(1 + name.indexOf('/')); // foo/bar => foo |
|
686 MethodType type = MethodType.methodType(ret, params); |
|
687 Object receiver = randomArg(defc); |
|
688 MethodHandle target = null; |
|
689 Exception noAccess = null; |
|
690 try { |
|
691 if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type); |
|
692 target = lookup.in(defc).bind(receiver, methodName, type); |
|
693 } catch (ReflectiveOperationException ex) { |
|
694 noAccess = ex; |
|
695 if (name.contains("bogus")) |
|
696 assertTrue(noAccess instanceof NoSuchMethodException); |
|
697 else |
|
698 assertTrue(noAccess instanceof IllegalAccessException); |
|
699 } |
|
700 if (verbosity >= 3) |
|
701 System.out.println("bind "+receiver+"."+name+"/"+type+" => "+target |
|
702 +(noAccess == null ? "" : " !! "+noAccess)); |
|
703 if (positive && noAccess != null) throw noAccess; |
|
704 assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null); |
|
705 if (!positive) return; // negative test failed as expected |
|
706 assertEquals(type, target.type()); |
|
707 Object[] args = randomArgs(params); |
|
708 printCalled(target, name, args); |
|
709 target.invokeWithArguments(args); |
|
710 Object[] argsWithReceiver = cat(array(Object[].class, receiver), args); |
|
711 assertCalled(name, argsWithReceiver); |
|
712 if (verbosity >= 1) |
|
713 System.out.print(':'); |
|
714 } |
|
715 |
|
716 @Test |
|
717 public void testUnreflect() throws Throwable { |
|
718 if (CAN_SKIP_WORKING) return; |
|
719 startTest("unreflect"); |
|
720 testUnreflect(Example.class, true, void.class, "s0"); |
|
721 testUnreflect(Example.class, true, void.class, "pkg_s0"); |
|
722 testUnreflect(Example.class, true, void.class, "pri_s0"); |
|
723 |
|
724 testUnreflect(Example.class, true, Object.class, "s1", Object.class); |
|
725 testUnreflect(Example.class, true, Object.class, "s2", int.class); |
|
726 testUnreflect(Example.class, true, Object.class, "s3", long.class); |
|
727 testUnreflect(Example.class, true, Object.class, "s4", int.class, int.class); |
|
728 testUnreflect(Example.class, true, Object.class, "s5", long.class, int.class); |
|
729 testUnreflect(Example.class, true, Object.class, "s6", int.class, long.class); |
|
730 |
|
731 testUnreflect(Example.class, false, void.class, "v0"); |
|
732 testUnreflect(Example.class, false, void.class, "pkg_v0"); |
|
733 testUnreflect(Example.class, false, void.class, "pri_v0"); |
|
734 testUnreflect(Example.class, false, Object.class, "v1", Object.class); |
|
735 testUnreflect(Example.class, false, Object.class, "v2", Object.class, Object.class); |
|
736 testUnreflect(Example.class, false, Object.class, "v2", Object.class, int.class); |
|
737 testUnreflect(Example.class, false, Object.class, "v2", int.class, Object.class); |
|
738 testUnreflect(Example.class, false, Object.class, "v2", int.class, int.class); |
|
739 } |
|
740 |
|
741 void testUnreflect(Class<?> defc, boolean isStatic, Class<?> ret, String name, Class<?>... params) throws Throwable { |
|
742 for (Object[] ac : accessCases(defc, name)) { |
|
743 testUnreflectMaybeSpecial(null, (Boolean)ac[0], (Lookup)ac[1], defc, (isStatic ? null : defc), ret, name, params); |
|
744 } |
|
745 } |
|
746 void testUnreflect(Class<?> defc, Class<?> rcvc, Class<?> ret, String name, Class<?>... params) throws Throwable { |
|
747 for (Object[] ac : accessCases(defc, name)) { |
|
748 testUnreflectMaybeSpecial(null, (Boolean)ac[0], (Lookup)ac[1], defc, rcvc, ret, name, params); |
|
749 } |
|
750 } |
|
751 void testUnreflectMaybeSpecial(Class<?> specialCaller, |
|
752 boolean positive, Lookup lookup, |
|
753 Class<?> defc, Class<?> rcvc, Class<?> ret, String name, Class<?>... params) throws Throwable { |
|
754 countTest(positive); |
|
755 MethodType type = MethodType.methodType(ret, params); |
|
756 Method rmethod = defc.getDeclaredMethod(name, params); |
|
757 MethodHandle target = null; |
|
758 Exception noAccess = null; |
|
759 boolean isStatic = (rcvc == null); |
|
760 boolean isSpecial = (specialCaller != null); |
|
761 try { |
|
762 if (verbosity >= 4) System.out.println("lookup via "+lookup+" of "+defc+" "+name+type); |
|
763 if (isSpecial) |
|
764 target = lookup.in(specialCaller).unreflectSpecial(rmethod, specialCaller); |
|
765 else |
|
766 target = lookup.in(defc).unreflect(rmethod); |
|
767 } catch (ReflectiveOperationException ex) { |
|
768 noAccess = ex; |
|
769 if (name.contains("bogus")) |
|
770 assertTrue(noAccess instanceof NoSuchMethodException); |
|
771 else |
|
772 assertTrue(noAccess instanceof IllegalAccessException); |
|
773 } |
|
774 if (verbosity >= 3) |
|
775 System.out.println("unreflect"+(isSpecial?"Special":"")+" "+defc.getName()+"."+name+"/"+type |
|
776 +(!isSpecial ? "" : " specialCaller="+specialCaller) |
|
777 +( isStatic ? "" : " receiver="+rcvc) |
|
778 +" => "+target |
|
779 +(noAccess == null ? "" : " !! "+noAccess)); |
|
780 if (positive && noAccess != null) throw noAccess; |
|
781 assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null); |
|
782 if (!positive) return; // negative test failed as expected |
|
783 assertEquals(isStatic, Modifier.isStatic(rmethod.getModifiers())); |
|
784 Class<?>[] paramsMaybeWithSelf = params; |
|
785 if (!isStatic) { |
|
786 paramsMaybeWithSelf = cat(array(Class[].class, (Class)rcvc), params); |
|
787 } |
|
788 MethodType typeMaybeWithSelf = MethodType.methodType(ret, paramsMaybeWithSelf); |
|
789 if (isStatic) { |
|
790 assertEquals(typeMaybeWithSelf, target.type()); |
|
791 } else { |
|
792 if (isSpecial) |
|
793 assertEquals(specialCaller, target.type().parameterType(0)); |
|
794 else |
|
795 assertEquals(defc, target.type().parameterType(0)); |
|
796 assertEquals(typeMaybeWithSelf, target.type().changeParameterType(0, rcvc)); |
|
797 } |
|
798 Object[] argsMaybeWithSelf = randomArgs(paramsMaybeWithSelf); |
|
799 printCalled(target, name, argsMaybeWithSelf); |
|
800 target.invokeWithArguments(argsMaybeWithSelf); |
|
801 assertCalled(name, argsMaybeWithSelf); |
|
802 if (verbosity >= 1) |
|
803 System.out.print(':'); |
|
804 } |
|
805 |
|
806 void testUnreflectSpecial(Class<?> defc, Class<?> rcvc, Class<?> ret, String name, Class<?>... params) throws Throwable { |
|
807 for (Object[] ac : accessCases(defc, name, true)) { |
|
808 Class<?> specialCaller = rcvc; |
|
809 testUnreflectMaybeSpecial(specialCaller, (Boolean)ac[0], (Lookup)ac[1], defc, rcvc, ret, name, params); |
|
810 } |
|
811 } |
|
812 |
|
813 @Test |
|
814 public void testUnreflectSpecial() throws Throwable { |
|
815 if (CAN_SKIP_WORKING) return; |
|
816 startTest("unreflectSpecial"); |
|
817 testUnreflectSpecial(Example.class, Example.class, void.class, "v0"); |
|
818 testUnreflectSpecial(Example.class, SubExample.class, void.class, "v0"); |
|
819 testUnreflectSpecial(Example.class, Example.class, void.class, "pkg_v0"); |
|
820 testUnreflectSpecial(Example.class, SubExample.class, void.class, "pkg_v0"); |
|
821 testUnreflectSpecial(Example.class, Example.class, Object.class, "v2", int.class, int.class); |
|
822 testUnreflectSpecial(Example.class, SubExample.class, Object.class, "v2", int.class, int.class); |
|
823 testUnreflectMaybeSpecial(Example.class, false, PRIVATE, Example.class, Example.class, void.class, "s0"); |
|
824 } |
|
825 |
|
826 public static class HasFields { |
|
827 boolean fZ = false; |
|
828 byte fB = (byte)'B'; |
|
829 short fS = (short)'S'; |
|
830 char fC = 'C'; |
|
831 int fI = 'I'; |
|
832 long fJ = 'J'; |
|
833 float fF = 'F'; |
|
834 double fD = 'D'; |
|
835 static boolean sZ = true; |
|
836 static byte sB = 1+(byte)'B'; |
|
837 static short sS = 1+(short)'S'; |
|
838 static char sC = 1+'C'; |
|
839 static int sI = 1+'I'; |
|
840 static long sJ = 1+'J'; |
|
841 static float sF = 1+'F'; |
|
842 static double sD = 1+'D'; |
|
843 |
|
844 Object fL = 'L'; |
|
845 String fR = "R"; |
|
846 static Object sL = 'M'; |
|
847 static String sR = "S"; |
|
848 |
|
849 static final Object[][] CASES; |
|
850 static { |
|
851 ArrayList<Object[]> cases = new ArrayList<Object[]>(); |
|
852 Object types[][] = { |
|
853 {'L',Object.class}, {'R',String.class}, |
|
854 {'I',int.class}, {'J',long.class}, |
|
855 {'F',float.class}, {'D',double.class}, |
|
856 {'Z',boolean.class}, {'B',byte.class}, |
|
857 {'S',short.class}, {'C',char.class}, |
|
858 }; |
|
859 HasFields fields = new HasFields(); |
|
860 for (Object[] t : types) { |
|
861 for (int kind = 0; kind <= 1; kind++) { |
|
862 boolean isStatic = (kind != 0); |
|
863 char btc = (Character)t[0]; |
|
864 String name = (isStatic ? "s" : "f") + btc; |
|
865 Class<?> type = (Class<?>) t[1]; |
|
866 Object value; |
|
867 Field field; |
|
868 try { |
|
869 field = HasFields.class.getDeclaredField(name); |
|
870 } catch (Exception ex) { |
|
871 throw new InternalError("no field HasFields."+name); |
|
872 } |
|
873 try { |
|
874 value = field.get(fields); |
|
875 } catch (Exception ex) { |
|
876 throw new InternalError("cannot fetch field HasFields."+name); |
|
877 } |
|
878 if (type == float.class) { |
|
879 float v = 'F'; |
|
880 if (isStatic) v++; |
|
881 assertTrue(value.equals(v)); |
|
882 } |
|
883 assertTrue(name.equals(field.getName())); |
|
884 assertTrue(type.equals(field.getType())); |
|
885 assertTrue(isStatic == (Modifier.isStatic(field.getModifiers()))); |
|
886 cases.add(new Object[]{ field, value }); |
|
887 } |
|
888 } |
|
889 cases.add(new Object[]{ new Object[]{ false, HasFields.class, "bogus_fD", double.class }, Error.class }); |
|
890 cases.add(new Object[]{ new Object[]{ true, HasFields.class, "bogus_sL", Object.class }, Error.class }); |
|
891 CASES = cases.toArray(new Object[0][]); |
|
892 } |
|
893 } |
|
894 |
|
895 static final int TEST_UNREFLECT = 1, TEST_FIND_FIELD = 2, TEST_FIND_STATIC = 3, TEST_SETTER = 0x10; |
|
896 static boolean testModeMatches(int testMode, boolean isStatic) { |
|
897 switch (testMode) { |
|
898 case TEST_FIND_STATIC: return isStatic; |
|
899 case TEST_FIND_FIELD: return !isStatic; |
|
900 case TEST_UNREFLECT: return true; // unreflect matches both |
|
901 } |
|
902 throw new InternalError("testMode="+testMode); |
|
903 } |
|
904 |
|
905 @Test |
|
906 public void testUnreflectGetter() throws Throwable { |
|
907 startTest("unreflectGetter"); |
|
908 testGetter(TEST_UNREFLECT); |
|
909 } |
|
910 @Test |
|
911 public void testFindGetter() throws Throwable { |
|
912 startTest("findGetter"); |
|
913 testGetter(TEST_FIND_FIELD); |
|
914 } |
|
915 @Test |
|
916 public void testFindStaticGetter() throws Throwable { |
|
917 startTest("findStaticGetter"); |
|
918 testGetter(TEST_FIND_STATIC); |
|
919 } |
|
920 public void testGetter(int testMode) throws Throwable { |
|
921 Lookup lookup = PRIVATE; // FIXME: test more lookups than this one |
|
922 for (Object[] c : HasFields.CASES) { |
|
923 boolean positive = (c[1] != Error.class); |
|
924 testGetter(positive, lookup, c[0], c[1], testMode); |
|
925 } |
|
926 testGetter(true, lookup, |
|
927 new Object[]{ true, System.class, "out", java.io.PrintStream.class }, |
|
928 System.out, testMode); |
|
929 for (int isStaticN = 0; isStaticN <= 1; isStaticN++) { |
|
930 testGetter(false, lookup, |
|
931 new Object[]{ (isStaticN != 0), System.class, "bogus", char.class }, |
|
932 null, testMode); |
|
933 } |
|
934 } |
|
935 public void testGetter(boolean positive, MethodHandles.Lookup lookup, |
|
936 Object fieldRef, Object value, int testMode) throws Throwable { |
|
937 testAccessor(positive, lookup, fieldRef, value, testMode); |
|
938 } |
|
939 |
|
940 public void testAccessor(boolean positive, MethodHandles.Lookup lookup, |
|
941 Object fieldRef, Object value, int testMode0) throws Throwable { |
|
942 boolean isGetter = ((testMode0 & TEST_SETTER) == 0); |
|
943 int testMode = testMode0 & ~TEST_SETTER; |
|
944 boolean isStatic; |
|
945 Class<?> fclass; |
|
946 String fname; |
|
947 Class<?> ftype; |
|
948 Field f = (fieldRef instanceof Field ? (Field)fieldRef : null); |
|
949 if (f != null) { |
|
950 isStatic = Modifier.isStatic(f.getModifiers()); |
|
951 fclass = f.getDeclaringClass(); |
|
952 fname = f.getName(); |
|
953 ftype = f.getType(); |
|
954 } else { |
|
955 Object[] scnt = (Object[]) fieldRef; |
|
956 isStatic = (Boolean) scnt[0]; |
|
957 fclass = (Class<?>) scnt[1]; |
|
958 fname = (String) scnt[2]; |
|
959 ftype = (Class<?>) scnt[3]; |
|
960 try { |
|
961 f = fclass.getDeclaredField(fname); |
|
962 } catch (ReflectiveOperationException ex) { |
|
963 f = null; |
|
964 } |
|
965 } |
|
966 if (!testModeMatches(testMode, isStatic)) return; |
|
967 if (f == null && testMode == TEST_UNREFLECT) return; |
|
968 countTest(positive); |
|
969 MethodType expType; |
|
970 if (isGetter) |
|
971 expType = MethodType.methodType(ftype, HasFields.class); |
|
972 else |
|
973 expType = MethodType.methodType(void.class, HasFields.class, ftype); |
|
974 if (isStatic) expType = expType.dropParameterTypes(0, 1); |
|
975 Exception noAccess = null; |
|
976 MethodHandle mh; |
|
977 try { |
|
978 switch (testMode0) { |
|
979 case TEST_UNREFLECT: mh = lookup.unreflectGetter(f); break; |
|
980 case TEST_FIND_FIELD: mh = lookup.findGetter(fclass, fname, ftype); break; |
|
981 case TEST_FIND_STATIC: mh = lookup.findStaticGetter(fclass, fname, ftype); break; |
|
982 case TEST_SETTER| |
|
983 TEST_UNREFLECT: mh = lookup.unreflectSetter(f); break; |
|
984 case TEST_SETTER| |
|
985 TEST_FIND_FIELD: mh = lookup.findSetter(fclass, fname, ftype); break; |
|
986 case TEST_SETTER| |
|
987 TEST_FIND_STATIC: mh = lookup.findStaticSetter(fclass, fname, ftype); break; |
|
988 default: |
|
989 throw new InternalError("testMode="+testMode); |
|
990 } |
|
991 } catch (ReflectiveOperationException ex) { |
|
992 mh = null; |
|
993 noAccess = ex; |
|
994 if (fname.contains("bogus")) |
|
995 assertTrue(noAccess instanceof NoSuchFieldException); |
|
996 else |
|
997 assertTrue(noAccess instanceof IllegalAccessException); |
|
998 } |
|
999 if (verbosity >= 3) |
|
1000 System.out.println("find"+(isStatic?"Static":"")+(isGetter?"Getter":"Setter")+" "+fclass.getName()+"."+fname+"/"+ftype |
|
1001 +" => "+mh |
|
1002 +(noAccess == null ? "" : " !! "+noAccess)); |
|
1003 if (positive && noAccess != null) throw new RuntimeException(noAccess); |
|
1004 assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, mh != null); |
|
1005 if (!positive) return; // negative test failed as expected |
|
1006 assertEquals((isStatic ? 0 : 1)+(isGetter ? 0 : 1), mh.type().parameterCount()); |
|
1007 |
|
1008 |
|
1009 assertSame(mh.type(), expType); |
|
1010 assertNameStringContains(mh, fname); |
|
1011 HasFields fields = new HasFields(); |
|
1012 Object sawValue; |
|
1013 Class<?> vtype = ftype; |
|
1014 if (ftype != int.class) vtype = Object.class; |
|
1015 if (isGetter) { |
|
1016 mh = MethodHandles.convertArguments(mh, mh.type().generic() |
|
1017 .changeReturnType(vtype)); |
|
1018 } else { |
|
1019 int last = mh.type().parameterCount() - 1; |
|
1020 mh = MethodHandles.convertArguments(mh, mh.type().generic() |
|
1021 .changeReturnType(void.class) |
|
1022 .changeParameterType(last, vtype)); |
|
1023 } |
|
1024 if (f != null && f.getDeclaringClass() == HasFields.class) { |
|
1025 assertEquals(f.get(fields), value); // clean to start with |
|
1026 } |
|
1027 if (isGetter) { |
|
1028 Object expValue = value; |
|
1029 for (int i = 0; i <= 1; i++) { |
|
1030 if (isStatic) { |
|
1031 if (ftype == int.class) |
|
1032 sawValue = (int) mh.invokeExact(); // do these exactly |
|
1033 else |
|
1034 sawValue = mh.invokeExact(); |
|
1035 } else { |
|
1036 if (ftype == int.class) |
|
1037 sawValue = (int) mh.invokeExact((Object) fields); |
|
1038 else |
|
1039 sawValue = mh.invokeExact((Object) fields); |
|
1040 } |
|
1041 assertEquals(sawValue, expValue); |
|
1042 if (f != null && f.getDeclaringClass() == HasFields.class |
|
1043 && !Modifier.isFinal(f.getModifiers())) { |
|
1044 Object random = randomArg(ftype); |
|
1045 f.set(fields, random); |
|
1046 expValue = random; |
|
1047 } else { |
|
1048 break; |
|
1049 } |
|
1050 } |
|
1051 } else { |
|
1052 for (int i = 0; i <= 1; i++) { |
|
1053 Object putValue = randomArg(ftype); |
|
1054 if (isStatic) { |
|
1055 if (ftype == int.class) |
|
1056 mh.invokeExact((int)putValue); // do these exactly |
|
1057 else |
|
1058 mh.invokeExact(putValue); |
|
1059 } else { |
|
1060 if (ftype == int.class) |
|
1061 mh.invokeExact((Object) fields, (int)putValue); |
|
1062 else |
|
1063 mh.invokeExact((Object) fields, putValue); |
|
1064 } |
|
1065 if (f != null && f.getDeclaringClass() == HasFields.class) { |
|
1066 assertEquals(f.get(fields), putValue); |
|
1067 } |
|
1068 } |
|
1069 } |
|
1070 if (f != null && f.getDeclaringClass() == HasFields.class) { |
|
1071 f.set(fields, value); // put it back |
|
1072 } |
|
1073 } |
|
1074 |
|
1075 |
|
1076 @Test |
|
1077 public void testUnreflectSetter() throws Throwable { |
|
1078 startTest("unreflectSetter"); |
|
1079 testSetter(TEST_UNREFLECT); |
|
1080 } |
|
1081 @Test |
|
1082 public void testFindSetter() throws Throwable { |
|
1083 startTest("findSetter"); |
|
1084 testSetter(TEST_FIND_FIELD); |
|
1085 } |
|
1086 @Test |
|
1087 public void testFindStaticSetter() throws Throwable { |
|
1088 startTest("findStaticSetter"); |
|
1089 testSetter(TEST_FIND_STATIC); |
|
1090 } |
|
1091 public void testSetter(int testMode) throws Throwable { |
|
1092 Lookup lookup = PRIVATE; // FIXME: test more lookups than this one |
|
1093 startTest("unreflectSetter"); |
|
1094 for (Object[] c : HasFields.CASES) { |
|
1095 boolean positive = (c[1] != Error.class); |
|
1096 testSetter(positive, lookup, c[0], c[1], testMode); |
|
1097 } |
|
1098 for (int isStaticN = 0; isStaticN <= 1; isStaticN++) { |
|
1099 testSetter(false, lookup, |
|
1100 new Object[]{ (isStaticN != 0), System.class, "bogus", char.class }, |
|
1101 null, testMode); |
|
1102 } |
|
1103 } |
|
1104 public void testSetter(boolean positive, MethodHandles.Lookup lookup, |
|
1105 Object fieldRef, Object value, int testMode) throws Throwable { |
|
1106 testAccessor(positive, lookup, fieldRef, value, testMode | TEST_SETTER); |
|
1107 } |
|
1108 |
|
1109 @Test |
|
1110 public void testArrayElementGetter() throws Throwable { |
|
1111 startTest("arrayElementGetter"); |
|
1112 testArrayElementGetterSetter(false); |
|
1113 } |
|
1114 |
|
1115 @Test |
|
1116 public void testArrayElementSetter() throws Throwable { |
|
1117 startTest("arrayElementSetter"); |
|
1118 testArrayElementGetterSetter(true); |
|
1119 } |
|
1120 |
|
1121 public void testArrayElementGetterSetter(boolean testSetter) throws Throwable { |
|
1122 testArrayElementGetterSetter(new Object[10], testSetter); |
|
1123 testArrayElementGetterSetter(new String[10], testSetter); |
|
1124 testArrayElementGetterSetter(new boolean[10], testSetter); |
|
1125 testArrayElementGetterSetter(new byte[10], testSetter); |
|
1126 testArrayElementGetterSetter(new char[10], testSetter); |
|
1127 testArrayElementGetterSetter(new short[10], testSetter); |
|
1128 testArrayElementGetterSetter(new int[10], testSetter); |
|
1129 testArrayElementGetterSetter(new float[10], testSetter); |
|
1130 testArrayElementGetterSetter(new long[10], testSetter); |
|
1131 testArrayElementGetterSetter(new double[10], testSetter); |
|
1132 } |
|
1133 |
|
1134 public void testArrayElementGetterSetter(Object array, boolean testSetter) throws Throwable { |
|
1135 countTest(true); |
|
1136 if (verbosity >= 2) System.out.println("array type = "+array.getClass().getComponentType().getName()+"["+Array.getLength(array)+"]"); |
|
1137 Class<?> arrayType = array.getClass(); |
|
1138 Class<?> elemType = arrayType.getComponentType(); |
|
1139 MethodType expType = !testSetter |
|
1140 ? MethodType.methodType(elemType, arrayType, int.class) |
|
1141 : MethodType.methodType(void.class, arrayType, int.class, elemType); |
|
1142 MethodHandle mh = !testSetter |
|
1143 ? MethodHandles.arrayElementGetter(arrayType) |
|
1144 : MethodHandles.arrayElementSetter(arrayType); |
|
1145 assertSame(mh.type(), expType); |
|
1146 if (elemType != int.class && elemType != boolean.class) { |
|
1147 MethodType gtype; |
|
1148 if (true) { // FIXME: remove this path (and remove <void> below in the mh.invokes) |
|
1149 gtype = mh.type().changeParameterType(0, Object.class); |
|
1150 if (testSetter) |
|
1151 gtype = gtype.changeParameterType(2, Object.class); |
|
1152 else |
|
1153 gtype = gtype.changeReturnType(Object.class); |
|
1154 } else |
|
1155 // FIXME: This simpler path hits a bug in convertArguments => ToGeneric |
|
1156 gtype = mh.type().generic().changeParameterType(1, int.class); |
|
1157 mh = MethodHandles.convertArguments(mh, gtype); |
|
1158 } |
|
1159 Object sawValue, expValue; |
|
1160 List<Object> model = array2list(array); |
|
1161 int length = Array.getLength(array); |
|
1162 for (int i = 0; i < length; i++) { |
|
1163 // update array element |
|
1164 Object random = randomArg(elemType); |
|
1165 model.set(i, random); |
|
1166 if (testSetter) { |
|
1167 if (elemType == int.class) |
|
1168 mh.invokeExact((int[]) array, i, (int)random); |
|
1169 else if (elemType == boolean.class) |
|
1170 mh.invokeExact((boolean[]) array, i, (boolean)random); |
|
1171 else |
|
1172 mh.invokeExact(array, i, random); |
|
1173 assertEquals(model, array2list(array)); |
|
1174 } else { |
|
1175 Array.set(array, i, random); |
|
1176 } |
|
1177 if (verbosity >= 5) { |
|
1178 List<Object> array2list = array2list(array); |
|
1179 System.out.println("a["+i+"]="+random+" => "+array2list); |
|
1180 if (!array2list.equals(model)) |
|
1181 System.out.println("*** != "+model); |
|
1182 } |
|
1183 // observe array element |
|
1184 sawValue = Array.get(array, i); |
|
1185 if (!testSetter) { |
|
1186 expValue = sawValue; |
|
1187 if (elemType == int.class) |
|
1188 sawValue = (int) mh.invokeExact((int[]) array, i); |
|
1189 else if (elemType == boolean.class) |
|
1190 sawValue = (boolean) mh.invokeExact((boolean[]) array, i); |
|
1191 else |
|
1192 sawValue = mh.invokeExact(array, i); |
|
1193 assertEquals(sawValue, expValue); |
|
1194 assertEquals(model, array2list(array)); |
|
1195 } |
|
1196 } |
|
1197 } |
|
1198 |
|
1199 List<Object> array2list(Object array) { |
|
1200 int length = Array.getLength(array); |
|
1201 ArrayList<Object> model = new ArrayList<Object>(length); |
|
1202 for (int i = 0; i < length; i++) |
|
1203 model.add(Array.get(array, i)); |
|
1204 return model; |
|
1205 } |
|
1206 |
|
1207 static class Callee { |
|
1208 static Object id() { return called("id"); } |
|
1209 static Object id(Object x) { return called("id", x); } |
|
1210 static Object id(Object x, Object y) { return called("id", x, y); } |
|
1211 static Object id(Object x, Object y, Object z) { return called("id", x, y, z); } |
|
1212 static Object id(Object... vx) { return called("id", vx); } |
|
1213 static MethodHandle ofType(int n) { |
|
1214 return ofType(Object.class, n); |
|
1215 } |
|
1216 static MethodHandle ofType(Class<?> rtype, int n) { |
|
1217 if (n == -1) |
|
1218 return ofType(MethodType.methodType(rtype, Object[].class)); |
|
1219 return ofType(MethodType.genericMethodType(n).changeReturnType(rtype)); |
|
1220 } |
|
1221 static MethodHandle ofType(Class<?> rtype, Class<?>... ptypes) { |
|
1222 return ofType(MethodType.methodType(rtype, ptypes)); |
|
1223 } |
|
1224 static MethodHandle ofType(MethodType type) { |
|
1225 Class<?> rtype = type.returnType(); |
|
1226 String pfx = ""; |
|
1227 if (rtype != Object.class) |
|
1228 pfx = rtype.getSimpleName().substring(0, 1).toLowerCase(); |
|
1229 String name = pfx+"id"; |
|
1230 try { |
|
1231 return PRIVATE.findStatic(Callee.class, name, type); |
|
1232 } catch (Exception ex) { |
|
1233 throw new RuntimeException(ex); |
|
1234 } |
|
1235 } |
|
1236 } |
|
1237 |
|
1238 @Test |
|
1239 public void testConvertArguments() throws Throwable { |
|
1240 if (CAN_SKIP_WORKING) return; |
|
1241 startTest("convertArguments"); |
|
1242 testConvert(Callee.ofType(1), null, "id", int.class); |
|
1243 testConvert(Callee.ofType(1), null, "id", String.class); |
|
1244 testConvert(Callee.ofType(1), null, "id", Integer.class); |
|
1245 testConvert(Callee.ofType(1), null, "id", short.class); |
|
1246 testConvert(Callee.ofType(1), null, "id", char.class); |
|
1247 testConvert(Callee.ofType(1), null, "id", byte.class); |
|
1248 } |
|
1249 |
|
1250 void testConvert(MethodHandle id, Class<?> rtype, String name, Class<?>... params) throws Throwable { |
|
1251 testConvert(true, false, id, rtype, name, params); |
|
1252 testConvert(true, true, id, rtype, name, params); |
|
1253 } |
|
1254 |
|
1255 void testConvert(boolean positive, boolean useAsType, |
|
1256 MethodHandle id, Class<?> rtype, String name, Class<?>... params) throws Throwable { |
|
1257 countTest(positive); |
|
1258 MethodType idType = id.type(); |
|
1259 if (rtype == null) rtype = idType.returnType(); |
|
1260 for (int i = 0; i < params.length; i++) { |
|
1261 if (params[i] == null) params[i] = idType.parameterType(i); |
|
1262 } |
|
1263 // simulate the pairwise conversion |
|
1264 MethodType newType = MethodType.methodType(rtype, params); |
|
1265 Object[] args = randomArgs(newType.parameterArray()); |
|
1266 Object[] convArgs = args.clone(); |
|
1267 for (int i = 0; i < args.length; i++) { |
|
1268 Class<?> src = newType.parameterType(i); |
|
1269 Class<?> dst = idType.parameterType(i); |
|
1270 if (src != dst) |
|
1271 convArgs[i] = castToWrapper(convArgs[i], dst); |
|
1272 } |
|
1273 Object convResult = id.invokeWithArguments(convArgs); |
|
1274 { |
|
1275 Class<?> dst = newType.returnType(); |
|
1276 Class<?> src = idType.returnType(); |
|
1277 if (src != dst) |
|
1278 convResult = castToWrapper(convResult, dst); |
|
1279 } |
|
1280 MethodHandle target = null; |
|
1281 RuntimeException error = null; |
|
1282 try { |
|
1283 if (useAsType) |
|
1284 target = id.asType(newType); |
|
1285 else |
|
1286 target = MethodHandles.convertArguments(id, newType); |
|
1287 } catch (RuntimeException ex) { |
|
1288 error = ex; |
|
1289 } |
|
1290 if (verbosity >= 3) |
|
1291 System.out.println("convert "+id+ " to "+newType+" => "+target |
|
1292 +(error == null ? "" : " !! "+error)); |
|
1293 if (positive && error != null) throw error; |
|
1294 assertEquals(positive ? "positive test" : "negative test erroneously passed", positive, target != null); |
|
1295 if (!positive) return; // negative test failed as expected |
|
1296 assertEquals(newType, target.type()); |
|
1297 printCalled(target, id.toString(), args); |
|
1298 Object result = target.invokeWithArguments(args); |
|
1299 assertCalled(name, convArgs); |
|
1300 assertEquals(convResult, result); |
|
1301 if (verbosity >= 1) |
|
1302 System.out.print(':'); |
|
1303 } |
|
1304 |
|
1305 @Test |
|
1306 public void testVarargsCollector() throws Throwable { |
|
1307 MethodHandle vac0 = PRIVATE.findStatic(MethodHandlesTest.class, "called", |
|
1308 MethodType.methodType(Object.class, String.class, Object[].class)); |
|
1309 vac0 = vac0.bindTo("vac"); |
|
1310 MethodHandle vac = vac0.asVarargsCollector(Object[].class); |
|
1311 testConvert(true, true, vac.asType(MethodType.genericMethodType(0)), null, "vac"); |
|
1312 testConvert(true, true, vac.asType(MethodType.genericMethodType(0)), null, "vac"); |
|
1313 for (Class<?> at : new Class[] { Object.class, String.class, Integer.class }) { |
|
1314 testConvert(true, true, vac.asType(MethodType.genericMethodType(1)), null, "vac", at); |
|
1315 testConvert(true, true, vac.asType(MethodType.genericMethodType(2)), null, "vac", at, at); |
|
1316 } |
|
1317 } |
|
1318 |
|
1319 @Test |
|
1320 public void testPermuteArguments() throws Throwable { |
|
1321 if (CAN_SKIP_WORKING) return; |
|
1322 startTest("permuteArguments"); |
|
1323 testPermuteArguments(4, Integer.class, 2, String.class, 0); |
|
1324 //testPermuteArguments(6, Integer.class, 0, null, 30); |
|
1325 //testPermuteArguments(4, Integer.class, 1, int.class, 6); |
|
1326 } |
|
1327 public void testPermuteArguments(int max, Class<?> type1, int t2c, Class<?> type2, int dilution) throws Throwable { |
|
1328 if (verbosity >= 2) |
|
1329 System.out.println("permuteArguments "+max+"*"+type1.getName() |
|
1330 +(t2c==0?"":"/"+t2c+"*"+type2.getName()) |
|
1331 +(dilution > 0 ? " with dilution "+dilution : "")); |
|
1332 int t2pos = t2c == 0 ? 0 : 1; |
|
1333 for (int inargs = t2pos+1; inargs <= max; inargs++) { |
|
1334 Class<?>[] types = new Class<?>[inargs]; |
|
1335 Arrays.fill(types, type1); |
|
1336 if (t2c != 0) { |
|
1337 // Fill in a middle range with type2: |
|
1338 Arrays.fill(types, t2pos, Math.min(t2pos+t2c, inargs), type2); |
|
1339 } |
|
1340 Object[] args = randomArgs(types); |
|
1341 int numcases = 1; |
|
1342 for (int outargs = 0; outargs <= max; outargs++) { |
|
1343 if (outargs - inargs >= MAX_ARG_INCREASE) continue; |
|
1344 int[] reorder = new int[outargs]; |
|
1345 int casStep = dilution + 1; |
|
1346 // Avoid some common factors: |
|
1347 while ((casStep > 2 && casStep % 2 == 0 && inargs % 2 == 0) || |
|
1348 (casStep > 3 && casStep % 3 == 0 && inargs % 3 == 0)) |
|
1349 casStep++; |
|
1350 for (int cas = 0; cas < numcases; cas += casStep) { |
|
1351 for (int i = 0, c = cas; i < outargs; i++) { |
|
1352 reorder[i] = c % inargs; |
|
1353 c /= inargs; |
|
1354 } |
|
1355 testPermuteArguments(args, types, reorder); |
|
1356 } |
|
1357 numcases *= inargs; |
|
1358 if (dilution > 10 && outargs >= 4) { |
|
1359 // Do some special patterns, which we probably missed. |
|
1360 // Replication of a single argument or argument pair. |
|
1361 for (int i = 0; i < inargs; i++) { |
|
1362 Arrays.fill(reorder, i); |
|
1363 testPermuteArguments(args, types, reorder); |
|
1364 for (int d = 1; d <= 2; d++) { |
|
1365 if (i + d >= inargs) continue; |
|
1366 for (int j = 1; j < outargs; j += 2) |
|
1367 reorder[j] += 1; |
|
1368 testPermuteArguments(args, types, reorder); |
|
1369 testPermuteArguments(args, types, reverse(reorder)); |
|
1370 } |
|
1371 } |
|
1372 // Repetition of a sequence of 3 or more arguments. |
|
1373 for (int i = 1; i < inargs; i++) { |
|
1374 for (int len = 3; len <= inargs; len++) { |
|
1375 for (int j = 0; j < outargs; j++) |
|
1376 reorder[j] = (i + (j % len)) % inargs; |
|
1377 testPermuteArguments(args, types, reorder); |
|
1378 testPermuteArguments(args, types, reverse(reorder)); |
|
1379 } |
|
1380 } |
|
1381 } |
|
1382 } |
|
1383 } |
|
1384 } |
|
1385 |
|
1386 static int[] reverse(int[] reorder) { |
|
1387 reorder = reorder.clone(); |
|
1388 for (int i = 0, imax = reorder.length / 2; i < imax; i++) { |
|
1389 int j = reorder.length - 1 - i; |
|
1390 int tem = reorder[i]; |
|
1391 reorder[i] = reorder[j]; |
|
1392 reorder[j] = tem; |
|
1393 } |
|
1394 return reorder; |
|
1395 } |
|
1396 |
|
1397 void testPermuteArguments(Object[] args, Class<?>[] types, int[] reorder) throws Throwable { |
|
1398 countTest(); |
|
1399 if (args == null && types == null) { |
|
1400 int max = 0; |
|
1401 for (int j : reorder) { |
|
1402 if (max < j) max = j; |
|
1403 } |
|
1404 args = randomArgs(max+1, Integer.class); |
|
1405 } |
|
1406 if (args == null) { |
|
1407 args = randomArgs(types); |
|
1408 } |
|
1409 if (types == null) { |
|
1410 types = new Class<?>[args.length]; |
|
1411 for (int i = 0; i < args.length; i++) |
|
1412 types[i] = args[i].getClass(); |
|
1413 } |
|
1414 int inargs = args.length, outargs = reorder.length; |
|
1415 assertTrue(inargs == types.length); |
|
1416 if (verbosity >= 3) |
|
1417 System.out.println("permuteArguments "+Arrays.toString(reorder)); |
|
1418 Object[] permArgs = new Object[outargs]; |
|
1419 Class<?>[] permTypes = new Class<?>[outargs]; |
|
1420 for (int i = 0; i < outargs; i++) { |
|
1421 permArgs[i] = args[reorder[i]]; |
|
1422 permTypes[i] = types[reorder[i]]; |
|
1423 } |
|
1424 if (verbosity >= 4) { |
|
1425 System.out.println("in args: "+Arrays.asList(args)); |
|
1426 System.out.println("out args: "+Arrays.asList(permArgs)); |
|
1427 System.out.println("in types: "+Arrays.asList(types)); |
|
1428 System.out.println("out types: "+Arrays.asList(permTypes)); |
|
1429 } |
|
1430 MethodType inType = MethodType.methodType(Object.class, types); |
|
1431 MethodType outType = MethodType.methodType(Object.class, permTypes); |
|
1432 MethodHandle target = MethodHandles.convertArguments(varargsList(outargs), outType); |
|
1433 MethodHandle newTarget = MethodHandles.permuteArguments(target, inType, reorder); |
|
1434 Object result = newTarget.invokeWithArguments(args); |
|
1435 Object expected = Arrays.asList(permArgs); |
|
1436 assertEquals(expected, result); |
|
1437 } |
|
1438 |
|
1439 |
|
1440 @Test |
|
1441 public void testSpreadArguments() throws Throwable { |
|
1442 if (CAN_SKIP_WORKING) return; |
|
1443 startTest("spreadArguments"); |
|
1444 for (Class<?> argType : new Class[]{Object.class, Integer.class, int.class}) { |
|
1445 if (verbosity >= 3) |
|
1446 System.out.println("spreadArguments "+argType); |
|
1447 // FIXME: enable _adapter_spread_args and fix Fail_2 |
|
1448 for (int nargs = 0; nargs < 10; nargs++) { |
|
1449 if (argType == int.class && nargs >= 6) continue; // FIXME Fail_1 |
|
1450 for (int pos = 0; pos < nargs; pos++) { |
|
1451 if (argType == int.class && pos > 0) continue; // FIXME Fail_3 |
|
1452 testSpreadArguments(argType, pos, nargs); |
|
1453 } |
|
1454 } |
|
1455 } |
|
1456 } |
|
1457 public void testSpreadArguments(Class<?> argType, int pos, int nargs) throws Throwable { |
|
1458 countTest(); |
|
1459 MethodHandle target = varargsArray(nargs); |
|
1460 MethodHandle target2 = changeArgTypes(target, argType); |
|
1461 if (verbosity >= 3) |
|
1462 System.out.println("spread into "+target2+" ["+pos+".."+nargs+"]"); |
|
1463 Object[] args = randomArgs(target2.type().parameterArray()); |
|
1464 // make sure the target does what we think it does: |
|
1465 if (pos == 0 && nargs < 5) { |
|
1466 Object[] check = (Object[]) target.invokeWithArguments(args); |
|
1467 assertArrayEquals(args, check); |
|
1468 switch (nargs) { |
|
1469 case 0: |
|
1470 check = (Object[]) target.invokeExact(); |
|
1471 assertArrayEquals(args, check); |
|
1472 break; |
|
1473 case 1: |
|
1474 check = (Object[]) target.invokeExact(args[0]); |
|
1475 assertArrayEquals(args, check); |
|
1476 break; |
|
1477 case 2: |
|
1478 check = (Object[]) target.invokeExact(args[0], args[1]); |
|
1479 assertArrayEquals(args, check); |
|
1480 break; |
|
1481 } |
|
1482 } |
|
1483 List<Class<?>> newParams = new ArrayList<Class<?>>(target2.type().parameterList()); |
|
1484 { // modify newParams in place |
|
1485 List<Class<?>> spreadParams = newParams.subList(pos, nargs); |
|
1486 spreadParams.clear(); spreadParams.add(Object[].class); |
|
1487 } |
|
1488 MethodType newType = MethodType.methodType(Object.class, newParams); |
|
1489 MethodHandle result = target2.asSpreader(Object[].class, nargs-pos).asType(newType); |
|
1490 Object[] returnValue; |
|
1491 if (pos == 0) { |
|
1492 // In the following line, the first cast implies |
|
1493 // normal Object return value for the MH call (Object[])->Object, |
|
1494 // while the second cast dynamically converts to an Object array. |
|
1495 // Such a double cast is typical of MH.invokeExact. |
|
1496 returnValue = (Object[]) (Object) result.invokeExact(args); |
|
1497 } else { |
|
1498 Object[] args1 = Arrays.copyOfRange(args, 0, pos+1); |
|
1499 args1[pos] = Arrays.copyOfRange(args, pos, args.length); |
|
1500 returnValue = (Object[]) result.invokeWithArguments(args1); |
|
1501 } |
|
1502 assertArrayEquals(args, returnValue); |
|
1503 } |
|
1504 |
|
1505 @Test |
|
1506 public void testCollectArguments() throws Throwable { |
|
1507 if (CAN_SKIP_WORKING) return; |
|
1508 startTest("collectArguments"); |
|
1509 for (Class<?> argType : new Class[]{Object.class, Integer.class, int.class}) { |
|
1510 if (verbosity >= 3) |
|
1511 System.out.println("collectArguments "+argType); |
|
1512 for (int nargs = 0; nargs < 10; nargs++) { |
|
1513 for (int pos = 0; pos < nargs; pos++) { |
|
1514 if (argType == int.class) continue; // FIXME Fail_4 |
|
1515 testCollectArguments(argType, pos, nargs); |
|
1516 } |
|
1517 } |
|
1518 } |
|
1519 } |
|
1520 public void testCollectArguments(Class<?> argType, int pos, int nargs) throws Throwable { |
|
1521 countTest(); |
|
1522 // fake up a MH with the same type as the desired adapter: |
|
1523 MethodHandle fake = varargsArray(nargs); |
|
1524 fake = changeArgTypes(fake, argType); |
|
1525 MethodType newType = fake.type(); |
|
1526 Object[] args = randomArgs(newType.parameterArray()); |
|
1527 // here is what should happen: |
|
1528 Object[] collectedArgs = Arrays.copyOfRange(args, 0, pos+1); |
|
1529 collectedArgs[pos] = Arrays.copyOfRange(args, pos, args.length); |
|
1530 // here is the MH which will witness the collected argument tail: |
|
1531 MethodHandle target = varargsArray(pos+1); |
|
1532 target = changeArgTypes(target, 0, pos, argType); |
|
1533 target = changeArgTypes(target, pos, pos+1, Object[].class); |
|
1534 if (verbosity >= 3) |
|
1535 System.out.println("collect from "+Arrays.asList(args)+" ["+pos+".."+nargs+"]"); |
|
1536 MethodHandle result = target.asCollector(Object[].class, nargs-pos).asType(newType); |
|
1537 Object[] returnValue = (Object[]) result.invokeWithArguments(args); |
|
1538 // assertTrue(returnValue.length == pos+1 && returnValue[pos] instanceof Object[]); |
|
1539 // returnValue[pos] = Arrays.asList((Object[]) returnValue[pos]); |
|
1540 // collectedArgs[pos] = Arrays.asList((Object[]) collectedArgs[pos]); |
|
1541 assertArrayEquals(collectedArgs, returnValue); |
|
1542 } |
|
1543 |
|
1544 @Test |
|
1545 public void testInsertArguments() throws Throwable { |
|
1546 if (CAN_SKIP_WORKING) return; |
|
1547 startTest("insertArguments"); |
|
1548 for (int nargs = 0; nargs <= 4; nargs++) { |
|
1549 for (int ins = 0; ins <= 4; ins++) { |
|
1550 if (ins > MAX_ARG_INCREASE) continue; // FIXME Fail_6 |
|
1551 for (int pos = 0; pos <= nargs; pos++) { |
|
1552 testInsertArguments(nargs, pos, ins); |
|
1553 } |
|
1554 } |
|
1555 } |
|
1556 } |
|
1557 |
|
1558 void testInsertArguments(int nargs, int pos, int ins) throws Throwable { |
|
1559 countTest(); |
|
1560 MethodHandle target = varargsArray(nargs + ins); |
|
1561 Object[] args = randomArgs(target.type().parameterArray()); |
|
1562 List<Object> resList = Arrays.asList(args); |
|
1563 List<Object> argsToPass = new ArrayList<Object>(resList); |
|
1564 List<Object> argsToInsert = argsToPass.subList(pos, pos + ins); |
|
1565 if (verbosity >= 3) |
|
1566 System.out.println("insert: "+argsToInsert+" into "+target); |
|
1567 MethodHandle target2 = MethodHandles.insertArguments(target, pos, |
|
1568 (Object[]) argsToInsert.toArray()); |
|
1569 argsToInsert.clear(); // remove from argsToInsert |
|
1570 Object res2 = target2.invokeWithArguments(argsToPass); |
|
1571 Object res2List = Arrays.asList((Object[])res2); |
|
1572 if (verbosity >= 3) |
|
1573 System.out.println("result: "+res2List); |
|
1574 //if (!resList.equals(res2List)) |
|
1575 // System.out.println("*** fail at n/p/i = "+nargs+"/"+pos+"/"+ins+": "+resList+" => "+res2List); |
|
1576 assertEquals(resList, res2List); |
|
1577 } |
|
1578 |
|
1579 @Test |
|
1580 public void testFilterReturnValue() throws Throwable { |
|
1581 if (CAN_SKIP_WORKING) return; |
|
1582 startTest("filterReturnValue"); |
|
1583 Class<?> classOfVCList = varargsList(1).invokeWithArguments(0).getClass(); |
|
1584 assertTrue(List.class.isAssignableFrom(classOfVCList)); |
|
1585 for (int nargs = 0; nargs <= 3; nargs++) { |
|
1586 for (Class<?> rtype : new Class[] { Object.class, |
|
1587 List.class, |
|
1588 int.class, |
|
1589 //byte.class, //FIXME: add this |
|
1590 //long.class, //FIXME: add this |
|
1591 CharSequence.class, |
|
1592 String.class }) { |
|
1593 testFilterReturnValue(nargs, rtype); |
|
1594 } |
|
1595 } |
|
1596 } |
|
1597 |
|
1598 void testFilterReturnValue(int nargs, Class<?> rtype) throws Throwable { |
|
1599 countTest(); |
|
1600 MethodHandle target = varargsList(nargs, rtype); |
|
1601 MethodHandle filter; |
|
1602 if (List.class.isAssignableFrom(rtype) || rtype.isAssignableFrom(List.class)) |
|
1603 filter = varargsList(1); // add another layer of list-ness |
|
1604 else |
|
1605 filter = MethodHandles.identity(rtype); |
|
1606 filter = filter.asType(MethodType.methodType(target.type().returnType(), rtype)); |
|
1607 Object[] argsToPass = randomArgs(nargs, Object.class); |
|
1608 if (verbosity >= 3) |
|
1609 System.out.println("filter "+target+" to "+rtype.getSimpleName()+" with "+filter); |
|
1610 MethodHandle target2 = MethodHandles.filterReturnValue(target, filter); |
|
1611 if (verbosity >= 4) |
|
1612 System.out.println("filtered target: "+target2); |
|
1613 // Simulate expected effect of filter on return value: |
|
1614 Object unfiltered = target.invokeWithArguments(argsToPass); |
|
1615 Object expected = filter.invokeWithArguments(unfiltered); |
|
1616 if (verbosity >= 4) |
|
1617 System.out.println("unfiltered: "+unfiltered+" : "+unfiltered.getClass().getSimpleName()); |
|
1618 if (verbosity >= 4) |
|
1619 System.out.println("expected: "+expected+" : "+expected.getClass().getSimpleName()); |
|
1620 Object result = target2.invokeWithArguments(argsToPass); |
|
1621 if (verbosity >= 3) |
|
1622 System.out.println("result: "+result+" : "+result.getClass().getSimpleName()); |
|
1623 if (!expected.equals(result)) |
|
1624 System.out.println("*** fail at n/rt = "+nargs+"/"+rtype.getSimpleName()+": "+Arrays.asList(argsToPass)+" => "+result+" != "+expected); |
|
1625 assertEquals(expected, result); |
|
1626 } |
|
1627 |
|
1628 @Test |
|
1629 public void testFilterArguments() throws Throwable { |
|
1630 if (CAN_SKIP_WORKING) return; |
|
1631 startTest("filterArguments"); |
|
1632 for (int nargs = 1; nargs <= 6; nargs++) { |
|
1633 for (int pos = 0; pos < nargs; pos++) { |
|
1634 testFilterArguments(nargs, pos); |
|
1635 } |
|
1636 } |
|
1637 } |
|
1638 |
|
1639 void testFilterArguments(int nargs, int pos) throws Throwable { |
|
1640 countTest(); |
|
1641 MethodHandle target = varargsList(nargs); |
|
1642 MethodHandle filter = varargsList(1); |
|
1643 filter = MethodHandles.convertArguments(filter, filter.type().generic()); |
|
1644 Object[] argsToPass = randomArgs(nargs, Object.class); |
|
1645 if (verbosity >= 3) |
|
1646 System.out.println("filter "+target+" at "+pos+" with "+filter); |
|
1647 MethodHandle target2 = MethodHandles.filterArguments(target, pos, filter); |
|
1648 // Simulate expected effect of filter on arglist: |
|
1649 Object[] filteredArgs = argsToPass.clone(); |
|
1650 filteredArgs[pos] = filter.invokeExact(filteredArgs[pos]); |
|
1651 List<Object> expected = Arrays.asList(filteredArgs); |
|
1652 Object result = target2.invokeWithArguments(argsToPass); |
|
1653 if (verbosity >= 3) |
|
1654 System.out.println("result: "+result); |
|
1655 if (!expected.equals(result)) |
|
1656 System.out.println("*** fail at n/p = "+nargs+"/"+pos+": "+Arrays.asList(argsToPass)+" => "+result+" != "+expected); |
|
1657 assertEquals(expected, result); |
|
1658 } |
|
1659 |
|
1660 @Test |
|
1661 public void testFoldArguments() throws Throwable { |
|
1662 if (CAN_SKIP_WORKING) return; |
|
1663 startTest("foldArguments"); |
|
1664 for (int nargs = 0; nargs <= 4; nargs++) { |
|
1665 for (int fold = 0; fold <= nargs; fold++) { |
|
1666 for (int pos = 0; pos <= nargs; pos++) { |
|
1667 testFoldArguments(nargs, pos, fold); |
|
1668 } |
|
1669 } |
|
1670 } |
|
1671 } |
|
1672 |
|
1673 void testFoldArguments(int nargs, int pos, int fold) throws Throwable { |
|
1674 if (pos != 0) return; // can fold only at pos=0 for now |
|
1675 countTest(); |
|
1676 MethodHandle target = varargsList(1 + nargs); |
|
1677 MethodHandle combine = varargsList(fold).asType(MethodType.genericMethodType(fold)); |
|
1678 List<Object> argsToPass = Arrays.asList(randomArgs(nargs, Object.class)); |
|
1679 if (verbosity >= 3) |
|
1680 System.out.println("fold "+target+" with "+combine); |
|
1681 MethodHandle target2 = MethodHandles.foldArguments(target, combine); |
|
1682 // Simulate expected effect of combiner on arglist: |
|
1683 List<Object> expected = new ArrayList<Object>(argsToPass); |
|
1684 List<Object> argsToFold = expected.subList(pos, pos + fold); |
|
1685 if (verbosity >= 3) |
|
1686 System.out.println("fold: "+argsToFold+" into "+target2); |
|
1687 Object foldedArgs = combine.invokeWithArguments(argsToFold); |
|
1688 argsToFold.add(0, foldedArgs); |
|
1689 Object result = target2.invokeWithArguments(argsToPass); |
|
1690 if (verbosity >= 3) |
|
1691 System.out.println("result: "+result); |
|
1692 if (!expected.equals(result)) |
|
1693 System.out.println("*** fail at n/p/f = "+nargs+"/"+pos+"/"+fold+": "+argsToPass+" => "+result+" != "+expected); |
|
1694 assertEquals(expected, result); |
|
1695 } |
|
1696 |
|
1697 @Test |
|
1698 public void testDropArguments() throws Throwable { |
|
1699 if (CAN_SKIP_WORKING) return; |
|
1700 startTest("dropArguments"); |
|
1701 for (int nargs = 0; nargs <= 4; nargs++) { |
|
1702 for (int drop = 1; drop <= 4; drop++) { |
|
1703 for (int pos = 0; pos <= nargs; pos++) { |
|
1704 testDropArguments(nargs, pos, drop); |
|
1705 } |
|
1706 } |
|
1707 } |
|
1708 } |
|
1709 |
|
1710 void testDropArguments(int nargs, int pos, int drop) throws Throwable { |
|
1711 countTest(); |
|
1712 MethodHandle target = varargsArray(nargs); |
|
1713 Object[] args = randomArgs(target.type().parameterArray()); |
|
1714 MethodHandle target2 = MethodHandles.dropArguments(target, pos, |
|
1715 Collections.nCopies(drop, Object.class).toArray(new Class[0])); |
|
1716 List<Object> resList = Arrays.asList(args); |
|
1717 List<Object> argsToDrop = new ArrayList<Object>(resList); |
|
1718 for (int i = drop; i > 0; i--) { |
|
1719 argsToDrop.add(pos, "blort#"+i); |
|
1720 } |
|
1721 Object res2 = target2.invokeWithArguments(argsToDrop); |
|
1722 Object res2List = Arrays.asList((Object[])res2); |
|
1723 //if (!resList.equals(res2List)) |
|
1724 // System.out.println("*** fail at n/p/d = "+nargs+"/"+pos+"/"+drop+": "+argsToDrop+" => "+res2List); |
|
1725 assertEquals(resList, res2List); |
|
1726 } |
|
1727 |
|
1728 @Test |
|
1729 public void testInvokers() throws Throwable { |
|
1730 if (CAN_SKIP_WORKING) return; |
|
1731 startTest("exactInvoker, genericInvoker, varargsInvoker, dynamicInvoker"); |
|
1732 // exactInvoker, genericInvoker, varargsInvoker[0..N], dynamicInvoker |
|
1733 Set<MethodType> done = new HashSet<MethodType>(); |
|
1734 for (int i = 0; i <= 6; i++) { |
|
1735 MethodType gtype = MethodType.genericMethodType(i); |
|
1736 for (Class<?> argType : new Class[]{Object.class, Integer.class, int.class}) { |
|
1737 for (int j = -1; j < i; j++) { |
|
1738 MethodType type = gtype; |
|
1739 if (j < 0) |
|
1740 type = type.changeReturnType(argType); |
|
1741 else if (argType == void.class) |
|
1742 continue; |
|
1743 else |
|
1744 type = type.changeParameterType(j, argType); |
|
1745 if (argType.isPrimitive() && j != i-1) continue; // FIXME Fail_5 |
|
1746 if (done.add(type)) |
|
1747 testInvokers(type); |
|
1748 MethodType vtype = type.changeReturnType(void.class); |
|
1749 if (done.add(vtype)) |
|
1750 testInvokers(vtype); |
|
1751 } |
|
1752 } |
|
1753 } |
|
1754 } |
|
1755 |
|
1756 public void testInvokers(MethodType type) throws Throwable { |
|
1757 if (verbosity >= 3) |
|
1758 System.out.println("test invokers for "+type); |
|
1759 int nargs = type.parameterCount(); |
|
1760 boolean testRetCode = type.returnType() != void.class; |
|
1761 MethodHandle target = PRIVATE.findStatic(MethodHandlesTest.class, "invokee", |
|
1762 MethodType.genericMethodType(0, true)); |
|
1763 assertTrue(target.isVarargsCollector()); |
|
1764 target = target.asType(type); |
|
1765 Object[] args = randomArgs(type.parameterArray()); |
|
1766 List<Object> targetPlusArgs = new ArrayList<Object>(Arrays.asList(args)); |
|
1767 targetPlusArgs.add(0, target); |
|
1768 int code = (Integer) invokee(args); |
|
1769 Object log = logEntry("invokee", args); |
|
1770 assertEquals(log.hashCode(), code); |
|
1771 assertCalled("invokee", args); |
|
1772 MethodHandle inv; |
|
1773 Object result; |
|
1774 // exact invoker |
|
1775 countTest(); |
|
1776 calledLog.clear(); |
|
1777 inv = MethodHandles.exactInvoker(type); |
|
1778 result = inv.invokeWithArguments(targetPlusArgs); |
|
1779 if (testRetCode) assertEquals(code, result); |
|
1780 assertCalled("invokee", args); |
|
1781 // generic invoker |
|
1782 countTest(); |
|
1783 inv = MethodHandles.genericInvoker(type); |
|
1784 if (nargs <= 3) { |
|
1785 calledLog.clear(); |
|
1786 switch (nargs) { |
|
1787 case 0: |
|
1788 result = inv.invokeExact(target); |
|
1789 break; |
|
1790 case 1: |
|
1791 result = inv.invokeExact(target, args[0]); |
|
1792 break; |
|
1793 case 2: |
|
1794 result = inv.invokeExact(target, args[0], args[1]); |
|
1795 break; |
|
1796 case 3: |
|
1797 result = inv.invokeExact(target, args[0], args[1], args[2]); |
|
1798 break; |
|
1799 } |
|
1800 if (testRetCode) assertEquals(code, result); |
|
1801 assertCalled("invokee", args); |
|
1802 } |
|
1803 calledLog.clear(); |
|
1804 result = inv.invokeWithArguments(targetPlusArgs); |
|
1805 if (testRetCode) assertEquals(code, result); |
|
1806 assertCalled("invokee", args); |
|
1807 // varargs invoker #0 |
|
1808 calledLog.clear(); |
|
1809 inv = MethodHandles.spreadInvoker(type, 0); |
|
1810 result = inv.invokeExact(target, args); |
|
1811 if (testRetCode) assertEquals(code, result); |
|
1812 assertCalled("invokee", args); |
|
1813 if (nargs >= 1) { |
|
1814 // varargs invoker #1 |
|
1815 calledLog.clear(); |
|
1816 inv = MethodHandles.spreadInvoker(type, 1); |
|
1817 result = inv.invokeExact(target, args[0], Arrays.copyOfRange(args, 1, nargs)); |
|
1818 if (testRetCode) assertEquals(code, result); |
|
1819 assertCalled("invokee", args); |
|
1820 } |
|
1821 if (nargs >= 2) { |
|
1822 // varargs invoker #2 |
|
1823 calledLog.clear(); |
|
1824 inv = MethodHandles.spreadInvoker(type, 2); |
|
1825 result = inv.invokeExact(target, args[0], args[1], Arrays.copyOfRange(args, 2, nargs)); |
|
1826 if (testRetCode) assertEquals(code, result); |
|
1827 assertCalled("invokee", args); |
|
1828 } |
|
1829 if (nargs >= 3) { |
|
1830 // varargs invoker #3 |
|
1831 calledLog.clear(); |
|
1832 inv = MethodHandles.spreadInvoker(type, 3); |
|
1833 result = inv.invokeExact(target, args[0], args[1], args[2], Arrays.copyOfRange(args, 3, nargs)); |
|
1834 if (testRetCode) assertEquals(code, result); |
|
1835 assertCalled("invokee", args); |
|
1836 } |
|
1837 for (int k = 0; k <= nargs; k++) { |
|
1838 // varargs invoker #0..N |
|
1839 countTest(); |
|
1840 calledLog.clear(); |
|
1841 inv = MethodHandles.spreadInvoker(type, k); |
|
1842 List<Object> targetPlusVarArgs = new ArrayList<Object>(targetPlusArgs); |
|
1843 List<Object> tailList = targetPlusVarArgs.subList(1+k, 1+nargs); |
|
1844 Object[] tail = tailList.toArray(); |
|
1845 tailList.clear(); tailList.add(tail); |
|
1846 result = inv.invokeWithArguments(targetPlusVarArgs); |
|
1847 if (testRetCode) assertEquals(code, result); |
|
1848 assertCalled("invokee", args); |
|
1849 } |
|
1850 |
|
1851 // dynamic invoker |
|
1852 countTest(); |
|
1853 CallSite site = new MutableCallSite(type); |
|
1854 inv = site.dynamicInvoker(); |
|
1855 |
|
1856 // see if we get the result of the original target: |
|
1857 try { |
|
1858 result = inv.invokeWithArguments(args); |
|
1859 assertTrue("should not reach here", false); |
|
1860 } catch (IllegalStateException ex) { |
|
1861 String msg = ex.getMessage(); |
|
1862 assertTrue(msg, msg.contains("site")); |
|
1863 } |
|
1864 |
|
1865 // set new target after invoker is created, to make sure we track target |
|
1866 site.setTarget(target); |
|
1867 calledLog.clear(); |
|
1868 result = inv.invokeWithArguments(args); |
|
1869 if (testRetCode) assertEquals(code, result); |
|
1870 assertCalled("invokee", args); |
|
1871 } |
|
1872 |
|
1873 static Object invokee(Object... args) { |
|
1874 return called("invokee", args).hashCode(); |
|
1875 } |
|
1876 |
|
1877 private static final String MISSING_ARG = "missingArg"; |
|
1878 static Object targetIfEquals() { |
|
1879 return called("targetIfEquals"); |
|
1880 } |
|
1881 static Object fallbackIfNotEquals() { |
|
1882 return called("fallbackIfNotEquals"); |
|
1883 } |
|
1884 static Object targetIfEquals(Object x) { |
|
1885 assertEquals(x, MISSING_ARG); |
|
1886 return called("targetIfEquals", x); |
|
1887 } |
|
1888 static Object fallbackIfNotEquals(Object x) { |
|
1889 assertFalse(x.toString(), x.equals(MISSING_ARG)); |
|
1890 return called("fallbackIfNotEquals", x); |
|
1891 } |
|
1892 static Object targetIfEquals(Object x, Object y) { |
|
1893 assertEquals(x, y); |
|
1894 return called("targetIfEquals", x, y); |
|
1895 } |
|
1896 static Object fallbackIfNotEquals(Object x, Object y) { |
|
1897 assertFalse(x.toString(), x.equals(y)); |
|
1898 return called("fallbackIfNotEquals", x, y); |
|
1899 } |
|
1900 static Object targetIfEquals(Object x, Object y, Object z) { |
|
1901 assertEquals(x, y); |
|
1902 return called("targetIfEquals", x, y, z); |
|
1903 } |
|
1904 static Object fallbackIfNotEquals(Object x, Object y, Object z) { |
|
1905 assertFalse(x.toString(), x.equals(y)); |
|
1906 return called("fallbackIfNotEquals", x, y, z); |
|
1907 } |
|
1908 |
|
1909 @Test |
|
1910 public void testGuardWithTest() throws Throwable { |
|
1911 if (CAN_SKIP_WORKING) return; |
|
1912 startTest("guardWithTest"); |
|
1913 for (int nargs = 0; nargs <= 3; nargs++) { |
|
1914 if (nargs != 2) continue; // FIXME: test more later |
|
1915 testGuardWithTest(nargs, Object.class); |
|
1916 testGuardWithTest(nargs, String.class); |
|
1917 } |
|
1918 } |
|
1919 void testGuardWithTest(int nargs, Class<?> argClass) throws Throwable { |
|
1920 countTest(); |
|
1921 MethodHandle test = PRIVATE.findVirtual(Object.class, "equals", MethodType.methodType(boolean.class, Object.class)); |
|
1922 MethodHandle target = PRIVATE.findStatic(MethodHandlesTest.class, "targetIfEquals", MethodType.genericMethodType(nargs)); |
|
1923 MethodHandle fallback = PRIVATE.findStatic(MethodHandlesTest.class, "fallbackIfNotEquals", MethodType.genericMethodType(nargs)); |
|
1924 while (test.type().parameterCount() < nargs) |
|
1925 test = MethodHandles.dropArguments(test, test.type().parameterCount()-1, Object.class); |
|
1926 while (test.type().parameterCount() > nargs) |
|
1927 test = MethodHandles.insertArguments(test, 0, MISSING_ARG); |
|
1928 if (argClass != Object.class) { |
|
1929 test = changeArgTypes(test, argClass); |
|
1930 target = changeArgTypes(target, argClass); |
|
1931 fallback = changeArgTypes(fallback, argClass); |
|
1932 } |
|
1933 MethodHandle mh = MethodHandles.guardWithTest(test, target, fallback); |
|
1934 assertEquals(target.type(), mh.type()); |
|
1935 Object[][] argLists = { |
|
1936 { }, |
|
1937 { "foo" }, { MISSING_ARG }, |
|
1938 { "foo", "foo" }, { "foo", "bar" }, |
|
1939 { "foo", "foo", "baz" }, { "foo", "bar", "baz" } |
|
1940 }; |
|
1941 for (Object[] argList : argLists) { |
|
1942 if (argList.length != nargs) continue; |
|
1943 boolean equals; |
|
1944 switch (nargs) { |
|
1945 case 0: equals = true; break; |
|
1946 case 1: equals = MISSING_ARG.equals(argList[0]); break; |
|
1947 default: equals = argList[0].equals(argList[1]); break; |
|
1948 } |
|
1949 String willCall = (equals ? "targetIfEquals" : "fallbackIfNotEquals"); |
|
1950 if (verbosity >= 3) |
|
1951 System.out.println(logEntry(willCall, argList)); |
|
1952 Object result = mh.invokeWithArguments(argList); |
|
1953 assertCalled(willCall, argList); |
|
1954 } |
|
1955 } |
|
1956 |
|
1957 @Test |
|
1958 public void testCatchException() throws Throwable { |
|
1959 if (CAN_SKIP_WORKING) return; |
|
1960 startTest("catchException"); |
|
1961 for (int nargs = 2; nargs <= 6; nargs++) { |
|
1962 for (int ti = 0; ti <= 1; ti++) { |
|
1963 boolean throwIt = (ti != 0); |
|
1964 testCatchException(int.class, new ClassCastException("testing"), throwIt, nargs); |
|
1965 testCatchException(void.class, new java.io.IOException("testing"), throwIt, nargs); |
|
1966 testCatchException(String.class, new LinkageError("testing"), throwIt, nargs); |
|
1967 } |
|
1968 } |
|
1969 } |
|
1970 |
|
1971 private static <T extends Throwable> |
|
1972 Object throwOrReturn(Object normal, T exception) throws T { |
|
1973 if (exception != null) throw exception; |
|
1974 return normal; |
|
1975 } |
|
1976 |
|
1977 void testCatchException(Class<?> returnType, Throwable thrown, boolean throwIt, int nargs) throws Throwable { |
|
1978 countTest(); |
|
1979 if (verbosity >= 3) |
|
1980 System.out.println("catchException rt="+returnType+" throw="+throwIt+" nargs="+nargs); |
|
1981 Class<? extends Throwable> exType = thrown.getClass(); |
|
1982 MethodHandle throwOrReturn |
|
1983 = PRIVATE.findStatic(MethodHandlesTest.class, "throwOrReturn", |
|
1984 MethodType.methodType(Object.class, Object.class, Throwable.class)); |
|
1985 MethodHandle thrower = throwOrReturn.asType(MethodType.genericMethodType(2)); |
|
1986 while (thrower.type().parameterCount() < nargs) |
|
1987 thrower = MethodHandles.dropArguments(thrower, thrower.type().parameterCount(), Object.class); |
|
1988 MethodHandle catcher = varargsList(1+nargs).asType(MethodType.genericMethodType(1+nargs)); |
|
1989 MethodHandle target = MethodHandles.catchException(thrower, |
|
1990 thrown.getClass(), catcher); |
|
1991 assertEquals(thrower.type(), target.type()); |
|
1992 //System.out.println("catching with "+target+" : "+throwOrReturn); |
|
1993 Object[] args = randomArgs(nargs, Object.class); |
|
1994 args[1] = (throwIt ? thrown : null); |
|
1995 Object returned = target.invokeWithArguments(args); |
|
1996 //System.out.println("return from "+target+" : "+returned); |
|
1997 if (!throwIt) { |
|
1998 assertSame(args[0], returned); |
|
1999 } else { |
|
2000 List<Object> catchArgs = new ArrayList<Object>(Arrays.asList(args)); |
|
2001 catchArgs.add(0, thrown); |
|
2002 assertEquals(catchArgs, returned); |
|
2003 } |
|
2004 } |
|
2005 |
|
2006 @Test |
|
2007 public void testThrowException() throws Throwable { |
|
2008 if (CAN_SKIP_WORKING) return; |
|
2009 startTest("throwException"); |
|
2010 testThrowException(int.class, new ClassCastException("testing")); |
|
2011 testThrowException(void.class, new java.io.IOException("testing")); |
|
2012 testThrowException(String.class, new LinkageError("testing")); |
|
2013 } |
|
2014 |
|
2015 void testThrowException(Class<?> returnType, Throwable thrown) throws Throwable { |
|
2016 countTest(); |
|
2017 Class<? extends Throwable> exType = thrown.getClass(); |
|
2018 MethodHandle target = MethodHandles.throwException(returnType, exType); |
|
2019 //System.out.println("throwing with "+target+" : "+thrown); |
|
2020 MethodType expectedType = MethodType.methodType(returnType, exType); |
|
2021 assertEquals(expectedType, target.type()); |
|
2022 target = MethodHandles.convertArguments(target, target.type().generic()); |
|
2023 Throwable caught = null; |
|
2024 try { |
|
2025 Object res = target.invokeExact((Object) thrown); |
|
2026 fail("got "+res+" instead of throwing "+thrown); |
|
2027 } catch (Throwable ex) { |
|
2028 if (ex != thrown) { |
|
2029 if (ex instanceof Error) throw (Error)ex; |
|
2030 if (ex instanceof RuntimeException) throw (RuntimeException)ex; |
|
2031 } |
|
2032 caught = ex; |
|
2033 } |
|
2034 assertSame(thrown, caught); |
|
2035 } |
|
2036 |
|
2037 @Test |
|
2038 public void testCastFailure() throws Throwable { |
|
2039 if (CAN_SKIP_WORKING) return; |
|
2040 startTest("testCastFailure"); |
|
2041 testCastFailure("cast/argument", 11000); |
|
2042 testCastFailure("unbox/argument", 11000); |
|
2043 testCastFailure("cast/return", 11000); |
|
2044 testCastFailure("unbox/return", 11000); |
|
2045 } |
|
2046 |
|
2047 static class Surprise { |
|
2048 public MethodHandle asMethodHandle() { |
|
2049 return VALUE.bindTo(this); |
|
2050 } |
|
2051 Object value(Object x) { |
|
2052 trace("value", x); |
|
2053 if (boo != null) return boo; |
|
2054 return x; |
|
2055 } |
|
2056 Object boo; |
|
2057 void boo(Object x) { boo = x; } |
|
2058 |
|
2059 static void trace(String x, Object y) { |
|
2060 if (verbosity > 8) System.out.println(x+"="+y); |
|
2061 } |
|
2062 static Object refIdentity(Object x) { trace("ref.x", x); return x; } |
|
2063 static Integer boxIdentity(Integer x) { trace("box.x", x); return x; } |
|
2064 static int intIdentity(int x) { trace("int.x", x); return x; } |
|
2065 static MethodHandle VALUE, REF_IDENTITY, BOX_IDENTITY, INT_IDENTITY; |
|
2066 static { |
|
2067 try { |
|
2068 VALUE = PRIVATE.findVirtual( |
|
2069 Surprise.class, "value", |
|
2070 MethodType.methodType(Object.class, Object.class)); |
|
2071 REF_IDENTITY = PRIVATE.findStatic( |
|
2072 Surprise.class, "refIdentity", |
|
2073 MethodType.methodType(Object.class, Object.class)); |
|
2074 BOX_IDENTITY = PRIVATE.findStatic( |
|
2075 Surprise.class, "boxIdentity", |
|
2076 MethodType.methodType(Integer.class, Integer.class)); |
|
2077 INT_IDENTITY = PRIVATE.findStatic( |
|
2078 Surprise.class, "intIdentity", |
|
2079 MethodType.methodType(int.class, int.class)); |
|
2080 } catch (Exception ex) { |
|
2081 throw new RuntimeException(ex); |
|
2082 } |
|
2083 } |
|
2084 } |
|
2085 |
|
2086 void testCastFailure(String mode, int okCount) throws Throwable { |
|
2087 countTest(false); |
|
2088 if (verbosity > 2) System.out.println("mode="+mode); |
|
2089 Surprise boo = new Surprise(); |
|
2090 MethodHandle identity = Surprise.REF_IDENTITY, surprise0 = boo.asMethodHandle(), surprise = surprise0; |
|
2091 if (mode.endsWith("/return")) { |
|
2092 if (mode.equals("unbox/return")) { |
|
2093 // fail on return to ((Integer)surprise).intValue |
|
2094 surprise = MethodHandles.convertArguments(surprise, MethodType.methodType(int.class, Object.class)); |
|
2095 identity = MethodHandles.convertArguments(identity, MethodType.methodType(int.class, Object.class)); |
|
2096 } else if (mode.equals("cast/return")) { |
|
2097 // fail on return to (Integer)surprise |
|
2098 surprise = MethodHandles.convertArguments(surprise, MethodType.methodType(Integer.class, Object.class)); |
|
2099 identity = MethodHandles.convertArguments(identity, MethodType.methodType(Integer.class, Object.class)); |
|
2100 } |
|
2101 } else if (mode.endsWith("/argument")) { |
|
2102 MethodHandle callee = null; |
|
2103 if (mode.equals("unbox/argument")) { |
|
2104 // fail on handing surprise to int argument |
|
2105 callee = Surprise.INT_IDENTITY; |
|
2106 } else if (mode.equals("cast/argument")) { |
|
2107 // fail on handing surprise to Integer argument |
|
2108 callee = Surprise.BOX_IDENTITY; |
|
2109 } |
|
2110 if (callee != null) { |
|
2111 callee = MethodHandles.convertArguments(callee, MethodType.genericMethodType(1)); |
|
2112 surprise = MethodHandles.filterArguments(callee, 0, surprise); |
|
2113 identity = MethodHandles.filterArguments(callee, 0, identity); |
|
2114 } |
|
2115 } |
|
2116 assertNotSame(mode, surprise, surprise0); |
|
2117 identity = MethodHandles.convertArguments(identity, MethodType.genericMethodType(1)); |
|
2118 surprise = MethodHandles.convertArguments(surprise, MethodType.genericMethodType(1)); |
|
2119 Object x = 42; |
|
2120 for (int i = 0; i < okCount; i++) { |
|
2121 Object y = identity.invokeExact(x); |
|
2122 assertEquals(x, y); |
|
2123 Object z = surprise.invokeExact(x); |
|
2124 assertEquals(x, z); |
|
2125 } |
|
2126 boo.boo("Boo!"); |
|
2127 Object y = identity.invokeExact(x); |
|
2128 assertEquals(x, y); |
|
2129 try { |
|
2130 Object z = surprise.invokeExact(x); |
|
2131 System.out.println("Failed to throw; got z="+z); |
|
2132 assertTrue(false); |
|
2133 } catch (Exception ex) { |
|
2134 if (verbosity > 2) |
|
2135 System.out.println("caught "+ex); |
|
2136 if (verbosity > 3) |
|
2137 ex.printStackTrace(); |
|
2138 assertTrue(ex instanceof ClassCastException |
|
2139 // FIXME: accept only one of the two for any given unit test |
|
2140 || ex instanceof WrongMethodTypeException |
|
2141 ); |
|
2142 } |
|
2143 } |
|
2144 |
|
2145 static Example userMethod(Object o, String s, int i) { |
|
2146 called("userMethod", o, s, i); |
|
2147 return null; |
|
2148 } |
|
2149 |
|
2150 @Test |
|
2151 public void testUserClassInSignature() throws Throwable { |
|
2152 if (CAN_SKIP_WORKING) return; |
|
2153 startTest("testUserClassInSignature"); |
|
2154 Lookup lookup = MethodHandles.lookup(); |
|
2155 String name; MethodType mt; MethodHandle mh; |
|
2156 Object[] args; |
|
2157 |
|
2158 // Try a static method. |
|
2159 name = "userMethod"; |
|
2160 mt = MethodType.methodType(Example.class, Object.class, String.class, int.class); |
|
2161 mh = lookup.findStatic(lookup.lookupClass(), name, mt); |
|
2162 assertEquals(mt, mh.type()); |
|
2163 assertEquals(Example.class, mh.type().returnType()); |
|
2164 args = randomArgs(mh.type().parameterArray()); |
|
2165 mh.invokeWithArguments(args); |
|
2166 assertCalled(name, args); |
|
2167 |
|
2168 // Try a virtual method. |
|
2169 name = "v2"; |
|
2170 mt = MethodType.methodType(Object.class, Object.class, int.class); |
|
2171 mh = lookup.findVirtual(Example.class, name, mt); |
|
2172 assertEquals(mt, mh.type().dropParameterTypes(0,1)); |
|
2173 assertTrue(mh.type().parameterList().contains(Example.class)); |
|
2174 args = randomArgs(mh.type().parameterArray()); |
|
2175 mh.invokeWithArguments(args); |
|
2176 assertCalled(name, args); |
|
2177 } |
|
2178 |
|
2179 static void runForRunnable() { |
|
2180 called("runForRunnable"); |
|
2181 } |
|
2182 private interface Fooable { |
|
2183 Object foo(Fooable x, Object y); |
|
2184 // this is for randomArg: |
|
2185 public class Impl implements Fooable { |
|
2186 public Object foo(Fooable x, Object y) { |
|
2187 throw new RuntimeException("do not call"); |
|
2188 } |
|
2189 final String name; |
|
2190 public Impl() { name = "Fooable#"+nextArg(); } |
|
2191 @Override public String toString() { return name; } |
|
2192 } |
|
2193 } |
|
2194 static Object fooForFooable(Fooable x, Object y) { |
|
2195 return called("fooForFooable", x, y); |
|
2196 } |
|
2197 private static class MyCheckedException extends Exception { |
|
2198 } |
|
2199 private interface WillThrow { |
|
2200 void willThrow() throws MyCheckedException; |
|
2201 } |
|
2202 |
|
2203 @Test |
|
2204 public void testAsInstance() throws Throwable { |
|
2205 if (CAN_SKIP_WORKING) return; |
|
2206 Lookup lookup = MethodHandles.lookup(); |
|
2207 { |
|
2208 MethodType mt = MethodType.methodType(void.class); |
|
2209 MethodHandle mh = lookup.findStatic(MethodHandlesTest.class, "runForRunnable", mt); |
|
2210 Runnable proxy = MethodHandles.asInstance(mh, Runnable.class); |
|
2211 proxy.run(); |
|
2212 assertCalled("runForRunnable"); |
|
2213 } |
|
2214 { |
|
2215 MethodType mt = MethodType.methodType(Object.class, Fooable.class, Object.class); |
|
2216 MethodHandle mh = lookup.findStatic(MethodHandlesTest.class, "fooForFooable", mt); |
|
2217 Fooable proxy = MethodHandles.asInstance(mh, Fooable.class); |
|
2218 Object[] args = randomArgs(mt.parameterArray()); |
|
2219 Object result = proxy.foo((Fooable) args[0], args[1]); |
|
2220 assertCalled("fooForFooable", args); |
|
2221 assertEquals(result, logEntry("fooForFooable", args)); |
|
2222 } |
|
2223 for (Throwable ex : new Throwable[] { new NullPointerException("ok"), |
|
2224 new InternalError("ok"), |
|
2225 new Throwable("fail"), |
|
2226 new Exception("fail"), |
|
2227 new MyCheckedException() |
|
2228 }) { |
|
2229 MethodHandle mh = MethodHandles.throwException(void.class, Throwable.class); |
|
2230 mh = MethodHandles.insertArguments(mh, 0, ex); |
|
2231 WillThrow proxy = MethodHandles.asInstance(mh, WillThrow.class); |
|
2232 try { |
|
2233 proxy.willThrow(); |
|
2234 System.out.println("Failed to throw: "+ex); |
|
2235 assertTrue(false); |
|
2236 } catch (Throwable ex1) { |
|
2237 if (verbosity > 2) { |
|
2238 System.out.println("throw "+ex); |
|
2239 System.out.println("catch "+(ex == ex1 ? "UNWRAPPED" : ex1)); |
|
2240 } |
|
2241 if (ex instanceof RuntimeException || |
|
2242 ex instanceof Error) { |
|
2243 assertSame("must pass unchecked exception out without wrapping", ex, ex1); |
|
2244 } else if (ex instanceof MyCheckedException) { |
|
2245 assertSame("must pass declared exception out without wrapping", ex, ex1); |
|
2246 } else { |
|
2247 assertNotSame("must pass undeclared checked exception with wrapping", ex, ex1); |
|
2248 UndeclaredThrowableException utex = (UndeclaredThrowableException) ex1; |
|
2249 assertSame(ex, utex.getCause()); |
|
2250 } |
|
2251 } |
|
2252 } |
|
2253 // Test error checking: |
|
2254 for (Class<?> nonSAM : new Class[] { Object.class, |
|
2255 String.class, |
|
2256 CharSequence.class, |
|
2257 Example.class }) { |
|
2258 try { |
|
2259 MethodHandles.asInstance(varargsArray(0), nonSAM); |
|
2260 System.out.println("Failed to throw"); |
|
2261 assertTrue(false); |
|
2262 } catch (IllegalArgumentException ex) { |
|
2263 } |
|
2264 } |
|
2265 } |
|
2266 } |
|
2267 // Local abbreviated copy of sun.invoke.util.ValueConversions |
|
2268 class ValueConversions { |
|
2269 private static final Lookup IMPL_LOOKUP = MethodHandles.lookup(); |
|
2270 private static final Object[] NO_ARGS_ARRAY = {}; |
|
2271 private static Object[] makeArray(Object... args) { return args; } |
|
2272 private static Object[] array() { return NO_ARGS_ARRAY; } |
|
2273 private static Object[] array(Object a0) |
|
2274 { return makeArray(a0); } |
|
2275 private static Object[] array(Object a0, Object a1) |
|
2276 { return makeArray(a0, a1); } |
|
2277 private static Object[] array(Object a0, Object a1, Object a2) |
|
2278 { return makeArray(a0, a1, a2); } |
|
2279 private static Object[] array(Object a0, Object a1, Object a2, Object a3) |
|
2280 { return makeArray(a0, a1, a2, a3); } |
|
2281 private static Object[] array(Object a0, Object a1, Object a2, Object a3, |
|
2282 Object a4) |
|
2283 { return makeArray(a0, a1, a2, a3, a4); } |
|
2284 private static Object[] array(Object a0, Object a1, Object a2, Object a3, |
|
2285 Object a4, Object a5) |
|
2286 { return makeArray(a0, a1, a2, a3, a4, a5); } |
|
2287 private static Object[] array(Object a0, Object a1, Object a2, Object a3, |
|
2288 Object a4, Object a5, Object a6) |
|
2289 { return makeArray(a0, a1, a2, a3, a4, a5, a6); } |
|
2290 private static Object[] array(Object a0, Object a1, Object a2, Object a3, |
|
2291 Object a4, Object a5, Object a6, Object a7) |
|
2292 { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7); } |
|
2293 private static Object[] array(Object a0, Object a1, Object a2, Object a3, |
|
2294 Object a4, Object a5, Object a6, Object a7, |
|
2295 Object a8) |
|
2296 { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8); } |
|
2297 private static Object[] array(Object a0, Object a1, Object a2, Object a3, |
|
2298 Object a4, Object a5, Object a6, Object a7, |
|
2299 Object a8, Object a9) |
|
2300 { return makeArray(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } |
|
2301 static MethodHandle[] makeArrays() { |
|
2302 ArrayList<MethodHandle> arrays = new ArrayList<MethodHandle>(); |
|
2303 MethodHandles.Lookup lookup = IMPL_LOOKUP; |
|
2304 for (;;) { |
|
2305 int nargs = arrays.size(); |
|
2306 MethodType type = MethodType.genericMethodType(nargs).changeReturnType(Object[].class); |
|
2307 String name = "array"; |
|
2308 MethodHandle array = null; |
|
2309 try { |
|
2310 array = lookup.findStatic(ValueConversions.class, name, type); |
|
2311 } catch (ReflectiveOperationException ex) { |
|
2312 // break from loop! |
|
2313 } |
|
2314 if (array == null) break; |
|
2315 arrays.add(array); |
|
2316 } |
|
2317 assertTrue(arrays.size() == 11); // current number of methods |
|
2318 return arrays.toArray(new MethodHandle[0]); |
|
2319 } |
|
2320 static final MethodHandle[] ARRAYS = makeArrays(); |
|
2321 |
|
2322 /** Return a method handle that takes the indicated number of Object |
|
2323 * arguments and returns an Object array of them, as if for varargs. |
|
2324 */ |
|
2325 public static MethodHandle varargsArray(int nargs) { |
|
2326 if (nargs < ARRAYS.length) |
|
2327 return ARRAYS[nargs]; |
|
2328 // else need to spin bytecode or do something else fancy |
|
2329 throw new UnsupportedOperationException("NYI: cannot form a varargs array of length "+nargs); |
|
2330 } |
|
2331 |
|
2332 private static final List<Object> NO_ARGS_LIST = Arrays.asList(NO_ARGS_ARRAY); |
|
2333 private static List<Object> makeList(Object... args) { return Arrays.asList(args); } |
|
2334 private static List<Object> list() { return NO_ARGS_LIST; } |
|
2335 private static List<Object> list(Object a0) |
|
2336 { return makeList(a0); } |
|
2337 private static List<Object> list(Object a0, Object a1) |
|
2338 { return makeList(a0, a1); } |
|
2339 private static List<Object> list(Object a0, Object a1, Object a2) |
|
2340 { return makeList(a0, a1, a2); } |
|
2341 private static List<Object> list(Object a0, Object a1, Object a2, Object a3) |
|
2342 { return makeList(a0, a1, a2, a3); } |
|
2343 private static List<Object> list(Object a0, Object a1, Object a2, Object a3, |
|
2344 Object a4) |
|
2345 { return makeList(a0, a1, a2, a3, a4); } |
|
2346 private static List<Object> list(Object a0, Object a1, Object a2, Object a3, |
|
2347 Object a4, Object a5) |
|
2348 { return makeList(a0, a1, a2, a3, a4, a5); } |
|
2349 private static List<Object> list(Object a0, Object a1, Object a2, Object a3, |
|
2350 Object a4, Object a5, Object a6) |
|
2351 { return makeList(a0, a1, a2, a3, a4, a5, a6); } |
|
2352 private static List<Object> list(Object a0, Object a1, Object a2, Object a3, |
|
2353 Object a4, Object a5, Object a6, Object a7) |
|
2354 { return makeList(a0, a1, a2, a3, a4, a5, a6, a7); } |
|
2355 private static List<Object> list(Object a0, Object a1, Object a2, Object a3, |
|
2356 Object a4, Object a5, Object a6, Object a7, |
|
2357 Object a8) |
|
2358 { return makeList(a0, a1, a2, a3, a4, a5, a6, a7, a8); } |
|
2359 private static List<Object> list(Object a0, Object a1, Object a2, Object a3, |
|
2360 Object a4, Object a5, Object a6, Object a7, |
|
2361 Object a8, Object a9) |
|
2362 { return makeList(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9); } |
|
2363 static MethodHandle[] makeLists() { |
|
2364 ArrayList<MethodHandle> lists = new ArrayList<MethodHandle>(); |
|
2365 MethodHandles.Lookup lookup = IMPL_LOOKUP; |
|
2366 for (;;) { |
|
2367 int nargs = lists.size(); |
|
2368 MethodType type = MethodType.genericMethodType(nargs).changeReturnType(List.class); |
|
2369 String name = "list"; |
|
2370 MethodHandle list = null; |
|
2371 try { |
|
2372 list = lookup.findStatic(ValueConversions.class, name, type); |
|
2373 } catch (ReflectiveOperationException ex) { |
|
2374 // break from loop! |
|
2375 } |
|
2376 if (list == null) break; |
|
2377 lists.add(list); |
|
2378 } |
|
2379 assertTrue(lists.size() == 11); // current number of methods |
|
2380 return lists.toArray(new MethodHandle[0]); |
|
2381 } |
|
2382 static final MethodHandle[] LISTS = makeLists(); |
|
2383 |
|
2384 /** Return a method handle that takes the indicated number of Object |
|
2385 * arguments and returns List. |
|
2386 */ |
|
2387 public static MethodHandle varargsList(int nargs) { |
|
2388 if (nargs < LISTS.length) |
|
2389 return LISTS[nargs]; |
|
2390 // else need to spin bytecode or do something else fancy |
|
2391 throw new UnsupportedOperationException("NYI"); |
|
2392 } |
|
2393 } |
|
2394 // This guy tests access from outside the same package member, but inside |
|
2395 // the package itself. |
|
2396 class PackageSibling { |
|
2397 static Lookup lookup() { |
|
2398 return MethodHandles.lookup(); |
|
2399 } |
|
2400 } |