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 }