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  */
  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
 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 }