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