blob: 780513f9eda9a2c5960d0837fad2bd189d8f556b [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;
22
Narayan Kamathe5eb5742016-11-02 14:16:27 +000023import java.lang.reflect.Constructor;
24import java.lang.reflect.Field;
25import java.lang.reflect.Method;
26
Narayan Kamath9bdaeeb2016-10-20 10:57:45 +010027public class Main {
28
29 public static class A {
30 public void foo() {
31 System.out.println("foo_A");
32 }
33
34 public static final Lookup lookup = MethodHandles.lookup();
35 }
36
37 public static class B extends A {
38 public void foo() {
39 System.out.println("foo_B");
40 }
41
42 public static final Lookup lookup = MethodHandles.lookup();
43 }
44
45 public static class C extends B {
46 public static final Lookup lookup = MethodHandles.lookup();
47 }
48
49 public static class D {
50 private final void privateRyan() {
51 System.out.println("privateRyan_D");
52 }
53
54 public static final Lookup lookup = MethodHandles.lookup();
55 }
56
57 public static class E extends D {
58 public static final Lookup lookup = MethodHandles.lookup();
59 }
60
61 public static void main(String[] args) throws Throwable {
62 testfindSpecial_invokeSuperBehaviour();
63 testfindSpecial_invokeDirectBehaviour();
Narayan Kamath3e0dce02016-10-31 13:55:55 +000064 testExceptionDetailMessages();
Narayan Kamath94bee022016-11-01 10:57:15 +000065 testfindVirtual();
Narayan Kamathe5eb5742016-11-02 14:16:27 +000066 testUnreflects();
Narayan Kamath0a8485e2016-11-02 18:47:11 +000067 testAsType();
Narayan Kamath9bdaeeb2016-10-20 10:57:45 +010068 }
69
70 public static void testfindSpecial_invokeSuperBehaviour() throws Throwable {
71 // This is equivalent to an invoke-super instruction where the referrer
72 // is B.class.
73 MethodHandle mh1 = B.lookup.findSpecial(A.class /* refC */, "foo",
74 MethodType.methodType(void.class), B.class /* specialCaller */);
75
76 A aInstance = new A();
77 B bInstance = new B();
78 C cInstance = new C();
79
80 // This should be as if an invoke-super was called from one of B's methods.
81 mh1.invokeExact(bInstance);
82 mh1.invoke(bInstance);
83
84 // This should not work. The receiver type in the handle will be suitably
85 // restricted to B and subclasses.
86 try {
87 mh1.invoke(aInstance);
88 System.out.println("mh1.invoke(aInstance) should not succeeed");
89 } catch (ClassCastException expected) {
90 }
91
92 try {
93 mh1.invokeExact(aInstance);
94 System.out.println("mh1.invoke(aInstance) should not succeeed");
95 } catch (WrongMethodTypeException expected) {
Narayan Kamath9bdaeeb2016-10-20 10:57:45 +010096 }
97
98 // This should *still* be as if an invoke-super was called from one of C's
99 // methods, despite the fact that we're operating on a C.
100 mh1.invoke(cInstance);
101
102 // Now that C is the special caller, the next invoke will call B.foo.
103 MethodHandle mh2 = C.lookup.findSpecial(A.class /* refC */, "foo",
104 MethodType.methodType(void.class), C.class /* specialCaller */);
105 mh2.invokeExact(cInstance);
106
107 // Shouldn't allow invoke-super semantics from an unrelated special caller.
108 try {
109 C.lookup.findSpecial(A.class, "foo",
110 MethodType.methodType(void.class), D.class /* specialCaller */);
111 System.out.println("findSpecial(A.class, foo, .. D.class) unexpectedly succeeded.");
112 } catch (IllegalAccessException expected) {
113 }
114 }
115
116 public static void testfindSpecial_invokeDirectBehaviour() throws Throwable {
117 D dInstance = new D();
118
119 MethodHandle mh3 = D.lookup.findSpecial(D.class, "privateRyan",
120 MethodType.methodType(void.class), D.class /* specialCaller */);
121 mh3.invoke(dInstance);
122
123 // The private method shouldn't be accessible from any special caller except
124 // itself...
125 try {
126 D.lookup.findSpecial(D.class, "privateRyan", MethodType.methodType(void.class), C.class);
127 System.out.println("findSpecial(privateRyan, C.class) unexpectedly succeeded");
128 } catch (IllegalAccessException expected) {
129 }
130
131 // ... or from any lookup context except its own.
132 try {
133 E.lookup.findSpecial(D.class, "privateRyan", MethodType.methodType(void.class), E.class);
134 System.out.println("findSpecial(privateRyan, E.class) unexpectedly succeeded");
135 } catch (IllegalAccessException expected) {
136 }
137 }
Narayan Kamath3e0dce02016-10-31 13:55:55 +0000138
139 public static void testExceptionDetailMessages() throws Throwable {
140 MethodHandle handle = MethodHandles.lookup().findVirtual(String.class, "concat",
141 MethodType.methodType(String.class, String.class));
142
143 try {
144 handle.invokeExact("a", new Object());
145 System.out.println("invokeExact(\"a\", new Object()) unexpectedly succeeded.");
146 } catch (WrongMethodTypeException ex) {
147 System.out.println("Received exception: " + ex.getMessage());
148 }
149 }
Narayan Kamath94bee022016-11-01 10:57:15 +0000150
151 public interface Foo {
152 public String foo();
153 }
154
155 public interface Bar extends Foo {
156 public String bar();
157 }
158
159 public static class BarSuper {
160 public String superPublicMethod() {
161 return "superPublicMethod";
162 }
163
164 public String superProtectedMethod() {
165 return "superProtectedMethod";
166 }
167
168 String superPackageMethod() {
169 return "superPackageMethod";
170 }
171 }
172
173 public static class BarImpl extends BarSuper implements Bar {
174 public BarImpl() {
175 }
176
177 @Override
178 public String foo() {
179 return "foo";
180 }
181
182 @Override
183 public String bar() {
184 return "bar";
185 }
186
187 private String privateMethod() { return "privateMethod"; }
188
189 public static String staticMethod() { return null; }
190
191 static final MethodHandles.Lookup lookup = MethodHandles.lookup();
192 }
193
194 public static void testfindVirtual() throws Throwable {
195 // Virtual lookups on static methods should not succeed.
196 try {
197 MethodHandles.lookup().findVirtual(
198 BarImpl.class, "staticMethod", MethodType.methodType(String.class));
199 System.out.println("findVirtual(staticMethod) unexpectedly succeeded");
200 } catch (IllegalAccessException expected) {
201 }
202
203 // Virtual lookups on private methods should not succeed, unless the Lookup
204 // context had sufficient privileges.
205 try {
206 MethodHandles.lookup().findVirtual(
207 BarImpl.class, "privateMethod", MethodType.methodType(String.class));
208 System.out.println("findVirtual(privateMethod) unexpectedly succeeded");
209 } catch (IllegalAccessException expected) {
210 }
211
212 // Virtual lookup on a private method with a context that *does* have sufficient
213 // privileges.
214 MethodHandle mh = BarImpl.lookup.findVirtual(
215 BarImpl.class, "privateMethod", MethodType.methodType(String.class));
216 String str = (String) mh.invoke(new BarImpl());
217 if (!"privateMethod".equals(str)) {
218 System.out.println("Unexpected return value for BarImpl#privateMethod: " + str);
219 }
220
221 // Find virtual must find interface methods defined by interfaces implemented
222 // by the class.
223 mh = MethodHandles.lookup().findVirtual(BarImpl.class, "foo",
224 MethodType.methodType(String.class));
225 str = (String) mh.invoke(new BarImpl());
226 if (!"foo".equals(str)) {
227 System.out.println("Unexpected return value for BarImpl#foo: " + str);
228 }
229
230 // .. and their super-interfaces.
231 mh = MethodHandles.lookup().findVirtual(BarImpl.class, "bar",
232 MethodType.methodType(String.class));
233 str = (String) mh.invoke(new BarImpl());
234 if (!"bar".equals(str)) {
235 System.out.println("Unexpected return value for BarImpl#bar: " + str);
236 }
237
238 // TODO(narayan): Fix this case, we're using the wrong ArtMethod for the
239 // invoke resulting in a failing check in the interpreter.
240 //
241 // mh = MethodHandles.lookup().findVirtual(Bar.class, "bar",
242 // MethodType.methodType(String.class));
243 // str = (String) mh.invoke(new BarImpl());
244 // if (!"bar".equals(str)) {
245 // System.out.println("Unexpected return value for BarImpl#bar: " + str);
246 // }
247
248 // We should also be able to lookup public / protected / package methods in
249 // the super class, given sufficient access privileges.
250 mh = MethodHandles.lookup().findVirtual(BarImpl.class, "superPublicMethod",
251 MethodType.methodType(String.class));
252 str = (String) mh.invoke(new BarImpl());
253 if (!"superPublicMethod".equals(str)) {
254 System.out.println("Unexpected return value for BarImpl#superPublicMethod: " + str);
255 }
256
257 mh = MethodHandles.lookup().findVirtual(BarImpl.class, "superProtectedMethod",
258 MethodType.methodType(String.class));
259 str = (String) mh.invoke(new BarImpl());
260 if (!"superProtectedMethod".equals(str)) {
261 System.out.println("Unexpected return value for BarImpl#superProtectedMethod: " + str);
262 }
263
264 mh = MethodHandles.lookup().findVirtual(BarImpl.class, "superPackageMethod",
265 MethodType.methodType(String.class));
266 str = (String) mh.invoke(new BarImpl());
267 if (!"superPackageMethod".equals(str)) {
268 System.out.println("Unexpected return value for BarImpl#superPackageMethod: " + str);
269 }
270 }
Narayan Kamathe5eb5742016-11-02 14:16:27 +0000271
272 static class UnreflectTester {
273 public String publicField;
274 private String privateField;
275
276 public static String publicStaticField = "publicStaticValue";
277 private static String privateStaticField = "privateStaticValue";
278
279 private UnreflectTester(String val) {
280 publicField = val;
281 privateField = val;
282 }
283
284 // NOTE: The boolean constructor argument only exists to give this a
285 // different signature.
286 public UnreflectTester(String val, boolean unused) {
287 this(val);
288 }
289
290 private static String privateStaticMethod() {
291 return "privateStaticMethod";
292 }
293
294 private String privateMethod() {
295 return "privateMethod";
296 }
297
298 public static String publicStaticMethod() {
299 return "publicStaticMethod";
300 }
301
302 public String publicMethod() {
303 return "publicMethod";
304 }
305 }
306
307 public static void testUnreflects() throws Throwable {
308 UnreflectTester instance = new UnreflectTester("unused");
309 Method publicMethod = UnreflectTester.class.getMethod("publicMethod");
310
311 MethodHandle mh = MethodHandles.lookup().unreflect(publicMethod);
312 assertEquals("publicMethod", (String) mh.invoke(instance));
313 assertEquals("publicMethod", (String) mh.invokeExact(instance));
314
315 Method publicStaticMethod = UnreflectTester.class.getMethod("publicStaticMethod");
316 mh = MethodHandles.lookup().unreflect(publicStaticMethod);
317 assertEquals("publicStaticMethod", (String) mh.invoke());
318 assertEquals("publicStaticMethod", (String) mh.invokeExact());
319
320 Method privateMethod = UnreflectTester.class.getDeclaredMethod("privateMethod");
321 try {
322 mh = MethodHandles.lookup().unreflect(privateMethod);
323 fail();
324 } catch (IllegalAccessException expected) {}
325
326 privateMethod.setAccessible(true);
327 mh = MethodHandles.lookup().unreflect(privateMethod);
328 assertEquals("privateMethod", (String) mh.invoke(instance));
329 assertEquals("privateMethod", (String) mh.invokeExact(instance));
330
331 Method privateStaticMethod = UnreflectTester.class.getDeclaredMethod("privateStaticMethod");
332 try {
333 mh = MethodHandles.lookup().unreflect(privateStaticMethod);
334 fail();
335 } catch (IllegalAccessException expected) {}
336
337 privateStaticMethod.setAccessible(true);
338 mh = MethodHandles.lookup().unreflect(privateStaticMethod);
339 assertEquals("privateStaticMethod", (String) mh.invoke());
340 assertEquals("privateStaticMethod", (String) mh.invokeExact());
341
342 Constructor privateConstructor = UnreflectTester.class.getDeclaredConstructor(String.class);
343 try {
344 mh = MethodHandles.lookup().unreflectConstructor(privateConstructor);
345 fail();
346 } catch (IllegalAccessException expected) {}
347
348 privateConstructor.setAccessible(true);
349 mh = MethodHandles.lookup().unreflectConstructor(privateConstructor);
350 // TODO(narayan): Method handle constructor invokes are not supported yet.
351 //
352 // UnreflectTester tester = (UnreflectTester) mh.invoke("foo");
353 // UnreflectTester tester = (UnreflectTester) mh.invoke("fooExact");
354
355 Constructor publicConstructor = UnreflectTester.class.getConstructor(String.class,
356 boolean.class);
357 mh = MethodHandles.lookup().unreflectConstructor(publicConstructor);
358 // TODO(narayan): Method handle constructor invokes are not supported yet.
359 //
360 // UnreflectTester tester = (UnreflectTester) mh.invoke("foo");
361 // UnreflectTester tester = (UnreflectTester) mh.invoke("fooExact");
362
363 // TODO(narayan): Non exact invokes for field sets/gets are not implemented yet.
364 //
365 // assertEquals("instanceValue", (String) mh.invoke(new UnreflectTester("instanceValue")));
366 Field publicField = UnreflectTester.class.getField("publicField");
367 mh = MethodHandles.lookup().unreflectGetter(publicField);
368 instance = new UnreflectTester("instanceValue");
369 assertEquals("instanceValue", (String) mh.invokeExact(instance));
370
371 mh = MethodHandles.lookup().unreflectSetter(publicField);
372 instance = new UnreflectTester("instanceValue");
373 mh.invokeExact(instance, "updatedInstanceValue");
374 assertEquals("updatedInstanceValue", instance.publicField);
375
376 Field publicStaticField = UnreflectTester.class.getField("publicStaticField");
377 mh = MethodHandles.lookup().unreflectGetter(publicStaticField);
378 UnreflectTester.publicStaticField = "updatedStaticValue";
379 assertEquals("updatedStaticValue", (String) mh.invokeExact());
380
381 mh = MethodHandles.lookup().unreflectSetter(publicStaticField);
382 UnreflectTester.publicStaticField = "updatedStaticValue";
383 mh.invokeExact("updatedStaticValue2");
384 assertEquals("updatedStaticValue2", UnreflectTester.publicStaticField);
385
386 Field privateField = UnreflectTester.class.getDeclaredField("privateField");
387 try {
388 mh = MethodHandles.lookup().unreflectGetter(privateField);
389 fail();
390 } catch (IllegalAccessException expected) {
391 }
392 try {
393 mh = MethodHandles.lookup().unreflectSetter(privateField);
394 fail();
395 } catch (IllegalAccessException expected) {
396 }
397
398 privateField.setAccessible(true);
399
400 mh = MethodHandles.lookup().unreflectGetter(privateField);
401 instance = new UnreflectTester("instanceValue");
402 assertEquals("instanceValue", (String) mh.invokeExact(instance));
403
404 mh = MethodHandles.lookup().unreflectSetter(privateField);
405 instance = new UnreflectTester("instanceValue");
406 mh.invokeExact(instance, "updatedInstanceValue");
407 assertEquals("updatedInstanceValue", instance.privateField);
408
409 Field privateStaticField = UnreflectTester.class.getDeclaredField("privateStaticField");
410 try {
411 mh = MethodHandles.lookup().unreflectGetter(privateStaticField);
412 fail();
413 } catch (IllegalAccessException expected) {
414 }
415 try {
416 mh = MethodHandles.lookup().unreflectSetter(privateStaticField);
417 fail();
418 } catch (IllegalAccessException expected) {
419 }
420
421 privateStaticField.setAccessible(true);
422 mh = MethodHandles.lookup().unreflectGetter(privateStaticField);
423 privateStaticField.set(null, "updatedStaticValue");
424 assertEquals("updatedStaticValue", (String) mh.invokeExact());
425
426 mh = MethodHandles.lookup().unreflectSetter(privateStaticField);
427 privateStaticField.set(null, "updatedStaticValue");
428 mh.invokeExact("updatedStaticValue2");
429 assertEquals("updatedStaticValue2", (String) privateStaticField.get(null));
430 }
431
Narayan Kamath0a8485e2016-11-02 18:47:11 +0000432 // This method only exists to fool Jack's handling of types. See b/32536744.
433 public static CharSequence getSequence() {
434 return "foo";
435 }
436
437 public static void testAsType() throws Throwable {
438 // The type of this handle is (String, String)String.
439 MethodHandle mh = MethodHandles.lookup().findVirtual(String.class,
440 "concat", MethodType.methodType(String.class, String.class));
441
442 // Change it to (CharSequence, String)Object.
443 MethodHandle asType = mh.asType(
444 MethodType.methodType(Object.class, CharSequence.class, String.class));
445
446 Object obj = asType.invokeExact((CharSequence) getSequence(), "bar");
447 assertEquals("foobar", (String) obj);
448
449 // Should fail due to a wrong return type.
450 try {
451 String str = (String) asType.invokeExact((CharSequence) getSequence(), "bar");
452 fail();
453 } catch (WrongMethodTypeException expected) {
454 }
455
456 // Should fail due to a wrong argument type (String instead of Charsequence).
457 try {
458 String str = (String) asType.invokeExact("baz", "bar");
459 fail();
460 } catch (WrongMethodTypeException expected) {
461 }
462
463 // Calls to asType should fail if the types are not convertible.
464 //
465 // Bad return type conversion.
466 try {
467 mh.asType(MethodType.methodType(int.class, String.class, String.class));
468 fail();
469 } catch (WrongMethodTypeException expected) {
470 }
471
472 // Bad argument conversion.
473 try {
474 mh.asType(MethodType.methodType(String.class, int.class, String.class));
475 fail();
476 } catch (WrongMethodTypeException expected) {
477 }
478 }
479
Narayan Kamathe5eb5742016-11-02 14:16:27 +0000480 public static void assertEquals(String s1, String s2) {
481 if (s1 == s2) {
482 return;
483 }
484
485 if (s1 != null && s2 != null && s1.equals(s2)) {
486 return;
487 }
488
489 throw new AssertionError("assertEquals s1: " + s1 + ", s2: " + s2);
490 }
491
492 public static void fail() {
493 System.out.println("fail");
494 Thread.dumpStack();
495 }
Narayan Kamath9bdaeeb2016-10-20 10:57:45 +0100496}
497
498