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 }