1 /*
2  * Copyright (c) 2014, 2016, 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  * @bug 8165827
29  * @summary  JDI: Add support for static, private and default methods in interfaces
30  *
31  * @run build TestScaffold VMConnection TargetListener TargetAdapter
32  * @run build InterfaceMethodsTest
33  * @run driver InterfaceMethodsTest
34  */
35 import com.sun.jdi.*;
36 import com.sun.jdi.event.*;
37 import java.util.Collections;
38 import java.util.Iterator;
39 import java.util.List;
40 
41 public class InterfaceMethodsTest extends TestScaffold {
42     private static final int RESULT_A = 1;
43     private static final int RESULT_B = 2;
44     private static final int RESULT_TARGET = 3;
45 
46     static interface InterfaceA {
staticMethodA()47         static int staticMethodA() {
48             System.out.println("-InterfaceA: static interface method A-");
49             return RESULT_A;
50         }
staticMethodB()51         static int staticMethodB() {
52             System.out.println("-InterfaceA: static interface method B-");
53             return RESULT_A;
54         }
defaultMethodA()55         default int defaultMethodA() {
56             System.out.println("-InterfaceA: default interface method A-");
57             return RESULT_A;
58         }
defaultMethodB()59         default int defaultMethodB() {
60             System.out.println("-InterfaceA: default interface method B-");
61             return RESULT_A;
62         }
defaultMethodC()63         default int defaultMethodC() {
64             System.out.println("-InterfaceA: default interface method C-");
65             return RESULT_A;
66         }
privateMethodA()67         private int privateMethodA() {
68             System.out.println("-InterfaceA: private interface method A-");
69             return RESULT_A;
70         }
implementedMethod()71         int implementedMethod();
72     }
73 
74     static interface InterfaceB extends InterfaceA {
75         @Override
defaultMethodC()76         default int defaultMethodC() {
77             System.out.println("-InterfaceB: overridden default interface method C-");
78             return RESULT_B;
79         }
defaultMethodD()80         default int defaultMethodD() {
81             System.out.println("-InterfaceB: default interface method D-");
82             return RESULT_B;
83         }
staticMethodB()84         static int staticMethodB() {
85             System.out.println("-InterfaceB: overridden static interface method B-");
86             return RESULT_B;
87         }
staticMethodC()88         static int staticMethodC() {
89             System.out.println("-InterfaceB: static interface method C-");
90             return RESULT_B;
91         }
privateMethodB()92         private int privateMethodB() {
93             System.out.println("-InterfaceB: private interface method B-");
94             return RESULT_B;
95         }
96     }
97 
98     final static class TargetClass implements InterfaceB {
classMethod()99         public int classMethod() {
100             System.out.println("-TargetClass: class only method-");
101             return RESULT_TARGET;
102         }
103 
104         @Override
implementedMethod()105         public int implementedMethod() {
106             System.out.println("-TargetClass: implemented non-default interface method-");
107             return RESULT_TARGET;
108         }
109 
110         @Override
defaultMethodB()111         public int defaultMethodB() {
112             System.out.println("-TargetClass: overridden default interface method B");
113 
114             return RESULT_TARGET;
115         }
116 
main(String[] args)117         public static void main(String[] args) {
118             TargetClass tc = new TargetClass();
119             tc.doTests(tc);
120         }
121 
doTests(TargetClass ref)122         private void doTests(TargetClass ref) {
123             // break
124         }
125     }
126 
InterfaceMethodsTest(String[] args)127     public InterfaceMethodsTest(String[] args) {
128         super(args);
129     }
130 
main(String[] args)131     public static void main(String[] args) throws Exception {
132         new InterfaceMethodsTest(args).startTests();
133     }
134 
135     private static final String TEST_CLASS_NAME = InterfaceMethodsTest.class.getName().replace('.', '/');
136     private static final String TARGET_CLASS_NAME = TargetClass.class.getName().replace('.', '/');
137     private static final String INTERFACEA_NAME = InterfaceA.class.getName().replace('.', '/');
138     private static final String INTERFACEB_NAME = InterfaceB.class.getName().replace('.', '/');
139 
runTests()140     protected void runTests() throws Exception {
141         /*
142          * Get to the top of main()
143          * to determine targetClass and mainThread
144          */
145         BreakpointEvent bpe = startToMain(TARGET_CLASS_NAME);
146 
147         bpe = resumeTo(TARGET_CLASS_NAME, "doTests", "(L" + TARGET_CLASS_NAME +";)V");
148 
149         mainThread = bpe.thread();
150 
151         StackFrame frame = mainThread.frame(0);
152         ObjectReference thisObject = frame.thisObject();
153         ObjectReference ref = (ObjectReference)frame.getArgumentValues().get(0);
154 
155         ReferenceType targetClass = bpe.location().declaringType();
156         testImplementationClass(targetClass, thisObject);
157 
158         testInterfaceA(ref);
159 
160         testInterfaceB(ref);
161 
162         /*
163          * resume the target listening for events
164          */
165         listenUntilVMDisconnect();
166 
167         /*
168          * deal with results of test
169          * if anything has called failure("foo") testFailed will be true
170          */
171         if (!testFailed) {
172             println("InterfaceMethodsTest: passed");
173         } else {
174             throw new Exception("InterfaceMethodsTest: failed");
175         }
176     }
177 
testInterfaceA(ObjectReference ref)178     private void testInterfaceA(ObjectReference ref) {
179 
180         ReferenceType ifaceClass = (ReferenceType)vm().classesByName(INTERFACEA_NAME).get(0);
181 
182         /* Private method calls */
183 
184         Method m = testLookup(ifaceClass, "privateMethodA", "()I", true, null); // should succeed
185 
186         testInvokePos(m, ref, vm().mirrorOf(RESULT_A), false);
187         testInvokePos(m, ref, vm().mirrorOf(RESULT_A), true);
188 
189         // Test non-virtual calls on InterfaceA
190 
191         /* Default method calls */
192 
193         // invoke the InterfaceA's "defaultMethodA"
194         testInvokePos(ifaceClass, ref, "defaultMethodA", "()I", vm().mirrorOf(RESULT_A));
195 
196         // invoke the InterfaceA's "defaultMethodB"
197         testInvokePos(ifaceClass, ref, "defaultMethodB", "()I", vm().mirrorOf(RESULT_A));
198 
199         // invoke the InterfaceA's "defaultMethodC"
200         testInvokePos(ifaceClass, ref, "defaultMethodC", "()I", vm().mirrorOf(RESULT_A));
201 
202         // "defaultMethodD" from InterfaceB is not accessible from here
203         testInvokeNeg(ifaceClass, ref, "defaultMethodD", "()I", vm().mirrorOf(RESULT_B),
204                       "Attempted to invoke non-existing method");
205 
206         // non-virtual invoke of the abstract method "implementedMethod" fails
207         testInvokeNeg(ifaceClass, ref, "implementedMethod", "()I", vm().mirrorOf(TARGET_CLASS_NAME),
208                       "Invocation of abstract methods is not supported");
209 
210         /* Static method calls */
211 
212         // invoke static interface method A
213         testInvokePos(ifaceClass, null, "staticMethodA", "()I", vm().mirrorOf(RESULT_A));
214 
215         // invoking static method A on the instance fails because static method A is
216         // not inherited by TargetClass.
217         testInvokeNeg(ifaceClass, ref, "staticMethodA", "()I", vm().mirrorOf(RESULT_A),
218                       "Invalid MethodID");
219 
220         // invoke static interface method B
221         testInvokePos(ifaceClass, null, "staticMethodB", "()I", vm().mirrorOf(RESULT_A));
222 
223         // invoking static method B on the instance fails because static method B is
224         // not inherited by TargetClass.
225         testInvokeNeg(ifaceClass, ref, "staticMethodB", "()I", vm().mirrorOf(RESULT_A),
226                       "Invalid MethodID");
227 
228         // try to invoke a virtual method
229         testInvokePos(ifaceClass, ref, "implementedMethod", "()I", vm().mirrorOf(RESULT_TARGET), true);
230     }
231 
testInterfaceB(ObjectReference ref)232     private void testInterfaceB(ObjectReference ref) {
233         // Test non-virtual calls on InterfaceB
234         ReferenceType ifaceClass = (ReferenceType)vm().classesByName(INTERFACEB_NAME).get(0);
235 
236         /* private method calls */
237 
238         /* These should fail but won't because of JDK-8167416
239         testLookup(ifaceClass, "privateMethodA", "()I", true, NoSuchMethodError.class); // should fail
240         testLookup(ifaceClass, "privateMethodA", "()I", false, NoSuchMethodError.class); // should fail
241         */
242         Method m = testLookup(ifaceClass, "privateMethodB", "()I", true, null); // should succeed
243         testInvokePos(m, ref, vm().mirrorOf(RESULT_B), false);
244         testInvokePos(m, ref, vm().mirrorOf(RESULT_B), true);
245 
246         /* Default method calls */
247 
248         // invoke the inherited "defaultMethodA"
249         testInvokePos(ifaceClass, ref, "defaultMethodA", "()I", vm().mirrorOf(RESULT_A));
250 
251         // invoke the inherited "defaultMethodB"
252         testInvokePos(ifaceClass, ref, "defaultMethodB", "()I", vm().mirrorOf(RESULT_A));
253 
254         // invoke the inherited and overridden "defaultMethodC"
255         testInvokePos(ifaceClass, ref, "defaultMethodC", "()I", vm().mirrorOf(RESULT_B));
256 
257         // invoke InterfaceB only "defaultMethodD"
258         testInvokePos(ifaceClass, ref, "defaultMethodD", "()I", vm().mirrorOf(RESULT_B));
259 
260         // "implementedMethod" is not present in InterfaceB
261         testInvokeNeg(ifaceClass, ref, "implementedMethod", "()I", vm().mirrorOf(RESULT_TARGET),
262                 "Invocation of non-default methods is not supported");
263 
264 
265         /* Static method calls*/
266 
267         // "staticMethodA" must not be inherited by InterfaceB
268         testInvokeNeg(ifaceClass, null, "staticMethodA", "()I", vm().mirrorOf(RESULT_A),
269                 "Static interface methods are not inheritable");
270 
271         // "staticMethodA" is not inherited by InterfaceB even from an actual instance
272         testInvokeNeg(ifaceClass, ref, "staticMethodA", "()I", vm().mirrorOf(RESULT_A),
273                 "Static interface methods are not inheritable");
274 
275         // "staticMethodB" is re-defined in InterfaceB
276         testInvokePos(ifaceClass, null, "staticMethodB", "()I", vm().mirrorOf(RESULT_B));
277 
278         // the instance fails to invoke the re-defined form of "staticMethodB" from
279         // InterfaceB because staticMethodB is not inherited by TargetClass
280         testInvokeNeg(ifaceClass, ref, "staticMethodB", "()I", vm().mirrorOf(RESULT_B),
281                 "Invalid MethodID");
282 
283         // "staticMethodC" is present only in InterfaceB
284         testInvokePos(ifaceClass, null, "staticMethodC", "()I", vm().mirrorOf(RESULT_B));
285 
286         // "staticMethodC" is not reachable from the instance because staticMethodC
287         // is not inherited by TargetClass.
288         testInvokeNeg(ifaceClass, ref, "staticMethodC", "()I", vm().mirrorOf(RESULT_B),
289                 "Invalid MethodID");
290     }
291 
testImplementationClass(ReferenceType targetClass, ObjectReference thisObject)292     private void testImplementationClass(ReferenceType targetClass, ObjectReference thisObject) {
293         // Test invocations on the implementation object
294 
295         // Note: private interface calls have already been tested
296 
297         /* Default method calls */
298 
299         // "defaultMethodA" is accessible and not overridden
300         testInvokePos(targetClass, thisObject, "defaultMethodA", "()I", vm().mirrorOf(RESULT_A));
301 
302         // "defaultMethodB" is accessible and overridden in TargetClass
303         testInvokePos(targetClass, thisObject, "defaultMethodB", "()I", vm().mirrorOf(RESULT_TARGET));
304 
305         // "defaultMethodC" is accessible and overridden in InterfaceB
306         testInvokePos(targetClass, thisObject, "defaultMethodC", "()I", vm().mirrorOf(RESULT_B));
307 
308         // "defaultMethodD" is accessible
309         testInvokePos(targetClass, thisObject, "defaultMethodD", "()I", vm().mirrorOf(RESULT_B));
310 
311 
312         /* Non-default instance method calls */
313 
314         // "classMethod" declared in TargetClass is accessible
315         testInvokePos(targetClass, thisObject, "classMethod", "()I", vm().mirrorOf(RESULT_TARGET));
316 
317         // the abstract "implementedMethod" has been implemented in TargetClass
318         testInvokePos(targetClass, thisObject, "implementedMethod", "()I", vm().mirrorOf(RESULT_TARGET));
319 
320 
321         /* Static method calls */
322 
323         // All the static methods declared by the interfaces are not reachable from the instance of the implementor class
324         testInvokeNeg(targetClass, thisObject, "staticMethodA", "()I", vm().mirrorOf(RESULT_A),
325                 "Static interface methods are not inheritable");
326 
327         testInvokeNeg(targetClass, thisObject, "staticMethodB", "()I", vm().mirrorOf(RESULT_B),
328                 "Static interface methods are not inheritable");
329 
330         testInvokeNeg(targetClass, thisObject, "staticMethodC", "()I", vm().mirrorOf(RESULT_B),
331                 "Static interface methods are not inheritable");
332 
333         // All the static methods declared by the interfaces are not reachable through the implementor class
334         testInvokeNeg(targetClass, null, "staticMethodA", "()I", vm().mirrorOf(RESULT_A),
335                 "Static interface methods are not inheritable");
336 
337         testInvokeNeg(targetClass, null, "staticMethodB", "()I", vm().mirrorOf(RESULT_B),
338                 "Static interface methods are not inheritable");
339 
340         testInvokeNeg(targetClass, null, "staticMethodC", "()I", vm().mirrorOf(RESULT_B),
341                 "Static interface methods are not inheritable");
342     }
343 
344     // Non-virtual invocation
testInvokePos(ReferenceType targetClass, ObjectReference ref, String methodName, String methodSig, Value value)345     private void testInvokePos(ReferenceType targetClass, ObjectReference ref, String methodName,
346                                String methodSig, Value value) {
347         testInvokePos(targetClass, ref, methodName, methodSig, value, false);
348     }
349 
350     // Lookup the named method in the targetClass and invoke on the given object (for instance methods)
351     // using virtual, or non-virtual, invocation mode as specified, for instance methods. Verify the
352     // expected return value.
353     // Should succeed.
testInvokePos(ReferenceType targetClass, ObjectReference ref, String methodName, String methodSig, Value value, boolean virtual)354     private void testInvokePos(ReferenceType targetClass, ObjectReference ref, String methodName,
355                                String methodSig, Value value, boolean virtual) {
356         logInvocation(ref, methodName, methodSig, targetClass);
357         try {
358             invoke(targetClass, ref, methodName, methodSig, value, virtual);
359             System.err.println("--- PASSED");
360         } catch (Exception e) {
361             System.err.println("--- FAILED");
362             failure("FAILED: Invocation failed with error message " + e.getLocalizedMessage());
363         }
364     }
365 
366     // Invoke the given Method on the given object (for instance methods)
367     // using virtual, or non-virtual, invocation mode as specified, for instance methods. Verify the
368     // expected return value.
369     // Should succeed.
testInvokePos(Method method, ObjectReference ref, Value value, boolean virtual)370     private void testInvokePos(Method method, ObjectReference ref, Value value, boolean virtual) {
371         logInvocation(ref, method.name(), method.signature(), method.declaringType());
372         try {
373             invoke(method.declaringType(), ref, method, value, virtual);
374             System.err.println("--- PASSED");
375         } catch (Exception e) {
376             System.err.println("--- FAILED");
377             failure("FAILED: Invocation failed with error message " + e.getLocalizedMessage());
378         }
379     }
380 
381     // Non-virtual invocation - with lookup in targetClass
testInvokeNeg(ReferenceType targetClass, ObjectReference ref, String methodName, String methodSig, Value value, String msg)382     private void testInvokeNeg(ReferenceType targetClass, ObjectReference ref, String methodName,
383                                String methodSig, Value value, String msg) {
384         testInvokeNeg(targetClass, ref, methodName, methodSig, value, msg, false);
385     }
386 
387     // Lookup the named method in the targetClass and invoke on the given object (for instance methods)
388     // using virtual, or non-virtual, invocation mode as specified, for instance methods. Verify the
389     // expected return value.
390     // Should fail - with msg decribing why failure was expected
testInvokeNeg(ReferenceType targetClass, ObjectReference ref, String methodName, String methodSig, Value value, String msg, boolean virtual)391     private void testInvokeNeg(ReferenceType targetClass, ObjectReference ref, String methodName,
392                                String methodSig, Value value, String msg, boolean virtual) {
393         logInvocation(ref, methodName, methodSig, targetClass);
394         try {
395             invoke(targetClass, ref, methodName, methodSig, value, virtual);
396             System.err.println("--- FAILED");
397             failure("FAILED: " + msg);
398         } catch (Exception e) {
399             System.err.println("--- PASSED");
400 
401         }
402     }
403 
invoke(ReferenceType targetClass, ObjectReference ref, String methodName, String methodSig, Value value, boolean virtual)404     private void invoke(ReferenceType targetClass, ObjectReference ref, String methodName,
405                         String methodSig, Value value, boolean virtual) throws Exception {
406 
407         Method method = getMethod(targetClass, methodName, methodSig);
408         if (method == null) {
409             throw new Exception("Can't find method: " + methodName  + " for class = " + targetClass);
410         }
411         invoke(targetClass, ref, method, value, virtual);
412     }
413 
invoke(ReferenceType targetClass, ObjectReference ref, Method method, Value value, boolean virtual)414     private void invoke(ReferenceType targetClass, ObjectReference ref, Method method,
415                         Value value, boolean virtual) throws Exception {
416 
417         println("Invoking " + (method.isAbstract() ? "abstract " : " ") + "method: " + method);
418         println(method.declaringType().toString());
419 
420         Value returnValue = null;
421         if (ref != null) {
422             if (virtual) {
423                 returnValue = invokeVirtual(ref, method);
424             } else {
425                 returnValue = invokeNonVirtual(ref, method);
426             }
427         } else {
428             returnValue = invokeStatic(targetClass, method);
429         }
430 
431         println("        return val = " + returnValue);
432         // It has to be the same value as what we passed in!
433         if (returnValue.equals(value)) {
434             println("         " + method.name() + " return value matches: "
435                     + value);
436         } else {
437             if (value != null) {
438                 throw new Exception(method.name() + " returned: " + returnValue +
439                                     " expected: " + value );
440             } else {
441                 println("         " + method.name() + " return value : " + returnValue);
442             }
443 
444         }
445     }
446 
invokeNonVirtual(ObjectReference ref, Method method)447     private Value invokeNonVirtual(ObjectReference ref, Method method) throws Exception {
448         return ref.invokeMethod(mainThread, method, Collections.emptyList(), ObjectReference.INVOKE_NONVIRTUAL);
449     }
450 
invokeVirtual(ObjectReference ref, Method method)451     private Value invokeVirtual(ObjectReference ref, Method method) throws Exception {
452         return ref.invokeMethod(mainThread, method, Collections.emptyList(), 0);
453     }
454 
invokeStatic(ReferenceType refType, Method method)455     private Value invokeStatic(ReferenceType refType, Method method) throws Exception {
456         if (refType instanceof ClassType) {
457             return ((ClassType)refType).invokeMethod(mainThread, method, Collections.emptyList(), ObjectReference.INVOKE_NONVIRTUAL);
458         } else {
459             return ((InterfaceType)refType).invokeMethod(mainThread, method, Collections.emptyList(), ObjectReference.INVOKE_NONVIRTUAL);
460         }
461     }
462 
getMethod(ReferenceType rt, String name, String signature)463     private Method getMethod(ReferenceType rt, String name, String signature) {
464         if (rt == null) return null;
465         Method m = findMethod(rt, name, signature);
466         if (m == null) {
467             if (rt instanceof ClassType) {
468                 for (Object ifc : ((ClassType)rt).interfaces()) {
469                     m = getMethod((ReferenceType)ifc, name, signature);
470                     if (m != null) {
471                         break;
472                     }
473                 }
474                 if (m == null) {
475                     m = getMethod(((ClassType)rt).superclass(), name, signature);
476                 } else {
477                     if (m.isStatic()) {
478                         // interface static methods are not inherited
479                         m = null;
480                     }
481                 }
482             } else if (rt instanceof InterfaceType) {
483                 for(Object ifc : ((InterfaceType)rt).superinterfaces()) {
484                     m = getMethod((ReferenceType)ifc, name, signature);
485                     if (m != null) {
486                         if (m.isStatic()) {
487                             // interface static methods are not inherited
488                             m = null;
489                         }
490                         break;
491                     }
492                 }
493             }
494         }
495 
496         return m;
497     }
498 
logInvocation(ObjectReference ref, String methodName, String methodSig, ReferenceType targetClass)499     private void logInvocation(ObjectReference ref, String methodName, String methodSig, ReferenceType targetClass) {
500         if (ref != null) {
501             System.err.println("Invoking: " + ref.referenceType().name() + "." +
502                     methodName + methodSig + " with target of type " +
503                     targetClass.name());
504         } else {
505             System.err.println("Invoking static : " + targetClass.name() + "." +
506                     methodName + methodSig);
507         }
508     }
509 
testLookup(ReferenceType targetClass, String methodName, String methodSig, boolean declaredOnly, Class<?> expectedException)510     private Method testLookup(ReferenceType targetClass, String methodName, String methodSig,
511                               boolean declaredOnly, Class<?> expectedException) {
512 
513         System.err.println("Looking up " + targetClass.name() + "." + methodName + methodSig);
514         try {
515             Method m = declaredOnly ?
516                 lookupDeclaredMethod(targetClass, methodName, methodSig) :
517                 lookupMethod(targetClass, methodName, methodSig);
518 
519             if (expectedException == null) {
520                 System.err.println("--- PASSED");
521                 return m;
522             }
523             else {
524                 System.err.println("--- FAILED");
525                 failure("FAILED: lookup succeeded but expected exception "
526                         + expectedException.getSimpleName());
527                 return null;
528             }
529         }
530         catch (Throwable t) {
531             if (t.getClass() != expectedException) {
532                 System.err.println("--- FAILED");
533                 failure("FAILED: got exception " + t + " but expected exception "
534                         + expectedException.getSimpleName());
535                 return null;
536             }
537             else {
538                 System.err.println("--- PASSED");
539                 return null;
540             }
541         }
542     }
543 
lookupMethod(ReferenceType targetClass, String methodName, String methodSig)544     private Method lookupMethod(ReferenceType targetClass, String methodName, String methodSig) {
545         List methods = targetClass.allMethods();
546         Iterator iter = methods.iterator();
547         while (iter.hasNext()) {
548             Method method = (Method)iter.next();
549             if (method.name().equals(methodName) &&
550                 method.signature().equals(methodSig)) {
551                 return method;
552             }
553         }
554         throw new NoSuchMethodError();
555     }
556 
lookupDeclaredMethod(ReferenceType targetClass, String methodName, String methodSig)557     private Method lookupDeclaredMethod(ReferenceType targetClass, String methodName, String methodSig) {
558         Method m = findMethod(targetClass, methodName, methodSig);
559         if (m == null)
560             throw new NoSuchMethodError();
561         return m;
562     }
563 }
564