|
1 /* |
|
2 * Copyright (c) 2018, 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. |
|
8 * |
|
9 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
12 * version 2 for more details (a copy is included in the LICENSE file that |
|
13 * accompanied this code). |
|
14 * |
|
15 * You should have received a copy of the GNU General Public License version |
|
16 * 2 along with this work; if not, write to the Free Software Foundation, |
|
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
18 * |
|
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
20 * or visit www.oracle.com if you need additional information or have any |
|
21 * questions. |
|
22 */ |
|
23 |
|
24 /** |
|
25 * @test |
|
26 * @bug 8046171 |
|
27 * @summary Test direct and MethodHandle access to private interface methods using invokeinterface semantics |
|
28 * to ensure all receiver typechecks occur as required. |
|
29 * @comment This complements SpecialInterfaceCall which tests invokespecial semantics. |
|
30 * @compile PrivateInterfaceCall.java |
|
31 * @compile PrivateInterfaceCallI4.jasm |
|
32 * @run main/othervm -Xint PrivateInterfaceCall |
|
33 * @run main/othervm -Xbatch -XX:+TieredCompilation -XX:TieredStopAtLevel=1 PrivateInterfaceCall |
|
34 * @run main/othervm -Xbatch -XX:+TieredCompilation -XX:TieredStopAtLevel=2 PrivateInterfaceCall |
|
35 * @run main/othervm -Xbatch -XX:+TieredCompilation -XX:TieredStopAtLevel=3 PrivateInterfaceCall |
|
36 * @run main/othervm -Xbatch -XX:-TieredCompilation PrivateInterfaceCall |
|
37 */ |
|
38 |
|
39 // This is an adaptation of SpecialInterfaceCall to only use private interface methods and with |
|
40 // virtual invocation semantics. Because we don't have the same corner cases as for invokespecial |
|
41 // there's no practical difference between the I3 and I2 cases here. But we do have to ensure the |
|
42 // correct versions of the methods get executed. |
|
43 // In addition we add tests that involve calls from nestmates - which also covers the distinction |
|
44 // between the caller being a class and being an interface. |
|
45 |
|
46 import java.lang.invoke.*; |
|
47 |
|
48 public class PrivateInterfaceCall { |
|
49 interface I1 { |
|
50 private void priv_m() { throw new Error("Should not call this"); }; |
|
51 } |
|
52 interface I2 extends I1 { |
|
53 private void priv_m() { }; |
|
54 |
|
55 static void invokeDirect(I2 i) { |
|
56 i.priv_m(); // generates invokeinterface |
|
57 } |
|
58 static void invokeInterfaceMH(I2 i) throws Throwable { |
|
59 // emulates behaviour of invokeDirect |
|
60 mh_I2_priv_m_from_I2.invokeExact(i); |
|
61 } |
|
62 // special case of invoking an Object method via an interface |
|
63 static void invokeInterfaceObjectMH(I2 i) throws Throwable { |
|
64 // emulates invokeInterface of I2.toString on i, which resolves |
|
65 // to Object.toString |
|
66 String s = (String) mh_I2_toString_from_I2.invokeExact(i); |
|
67 } |
|
68 // special case of invoking a final Object method via an interface |
|
69 static void invokeInterfaceObjectFinalMH(I2 i) throws Throwable { |
|
70 // emulates invokeInterface of I1.getClass on i, which resolves |
|
71 // to Object.getClass |
|
72 Class<?> c = (Class<?>) mh_I2_getClass_from_I2.invokeExact(i); |
|
73 } |
|
74 |
|
75 static void init() throws Throwable { |
|
76 MethodType mt = MethodType.methodType(void.class); |
|
77 MethodHandles.Lookup lookup = MethodHandles.lookup(); |
|
78 mh_I2_priv_m_from_I2 = lookup.findVirtual(I2.class, "priv_m", mt); |
|
79 |
|
80 mt = MethodType.methodType(String.class); |
|
81 mh_I2_toString_from_I2 = lookup.findVirtual(I2.class, "toString", mt); |
|
82 |
|
83 mt = MethodType.methodType(Class.class); |
|
84 mh_I2_getClass_from_I2 = lookup.findVirtual(I2.class, "getClass", mt); |
|
85 } |
|
86 } |
|
87 interface I3 extends I2 { |
|
88 static void invokeInterfaceMH(I2 i) throws Throwable { |
|
89 // emulates behaviour of I2.invokeDirect |
|
90 mh_I2_priv_m_from_I3.invokeExact(i); |
|
91 } |
|
92 static void init() throws Throwable { |
|
93 MethodType mt = MethodType.methodType(void.class); |
|
94 mh_I2_priv_m_from_I3 = MethodHandles.lookup().findVirtual(I2.class, "priv_m", mt); |
|
95 } |
|
96 } |
|
97 |
|
98 // This interface acts like I2 but we define directInvoke* methods |
|
99 // that we will rewrite the bytecode of to use invokeinterface |
|
100 // (see PrivateInterfaceCallI4.jasm). |
|
101 interface I4 extends I1 { |
|
102 static void invokeDirect(I4 i) { |
|
103 // invokeinterface I4.toString() |
|
104 throw new Error("Class file for I4 is not overwritten"); |
|
105 } |
|
106 static void invokeDirectFinal(I4 i) { |
|
107 // invokeinterface I4.getClass() - final method |
|
108 throw new Error("Class file for I4 is not overwritten"); |
|
109 } |
|
110 } |
|
111 |
|
112 // check invocations from nestmates outside the |
|
113 // inheritance hierarchy - and from a class not interface |
|
114 static void invokeDirect(I2 i) { |
|
115 i.priv_m(); // generates invokeinterface |
|
116 } |
|
117 static void invokeInterfaceMH(I2 i) throws Throwable { |
|
118 mh_I2_priv_m_from_PIC.invokeExact(i); |
|
119 } |
|
120 |
|
121 // Concrete classes |
|
122 static class C2 implements I2 { } |
|
123 static class C3 implements I3 { } |
|
124 static class C4 implements I4 { } |
|
125 |
|
126 // Classes that don't implement I2/I3 but do have a |
|
127 // priv_m method in their hierarchy |
|
128 static class D1 implements I1 { } |
|
129 static class E { |
|
130 private void priv_m() { throw new Error("Should not call this"); } |
|
131 } |
|
132 |
|
133 // This MH acts like the invocation in I2.invokeDirect with caller I2 |
|
134 static MethodHandle mh_I2_priv_m_from_I2; |
|
135 |
|
136 // This MH acts like the invocation in I3.invokeDirect with caller I3 |
|
137 static MethodHandle mh_I2_priv_m_from_I3; |
|
138 |
|
139 // This MH acts like the invocation in PrivateInterfaceCall.invokeDirect |
|
140 // with caller PrivateInterfaceCall |
|
141 static MethodHandle mh_I2_priv_m_from_PIC; |
|
142 |
|
143 // This MH acts likes an invokeinterface of I2.toString from I2 |
|
144 static MethodHandle mh_I2_toString_from_I2; |
|
145 |
|
146 // This MH acts likes an invokeinterface of I2.getClass from I2 |
|
147 static MethodHandle mh_I2_getClass_from_I2; |
|
148 |
|
149 static { |
|
150 try { |
|
151 MethodType mt = MethodType.methodType(void.class); |
|
152 mh_I2_priv_m_from_PIC = MethodHandles.lookup().findVirtual(I2.class, "priv_m", mt); |
|
153 I2.init(); |
|
154 I3.init(); |
|
155 } catch (Throwable e) { |
|
156 throw new Error(e); |
|
157 } |
|
158 } |
|
159 |
|
160 static void runPositiveTests() { |
|
161 shouldNotThrow(() -> PrivateInterfaceCall.invokeDirect(new C2())); |
|
162 shouldNotThrow(() -> PrivateInterfaceCall.invokeDirect(new C3())); |
|
163 shouldNotThrow(() -> PrivateInterfaceCall.invokeInterfaceMH(new C2())); |
|
164 shouldNotThrow(() -> PrivateInterfaceCall.invokeInterfaceMH(new C3())); |
|
165 |
|
166 shouldNotThrow(() -> I2.invokeDirect(new C2())); |
|
167 shouldNotThrow(() -> I2.invokeDirect(new C3())); |
|
168 shouldNotThrow(() -> I2.invokeInterfaceMH(new C2())); |
|
169 shouldNotThrow(() -> I2.invokeInterfaceMH(new C3())); |
|
170 shouldNotThrow(() -> I2.invokeInterfaceObjectMH(new C2())); |
|
171 shouldNotThrow(() -> I2.invokeInterfaceObjectMH(new C3())); |
|
172 shouldNotThrow(() -> I2.invokeInterfaceObjectFinalMH(new C2())); |
|
173 shouldNotThrow(() -> I2.invokeInterfaceObjectFinalMH(new C3())); |
|
174 |
|
175 // This looks odd but at runtime the only constraint is that the |
|
176 // receiver is an I2. In contrast in the invokespecial case the |
|
177 // receiver must be an I3. |
|
178 shouldNotThrow(() -> I3.invokeInterfaceMH(unsafeCastI3(new C2()))); |
|
179 shouldNotThrow(() -> I3.invokeInterfaceMH(new C3())); |
|
180 |
|
181 shouldNotThrow(() -> I4.invokeDirect(new C4())); |
|
182 shouldNotThrow(() -> I4.invokeDirectFinal(new C4())); |
|
183 } |
|
184 |
|
185 static void runNegativeTests() { |
|
186 System.out.println("ICCE PrivateInterfaceCall.invokeDirect D1"); |
|
187 shouldThrowICCE(() -> PrivateInterfaceCall.invokeDirect(unsafeCastI2(new D1()))); |
|
188 System.out.println("ICCE PrivateInterfaceCall.invokeDirect E"); |
|
189 shouldThrowICCE(() -> PrivateInterfaceCall.invokeDirect(unsafeCastI2(new E()))); |
|
190 System.out.println("ICCE PrivateInterfaceCall.invokeInterfaceMH D1"); |
|
191 shouldThrowICCE(() -> PrivateInterfaceCall.invokeInterfaceMH(unsafeCastI2(new D1()))); |
|
192 System.out.println("ICCE PrivateInterfaceCall.invokeInterfaceMH E"); |
|
193 shouldThrowICCE(() -> PrivateInterfaceCall.invokeInterfaceMH(unsafeCastI2(new E()))); |
|
194 |
|
195 |
|
196 System.out.println("ICCE I2.invokeInterfaceMH D1"); |
|
197 shouldThrowICCE(() -> I2.invokeInterfaceMH(unsafeCastI2(new D1()))); |
|
198 System.out.println("ICCE I2.invokeInterfaceMH E"); |
|
199 shouldThrowICCE(() -> I2.invokeInterfaceMH(unsafeCastI2(new E()))); |
|
200 |
|
201 System.out.println("ICCE I2.invokeInterfaceObjectFinalMH D1"); |
|
202 shouldThrowICCE(() -> I2.invokeInterfaceObjectFinalMH(unsafeCastI2(new D1()))); |
|
203 System.out.println("ICCE I2.invokeInterfaceObjectFinalMH E"); |
|
204 shouldThrowICCE(() -> I2.invokeInterfaceObjectFinalMH(unsafeCastI2(new E()))); |
|
205 |
|
206 System.out.println("ICCE I3.invokeInterfaceMH D1"); |
|
207 shouldThrowICCE(() -> I3.invokeInterfaceMH(unsafeCastI3(new D1()))); |
|
208 System.out.println("ICCE I3.invokeInterfaceMH E"); |
|
209 shouldThrowICCE(() -> I3.invokeInterfaceMH(unsafeCastI3(new E()))); |
|
210 |
|
211 System.out.println("ICCE I4.invokeDirect D1"); |
|
212 shouldThrowICCE(() -> I4.invokeDirect(unsafeCastI4(new D1()))); |
|
213 System.out.println("ICCE I4.invokeDirect E"); |
|
214 shouldThrowICCE(() -> I4.invokeDirect(unsafeCastI4(new E()))); |
|
215 } |
|
216 |
|
217 static void warmup() { |
|
218 for (int i = 0; i < 20_000; i++) { |
|
219 runPositiveTests(); |
|
220 } |
|
221 } |
|
222 |
|
223 public static void main(String[] args) throws Throwable { |
|
224 System.out.println("UNRESOLVED:"); |
|
225 runNegativeTests(); |
|
226 runPositiveTests(); |
|
227 |
|
228 System.out.println("RESOLVED:"); |
|
229 runNegativeTests(); |
|
230 |
|
231 System.out.println("WARMUP:"); |
|
232 warmup(); |
|
233 |
|
234 System.out.println("COMPILED:"); |
|
235 runNegativeTests(); |
|
236 runPositiveTests(); |
|
237 } |
|
238 |
|
239 static interface Test { |
|
240 void run() throws Throwable; |
|
241 } |
|
242 |
|
243 static void shouldThrowICCE(Test t) { |
|
244 shouldThrow(IncompatibleClassChangeError.class, |
|
245 "does not implement the requested interface", t); |
|
246 } |
|
247 |
|
248 // Depending on whether the exception originates in the linkResolver, the interpreter |
|
249 // or the compiler, the message can be different - which is unfortunate and could be |
|
250 // fixed. So we allow the listed reason or else a null message. |
|
251 static void shouldThrow(Class<?> expectedError, String reason, Test t) { |
|
252 try { |
|
253 t.run(); |
|
254 } catch (Throwable e) { |
|
255 // Don't accept subclasses as they can hide unexpected failure modes |
|
256 if (expectedError == e.getClass()) { |
|
257 String msg = e.getMessage(); |
|
258 if ((msg != null && msg.contains(reason)) || msg == null) { |
|
259 // passed |
|
260 System.out.println("Threw expected: " + e); |
|
261 return; |
|
262 } |
|
263 else { |
|
264 throw new AssertionError("Wrong exception reason: expected '" + reason |
|
265 + "', got '" + msg + "'", e); |
|
266 } |
|
267 } else { |
|
268 String msg = String.format("Wrong exception thrown: expected=%s; thrown=%s", |
|
269 expectedError.getName(), e.getClass().getName()); |
|
270 throw new AssertionError(msg, e); |
|
271 } |
|
272 } |
|
273 throw new AssertionError("No exception thrown: expected " + expectedError.getName()); |
|
274 } |
|
275 |
|
276 static void shouldNotThrow(Test t) { |
|
277 try { |
|
278 t.run(); |
|
279 // passed |
|
280 } catch (Throwable e) { |
|
281 throw new AssertionError("Exception was thrown: ", e); |
|
282 } |
|
283 } |
|
284 |
|
285 // Note: these unsafe casts are only possible for interface types |
|
286 |
|
287 static I2 unsafeCastI2(Object obj) { |
|
288 try { |
|
289 MethodHandle mh = MethodHandles.identity(Object.class); |
|
290 mh = MethodHandles.explicitCastArguments(mh, mh.type().changeReturnType(I2.class)); |
|
291 return (I2)mh.invokeExact((Object) obj); |
|
292 } catch (Throwable e) { |
|
293 throw new Error(e); |
|
294 } |
|
295 } |
|
296 |
|
297 static I3 unsafeCastI3(Object obj) { |
|
298 try { |
|
299 MethodHandle mh = MethodHandles.identity(Object.class); |
|
300 mh = MethodHandles.explicitCastArguments(mh, mh.type().changeReturnType(I3.class)); |
|
301 return (I3)mh.invokeExact((Object) obj); |
|
302 } catch (Throwable e) { |
|
303 throw new Error(e); |
|
304 } |
|
305 } |
|
306 |
|
307 static I4 unsafeCastI4(Object obj) { |
|
308 try { |
|
309 MethodHandle mh = MethodHandles.identity(Object.class); |
|
310 mh = MethodHandles.explicitCastArguments(mh, mh.type().changeReturnType(I4.class)); |
|
311 return (I4)mh.invokeExact((Object) obj); |
|
312 } catch (Throwable e) { |
|
313 throw new Error(e); |
|
314 } |
|
315 } |
|
316 } |