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  *  @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  */
  35 import com.sun.jdi.*;
  36 import com.sun.jdi.event.*;
  37 import java.util.Collections;
  38 
  39 public 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. This should fail because ref
 201         // is of type InterfacemethodsTest$TargetClass which is not a subtype of the
 202         // class containing staticMethodA.
 203         testInvokeNeg(ifaceClass, ref, "staticMethodA", "()I", vm().mirrorOf(RESULT_A),
 204                 "Invalid MethodID");
 205 
 206         // invoke interface static method B
 207         testInvokePos(ifaceClass, null, "staticMethodB", "()I", vm().mirrorOf(RESULT_A));
 208 
 209         // try to invoke static method B on the instance. This should fail because ref
 210         // is of type InterfacemethodsTest$TargetClass which is not a subtype of the
 211         // class containing staticMethodB.
 212         testInvokeNeg(ifaceClass, ref, "staticMethodB", "()I", vm().mirrorOf(RESULT_A),
 213                 "Invalid MethodID");
 214 
 215         // try to invoke a virtual method
 216         testInvokePos(ifaceClass, ref, "implementedMethod", "()I", vm().mirrorOf(RESULT_A), true);
 217     }
 218 
 219     private void testInterfaceB(ObjectReference ref) {
 220         // Test non-virtual calls on InterfaceB
 221         ReferenceType ifaceClass = (ReferenceType)vm().classesByName(INTERFACEB_NAME).get(0);
 222 
 223         /* Default method calls */
 224 
 225         // invoke the inherited "defaultMethodA"
 226         testInvokePos(ifaceClass, ref, "defaultMethodA", "()I", vm().mirrorOf(RESULT_A));
 227 
 228         // invoke the inherited "defaultMethodB"
 229         testInvokePos(ifaceClass, ref, "defaultMethodB", "()I", vm().mirrorOf(RESULT_A));
 230 
 231         // invoke the inherited and overridden "defaultMethodC"
 232         testInvokePos(ifaceClass, ref, "defaultMethodC", "()I", vm().mirrorOf(RESULT_B));
 233 
 234         // invoke InterfaceB only "defaultMethodD"
 235         testInvokePos(ifaceClass, ref, "defaultMethodD", "()I", vm().mirrorOf(RESULT_B));
 236 
 237         // "implementedMethod" is not present in InterfaceB
 238         testInvokeNeg(ifaceClass, ref, "implementedMethod", "()I", vm().mirrorOf(RESULT_TARGET),
 239                 "Invocation of non-default methods is not supported");
 240 
 241 
 242         /* Static method calls*/
 243 
 244         // "staticMethodA" must not be inherited by InterfaceB
 245         testInvokeNeg(ifaceClass, null, "staticMethodA", "()I", vm().mirrorOf(RESULT_A),
 246                 "Static interface methods are not inheritable");
 247 
 248         // however it is possible to call "staticMethodA" on the actual instance
 249         testInvokeNeg(ifaceClass, ref, "staticMethodA", "()I", vm().mirrorOf(RESULT_A),
 250                 "Static interface methods are not inheritable");
 251 
 252         // "staticMethodB" is overridden in InterfaceB
 253         testInvokePos(ifaceClass, null, "staticMethodB", "()I", vm().mirrorOf(RESULT_B));
 254 
 255         // the instance invokes the overriden form of "staticMethodB" from InterfaceB
 256         // This should fail because ref is of type InterfacemethodsTest$TargetClass
 257         // which is not a subtype of the class containing staticMethodB.
 258         testInvokeNeg(ifaceClass, ref, "staticMethodB", "()I", vm().mirrorOf(RESULT_B),
 259                 "Invalid MethodID");
 260 
 261         // "staticMethodC" is present only in InterfaceB
 262         testInvokePos(ifaceClass, null, "staticMethodC", "()I", vm().mirrorOf(RESULT_B));
 263 
 264         // "staticMethodC" should not be reachable from the instance because ref is of
 265         // type InterfacemethodsTest$TargetClass which is not a subtype of the class
 266         // containing staticMethodC.
 267         testInvokeNeg(ifaceClass, ref, "staticMethodC", "()I", vm().mirrorOf(RESULT_B),
 268                 "Invalid MethodID");
 269     }
 270 
 271     private void testImplementationClass(ReferenceType targetClass, ObjectReference thisObject) {
 272         // Test invocations on the implementation object
 273 
 274         /* Default method calls */
 275 
 276         // "defaultMethodA" is accessible and not overridden
 277         testInvokePos(targetClass, thisObject, "defaultMethodA", "()I", vm().mirrorOf(RESULT_TARGET));
 278 
 279         // "defaultMethodB" is accessible and overridden in TargetClass
 280         testInvokePos(targetClass, thisObject, "defaultMethodB", "()I", vm().mirrorOf(RESULT_TARGET));
 281 
 282         // "defaultMethodC" is accessible and overridden in InterfaceB
 283         testInvokePos(targetClass, thisObject, "defaultMethodC", "()I", vm().mirrorOf(RESULT_TARGET));
 284 
 285         // "defaultMethodD" is accessible
 286         testInvokePos(targetClass, thisObject, "defaultMethodD", "()I", vm().mirrorOf(RESULT_TARGET));
 287 
 288 
 289         /* Non-default instance method calls */
 290 
 291         // "classMethod" declared in TargetClass is accessible
 292         testInvokePos(targetClass, thisObject, "classMethod", "()I", vm().mirrorOf(RESULT_TARGET));
 293 
 294         // the abstract "implementedMethod" has been implemented in TargetClass
 295         testInvokePos(targetClass, thisObject, "implementedMethod", "()I", vm().mirrorOf(RESULT_TARGET));
 296 
 297 
 298         /* Static method calls */
 299 
 300         // All the static methods declared by the interfaces are not reachable from the instance of the implementor class
 301         testInvokeNeg(targetClass, thisObject, "staticMethodA", "()I", vm().mirrorOf(RESULT_A),
 302                 "Static interface methods are not inheritable");
 303 
 304         testInvokeNeg(targetClass, thisObject, "staticMethodB", "()I", vm().mirrorOf(RESULT_B),
 305                 "Static interface methods are not inheritable");
 306 
 307         testInvokeNeg(targetClass, thisObject, "staticMethodC", "()I", vm().mirrorOf(RESULT_B),
 308                 "Static interface methods are not inheritable");
 309 
 310         // All the static methods declared by the interfaces are not reachable through the implementor class
 311         testInvokeNeg(targetClass, null, "staticMethodA", "()I", vm().mirrorOf(RESULT_A),
 312                 "Static interface methods are not inheritable");
 313 
 314         testInvokeNeg(targetClass, null, "staticMethodB", "()I", vm().mirrorOf(RESULT_B),
 315                 "Static interface methods are not inheritable");
 316 
 317         testInvokeNeg(targetClass, null, "staticMethodC", "()I", vm().mirrorOf(RESULT_B),
 318                 "Static interface methods are not inheritable");
 319     }
 320 
 321     private void testInvokePos(ReferenceType targetClass, ObjectReference ref, String methodName,
 322                                String methodSig, Value value) {
 323         testInvokePos(targetClass, ref, methodName, methodSig, value, false);
 324     }
 325 
 326     private void testInvokePos(ReferenceType targetClass, ObjectReference ref, String methodName,
 327                                String methodSig, Value value, boolean virtual) {
 328         logInvocation(ref, methodName, methodSig, targetClass);
 329         try {
 330             invoke(targetClass, ref, methodName, methodSig, value, virtual);
 331             System.err.println("--- PASSED");
 332         } catch (Exception e) {
 333             System.err.println("--- FAILED");
 334             failure("FAILED: Invocation failed with error message " + e.getLocalizedMessage());
 335         }
 336     }
 337 
 338     private void testInvokeNeg(ReferenceType targetClass, ObjectReference ref, String methodName,
 339                                String methodSig, Value value, String msg) {
 340         testInvokeNeg(targetClass, ref, methodName, methodSig, value, msg, false);
 341     }
 342 
 343     private void testInvokeNeg(ReferenceType targetClass, ObjectReference ref, String methodName,
 344                                String methodSig, Value value, String msg, boolean virtual) {
 345         logInvocation(ref, methodName, methodSig, targetClass);
 346         try {
 347             invoke(targetClass, ref, methodName, methodSig, value, virtual);
 348             System.err.println("--- FAILED");
 349             failure("FAILED: " + msg);
 350         } catch (Exception e) {
 351             System.err.println("--- PASSED");
 352 
 353         }
 354     }
 355 
 356     private void invoke(ReferenceType targetClass, ObjectReference ref, String methodName,
 357                         String methodSig, Value value, boolean virtual)
 358     throws Exception {
 359         Method method = getMethod(targetClass, methodName, methodSig);
 360         if (method == null) {
 361             throw new Exception("Can't find method: " + methodName  + " for class = " + targetClass);
 362         }
 363 
 364         println("Invoking " + (method.isAbstract() ? "abstract " : " ") + "method: " + method);
 365         println(method.declaringType().toString());
 366 
 367         Value returnValue = null;
 368         if (ref != null) {
 369             if (virtual) {
 370                 returnValue = invokeVirtual(ref, method);
 371             } else {
 372                 returnValue = invokeInstance(ref, method);
 373             }
 374         } else {
 375             returnValue = invokeStatic(targetClass, method);
 376         }
 377 
 378         println("        return val = " + returnValue);
 379         // It has to be the same value as what we passed in!
 380         if (returnValue.equals(value)) {
 381             println("         " + method.name() + " return value matches: "
 382                     + value);
 383         } else {
 384             if (value != null) {
 385                 throw new Exception(method.name() + " returned: " + returnValue +
 386                                     " expected: " + value );
 387             } else {
 388                 println("         " + method.name() + " return value : " + returnValue);
 389             }
 390 
 391         }
 392     }
 393 
 394     private Value invokeInstance(ObjectReference ref, Method method) throws Exception {
 395         return ref.invokeMethod(mainThread, method, Collections.emptyList(), ObjectReference.INVOKE_NONVIRTUAL);
 396     }
 397 
 398     private Value invokeVirtual(ObjectReference ref, Method method) throws Exception {
 399         return ref.invokeMethod(mainThread, method, Collections.emptyList(), 0);
 400     }
 401 
 402     private Value invokeStatic(ReferenceType refType, Method method) throws Exception {
 403         if (refType instanceof ClassType) {
 404             return ((ClassType)refType).invokeMethod(mainThread, method, Collections.emptyList(), ObjectReference.INVOKE_NONVIRTUAL);
 405         } else {
 406             return ((InterfaceType)refType).invokeMethod(mainThread, method, Collections.emptyList(), ObjectReference.INVOKE_NONVIRTUAL);
 407         }
 408     }
 409 
 410     private Method getMethod(ReferenceType rt, String name, String signature) {
 411         if (rt == null) return null;
 412         Method m = findMethod(rt, name, signature);
 413         if (m == null) {
 414             if (rt instanceof ClassType) {
 415                 for (Object ifc : ((ClassType)rt).interfaces()) {
 416                     m = getMethod((ReferenceType)ifc, name, signature);
 417                     if (m != null) {
 418                         break;
 419                     }
 420                 }
 421                 if (m == null) {
 422                     m = getMethod(((ClassType)rt).superclass(), name, signature);
 423                 } else {
 424                     if (m.isStatic()) {
 425                         // interface static methods are not inherited
 426                         m = null;
 427                     }
 428                 }
 429             } else if (rt instanceof InterfaceType) {
 430                 for(Object ifc : ((InterfaceType)rt).superinterfaces()) {
 431                     m = getMethod((ReferenceType)ifc, name, signature);
 432                     if (m != null) {
 433                         if (m.isStatic()) {
 434                             // interface static methods are not inherited
 435                             m = null;
 436                         }
 437                         break;
 438                     }
 439                 }
 440             }
 441         }
 442 
 443         return m;
 444     }
 445 
 446     private void logInvocation(ObjectReference ref, String methodName, String methodSig, ReferenceType targetClass) {
 447         if (ref != null) {
 448             System.err.println("Invoking: " + ref.referenceType().name() + "." +
 449                     methodName + methodSig + " with target of type " +
 450                     targetClass.name());
 451         } else {
 452             System.err.println("Invoking static : " + targetClass.name() + "." +
 453                     methodName + methodSig);
 454         }
 455     }
 456 }