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 */
35import com.sun.jdi.*;
36import com.sun.jdi.event.*;
37import java.util.Collections;
38import java.util.Iterator;
39import java.util.List;
40
41public 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 {
47        static int staticMethodA() {
48            System.out.println("-InterfaceA: static interface method A-");
49            return RESULT_A;
50        }
51        static int staticMethodB() {
52            System.out.println("-InterfaceA: static interface method B-");
53            return RESULT_A;
54        }
55        default int defaultMethodA() {
56            System.out.println("-InterfaceA: default interface method A-");
57            return RESULT_A;
58        }
59        default int defaultMethodB() {
60            System.out.println("-InterfaceA: default interface method B-");
61            return RESULT_A;
62        }
63        default int defaultMethodC() {
64            System.out.println("-InterfaceA: default interface method C-");
65            return RESULT_A;
66        }
67        private int privateMethodA() {
68            System.out.println("-InterfaceA: private interface method A-");
69            return RESULT_A;
70        }
71        int implementedMethod();
72    }
73
74    static interface InterfaceB extends InterfaceA {
75        @Override
76        default int defaultMethodC() {
77            System.out.println("-InterfaceB: overridden default interface method C-");
78            return RESULT_B;
79        }
80        default int defaultMethodD() {
81            System.out.println("-InterfaceB: default interface method D-");
82            return RESULT_B;
83        }
84        static int staticMethodB() {
85            System.out.println("-InterfaceB: overridden static interface method B-");
86            return RESULT_B;
87        }
88        static int staticMethodC() {
89            System.out.println("-InterfaceB: static interface method C-");
90            return RESULT_B;
91        }
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 {
99        public int classMethod() {
100            System.out.println("-TargetClass: class only method-");
101            return RESULT_TARGET;
102        }
103
104        @Override
105        public int implementedMethod() {
106            System.out.println("-TargetClass: implemented non-default interface method-");
107            return RESULT_TARGET;
108        }
109
110        @Override
111        public int defaultMethodB() {
112            System.out.println("-TargetClass: overridden default interface method B");
113
114            return RESULT_TARGET;
115        }
116
117        public static void main(String[] args) {
118            TargetClass tc = new TargetClass();
119            tc.doTests(tc);
120        }
121
122        private void doTests(TargetClass ref) {
123            // break
124        }
125    }
126
127    public InterfaceMethodsTest(String[] args) {
128        super(args);
129    }
130
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
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
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
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
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
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.
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.
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
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
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
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
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
447    private Value invokeNonVirtual(ObjectReference ref, Method method) throws Exception {
448        return ref.invokeMethod(mainThread, method, Collections.emptyList(), ObjectReference.INVOKE_NONVIRTUAL);
449    }
450
451    private Value invokeVirtual(ObjectReference ref, Method method) throws Exception {
452        return ref.invokeMethod(mainThread, method, Collections.emptyList(), 0);
453    }
454
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
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
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
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
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
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