blob: 44c04477718cf46989dd25e892f4e6aae5b56f0e [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
23public class Main {
24
25 public static class A {
26 public void foo() {
27 System.out.println("foo_A");
28 }
29
30 public static final Lookup lookup = MethodHandles.lookup();
31 }
32
33 public static class B extends A {
34 public void foo() {
35 System.out.println("foo_B");
36 }
37
38 public static final Lookup lookup = MethodHandles.lookup();
39 }
40
41 public static class C extends B {
42 public static final Lookup lookup = MethodHandles.lookup();
43 }
44
45 public static class D {
46 private final void privateRyan() {
47 System.out.println("privateRyan_D");
48 }
49
50 public static final Lookup lookup = MethodHandles.lookup();
51 }
52
53 public static class E extends D {
54 public static final Lookup lookup = MethodHandles.lookup();
55 }
56
57 public static void main(String[] args) throws Throwable {
58 testfindSpecial_invokeSuperBehaviour();
59 testfindSpecial_invokeDirectBehaviour();
Narayan Kamath3e0dce02016-10-31 13:55:55 +000060 testExceptionDetailMessages();
Narayan Kamath94bee022016-11-01 10:57:15 +000061 testfindVirtual();
Narayan Kamath9bdaeeb2016-10-20 10:57:45 +010062 }
63
64 public static void testfindSpecial_invokeSuperBehaviour() throws Throwable {
65 // This is equivalent to an invoke-super instruction where the referrer
66 // is B.class.
67 MethodHandle mh1 = B.lookup.findSpecial(A.class /* refC */, "foo",
68 MethodType.methodType(void.class), B.class /* specialCaller */);
69
70 A aInstance = new A();
71 B bInstance = new B();
72 C cInstance = new C();
73
74 // This should be as if an invoke-super was called from one of B's methods.
75 mh1.invokeExact(bInstance);
76 mh1.invoke(bInstance);
77
78 // This should not work. The receiver type in the handle will be suitably
79 // restricted to B and subclasses.
80 try {
81 mh1.invoke(aInstance);
82 System.out.println("mh1.invoke(aInstance) should not succeeed");
83 } catch (ClassCastException expected) {
84 }
85
86 try {
87 mh1.invokeExact(aInstance);
88 System.out.println("mh1.invoke(aInstance) should not succeeed");
89 } catch (WrongMethodTypeException expected) {
Narayan Kamath9bdaeeb2016-10-20 10:57:45 +010090 }
91
92 // This should *still* be as if an invoke-super was called from one of C's
93 // methods, despite the fact that we're operating on a C.
94 mh1.invoke(cInstance);
95
96 // Now that C is the special caller, the next invoke will call B.foo.
97 MethodHandle mh2 = C.lookup.findSpecial(A.class /* refC */, "foo",
98 MethodType.methodType(void.class), C.class /* specialCaller */);
99 mh2.invokeExact(cInstance);
100
101 // Shouldn't allow invoke-super semantics from an unrelated special caller.
102 try {
103 C.lookup.findSpecial(A.class, "foo",
104 MethodType.methodType(void.class), D.class /* specialCaller */);
105 System.out.println("findSpecial(A.class, foo, .. D.class) unexpectedly succeeded.");
106 } catch (IllegalAccessException expected) {
107 }
108 }
109
110 public static void testfindSpecial_invokeDirectBehaviour() throws Throwable {
111 D dInstance = new D();
112
113 MethodHandle mh3 = D.lookup.findSpecial(D.class, "privateRyan",
114 MethodType.methodType(void.class), D.class /* specialCaller */);
115 mh3.invoke(dInstance);
116
117 // The private method shouldn't be accessible from any special caller except
118 // itself...
119 try {
120 D.lookup.findSpecial(D.class, "privateRyan", MethodType.methodType(void.class), C.class);
121 System.out.println("findSpecial(privateRyan, C.class) unexpectedly succeeded");
122 } catch (IllegalAccessException expected) {
123 }
124
125 // ... or from any lookup context except its own.
126 try {
127 E.lookup.findSpecial(D.class, "privateRyan", MethodType.methodType(void.class), E.class);
128 System.out.println("findSpecial(privateRyan, E.class) unexpectedly succeeded");
129 } catch (IllegalAccessException expected) {
130 }
131 }
Narayan Kamath3e0dce02016-10-31 13:55:55 +0000132
133 public static void testExceptionDetailMessages() throws Throwable {
134 MethodHandle handle = MethodHandles.lookup().findVirtual(String.class, "concat",
135 MethodType.methodType(String.class, String.class));
136
137 try {
138 handle.invokeExact("a", new Object());
139 System.out.println("invokeExact(\"a\", new Object()) unexpectedly succeeded.");
140 } catch (WrongMethodTypeException ex) {
141 System.out.println("Received exception: " + ex.getMessage());
142 }
143 }
Narayan Kamath94bee022016-11-01 10:57:15 +0000144
145 public interface Foo {
146 public String foo();
147 }
148
149 public interface Bar extends Foo {
150 public String bar();
151 }
152
153 public static class BarSuper {
154 public String superPublicMethod() {
155 return "superPublicMethod";
156 }
157
158 public String superProtectedMethod() {
159 return "superProtectedMethod";
160 }
161
162 String superPackageMethod() {
163 return "superPackageMethod";
164 }
165 }
166
167 public static class BarImpl extends BarSuper implements Bar {
168 public BarImpl() {
169 }
170
171 @Override
172 public String foo() {
173 return "foo";
174 }
175
176 @Override
177 public String bar() {
178 return "bar";
179 }
180
181 private String privateMethod() { return "privateMethod"; }
182
183 public static String staticMethod() { return null; }
184
185 static final MethodHandles.Lookup lookup = MethodHandles.lookup();
186 }
187
188 public static void testfindVirtual() throws Throwable {
189 // Virtual lookups on static methods should not succeed.
190 try {
191 MethodHandles.lookup().findVirtual(
192 BarImpl.class, "staticMethod", MethodType.methodType(String.class));
193 System.out.println("findVirtual(staticMethod) unexpectedly succeeded");
194 } catch (IllegalAccessException expected) {
195 }
196
197 // Virtual lookups on private methods should not succeed, unless the Lookup
198 // context had sufficient privileges.
199 try {
200 MethodHandles.lookup().findVirtual(
201 BarImpl.class, "privateMethod", MethodType.methodType(String.class));
202 System.out.println("findVirtual(privateMethod) unexpectedly succeeded");
203 } catch (IllegalAccessException expected) {
204 }
205
206 // Virtual lookup on a private method with a context that *does* have sufficient
207 // privileges.
208 MethodHandle mh = BarImpl.lookup.findVirtual(
209 BarImpl.class, "privateMethod", MethodType.methodType(String.class));
210 String str = (String) mh.invoke(new BarImpl());
211 if (!"privateMethod".equals(str)) {
212 System.out.println("Unexpected return value for BarImpl#privateMethod: " + str);
213 }
214
215 // Find virtual must find interface methods defined by interfaces implemented
216 // by the class.
217 mh = MethodHandles.lookup().findVirtual(BarImpl.class, "foo",
218 MethodType.methodType(String.class));
219 str = (String) mh.invoke(new BarImpl());
220 if (!"foo".equals(str)) {
221 System.out.println("Unexpected return value for BarImpl#foo: " + str);
222 }
223
224 // .. and their super-interfaces.
225 mh = MethodHandles.lookup().findVirtual(BarImpl.class, "bar",
226 MethodType.methodType(String.class));
227 str = (String) mh.invoke(new BarImpl());
228 if (!"bar".equals(str)) {
229 System.out.println("Unexpected return value for BarImpl#bar: " + str);
230 }
231
232 // TODO(narayan): Fix this case, we're using the wrong ArtMethod for the
233 // invoke resulting in a failing check in the interpreter.
234 //
235 // mh = MethodHandles.lookup().findVirtual(Bar.class, "bar",
236 // MethodType.methodType(String.class));
237 // str = (String) mh.invoke(new BarImpl());
238 // if (!"bar".equals(str)) {
239 // System.out.println("Unexpected return value for BarImpl#bar: " + str);
240 // }
241
242 // We should also be able to lookup public / protected / package methods in
243 // the super class, given sufficient access privileges.
244 mh = MethodHandles.lookup().findVirtual(BarImpl.class, "superPublicMethod",
245 MethodType.methodType(String.class));
246 str = (String) mh.invoke(new BarImpl());
247 if (!"superPublicMethod".equals(str)) {
248 System.out.println("Unexpected return value for BarImpl#superPublicMethod: " + str);
249 }
250
251 mh = MethodHandles.lookup().findVirtual(BarImpl.class, "superProtectedMethod",
252 MethodType.methodType(String.class));
253 str = (String) mh.invoke(new BarImpl());
254 if (!"superProtectedMethod".equals(str)) {
255 System.out.println("Unexpected return value for BarImpl#superProtectedMethod: " + str);
256 }
257
258 mh = MethodHandles.lookup().findVirtual(BarImpl.class, "superPackageMethod",
259 MethodType.methodType(String.class));
260 str = (String) mh.invoke(new BarImpl());
261 if (!"superPackageMethod".equals(str)) {
262 System.out.println("Unexpected return value for BarImpl#superPackageMethod: " + str);
263 }
264 }
Narayan Kamath9bdaeeb2016-10-20 10:57:45 +0100265}
266
267