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