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