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