blob: e127fa5e298b89a452318cca18a4aac2fe498fdb [file] [log] [blame]
jbachorik7844c3a2014-04-30 11:28:05 +02001/*
2 * Copyright (c) 2014, 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 8031195
27 * @summary JDI: Add support for static and default methods in interfaces
28 *
29 * @run build TestScaffold VMConnection TargetListener TargetAdapter
30 * @run build InterfaceMethodsTest
31 * @run main InterfaceMethodsTest
32 */
33import com.sun.jdi.*;
34import com.sun.jdi.event.*;
35import java.util.Collections;
36
37public class InterfaceMethodsTest extends TestScaffold {
38 private static final int RESULT_A = 1;
39 private static final int RESULT_B = 1;
40 private static final int RESULT_TARGET = 1;
41 static interface InterfaceA {
42 static int staticMethodA() {
43 System.out.println("-InterfaceA: static interface method A-");
44 return RESULT_A;
45 }
46 static int staticMethodB() {
47 System.out.println("-InterfaceA: static interface method B-");
48 return RESULT_A;
49 }
50 default int defaultMethodA() {
51 System.out.println("-InterfaceA: default interface method A-");
52 return RESULT_A;
53 }
54 default int defaultMethodB() {
55 System.out.println("-InterfaceA: default interface method B-");
56 return RESULT_A;
57 }
58 default int defaultMethodC() {
59 System.out.println("-InterfaceA: default interface method C-");
60 return RESULT_A;
61 }
62
63 int implementedMethod();
64 }
65
66 static interface InterfaceB extends InterfaceA {
67 @Override
68 default int defaultMethodC() {
69 System.out.println("-InterfaceB: overridden default interface method C-");
70 return RESULT_B;
71 }
72 default int defaultMethodD() {
73 System.out.println("-InterfaceB: default interface method D-");
74 return RESULT_B;
75 }
76
77 static int staticMethodB() {
78 System.out.println("-InterfaceB: overridden static interface method B-");
79 return RESULT_B;
80 }
81
82 static int staticMethodC() {
83 System.out.println("-InterfaceB: static interface method C-");
84 return RESULT_B;
85 }
86 }
87
88 final static class TargetClass implements InterfaceB {
89 public int classMethod() {
90 System.out.println("-TargetClass: class only method-");
91 return RESULT_TARGET;
92 }
93
94 @Override
95 public int implementedMethod() {
96 System.out.println("-TargetClass: implemented non-default interface method-");
97 return RESULT_TARGET;
98 }
99
100 @Override
101 public int defaultMethodB() {
102 System.out.println("-TargetClass: overridden default interface method D");
103
104 return RESULT_TARGET;
105 }
106
107 public static void main(String[] args) {
108 TargetClass tc = new TargetClass();
109 tc.doTests(tc);
110 }
111
112 private void doTests(TargetClass ref) {
113 // break
114 }
115 }
116
117 public InterfaceMethodsTest(String[] args) {
118 super(args);
119 }
120
121 public static void main(String[] args) throws Exception {
122 new InterfaceMethodsTest(args).startTests();
123 }
124
125 private static final String TEST_CLASS_NAME = InterfaceMethodsTest.class.getName().replace('.', '/');
126 private static final String TARGET_CLASS_NAME = TargetClass.class.getName().replace('.', '/');
127 private static final String INTERFACEA_NAME = InterfaceA.class.getName().replace('.', '/');
128 private static final String INTERFACEB_NAME = InterfaceB.class.getName().replace('.', '/');
129
130 protected void runTests() throws Exception {
131 /*
132 * Get to the top of main()
133 * to determine targetClass and mainThread
134 */
135 BreakpointEvent bpe = startToMain(TARGET_CLASS_NAME);
136
137 bpe = resumeTo(TARGET_CLASS_NAME, "doTests", "(L" + TARGET_CLASS_NAME +";)V");
138
139 mainThread = bpe.thread();
140
141 StackFrame frame = mainThread.frame(0);
142 ObjectReference thisObject = frame.thisObject();
143 ObjectReference ref = (ObjectReference)frame.getArgumentValues().get(0);
144
145 ReferenceType targetClass = bpe.location().declaringType();
146 testImplementationClass(targetClass, thisObject);
147
148 testInterfaceA(ref);
149
150 testInterfaceB(ref);
151
152 /*
153 * resume the target listening for events
154 */
155 listenUntilVMDisconnect();
156
157 /*
158 * deal with results of test
159 * if anything has called failure("foo") testFailed will be true
160 */
161 if (!testFailed) {
162 println("InterfaceMethodsTest: passed");
163 } else {
164 throw new Exception("InterfaceMethodsTest: failed");
165 }
166 }
167
168 private void testInterfaceA(ObjectReference ref) {
169 // Test non-virtual calls on InterfaceA
170
171 ReferenceType ifaceClass = (ReferenceType)vm().classesByName(INTERFACEA_NAME).get(0);
172 /* Default method calls */
173
174 // invoke the InterfaceA's "defaultMethodA"
175 testInvokePos(ifaceClass, ref, "defaultMethodA", "()I", vm().mirrorOf(RESULT_A));
176
177 // invoke the InterfaceA's "defaultMethodB"
178 testInvokePos(ifaceClass, ref, "defaultMethodB", "()I", vm().mirrorOf(RESULT_A));
179
180 // invoke the InterfaceA's "defaultMethodC"
181 testInvokePos(ifaceClass, ref, "defaultMethodC", "()I", vm().mirrorOf(RESULT_A));
182
183 // "defaultMethodD" from InterfaceB is not accessible from here
184 testInvokeNeg(ifaceClass, ref, "defaultMethodD", "()I", vm().mirrorOf(RESULT_B),
185 "Attempted to invoke non-existing method");
186
187 // trying to invoke the asbtract method "implementedMethod"
188 testInvokeNeg(ifaceClass, ref, "implementedMethod", "()I", vm().mirrorOf(TARGET_CLASS_NAME),
189 "Invocation of non-default methods is not supported");
190
191
192 /* Static method calls */
193
194 // invoke interface static method A
195 testInvokePos(ifaceClass, null, "staticMethodA", "()I", vm().mirrorOf(RESULT_A));
196
197 // try to invoke static method A on the instance
198 testInvokePos(ifaceClass, ref, "staticMethodA", "()I", vm().mirrorOf(RESULT_A));
199
200 // invoke interface static method B
201 testInvokePos(ifaceClass, null, "staticMethodB", "()I", vm().mirrorOf(RESULT_A));
202
203 // try to invoke static method B on the instance
204 testInvokePos(ifaceClass, ref, "staticMethodB", "()I", vm().mirrorOf(RESULT_A));
205 }
206
207 private void testInterfaceB(ObjectReference ref) {
208 // Test non-virtual calls on InterfaceB
209 ReferenceType ifaceClass = (ReferenceType)vm().classesByName(INTERFACEB_NAME).get(0);
210
211 /* Default method calls */
212
213 // invoke the inherited "defaultMethodA"
214 testInvokePos(ifaceClass, ref, "defaultMethodA", "()I", vm().mirrorOf(RESULT_A));
215
216 // invoke the inherited "defaultMethodB"
217 testInvokePos(ifaceClass, ref, "defaultMethodB", "()I", vm().mirrorOf(RESULT_A));
218
219 // invoke the inherited and overridden "defaultMethodC"
220 testInvokePos(ifaceClass, ref, "defaultMethodC", "()I", vm().mirrorOf(RESULT_B));
221
222 // invoke InterfaceB only "defaultMethodD"
223 testInvokePos(ifaceClass, ref, "defaultMethodD", "()I", vm().mirrorOf(RESULT_B));
224
225 // "implementedMethod" is not present in InterfaceB
226 testInvokeNeg(ifaceClass, ref, "implementedMethod", "()I", vm().mirrorOf(RESULT_TARGET),
227 "Invocation of non-default methods is not supported");
228
229
230 /* Static method calls*/
231
232 // "staticMethodA" must not be inherited by InterfaceB
233 testInvokeNeg(ifaceClass, null, "staticMethodA", "()I", vm().mirrorOf(RESULT_A),
234 "Static interface methods are not inheritable");
235
236 // however it is possible to call "staticMethodA" on the actual instance
237 testInvokeNeg(ifaceClass, ref, "staticMethodA", "()I", vm().mirrorOf(RESULT_A),
238 "Static interface methods are not inheritable");
239
240 // "staticMethodB" is overridden in InterfaceB
241 testInvokePos(ifaceClass, null, "staticMethodB", "()I", vm().mirrorOf(RESULT_B));
242
243 // the instance invokes the overriden form of "staticMethodB" from InterfaceB
244 testInvokePos(ifaceClass, ref, "staticMethodB", "()I", vm().mirrorOf(RESULT_B));
245
246 // "staticMethodC" is present only in InterfaceB
247 testInvokePos(ifaceClass, null, "staticMethodC", "()I", vm().mirrorOf(RESULT_B));
248
249 // "staticMethodC" should be reachable from the instance too
250 testInvokePos(ifaceClass, ref, "staticMethodC", "()I", vm().mirrorOf(RESULT_B));
251 }
252
253 private void testImplementationClass(ReferenceType targetClass, ObjectReference thisObject) {
254 // Test invocations on the implementation object
255
256 /* Default method calls */
257
258 // "defaultMethodA" is accessible and not overridden
259 testInvokePos(targetClass, thisObject, "defaultMethodA", "()I", vm().mirrorOf(RESULT_TARGET));
260
261 // "defaultMethodB" is accessible and overridden in TargetClass
262 testInvokePos(targetClass, thisObject, "defaultMethodB", "()I", vm().mirrorOf(RESULT_TARGET));
263
264 // "defaultMethodC" is accessible and overridden in InterfaceB
265 testInvokePos(targetClass, thisObject, "defaultMethodC", "()I", vm().mirrorOf(RESULT_TARGET));
266
267 // "defaultMethodD" is accessible
268 testInvokePos(targetClass, thisObject, "defaultMethodD", "()I", vm().mirrorOf(RESULT_TARGET));
269
270
271 /* Non-default instance method calls */
272
273 // "classMethod" declared in TargetClass is accessible
274 testInvokePos(targetClass, thisObject, "classMethod", "()I", vm().mirrorOf(RESULT_TARGET));
275
276 // the abstract "implementedMethod" has been implemented in TargetClass
277 testInvokePos(targetClass, thisObject, "implementedMethod", "()I", vm().mirrorOf(RESULT_TARGET));
278
279
280 /* Static method calls */
281
282 // All the static methods declared by the interfaces are not reachable from the instance of the implementor class
283 testInvokeNeg(targetClass, thisObject, "staticMethodA", "()I", vm().mirrorOf(RESULT_A),
284 "Static interface methods are not inheritable");
285
286 testInvokeNeg(targetClass, thisObject, "staticMethodB", "()I", vm().mirrorOf(RESULT_B),
287 "Static interface methods are not inheritable");
288
289 testInvokeNeg(targetClass, thisObject, "staticMethodC", "()I", vm().mirrorOf(RESULT_B),
290 "Static interface methods are not inheritable");
291
292 // All the static methods declared by the interfaces are not reachable through the implementor class
293 testInvokeNeg(targetClass, null, "staticMethodA", "()I", vm().mirrorOf(RESULT_A),
294 "Static interface methods are not inheritable");
295
296 testInvokeNeg(targetClass, null, "staticMethodB", "()I", vm().mirrorOf(RESULT_B),
297 "Static interface methods are not inheritable");
298
299 testInvokeNeg(targetClass, null, "staticMethodC", "()I", vm().mirrorOf(RESULT_B),
300 "Static interface methods are not inheritable");
301 }
302
303 private void testInvokePos(ReferenceType targetClass, ObjectReference ref, String methodName,
304 String methodSig, Value value) {
305 logInvocation(ref, methodName, methodSig, targetClass);
306 try {
307 invoke(targetClass, ref, methodName, methodSig, value);
308 System.err.println("--- PASSED");
309 } catch (Exception e) {
310 System.err.println("--- FAILED");
311 failure("FAILED: Invocation failed with error message " + e.getLocalizedMessage());
312 }
313 }
314
315 private void testInvokeNeg(ReferenceType targetClass, ObjectReference ref, String methodName,
316 String methodSig, Value value, String msg) {
317 logInvocation(ref, methodName, methodSig, targetClass);
318 try {
319 invoke(targetClass, ref, methodName, methodSig, value);
320 System.err.println("--- FAILED");
321 failure("FAILED: " + msg);
322 } catch (Exception e) {
323 System.err.println("--- PASSED");
324
325 }
326 }
327
328 private void invoke(ReferenceType targetClass, ObjectReference ref, String methodName,
329 String methodSig, Value value)
330 throws Exception {
331 Method method = getMethod(targetClass, methodName, methodSig);
332 if (method == null) {
333 throw new Exception("Can't find method: " + methodName + " for class = " + targetClass);
334 }
335
336 println("Invoking " + (method.isAbstract() ? "abstract " : " ") + "method: " + method);
337
338 Value returnValue = null;
339 if (ref != null) {
340 returnValue = invokeInstance(ref, method);
341 } else {
342 returnValue = invokeStatic(targetClass, method);
343 }
344
345 println(" return val = " + returnValue);
346 // It has to be the same value as what we passed in!
347 if (returnValue.equals(value)) {
348 println(" " + method.name() + " return value matches: "
349 + value);
350 } else {
351 if (value != null) {
352 throw new Exception(method.name() + " returned: " + returnValue +
353 " expected: " + value );
354 } else {
355 println(" " + method.name() + " return value : " + returnValue);
356 }
357
358 }
359 }
360
361 private Value invokeInstance(ObjectReference ref, Method method) throws Exception {
362 return ref.invokeMethod(mainThread, method, Collections.emptyList(), ObjectReference.INVOKE_NONVIRTUAL);
363 }
364
365 private Value invokeStatic(ReferenceType refType, Method method) throws Exception {
366 if (refType instanceof ClassType) {
367 return ((ClassType)refType).invokeMethod(mainThread, method, Collections.emptyList(), ObjectReference.INVOKE_NONVIRTUAL);
368 } else {
369 return ((InterfaceType)refType).invokeMethod(mainThread, method, Collections.emptyList(), ObjectReference.INVOKE_NONVIRTUAL);
370 }
371 }
372
373 private Method getMethod(ReferenceType rt, String name, String signature) {
374 if (rt == null) return null;
375 Method m = findMethod(rt, name, signature);
376 if (m == null) {
377 if (rt instanceof ClassType) {
378 for (Object ifc : ((ClassType)rt).interfaces()) {
379 m = getMethod((ReferenceType)ifc, name, signature);
380 if (m != null) {
381 break;
382 }
383 }
384 if (m == null) {
385 m = getMethod(((ClassType)rt).superclass(), name, signature);
386 } else {
387 if (m.isStatic()) {
388 // interface static methods are not inherited
389 m = null;
390 }
391 }
392 } else if (rt instanceof InterfaceType) {
393 for(Object ifc : ((InterfaceType)rt).superinterfaces()) {
394 m = getMethod((ReferenceType)ifc, name, signature);
395 if (m != null) {
396 if (m.isStatic()) {
397 // interface static methods are not inherited
398 m = null;
399 }
400 break;
401 }
402 }
403 }
404 }
405
406 return m;
407 }
408
409 private void logInvocation(ObjectReference ref, String methodName, String methodSig, ReferenceType targetClass) {
410 if (ref != null) {
411 System.err.println("Invoking: " + ref.referenceType().name() + "." +
412 methodName + methodSig + " with target of type " +
413 targetClass.name());
414 } else {
415 System.err.println("Invoking static : " + targetClass.name() + "." +
416 methodName + methodSig);
417 }
418 }
419}
420
421
422