blob: 8713caac1af3180bd9018fe6053f60937fdcd34e [file] [log] [blame]
Narayan Kamath9bdaeeb2016-10-20 10:57:45 +01001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17import java.lang.invoke.MethodHandle;
18import java.lang.invoke.MethodHandles;
19import java.lang.invoke.MethodHandles.Lookup;
20import java.lang.invoke.MethodType;
21import java.lang.invoke.WrongMethodTypeException;
Orion Hodson0d781e62016-11-04 11:09:53 +000022import java.nio.charset.Charset;
23import java.nio.charset.StandardCharsets;
Narayan Kamath9bdaeeb2016-10-20 10:57:45 +010024
Narayan Kamathe5eb5742016-11-02 14:16:27 +000025import java.lang.reflect.Constructor;
26import java.lang.reflect.Field;
27import java.lang.reflect.Method;
Orion Hodson8797fdf2016-11-16 13:43:26 +000028import java.util.Arrays;
Narayan Kamathe5eb5742016-11-02 14:16:27 +000029
Narayan Kamath9bdaeeb2016-10-20 10:57:45 +010030public class Main {
31
32 public static class A {
Orion Hodson8797fdf2016-11-16 13:43:26 +000033 public A() {}
34
Narayan Kamath9bdaeeb2016-10-20 10:57:45 +010035 public void foo() {
36 System.out.println("foo_A");
37 }
38
39 public static final Lookup lookup = MethodHandles.lookup();
40 }
41
42 public static class B extends A {
43 public void foo() {
44 System.out.println("foo_B");
45 }
46
47 public static final Lookup lookup = MethodHandles.lookup();
48 }
49
50 public static class C extends B {
51 public static final Lookup lookup = MethodHandles.lookup();
52 }
53
54 public static class D {
55 private final void privateRyan() {
56 System.out.println("privateRyan_D");
57 }
58
59 public static final Lookup lookup = MethodHandles.lookup();
60 }
61
62 public static class E extends D {
63 public static final Lookup lookup = MethodHandles.lookup();
64 }
65
66 public static void main(String[] args) throws Throwable {
67 testfindSpecial_invokeSuperBehaviour();
68 testfindSpecial_invokeDirectBehaviour();
Narayan Kamath3e0dce02016-10-31 13:55:55 +000069 testExceptionDetailMessages();
Narayan Kamath94bee022016-11-01 10:57:15 +000070 testfindVirtual();
Orion Hodson8797fdf2016-11-16 13:43:26 +000071 testfindStatic();
Narayan Kamathe5eb5742016-11-02 14:16:27 +000072 testUnreflects();
Narayan Kamath0a8485e2016-11-02 18:47:11 +000073 testAsType();
Orion Hodson0d781e62016-11-04 11:09:53 +000074 testConstructors();
75 testStringConstructors();
Orion Hodson1a06f9f2016-11-09 08:32:42 +000076 testReturnValueConversions();
Narayan Kamath9bdaeeb2016-10-20 10:57:45 +010077 }
78
79 public static void testfindSpecial_invokeSuperBehaviour() throws Throwable {
80 // This is equivalent to an invoke-super instruction where the referrer
81 // is B.class.
82 MethodHandle mh1 = B.lookup.findSpecial(A.class /* refC */, "foo",
83 MethodType.methodType(void.class), B.class /* specialCaller */);
84
85 A aInstance = new A();
86 B bInstance = new B();
87 C cInstance = new C();
88
89 // This should be as if an invoke-super was called from one of B's methods.
90 mh1.invokeExact(bInstance);
91 mh1.invoke(bInstance);
92
93 // This should not work. The receiver type in the handle will be suitably
94 // restricted to B and subclasses.
95 try {
96 mh1.invoke(aInstance);
97 System.out.println("mh1.invoke(aInstance) should not succeeed");
98 } catch (ClassCastException expected) {
99 }
100
101 try {
102 mh1.invokeExact(aInstance);
103 System.out.println("mh1.invoke(aInstance) should not succeeed");
104 } catch (WrongMethodTypeException expected) {
Narayan Kamath9bdaeeb2016-10-20 10:57:45 +0100105 }
106
107 // This should *still* be as if an invoke-super was called from one of C's
108 // methods, despite the fact that we're operating on a C.
109 mh1.invoke(cInstance);
110
111 // Now that C is the special caller, the next invoke will call B.foo.
112 MethodHandle mh2 = C.lookup.findSpecial(A.class /* refC */, "foo",
113 MethodType.methodType(void.class), C.class /* specialCaller */);
114 mh2.invokeExact(cInstance);
115
116 // Shouldn't allow invoke-super semantics from an unrelated special caller.
117 try {
118 C.lookup.findSpecial(A.class, "foo",
119 MethodType.methodType(void.class), D.class /* specialCaller */);
120 System.out.println("findSpecial(A.class, foo, .. D.class) unexpectedly succeeded.");
121 } catch (IllegalAccessException expected) {
122 }
Orion Hodson8797fdf2016-11-16 13:43:26 +0000123
124 // Check return type matches for find.
125 try {
126 B.lookup.findSpecial(A.class /* refC */, "foo",
127 MethodType.methodType(int.class), B.class /* specialCaller */);
128 fail();
129 } catch (NoSuchMethodException e) {}
130 // Check constructors
131 try {
132 B.lookup.findSpecial(A.class /* refC */, "<init>",
133 MethodType.methodType(void.class), B.class /* specialCaller */);
134 fail();
135 } catch (NoSuchMethodException e) {}
Narayan Kamath9bdaeeb2016-10-20 10:57:45 +0100136 }
137
138 public static void testfindSpecial_invokeDirectBehaviour() throws Throwable {
139 D dInstance = new D();
140
141 MethodHandle mh3 = D.lookup.findSpecial(D.class, "privateRyan",
142 MethodType.methodType(void.class), D.class /* specialCaller */);
143 mh3.invoke(dInstance);
144
145 // The private method shouldn't be accessible from any special caller except
146 // itself...
147 try {
148 D.lookup.findSpecial(D.class, "privateRyan", MethodType.methodType(void.class), C.class);
149 System.out.println("findSpecial(privateRyan, C.class) unexpectedly succeeded");
150 } catch (IllegalAccessException expected) {
151 }
152
153 // ... or from any lookup context except its own.
154 try {
155 E.lookup.findSpecial(D.class, "privateRyan", MethodType.methodType(void.class), E.class);
156 System.out.println("findSpecial(privateRyan, E.class) unexpectedly succeeded");
157 } catch (IllegalAccessException expected) {
158 }
159 }
Narayan Kamath3e0dce02016-10-31 13:55:55 +0000160
161 public static void testExceptionDetailMessages() throws Throwable {
162 MethodHandle handle = MethodHandles.lookup().findVirtual(String.class, "concat",
163 MethodType.methodType(String.class, String.class));
164
165 try {
166 handle.invokeExact("a", new Object());
167 System.out.println("invokeExact(\"a\", new Object()) unexpectedly succeeded.");
168 } catch (WrongMethodTypeException ex) {
169 System.out.println("Received exception: " + ex.getMessage());
170 }
171 }
Narayan Kamath94bee022016-11-01 10:57:15 +0000172
173 public interface Foo {
174 public String foo();
175 }
176
177 public interface Bar extends Foo {
178 public String bar();
179 }
180
181 public static class BarSuper {
182 public String superPublicMethod() {
183 return "superPublicMethod";
184 }
185
186 public String superProtectedMethod() {
187 return "superProtectedMethod";
188 }
189
190 String superPackageMethod() {
191 return "superPackageMethod";
192 }
193 }
194
195 public static class BarImpl extends BarSuper implements Bar {
196 public BarImpl() {
197 }
198
199 @Override
200 public String foo() {
201 return "foo";
202 }
203
204 @Override
205 public String bar() {
206 return "bar";
207 }
208
Orion Hodson8797fdf2016-11-16 13:43:26 +0000209 public String add(int x, int y) {
210 return Arrays.toString(new int[] { x, y });
211 }
212
Narayan Kamath94bee022016-11-01 10:57:15 +0000213 private String privateMethod() { return "privateMethod"; }
214
Orion Hodson8797fdf2016-11-16 13:43:26 +0000215 public static String staticMethod() { return staticString; }
216
217 private static String staticString;
218
219 {
220 // Static constructor
221 staticString = Long.toString(System.currentTimeMillis());
222 }
Narayan Kamath94bee022016-11-01 10:57:15 +0000223
224 static final MethodHandles.Lookup lookup = MethodHandles.lookup();
225 }
226
227 public static void testfindVirtual() throws Throwable {
228 // Virtual lookups on static methods should not succeed.
229 try {
230 MethodHandles.lookup().findVirtual(
231 BarImpl.class, "staticMethod", MethodType.methodType(String.class));
232 System.out.println("findVirtual(staticMethod) unexpectedly succeeded");
233 } catch (IllegalAccessException expected) {
234 }
235
236 // Virtual lookups on private methods should not succeed, unless the Lookup
237 // context had sufficient privileges.
238 try {
239 MethodHandles.lookup().findVirtual(
240 BarImpl.class, "privateMethod", MethodType.methodType(String.class));
241 System.out.println("findVirtual(privateMethod) unexpectedly succeeded");
242 } catch (IllegalAccessException expected) {
243 }
244
245 // Virtual lookup on a private method with a context that *does* have sufficient
246 // privileges.
247 MethodHandle mh = BarImpl.lookup.findVirtual(
248 BarImpl.class, "privateMethod", MethodType.methodType(String.class));
249 String str = (String) mh.invoke(new BarImpl());
250 if (!"privateMethod".equals(str)) {
251 System.out.println("Unexpected return value for BarImpl#privateMethod: " + str);
252 }
253
254 // Find virtual must find interface methods defined by interfaces implemented
255 // by the class.
256 mh = MethodHandles.lookup().findVirtual(BarImpl.class, "foo",
257 MethodType.methodType(String.class));
258 str = (String) mh.invoke(new BarImpl());
259 if (!"foo".equals(str)) {
260 System.out.println("Unexpected return value for BarImpl#foo: " + str);
261 }
262
Orion Hodson8797fdf2016-11-16 13:43:26 +0000263 // Find virtual should check rtype.
264 try {
265 mh = MethodHandles.lookup().findVirtual(BarImpl.class, "foo",
266 MethodType.methodType(void.class));
267 fail();
268 } catch (NoSuchMethodException e) {}
269
270 // And ptypes
271 mh = MethodHandles.lookup().findVirtual(
272 BarImpl.class, "add", MethodType.methodType(String.class, int.class, int.class));
273 try {
274 mh = MethodHandles.lookup().findVirtual(
275 BarImpl.class, "add", MethodType.methodType(String.class, Integer.class, int.class));
276 } catch (NoSuchMethodException e) {}
277
Narayan Kamath94bee022016-11-01 10:57:15 +0000278 // .. and their super-interfaces.
279 mh = MethodHandles.lookup().findVirtual(BarImpl.class, "bar",
280 MethodType.methodType(String.class));
281 str = (String) mh.invoke(new BarImpl());
282 if (!"bar".equals(str)) {
283 System.out.println("Unexpected return value for BarImpl#bar: " + str);
284 }
285
286 // TODO(narayan): Fix this case, we're using the wrong ArtMethod for the
287 // invoke resulting in a failing check in the interpreter.
288 //
289 // mh = MethodHandles.lookup().findVirtual(Bar.class, "bar",
290 // MethodType.methodType(String.class));
291 // str = (String) mh.invoke(new BarImpl());
292 // if (!"bar".equals(str)) {
293 // System.out.println("Unexpected return value for BarImpl#bar: " + str);
294 // }
295
296 // We should also be able to lookup public / protected / package methods in
297 // the super class, given sufficient access privileges.
298 mh = MethodHandles.lookup().findVirtual(BarImpl.class, "superPublicMethod",
299 MethodType.methodType(String.class));
300 str = (String) mh.invoke(new BarImpl());
301 if (!"superPublicMethod".equals(str)) {
302 System.out.println("Unexpected return value for BarImpl#superPublicMethod: " + str);
303 }
304
305 mh = MethodHandles.lookup().findVirtual(BarImpl.class, "superProtectedMethod",
306 MethodType.methodType(String.class));
307 str = (String) mh.invoke(new BarImpl());
308 if (!"superProtectedMethod".equals(str)) {
309 System.out.println("Unexpected return value for BarImpl#superProtectedMethod: " + str);
310 }
311
312 mh = MethodHandles.lookup().findVirtual(BarImpl.class, "superPackageMethod",
313 MethodType.methodType(String.class));
314 str = (String) mh.invoke(new BarImpl());
315 if (!"superPackageMethod".equals(str)) {
316 System.out.println("Unexpected return value for BarImpl#superPackageMethod: " + str);
317 }
Orion Hodson8797fdf2016-11-16 13:43:26 +0000318
319 try {
320 MethodHandles.lookup().findVirtual(BarImpl.class, "<init>",
321 MethodType.methodType(void.class));
322 fail();
323 } catch (NoSuchMethodException e) {}
324 }
325
326 public static void testfindStatic() throws Throwable {
327 MethodHandles.lookup().findStatic(BarImpl.class, "staticMethod",
328 MethodType.methodType(String.class));
329 try {
330 MethodHandles.lookup().findStatic(BarImpl.class, "staticMethod",
331 MethodType.methodType(void.class));
332 fail();
333 } catch (NoSuchMethodException e) {}
334 try {
335 MethodHandles.lookup().findStatic(BarImpl.class, "staticMethod",
336 MethodType.methodType(String.class, int.class));
337 fail();
338 } catch (NoSuchMethodException e) {}
339 try {
340 MethodHandles.lookup().findStatic(BarImpl.class, "<clinit>",
341 MethodType.methodType(void.class));
342 fail();
343 } catch (NoSuchMethodException e) {}
344 try {
345 MethodHandles.lookup().findStatic(BarImpl.class, "<init>",
346 MethodType.methodType(void.class));
347 fail();
348 } catch (NoSuchMethodException e) {}
Narayan Kamath94bee022016-11-01 10:57:15 +0000349 }
Narayan Kamathe5eb5742016-11-02 14:16:27 +0000350
351 static class UnreflectTester {
352 public String publicField;
353 private String privateField;
354
355 public static String publicStaticField = "publicStaticValue";
356 private static String privateStaticField = "privateStaticValue";
357
358 private UnreflectTester(String val) {
359 publicField = val;
360 privateField = val;
361 }
362
363 // NOTE: The boolean constructor argument only exists to give this a
364 // different signature.
365 public UnreflectTester(String val, boolean unused) {
366 this(val);
367 }
368
369 private static String privateStaticMethod() {
370 return "privateStaticMethod";
371 }
372
373 private String privateMethod() {
374 return "privateMethod";
375 }
376
377 public static String publicStaticMethod() {
378 return "publicStaticMethod";
379 }
380
381 public String publicMethod() {
382 return "publicMethod";
383 }
384 }
385
386 public static void testUnreflects() throws Throwable {
387 UnreflectTester instance = new UnreflectTester("unused");
388 Method publicMethod = UnreflectTester.class.getMethod("publicMethod");
389
390 MethodHandle mh = MethodHandles.lookup().unreflect(publicMethod);
391 assertEquals("publicMethod", (String) mh.invoke(instance));
392 assertEquals("publicMethod", (String) mh.invokeExact(instance));
393
394 Method publicStaticMethod = UnreflectTester.class.getMethod("publicStaticMethod");
395 mh = MethodHandles.lookup().unreflect(publicStaticMethod);
396 assertEquals("publicStaticMethod", (String) mh.invoke());
397 assertEquals("publicStaticMethod", (String) mh.invokeExact());
398
399 Method privateMethod = UnreflectTester.class.getDeclaredMethod("privateMethod");
400 try {
401 mh = MethodHandles.lookup().unreflect(privateMethod);
402 fail();
403 } catch (IllegalAccessException expected) {}
404
405 privateMethod.setAccessible(true);
406 mh = MethodHandles.lookup().unreflect(privateMethod);
407 assertEquals("privateMethod", (String) mh.invoke(instance));
408 assertEquals("privateMethod", (String) mh.invokeExact(instance));
409
410 Method privateStaticMethod = UnreflectTester.class.getDeclaredMethod("privateStaticMethod");
411 try {
412 mh = MethodHandles.lookup().unreflect(privateStaticMethod);
413 fail();
414 } catch (IllegalAccessException expected) {}
415
416 privateStaticMethod.setAccessible(true);
417 mh = MethodHandles.lookup().unreflect(privateStaticMethod);
418 assertEquals("privateStaticMethod", (String) mh.invoke());
419 assertEquals("privateStaticMethod", (String) mh.invokeExact());
420
421 Constructor privateConstructor = UnreflectTester.class.getDeclaredConstructor(String.class);
422 try {
423 mh = MethodHandles.lookup().unreflectConstructor(privateConstructor);
424 fail();
425 } catch (IllegalAccessException expected) {}
426
427 privateConstructor.setAccessible(true);
428 mh = MethodHandles.lookup().unreflectConstructor(privateConstructor);
Orion Hodson0d781e62016-11-04 11:09:53 +0000429 instance = (UnreflectTester) mh.invokeExact("abc");
430 assertEquals("abc", instance.publicField);
431 instance = (UnreflectTester) mh.invoke("def");
432 assertEquals("def", instance.publicField);
Narayan Kamathe5eb5742016-11-02 14:16:27 +0000433 Constructor publicConstructor = UnreflectTester.class.getConstructor(String.class,
434 boolean.class);
435 mh = MethodHandles.lookup().unreflectConstructor(publicConstructor);
Orion Hodson0d781e62016-11-04 11:09:53 +0000436 instance = (UnreflectTester) mh.invokeExact("abc", false);
437 assertEquals("abc", instance.publicField);
438 instance = (UnreflectTester) mh.invoke("def", true);
439 assertEquals("def", instance.publicField);
Narayan Kamathe5eb5742016-11-02 14:16:27 +0000440
441 // TODO(narayan): Non exact invokes for field sets/gets are not implemented yet.
442 //
443 // assertEquals("instanceValue", (String) mh.invoke(new UnreflectTester("instanceValue")));
444 Field publicField = UnreflectTester.class.getField("publicField");
445 mh = MethodHandles.lookup().unreflectGetter(publicField);
446 instance = new UnreflectTester("instanceValue");
447 assertEquals("instanceValue", (String) mh.invokeExact(instance));
448
449 mh = MethodHandles.lookup().unreflectSetter(publicField);
450 instance = new UnreflectTester("instanceValue");
451 mh.invokeExact(instance, "updatedInstanceValue");
452 assertEquals("updatedInstanceValue", instance.publicField);
453
454 Field publicStaticField = UnreflectTester.class.getField("publicStaticField");
455 mh = MethodHandles.lookup().unreflectGetter(publicStaticField);
456 UnreflectTester.publicStaticField = "updatedStaticValue";
457 assertEquals("updatedStaticValue", (String) mh.invokeExact());
458
459 mh = MethodHandles.lookup().unreflectSetter(publicStaticField);
460 UnreflectTester.publicStaticField = "updatedStaticValue";
461 mh.invokeExact("updatedStaticValue2");
462 assertEquals("updatedStaticValue2", UnreflectTester.publicStaticField);
463
464 Field privateField = UnreflectTester.class.getDeclaredField("privateField");
465 try {
466 mh = MethodHandles.lookup().unreflectGetter(privateField);
467 fail();
468 } catch (IllegalAccessException expected) {
469 }
470 try {
471 mh = MethodHandles.lookup().unreflectSetter(privateField);
472 fail();
473 } catch (IllegalAccessException expected) {
474 }
475
476 privateField.setAccessible(true);
477
478 mh = MethodHandles.lookup().unreflectGetter(privateField);
479 instance = new UnreflectTester("instanceValue");
480 assertEquals("instanceValue", (String) mh.invokeExact(instance));
481
482 mh = MethodHandles.lookup().unreflectSetter(privateField);
483 instance = new UnreflectTester("instanceValue");
484 mh.invokeExact(instance, "updatedInstanceValue");
485 assertEquals("updatedInstanceValue", instance.privateField);
486
487 Field privateStaticField = UnreflectTester.class.getDeclaredField("privateStaticField");
488 try {
489 mh = MethodHandles.lookup().unreflectGetter(privateStaticField);
490 fail();
491 } catch (IllegalAccessException expected) {
492 }
493 try {
494 mh = MethodHandles.lookup().unreflectSetter(privateStaticField);
495 fail();
496 } catch (IllegalAccessException expected) {
497 }
498
499 privateStaticField.setAccessible(true);
500 mh = MethodHandles.lookup().unreflectGetter(privateStaticField);
501 privateStaticField.set(null, "updatedStaticValue");
502 assertEquals("updatedStaticValue", (String) mh.invokeExact());
503
504 mh = MethodHandles.lookup().unreflectSetter(privateStaticField);
505 privateStaticField.set(null, "updatedStaticValue");
506 mh.invokeExact("updatedStaticValue2");
507 assertEquals("updatedStaticValue2", (String) privateStaticField.get(null));
508 }
509
Narayan Kamath0a8485e2016-11-02 18:47:11 +0000510 // This method only exists to fool Jack's handling of types. See b/32536744.
511 public static CharSequence getSequence() {
512 return "foo";
513 }
514
515 public static void testAsType() throws Throwable {
516 // The type of this handle is (String, String)String.
517 MethodHandle mh = MethodHandles.lookup().findVirtual(String.class,
518 "concat", MethodType.methodType(String.class, String.class));
519
520 // Change it to (CharSequence, String)Object.
521 MethodHandle asType = mh.asType(
522 MethodType.methodType(Object.class, CharSequence.class, String.class));
523
524 Object obj = asType.invokeExact((CharSequence) getSequence(), "bar");
525 assertEquals("foobar", (String) obj);
526
527 // Should fail due to a wrong return type.
528 try {
529 String str = (String) asType.invokeExact((CharSequence) getSequence(), "bar");
530 fail();
531 } catch (WrongMethodTypeException expected) {
532 }
533
534 // Should fail due to a wrong argument type (String instead of Charsequence).
535 try {
536 String str = (String) asType.invokeExact("baz", "bar");
537 fail();
538 } catch (WrongMethodTypeException expected) {
539 }
540
541 // Calls to asType should fail if the types are not convertible.
542 //
543 // Bad return type conversion.
544 try {
545 mh.asType(MethodType.methodType(int.class, String.class, String.class));
546 fail();
547 } catch (WrongMethodTypeException expected) {
548 }
549
550 // Bad argument conversion.
551 try {
552 mh.asType(MethodType.methodType(String.class, int.class, String.class));
553 fail();
554 } catch (WrongMethodTypeException expected) {
555 }
556 }
557
Narayan Kamathe5eb5742016-11-02 14:16:27 +0000558 public static void assertEquals(String s1, String s2) {
559 if (s1 == s2) {
560 return;
561 }
562
563 if (s1 != null && s2 != null && s1.equals(s2)) {
564 return;
565 }
566
567 throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
568 }
569
570 public static void fail() {
571 System.out.println("fail");
572 Thread.dumpStack();
573 }
Orion Hodson0d781e62016-11-04 11:09:53 +0000574
575 public static void fail(String message) {
576 System.out.println("fail: " + message);
577 Thread.dumpStack();
578 }
579
580 public static void testConstructors() throws Throwable {
581 MethodHandle mh =
582 MethodHandles.lookup().findConstructor(Float.class,
583 MethodType.methodType(void.class,
584 float.class));
585 Float value = (Float) mh.invokeExact(0.33f);
586 if (value.floatValue() != 0.33f) {
587 fail("Unexpected float value from invokeExact " + value.floatValue());
588 }
589
590 value = (Float) mh.invoke(3.34f);
591 if (value.floatValue() != 3.34f) {
592 fail("Unexpected float value from invoke " + value.floatValue());
593 }
594
595 mh = MethodHandles.lookup().findConstructor(Double.class,
596 MethodType.methodType(void.class, String.class));
597 Double d = (Double) mh.invoke("8.45e3");
598 if (d.doubleValue() != 8.45e3) {
599 fail("Unexpected double value from Double(String) " + value.doubleValue());
600 }
601
602 mh = MethodHandles.lookup().findConstructor(Double.class,
603 MethodType.methodType(void.class, double.class));
604 d = (Double) mh.invoke(8.45e3);
605 if (d.doubleValue() != 8.45e3) {
606 fail("Unexpected double value from Double(double) " + value.doubleValue());
607 }
608
609 // Primitive type
610 try {
611 mh = MethodHandles.lookup().findConstructor(int.class, MethodType.methodType(void.class));
612 fail("Unexpected lookup success for primitive constructor");
613 } catch (NoSuchMethodException e) {}
614
615 // Interface
616 try {
617 mh = MethodHandles.lookup().findConstructor(Readable.class,
618 MethodType.methodType(void.class));
619 fail("Unexpected lookup success for interface constructor");
620 } catch (NoSuchMethodException e) {}
621
622 // Abstract
623 mh = MethodHandles.lookup().findConstructor(Process.class, MethodType.methodType(void.class));
624 try {
625 mh.invoke();
626 fail("Unexpected ability to instantiate an abstract class");
627 } catch (InstantiationException e) {}
628
629 // Non-existent
630 try {
631 MethodHandle bad = MethodHandles.lookup().findConstructor(
632 String.class, MethodType.methodType(String.class, Float.class));
633 fail("Unexpected success for non-existent constructor");
634 } catch (NoSuchMethodException e) {}
635
636 // Non-void constructor search. (I)I instead of (I)V.
637 try {
638 MethodHandle foo = MethodHandles.lookup().findConstructor(
639 Integer.class, MethodType.methodType(Integer.class, Integer.class));
640 fail("Unexpected success for non-void type for findConstructor");
641 } catch (NoSuchMethodException e) {}
642 }
643
644 public static void testStringConstructors() throws Throwable {
645 final String testPattern = "The system as we know it is broken";
646
647 // String()
648 MethodHandle mh = MethodHandles.lookup().findConstructor(
649 String.class, MethodType.methodType(void.class));
650 String s = (String) mh.invokeExact();
651 if (!s.equals("")) {
652 fail("Unexpected empty string constructor result: '" + s + "'");
653 }
654
655 // String(String)
656 mh = MethodHandles.lookup().findConstructor(
657 String.class, MethodType.methodType(void.class, String.class));
658 s = (String) mh.invokeExact(testPattern);
659 if (!s.equals(testPattern)) {
660 fail("Unexpected string constructor result: '" + s + "'");
661 }
662
663 // String(char[])
664 mh = MethodHandles.lookup().findConstructor(
665 String.class, MethodType.methodType(void.class, char[].class));
666 s = (String) mh.invokeExact(testPattern.toCharArray());
667 if (!s.equals(testPattern)) {
668 fail("Unexpected string constructor result: '" + s + "'");
669 }
670
671 // String(char[], int, int)
672 mh = MethodHandles.lookup().findConstructor(
673 String.class, MethodType.methodType(void.class, char[].class, int.class, int.class));
674 s = (String) mh.invokeExact(new char [] { 'a', 'b', 'c', 'd', 'e'}, 2, 3);
675 if (!s.equals("cde")) {
676 fail("Unexpected string constructor result: '" + s + "'");
677 }
678
679 // String(int[] codePoints, int offset, int count)
680 StringBuffer sb = new StringBuffer(testPattern);
681 int[] codePoints = new int[sb.codePointCount(0, sb.length())];
682 for (int i = 0; i < sb.length(); ++i) {
683 codePoints[i] = sb.codePointAt(i);
684 }
685 mh = MethodHandles.lookup().findConstructor(
686 String.class, MethodType.methodType(void.class, int[].class, int.class, int.class));
687 s = (String) mh.invokeExact(codePoints, 0, codePoints.length);
688 if (!s.equals(testPattern)) {
689 fail("Unexpected string constructor result: '" + s + "'");
690 }
691
692 // String(byte ascii[], int hibyte, int offset, int count)
693 byte [] ascii = testPattern.getBytes(StandardCharsets.US_ASCII);
694 mh = MethodHandles.lookup().findConstructor(
695 String.class, MethodType.methodType(void.class, byte[].class, int.class, int.class));
696 s = (String) mh.invokeExact(ascii, 0, ascii.length);
697 if (!s.equals(testPattern)) {
698 fail("Unexpected string constructor result: '" + s + "'");
699 }
700
701 // String(byte bytes[], int offset, int length, String charsetName)
702 mh = MethodHandles.lookup().findConstructor(
703 String.class,
704 MethodType.methodType(void.class, byte[].class, int.class, int.class, String.class));
705 s = (String) mh.invokeExact(ascii, 0, 5, StandardCharsets.US_ASCII.name());
706 if (!s.equals(testPattern.substring(0, 5))) {
707 fail("Unexpected string constructor result: '" + s + "'");
708 }
709
710 // String(byte bytes[], int offset, int length, Charset charset)
711 mh = MethodHandles.lookup().findConstructor(
712 String.class,
713 MethodType.methodType(void.class, byte[].class, int.class, int.class, Charset.class));
714 s = (String) mh.invokeExact(ascii, 0, 5, StandardCharsets.US_ASCII);
715 if (!s.equals(testPattern.substring(0, 5))) {
716 fail("Unexpected string constructor result: '" + s + "'");
717 }
718
719 // String(byte bytes[], String charsetName)
720 mh = MethodHandles.lookup().findConstructor(
721 String.class,
722 MethodType.methodType(void.class, byte[].class, String.class));
723 s = (String) mh.invokeExact(ascii, StandardCharsets.US_ASCII.name());
724 if (!s.equals(testPattern)) {
725 fail("Unexpected string constructor result: '" + s + "'");
726 }
727
728 // String(byte bytes[], Charset charset)
729 mh = MethodHandles.lookup().findConstructor(
730 String.class, MethodType.methodType(void.class, byte[].class, Charset.class));
731 s = (String) mh.invokeExact(ascii, StandardCharsets.US_ASCII);
732 if (!s.equals(testPattern)) {
733 fail("Unexpected string constructor result: '" + s + "'");
734 }
735
736 // String(byte bytes[], int offset, int length)
737 mh = MethodHandles.lookup().findConstructor(
738 String.class, MethodType.methodType(void.class, byte[].class, int.class, int.class));
739 s = (String) mh.invokeExact(ascii, 1, ascii.length - 2);
740 s = testPattern.charAt(0) + s + testPattern.charAt(testPattern.length() - 1);
741 if (!s.equals(testPattern)) {
742 fail("Unexpected string constructor result: '" + s + "'");
743 }
744
745 // String(byte bytes[])
746 mh = MethodHandles.lookup().findConstructor(
747 String.class, MethodType.methodType(void.class, byte[].class));
748 s = (String) mh.invokeExact(ascii);
749 if (!s.equals(testPattern)) {
750 fail("Unexpected string constructor result: '" + s + "'");
751 }
752
753 // String(StringBuffer buffer)
754 mh = MethodHandles.lookup().findConstructor(
755 String.class, MethodType.methodType(void.class, StringBuffer.class));
756 s = (String) mh.invokeExact(sb);
757 if (!s.equals(testPattern)) {
758 fail("Unexpected string constructor result: '" + s + "'");
759 }
760
761 System.out.println("String constructors done.");
762 }
Orion Hodson1a06f9f2016-11-09 08:32:42 +0000763
764 private static void testReferenceReturnValueConversions() throws Throwable {
765 MethodHandle mh = MethodHandles.lookup().findStatic(
766 Float.class, "valueOf", MethodType.methodType(Float.class, String.class));
767
768 // No conversion
769 Float f = (Float) mh.invokeExact("1.375");
770 if (f.floatValue() != 1.375) {
771 fail();
772 }
773 f = (Float) mh.invoke("1.875");
774 if (f.floatValue() != 1.875) {
775 fail();
776 }
777
778 // Bad conversion
779 try {
780 int i = (int) mh.invokeExact("7.77");
781 fail();
782 } catch (WrongMethodTypeException e) {}
783
784 try {
785 int i = (int) mh.invoke("7.77");
786 fail();
787 } catch (WrongMethodTypeException e) {}
788
789 // Assignment to super-class.
790 Number n = (Number) mh.invoke("1.11");
791 try {
792 Number o = (Number) mh.invokeExact("1.11");
793 fail();
794 } catch (WrongMethodTypeException e) {}
795
796 // Assignment to widened boxed primitive class.
797 try {
798 Double u = (Double) mh.invoke("1.11");
799 fail();
800 } catch (ClassCastException e) {}
801
802 try {
803 Double v = (Double) mh.invokeExact("1.11");
804 fail();
805 } catch (WrongMethodTypeException e) {}
806
807 // Unboxed
808 float p = (float) mh.invoke("1.11");
809 if (p != 1.11f) {
810 fail();
811 }
812
813 // Unboxed and widened
814 double d = (double) mh.invoke("2.5");
815 if (d != 2.5) {
816 fail();
817 }
818
819 // Interface
820 Comparable<Float> c = (Comparable<Float>) mh.invoke("2.125");
821 if (c.compareTo(new Float(2.125f)) != 0) {
822 fail();
823 }
824
825 System.out.println("testReferenceReturnValueConversions done.");
826 }
827
828 private static void testPrimitiveReturnValueConversions() throws Throwable {
829 MethodHandle mh = MethodHandles.lookup().findStatic(
830 Math.class, "min", MethodType.methodType(int.class, int.class, int.class));
831
832 final int SMALL = -8972;
833 final int LARGE = 7932529;
834
835 // No conversion
836 if ((int) mh.invokeExact(LARGE, SMALL) != SMALL) {
837 fail();
838 } else if ((int) mh.invoke(LARGE, SMALL) != SMALL) {
839 fail();
840 } else if ((int) mh.invokeExact(SMALL, LARGE) != SMALL) {
841 fail();
842 } else if ((int) mh.invoke(SMALL, LARGE) != SMALL) {
843 fail();
844 }
845
846 // int -> long
847 try {
848 if ((long) mh.invokeExact(LARGE, SMALL) != (long) SMALL) {}
849 fail();
850 } catch (WrongMethodTypeException e) {}
851
852 if ((long) mh.invoke(LARGE, SMALL) != (long) SMALL) {
853 fail();
854 }
855
856 // int -> short
857 try {
858 if ((short) mh.invokeExact(LARGE, SMALL) != (short) SMALL) {}
859 fail();
860 } catch (WrongMethodTypeException e) {}
861
862 try {
863 if ((short) mh.invoke(LARGE, SMALL) != (short) SMALL) {
864 fail();
865 }
866 } catch (WrongMethodTypeException e) {}
867
868 // int -> Integer
869 try {
870 if (!((Integer) mh.invokeExact(LARGE, SMALL)).equals(new Integer(SMALL))) {}
871 fail();
872 } catch (WrongMethodTypeException e) {}
873
874 if (!((Integer) mh.invoke(LARGE, SMALL)).equals(new Integer(SMALL))) {
875 fail();
876 }
877
878 // int -> Long
879 try {
880 Long l = (Long) mh.invokeExact(LARGE, SMALL);
881 fail();
882 } catch (WrongMethodTypeException e) {}
883
884 try {
885 Long l = (Long) mh.invoke(LARGE, SMALL);
886 fail();
887 } catch (WrongMethodTypeException e) {}
888
889 // int -> Short
890 try {
891 Short s = (Short) mh.invokeExact(LARGE, SMALL);
892 fail();
893 } catch (WrongMethodTypeException e) {}
894
895 try {
896 Short s = (Short) mh.invoke(LARGE, SMALL);
897 fail();
898 } catch (WrongMethodTypeException e) {}
899
900 // int -> Process
901 try {
902 Process p = (Process) mh.invokeExact(LARGE, SMALL);
903 fail();
904 } catch (WrongMethodTypeException e) {}
905
906 try {
907 Process p = (Process) mh.invoke(LARGE, SMALL);
908 fail();
909 } catch (WrongMethodTypeException e) {}
910
911 // void -> Object
912 mh = MethodHandles.lookup().findStatic(System.class, "gc", MethodType.methodType(void.class));
913 Object o = (Object) mh.invoke();
914 if (o != null) fail();
915
916 // void -> long
917 long l = (long) mh.invoke();
918 if (l != 0) fail();
919
Orion Hodsonf1412b42016-11-11 12:03:29 +0000920 // boolean -> Boolean
921 mh = MethodHandles.lookup().findStatic(Boolean.class, "parseBoolean",
922 MethodType.methodType(boolean.class, String.class));
923 Boolean z = (Boolean) mh.invoke("True");
924 if (!z.booleanValue()) fail();
925
926 // boolean -> int
927 try {
928 int dummy = (int) mh.invoke("True");
929 fail();
930 } catch (WrongMethodTypeException e) {}
931
932 // boolean -> Integer
933 try {
934 Integer dummy = (Integer) mh.invoke("True");
935 fail();
936 } catch (WrongMethodTypeException e) {}
937
938 // Boolean -> boolean
939 mh = MethodHandles.lookup().findStatic(Boolean.class, "valueOf",
940 MethodType.methodType(Boolean.class, boolean.class));
941 boolean w = (boolean) mh.invoke(false);
942 if (w) fail();
943
944 // Boolean -> int
945 try {
946 int dummy = (int) mh.invoke(false);
947 fail();
948 } catch (WrongMethodTypeException e) {}
949
950 // Boolean -> Integer
951 try {
952 Integer dummy = (Integer) mh.invoke("True");
953 fail();
954 } catch (WrongMethodTypeException e) {}
955
Orion Hodson1a06f9f2016-11-09 08:32:42 +0000956 System.out.println("testPrimitiveReturnValueConversions done.");
957 }
958
959 public static void testReturnValueConversions() throws Throwable {
960 testReferenceReturnValueConversions();
961 testPrimitiveReturnValueConversions();
962 }
Narayan Kamath9bdaeeb2016-10-20 10:57:45 +0100963}