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  *  @modules jdk.jdi
  32  *  @run build TestScaffold VMConnection TargetListener TargetAdapter
  33  *  @run build InterfaceMethodsTest
  34  *  @run driver InterfaceMethodsTest
  35  */
  36 import com.sun.jdi.*;
  37 import com.sun.jdi.event.*;
  38 import java.util.Collections;
  39 import java.util.Iterator;
  40 import java.util.List;
  41 
  42 public class InterfaceMethodsTest extends TestScaffold {
  43     private static final int RESULT_A = 1;
  44     private static final int RESULT_B = 2;
  45     private static final int RESULT_TARGET = 3;
  46 
  47     static interface InterfaceA {
  48         static int staticMethodA() {
  49             System.out.println("-InterfaceA: static interface method A-");
  50             return RESULT_A;
  51         }
  52         static int staticMethodB() {
  53             System.out.println("-InterfaceA: static interface method B-");
  54             return RESULT_A;
  55         }
  56         default int defaultMethodA() {
  57             System.out.println("-InterfaceA: default interface method A-");
  58             return RESULT_A;
  59         }
  60         default int defaultMethodB() {
  61             System.out.println("-InterfaceA: default interface method B-");
  62             return RESULT_A;
  63         }
  64         default int defaultMethodC() {
  65             System.out.println("-InterfaceA: default interface method C-");
  66             return RESULT_A;
  67         }
  68         private int privateMethodA() {
  69             System.out.println("-InterfaceA: private interface method A-");
  70             return RESULT_A;
  71         }
  72         int implementedMethod();
  73     }
  74 
  75     static interface InterfaceB extends InterfaceA {
  76         @Override
  77         default int defaultMethodC() {
  78             System.out.println("-InterfaceB: overridden default interface method C-");
  79             return RESULT_B;
  80         }
  81         default int defaultMethodD() {
  82             System.out.println("-InterfaceB: default interface method D-");
  83             return RESULT_B;
  84         }
  85         static int staticMethodB() {
  86             System.out.println("-InterfaceB: overridden static interface method B-");
  87             return RESULT_B;
  88         }
  89         static int staticMethodC() {
  90             System.out.println("-InterfaceB: static interface method C-");
  91             return RESULT_B;
  92         }
  93         private int privateMethodB() {
  94             System.out.println("-InterfaceB: private interface method B-");
  95             return RESULT_B;
  96         }
  97     }
  98 
  99     final static class TargetClass implements InterfaceB {
 100         public int classMethod() {
 101             System.out.println("-TargetClass: class only method-");
 102             return RESULT_TARGET;
 103         }
 104 
 105         @Override
 106         public int implementedMethod() {
 107             System.out.println("-TargetClass: implemented non-default interface method-");
 108             return RESULT_TARGET;
 109         }
 110 
 111         @Override
 112         public int defaultMethodB() {
 113             System.out.println("-TargetClass: overridden default interface method B");
 114 
 115             return RESULT_TARGET;
 116         }
 117 
 118         public static void main(String[] args) {
 119             TargetClass tc = new TargetClass();
 120             tc.doTests(tc);
 121         }
 122 
 123         private void doTests(TargetClass ref) {
 124             // break
 125         }
 126     }
 127 
 128     public InterfaceMethodsTest(String[] args) {
 129         super(args);
 130     }
 131 
 132     public static void main(String[] args) throws Exception {
 133         new InterfaceMethodsTest(args).startTests();
 134     }
 135 
 136     private static final String TEST_CLASS_NAME = InterfaceMethodsTest.class.getName().replace('.', '/');
 137     private static final String TARGET_CLASS_NAME = TargetClass.class.getName().replace('.', '/');
 138     private static final String INTERFACEA_NAME = InterfaceA.class.getName().replace('.', '/');
 139     private static final String INTERFACEB_NAME = InterfaceB.class.getName().replace('.', '/');
 140 
 141     protected void runTests() throws Exception {
 142         /*
 143          * Get to the top of main()
 144          * to determine targetClass and mainThread
 145          */
 146         BreakpointEvent bpe = startToMain(TARGET_CLASS_NAME);
 147 
 148         bpe = resumeTo(TARGET_CLASS_NAME, "doTests", "(L" + TARGET_CLASS_NAME +";)V");
 149 
 150         mainThread = bpe.thread();
 151 
 152         StackFrame frame = mainThread.frame(0);
 153         ObjectReference thisObject = frame.thisObject();
 154         ObjectReference ref = (ObjectReference)frame.getArgumentValues().get(0);
 155 
 156         ReferenceType targetClass = bpe.location().declaringType();
 157         testImplementationClass(targetClass, thisObject);
 158 
 159         testInterfaceA(ref);
 160 
 161         testInterfaceB(ref);
 162 
 163         /*
 164          * resume the target listening for events
 165          */
 166         listenUntilVMDisconnect();
 167 
 168         /*
 169          * deal with results of test
 170          * if anything has called failure("foo") testFailed will be true
 171          */
 172         if (!testFailed) {
 173             println("InterfaceMethodsTest: passed");
 174         } else {
 175             throw new Exception("InterfaceMethodsTest: failed");
 176         }
 177     }
 178 
 179     private void testInterfaceA(ObjectReference ref) {
 180 
 181         ReferenceType ifaceClass = (ReferenceType)vm().classesByName(INTERFACEA_NAME).get(0);
 182 
 183         /* Private method calls */
 184 
 185         Method m = testLookup(ifaceClass, "privateMethodA", "()I", true, null); // should succeed
 186 
 187         testInvokePos(m, ref, vm().mirrorOf(RESULT_A), false);
 188         testInvokePos(m, ref, vm().mirrorOf(RESULT_A), true);
 189 
 190         // Test non-virtual calls on InterfaceA
 191 
 192         /* Default method calls */
 193 
 194         // invoke the InterfaceA's "defaultMethodA"
 195         testInvokePos(ifaceClass, ref, "defaultMethodA", "()I", vm().mirrorOf(RESULT_A));
 196 
 197         // invoke the InterfaceA's "defaultMethodB"
 198         testInvokePos(ifaceClass, ref, "defaultMethodB", "()I", vm().mirrorOf(RESULT_A));
 199 
 200         // invoke the InterfaceA's "defaultMethodC"
 201         testInvokePos(ifaceClass, ref, "defaultMethodC", "()I", vm().mirrorOf(RESULT_A));
 202 
 203         // "defaultMethodD" from InterfaceB is not accessible from here
 204         testInvokeNeg(ifaceClass, ref, "defaultMethodD", "()I", vm().mirrorOf(RESULT_B),
 205                       "Attempted to invoke non-existing method");
 206 
 207         // non-virtual invoke of the abstract method "implementedMethod" fails
 208         testInvokeNeg(ifaceClass, ref, "implementedMethod", "()I", vm().mirrorOf(TARGET_CLASS_NAME),
 209                       "Invocation of abstract methods is not supported");
 210 
 211         /* Static method calls */
 212 
 213         // invoke static interface method A
 214         testInvokePos(ifaceClass, null, "staticMethodA", "()I", vm().mirrorOf(RESULT_A));
 215 
 216         // invoking static method A on the instance fails because static method A is
 217         // not inherited by TargetClass.
 218         testInvokeNeg(ifaceClass, ref, "staticMethodA", "()I", vm().mirrorOf(RESULT_A),
 219                       "Invalid MethodID");
 220 
 221         // invoke static interface method B
 222         testInvokePos(ifaceClass, null, "staticMethodB", "()I", vm().mirrorOf(RESULT_A));
 223 
 224         // invoking static method B on the instance fails because static method B is
 225         // not inherited by TargetClass.
 226         testInvokeNeg(ifaceClass, ref, "staticMethodB", "()I", vm().mirrorOf(RESULT_A),
 227                       "Invalid MethodID");
 228 
 229         // try to invoke a virtual method
 230         testInvokePos(ifaceClass, ref, "implementedMethod", "()I", vm().mirrorOf(RESULT_TARGET), true);
 231     }
 232 
 233     private void testInterfaceB(ObjectReference ref) {
 234         // Test non-virtual calls on InterfaceB
 235         ReferenceType ifaceClass = (ReferenceType)vm().classesByName(INTERFACEB_NAME).get(0);
 236 
 237         /* private method calls */
 238 
 239         /* These should fail but won't because of JDK-8167416
 240         testLookup(ifaceClass, "privateMethodA", "()I", true, NoSuchMethodError.class); // should fail
 241         testLookup(ifaceClass, "privateMethodA", "()I", false, NoSuchMethodError.class); // should fail
 242         */
 243         Method m = testLookup(ifaceClass, "privateMethodB", "()I", true, null); // should succeed
 244         testInvokePos(m, ref, vm().mirrorOf(RESULT_B), false);
 245         testInvokePos(m, ref, vm().mirrorOf(RESULT_B), true);
 246 
 247         /* Default method calls */
 248 
 249         // invoke the inherited "defaultMethodA"
 250         testInvokePos(ifaceClass, ref, "defaultMethodA", "()I", vm().mirrorOf(RESULT_A));
 251 
 252         // invoke the inherited "defaultMethodB"
 253         testInvokePos(ifaceClass, ref, "defaultMethodB", "()I", vm().mirrorOf(RESULT_A));
 254 
 255         // invoke the inherited and overridden "defaultMethodC"
 256         testInvokePos(ifaceClass, ref, "defaultMethodC", "()I", vm().mirrorOf(RESULT_B));
 257 
 258         // invoke InterfaceB only "defaultMethodD"
 259         testInvokePos(ifaceClass, ref, "defaultMethodD", "()I", vm().mirrorOf(RESULT_B));
 260 
 261         // "implementedMethod" is not present in InterfaceB
 262         testInvokeNeg(ifaceClass, ref, "implementedMethod", "()I", vm().mirrorOf(RESULT_TARGET),
 263                 "Invocation of non-default methods is not supported");
 264 
 265 
 266         /* Static method calls*/
 267 
 268         // "staticMethodA" must not be inherited by InterfaceB
 269         testInvokeNeg(ifaceClass, null, "staticMethodA", "()I", vm().mirrorOf(RESULT_A),
 270                 "Static interface methods are not inheritable");
 271 
 272         // "staticMethodA" is not inherited by InterfaceB even from an actual instance
 273         testInvokeNeg(ifaceClass, ref, "staticMethodA", "()I", vm().mirrorOf(RESULT_A),
 274                 "Static interface methods are not inheritable");
 275 
 276         // "staticMethodB" is re-defined in InterfaceB
 277         testInvokePos(ifaceClass, null, "staticMethodB", "()I", vm().mirrorOf(RESULT_B));
 278 
 279         // the instance fails to invoke the re-defined form of "staticMethodB" from
 280         // InterfaceB because staticMethodB is not inherited by TargetClass
 281         testInvokeNeg(ifaceClass, ref, "staticMethodB", "()I", vm().mirrorOf(RESULT_B),
 282                 "Invalid MethodID");
 283 
 284         // "staticMethodC" is present only in InterfaceB
 285         testInvokePos(ifaceClass, null, "staticMethodC", "()I", vm().mirrorOf(RESULT_B));
 286 
 287         // "staticMethodC" is not reachable from the instance because staticMethodC
 288         // is not inherited by TargetClass.
 289         testInvokeNeg(ifaceClass, ref, "staticMethodC", "()I", vm().mirrorOf(RESULT_B),
 290                 "Invalid MethodID");
 291     }
 292 
 293     private void testImplementationClass(ReferenceType targetClass, ObjectReference thisObject) {
 294         // Test invocations on the implementation object
 295 
 296         // Note: private interface calls have already been tested
 297 
 298         /* Default method calls */
 299 
 300         // "defaultMethodA" is accessible and not overridden
 301         testInvokePos(targetClass, thisObject, "defaultMethodA", "()I", vm().mirrorOf(RESULT_A));
 302 
 303         // "defaultMethodB" is accessible and overridden in TargetClass
 304         testInvokePos(targetClass, thisObject, "defaultMethodB", "()I", vm().mirrorOf(RESULT_TARGET));
 305 
 306         // "defaultMethodC" is accessible and overridden in InterfaceB
 307         testInvokePos(targetClass, thisObject, "defaultMethodC", "()I", vm().mirrorOf(RESULT_B));
 308 
 309         // "defaultMethodD" is accessible
 310         testInvokePos(targetClass, thisObject, "defaultMethodD", "()I", vm().mirrorOf(RESULT_B));
 311 
 312 
 313         /* Non-default instance method calls */
 314 
 315         // "classMethod" declared in TargetClass is accessible
 316         testInvokePos(targetClass, thisObject, "classMethod", "()I", vm().mirrorOf(RESULT_TARGET));
 317 
 318         // the abstract "implementedMethod" has been implemented in TargetClass
 319         testInvokePos(targetClass, thisObject, "implementedMethod", "()I", vm().mirrorOf(RESULT_TARGET));
 320 
 321 
 322         /* Static method calls */
 323 
 324         // All the static methods declared by the interfaces are not reachable from the instance of the implementor class
 325         testInvokeNeg(targetClass, thisObject, "staticMethodA", "()I", vm().mirrorOf(RESULT_A),
 326                 "Static interface methods are not inheritable");
 327 
 328         testInvokeNeg(targetClass, thisObject, "staticMethodB", "()I", vm().mirrorOf(RESULT_B),
 329                 "Static interface methods are not inheritable");
 330 
 331         testInvokeNeg(targetClass, thisObject, "staticMethodC", "()I", vm().mirrorOf(RESULT_B),
 332                 "Static interface methods are not inheritable");
 333 
 334         // All the static methods declared by the interfaces are not reachable through the implementor class
 335         testInvokeNeg(targetClass, null, "staticMethodA", "()I", vm().mirrorOf(RESULT_A),
 336                 "Static interface methods are not inheritable");
 337 
 338         testInvokeNeg(targetClass, null, "staticMethodB", "()I", vm().mirrorOf(RESULT_B),
 339                 "Static interface methods are not inheritable");
 340 
 341         testInvokeNeg(targetClass, null, "staticMethodC", "()I", vm().mirrorOf(RESULT_B),
 342                 "Static interface methods are not inheritable");
 343     }
 344 
 345     // Non-virtual invocation
 346     private void testInvokePos(ReferenceType targetClass, ObjectReference ref, String methodName,
 347                                String methodSig, Value value) {
 348         testInvokePos(targetClass, ref, methodName, methodSig, value, false);
 349     }
 350 
 351     // Lookup the named method in the targetClass and invoke on the given object (for instance methods)
 352     // using virtual, or non-virtual, invocation mode as specified, for instance methods. Verify the
 353     // expected return value.
 354     // Should succeed.
 355     private void testInvokePos(ReferenceType targetClass, ObjectReference ref, String methodName,
 356                                String methodSig, Value value, boolean virtual) {
 357         logInvocation(ref, methodName, methodSig, targetClass);
 358         try {
 359             invoke(targetClass, ref, methodName, methodSig, value, virtual);
 360             System.err.println("--- PASSED");
 361         } catch (Exception e) {
 362             System.err.println("--- FAILED");
 363             failure("FAILED: Invocation failed with error message " + e.getLocalizedMessage());
 364         }
 365     }
 366 
 367     // Invoke the given Method on the given object (for instance methods)
 368     // using virtual, or non-virtual, invocation mode as specified, for instance methods. Verify the
 369     // expected return value.
 370     // Should succeed.
 371     private void testInvokePos(Method method, ObjectReference ref, Value value, boolean virtual) {
 372         logInvocation(ref, method.name(), method.signature(), method.declaringType());
 373         try {
 374             invoke(method.declaringType(), ref, method, value, virtual);
 375             System.err.println("--- PASSED");
 376         } catch (Exception e) {
 377             System.err.println("--- FAILED");
 378             failure("FAILED: Invocation failed with error message " + e.getLocalizedMessage());
 379         }
 380     }
 381 
 382     // Non-virtual invocation - with lookup in targetClass
 383     private void testInvokeNeg(ReferenceType targetClass, ObjectReference ref, String methodName,
 384                                String methodSig, Value value, String msg) {
 385         testInvokeNeg(targetClass, ref, methodName, methodSig, value, msg, false);
 386     }
 387 
 388     // Lookup the named method in the targetClass and invoke on the given object (for instance methods)
 389     // using virtual, or non-virtual, invocation mode as specified, for instance methods. Verify the
 390     // expected return value.
 391     // Should fail - with msg decribing why failure was expected
 392     private void testInvokeNeg(ReferenceType targetClass, ObjectReference ref, String methodName,
 393                                String methodSig, Value value, String msg, boolean virtual) {
 394         logInvocation(ref, methodName, methodSig, targetClass);
 395         try {
 396             invoke(targetClass, ref, methodName, methodSig, value, virtual);
 397             System.err.println("--- FAILED");
 398             failure("FAILED: " + msg);
 399         } catch (Exception e) {
 400             System.err.println("--- PASSED");
 401 
 402         }
 403     }
 404 
 405     private void invoke(ReferenceType targetClass, ObjectReference ref, String methodName,
 406                         String methodSig, Value value, boolean virtual) throws Exception {
 407 
 408         Method method = getMethod(targetClass, methodName, methodSig);
 409         if (method == null) {
 410             throw new Exception("Can't find method: " + methodName  + " for class = " + targetClass);
 411         }
 412         invoke(targetClass, ref, method, value, virtual);
 413     }
 414 
 415     private void invoke(ReferenceType targetClass, ObjectReference ref, Method method,
 416                         Value value, boolean virtual) throws Exception {
 417 
 418         println("Invoking " + (method.isAbstract() ? "abstract " : " ") + "method: " + method);
 419         println(method.declaringType().toString());
 420 
 421         Value returnValue = null;
 422         if (ref != null) {
 423             if (virtual) {
 424                 returnValue = invokeVirtual(ref, method);
 425             } else {
 426                 returnValue = invokeNonVirtual(ref, method);
 427             }
 428         } else {
 429             returnValue = invokeStatic(targetClass, method);
 430         }
 431 
 432         println("        return val = " + returnValue);
 433         // It has to be the same value as what we passed in!
 434         if (returnValue.equals(value)) {
 435             println("         " + method.name() + " return value matches: "
 436                     + value);
 437         } else {
 438             if (value != null) {
 439                 throw new Exception(method.name() + " returned: " + returnValue +
 440                                     " expected: " + value );
 441             } else {
 442                 println("         " + method.name() + " return value : " + returnValue);
 443             }
 444 
 445         }
 446     }
 447 
 448     private Value invokeNonVirtual(ObjectReference ref, Method method) throws Exception {
 449         return ref.invokeMethod(mainThread, method, Collections.emptyList(), ObjectReference.INVOKE_NONVIRTUAL);
 450     }
 451 
 452     private Value invokeVirtual(ObjectReference ref, Method method) throws Exception {
 453         return ref.invokeMethod(mainThread, method, Collections.emptyList(), 0);
 454     }
 455 
 456     private Value invokeStatic(ReferenceType refType, Method method) throws Exception {
 457         if (refType instanceof ClassType) {
 458             return ((ClassType)refType).invokeMethod(mainThread, method, Collections.emptyList(), ObjectReference.INVOKE_NONVIRTUAL);
 459         } else {
 460             return ((InterfaceType)refType).invokeMethod(mainThread, method, Collections.emptyList(), ObjectReference.INVOKE_NONVIRTUAL);
 461         }
 462     }
 463 
 464     private Method getMethod(ReferenceType rt, String name, String signature) {
 465         if (rt == null) return null;
 466         Method m = findMethod(rt, name, signature);
 467         if (m == null) {
 468             if (rt instanceof ClassType) {
 469                 for (Object ifc : ((ClassType)rt).interfaces()) {
 470                     m = getMethod((ReferenceType)ifc, name, signature);
 471                     if (m != null) {
 472                         break;
 473                     }
 474                 }
 475                 if (m == null) {
 476                     m = getMethod(((ClassType)rt).superclass(), name, signature);
 477                 } else {
 478                     if (m.isStatic()) {
 479                         // interface static methods are not inherited
 480                         m = null;
 481                     }
 482                 }
 483             } else if (rt instanceof InterfaceType) {
 484                 for(Object ifc : ((InterfaceType)rt).superinterfaces()) {
 485                     m = getMethod((ReferenceType)ifc, name, signature);
 486                     if (m != null) {
 487                         if (m.isStatic()) {
 488                             // interface static methods are not inherited
 489                             m = null;
 490                         }
 491                         break;
 492                     }
 493                 }
 494             }
 495         }
 496 
 497         return m;
 498     }
 499 
 500     private void logInvocation(ObjectReference ref, String methodName, String methodSig, ReferenceType targetClass) {
 501         if (ref != null) {
 502             System.err.println("Invoking: " + ref.referenceType().name() + "." +
 503                     methodName + methodSig + " with target of type " +
 504                     targetClass.name());
 505         } else {
 506             System.err.println("Invoking static : " + targetClass.name() + "." +
 507                     methodName + methodSig);
 508         }
 509     }
 510 
 511     private Method testLookup(ReferenceType targetClass, String methodName, String methodSig,
 512                               boolean declaredOnly, Class<?> expectedException) {
 513 
 514         System.err.println("Looking up " + targetClass.name() + "." + methodName + methodSig);
 515         try {
 516             Method m = declaredOnly ?
 517                 lookupDeclaredMethod(targetClass, methodName, methodSig) :
 518                 lookupMethod(targetClass, methodName, methodSig);
 519 
 520             if (expectedException == null) {
 521                 System.err.println("--- PASSED");
 522                 return m;
 523             }
 524             else {
 525                 System.err.println("--- FAILED");
 526                 failure("FAILED: lookup succeeded but expected exception "
 527                         + expectedException.getSimpleName());
 528                 return null;
 529             }
 530         }
 531         catch (Throwable t) {
 532             if (t.getClass() != expectedException) {
 533                 System.err.println("--- FAILED");
 534                 failure("FAILED: got exception " + t + " but expected exception "
 535                         + expectedException.getSimpleName());
 536                 return null;
 537             }
 538             else {
 539                 System.err.println("--- PASSED");
 540                 return null;
 541             }
 542         }
 543     }
 544 
 545     private Method lookupMethod(ReferenceType targetClass, String methodName, String methodSig) {
 546         List methods = targetClass.allMethods();
 547         Iterator iter = methods.iterator();
 548         while (iter.hasNext()) {
 549             Method method = (Method)iter.next();
 550             if (method.name().equals(methodName) &&
 551                 method.signature().equals(methodSig)) {
 552                 return method;
 553             }
 554         }
 555         throw new NoSuchMethodError();
 556     }
 557 
 558     private Method lookupDeclaredMethod(ReferenceType targetClass, String methodName, String methodSig) {
 559         Method m = findMethod(targetClass, methodName, methodSig);
 560         if (m == null)
 561             throw new NoSuchMethodError();
 562         return m;
 563     }
 564 }