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 { staticMethodA()47 static int staticMethodA() { 48 System.out.println("-InterfaceA: static interface method A-"); 49 return RESULT_A; 50 } staticMethodB()51 static int staticMethodB() { 52 System.out.println("-InterfaceA: static interface method B-"); 53 return RESULT_A; 54 } defaultMethodA()55 default int defaultMethodA() { 56 System.out.println("-InterfaceA: default interface method A-"); 57 return RESULT_A; 58 } defaultMethodB()59 default int defaultMethodB() { 60 System.out.println("-InterfaceA: default interface method B-"); 61 return RESULT_A; 62 } defaultMethodC()63 default int defaultMethodC() { 64 System.out.println("-InterfaceA: default interface method C-"); 65 return RESULT_A; 66 } privateMethodA()67 private int privateMethodA() { 68 System.out.println("-InterfaceA: private interface method A-"); 69 return RESULT_A; 70 } implementedMethod()71 int implementedMethod(); 72 } 73 74 static interface InterfaceB extends InterfaceA { 75 @Override defaultMethodC()76 default int defaultMethodC() { 77 System.out.println("-InterfaceB: overridden default interface method C-"); 78 return RESULT_B; 79 } defaultMethodD()80 default int defaultMethodD() { 81 System.out.println("-InterfaceB: default interface method D-"); 82 return RESULT_B; 83 } staticMethodB()84 static int staticMethodB() { 85 System.out.println("-InterfaceB: overridden static interface method B-"); 86 return RESULT_B; 87 } staticMethodC()88 static int staticMethodC() { 89 System.out.println("-InterfaceB: static interface method C-"); 90 return RESULT_B; 91 } privateMethodB()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 { classMethod()99 public int classMethod() { 100 System.out.println("-TargetClass: class only method-"); 101 return RESULT_TARGET; 102 } 103 104 @Override implementedMethod()105 public int implementedMethod() { 106 System.out.println("-TargetClass: implemented non-default interface method-"); 107 return RESULT_TARGET; 108 } 109 110 @Override defaultMethodB()111 public int defaultMethodB() { 112 System.out.println("-TargetClass: overridden default interface method B"); 113 114 return RESULT_TARGET; 115 } 116 main(String[] args)117 public static void main(String[] args) { 118 TargetClass tc = new TargetClass(); 119 tc.doTests(tc); 120 } 121 doTests(TargetClass ref)122 private void doTests(TargetClass ref) { 123 // break 124 } 125 } 126 InterfaceMethodsTest(String[] args)127 public InterfaceMethodsTest(String[] args) { 128 super(args); 129 } 130 main(String[] args)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 runTests()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 testInterfaceA(ObjectReference ref)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 testInterfaceB(ObjectReference ref)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 testImplementationClass(ReferenceType targetClass, ObjectReference thisObject)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 testInvokePos(ReferenceType targetClass, ObjectReference ref, String methodName, String methodSig, Value value)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. testInvokePos(ReferenceType targetClass, ObjectReference ref, String methodName, String methodSig, Value value, boolean virtual)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. testInvokePos(Method method, ObjectReference ref, Value value, boolean virtual)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 testInvokeNeg(ReferenceType targetClass, ObjectReference ref, String methodName, String methodSig, Value value, String msg)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 testInvokeNeg(ReferenceType targetClass, ObjectReference ref, String methodName, String methodSig, Value value, String msg, boolean virtual)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 invoke(ReferenceType targetClass, ObjectReference ref, String methodName, String methodSig, Value value, boolean virtual)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 invoke(ReferenceType targetClass, ObjectReference ref, Method method, Value value, boolean virtual)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 invokeNonVirtual(ObjectReference ref, Method method)447 private Value invokeNonVirtual(ObjectReference ref, Method method) throws Exception { 448 return ref.invokeMethod(mainThread, method, Collections.emptyList(), ObjectReference.INVOKE_NONVIRTUAL); 449 } 450 invokeVirtual(ObjectReference ref, Method method)451 private Value invokeVirtual(ObjectReference ref, Method method) throws Exception { 452 return ref.invokeMethod(mainThread, method, Collections.emptyList(), 0); 453 } 454 invokeStatic(ReferenceType refType, Method method)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 getMethod(ReferenceType rt, String name, String signature)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 logInvocation(ObjectReference ref, String methodName, String methodSig, ReferenceType targetClass)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 testLookup(ReferenceType targetClass, String methodName, String methodSig, boolean declaredOnly, Class<?> expectedException)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 lookupMethod(ReferenceType targetClass, String methodName, String methodSig)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 lookupDeclaredMethod(ReferenceType targetClass, String methodName, String methodSig)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 } 564