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  */
  35 import com.sun.jdi.*;
  36 import com.sun.jdi.event.*;
  37 import java.util.Collections;
  38 import java.util.Iterator;
  39 import java.util.List;
  40 
  41 public 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 }