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