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 * @summary JDI: Add support for static and default methods in interfaces 29 * 30 * @modules jdk.jdi 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 39 public class InterfaceMethodsTest extends TestScaffold { 40 private static final int RESULT_A = 1; 41 private static final int RESULT_B = 1; 42 private static final int RESULT_TARGET = 1; 43 44 static interface InterfaceA { 45 static int staticMethodA() { 46 System.out.println("-InterfaceA: static interface method A-"); 47 return RESULT_A; 48 } 49 static int staticMethodB() { 50 System.out.println("-InterfaceA: static interface method B-"); 51 return RESULT_A; 52 } 53 default int defaultMethodA() { 54 System.out.println("-InterfaceA: default interface method A-"); 55 return RESULT_A; 56 } 57 default int defaultMethodB() { 58 System.out.println("-InterfaceA: default interface method B-"); 59 return RESULT_A; 60 } 61 default int defaultMethodC() { 62 System.out.println("-InterfaceA: default interface method C-"); 63 return RESULT_A; 64 } 65 66 int implementedMethod(); 67 } 68 69 static interface InterfaceB extends InterfaceA { 70 @Override 71 default int defaultMethodC() { 72 System.out.println("-InterfaceB: overridden default interface method C-"); 73 return RESULT_B; 74 } 75 default int defaultMethodD() { 76 System.out.println("-InterfaceB: default interface method D-"); 77 return RESULT_B; 78 } 79 80 static int staticMethodB() { 81 System.out.println("-InterfaceB: overridden static interface method B-"); 82 return RESULT_B; 83 } 84 85 static int staticMethodC() { 86 System.out.println("-InterfaceB: static interface method C-"); 87 return RESULT_B; 88 } 89 } 90 91 final static class TargetClass implements InterfaceB { 92 public int classMethod() { 93 System.out.println("-TargetClass: class only method-"); 94 return RESULT_TARGET; 95 } 96 97 @Override 98 public int implementedMethod() { 99 System.out.println("-TargetClass: implemented non-default interface method-"); 100 return RESULT_TARGET; 101 } 102 103 @Override 104 public int defaultMethodB() { 105 System.out.println("-TargetClass: overridden default interface method D"); 106 107 return RESULT_TARGET; 108 } 109 110 public static void main(String[] args) { 111 TargetClass tc = new TargetClass(); 112 tc.doTests(tc); 113 } 114 115 private void doTests(TargetClass ref) { 116 // break 117 } 118 } 119 120 public InterfaceMethodsTest(String[] args) { 121 super(args); 122 } 123 124 public static void main(String[] args) throws Exception { 125 new InterfaceMethodsTest(args).startTests(); 126 } 127 128 private static final String TEST_CLASS_NAME = InterfaceMethodsTest.class.getName().replace('.', '/'); 129 private static final String TARGET_CLASS_NAME = TargetClass.class.getName().replace('.', '/'); 130 private static final String INTERFACEA_NAME = InterfaceA.class.getName().replace('.', '/'); 131 private static final String INTERFACEB_NAME = InterfaceB.class.getName().replace('.', '/'); 132 133 protected void runTests() throws Exception { 134 /* 135 * Get to the top of main() 136 * to determine targetClass and mainThread 137 */ 138 BreakpointEvent bpe = startToMain(TARGET_CLASS_NAME); 139 140 bpe = resumeTo(TARGET_CLASS_NAME, "doTests", "(L" + TARGET_CLASS_NAME +";)V"); 141 142 mainThread = bpe.thread(); 143 144 StackFrame frame = mainThread.frame(0); 145 ObjectReference thisObject = frame.thisObject(); 146 ObjectReference ref = (ObjectReference)frame.getArgumentValues().get(0); 147 148 ReferenceType targetClass = bpe.location().declaringType(); 149 testImplementationClass(targetClass, thisObject); 150 151 testInterfaceA(ref); 152 153 testInterfaceB(ref); 154 155 /* 156 * resume the target listening for events 157 */ 158 listenUntilVMDisconnect(); 159 160 /* 161 * deal with results of test 162 * if anything has called failure("foo") testFailed will be true 163 */ 164 if (!testFailed) { 165 println("InterfaceMethodsTest: passed"); 166 } else { 167 throw new Exception("InterfaceMethodsTest: failed"); 168 } 169 } 170 171 private void testInterfaceA(ObjectReference ref) { 172 // Test non-virtual calls on InterfaceA 173 174 ReferenceType ifaceClass = (ReferenceType)vm().classesByName(INTERFACEA_NAME).get(0); 175 /* Default method calls */ 176 177 // invoke the InterfaceA's "defaultMethodA" 178 testInvokePos(ifaceClass, ref, "defaultMethodA", "()I", vm().mirrorOf(RESULT_A)); 179 180 // invoke the InterfaceA's "defaultMethodB" 181 testInvokePos(ifaceClass, ref, "defaultMethodB", "()I", vm().mirrorOf(RESULT_A)); 182 183 // invoke the InterfaceA's "defaultMethodC" 184 testInvokePos(ifaceClass, ref, "defaultMethodC", "()I", vm().mirrorOf(RESULT_A)); 185 186 // "defaultMethodD" from InterfaceB is not accessible from here 187 testInvokeNeg(ifaceClass, ref, "defaultMethodD", "()I", vm().mirrorOf(RESULT_B), 188 "Attempted to invoke non-existing method"); 189 190 // trying to invoke the asbtract method "implementedMethod" 191 testInvokeNeg(ifaceClass, ref, "implementedMethod", "()I", vm().mirrorOf(TARGET_CLASS_NAME), 192 "Invocation of non-default methods is not supported"); 193 194 195 /* Static method calls */ 196 197 // invoke interface static method A 198 testInvokePos(ifaceClass, null, "staticMethodA", "()I", vm().mirrorOf(RESULT_A)); 199 200 // try to invoke static method A on the instance. This should fail because ref 201 // is of type InterfacemethodsTest$TargetClass which is not a subtype of the 202 // class containing staticMethodA. 203 testInvokeNeg(ifaceClass, ref, "staticMethodA", "()I", vm().mirrorOf(RESULT_A), 204 "Invalid MethodID"); 205 206 // invoke interface static method B 207 testInvokePos(ifaceClass, null, "staticMethodB", "()I", vm().mirrorOf(RESULT_A)); 208 209 // try to invoke static method B on the instance. This should fail because ref 210 // is of type InterfacemethodsTest$TargetClass which is not a subtype of the 211 // class containing staticMethodB. 212 testInvokeNeg(ifaceClass, ref, "staticMethodB", "()I", vm().mirrorOf(RESULT_A), 213 "Invalid MethodID"); 214 215 // try to invoke a virtual method 216 testInvokePos(ifaceClass, ref, "implementedMethod", "()I", vm().mirrorOf(RESULT_A), true); 217 } 218 219 private void testInterfaceB(ObjectReference ref) { 220 // Test non-virtual calls on InterfaceB 221 ReferenceType ifaceClass = (ReferenceType)vm().classesByName(INTERFACEB_NAME).get(0); 222 223 /* Default method calls */ 224 225 // invoke the inherited "defaultMethodA" 226 testInvokePos(ifaceClass, ref, "defaultMethodA", "()I", vm().mirrorOf(RESULT_A)); 227 228 // invoke the inherited "defaultMethodB" 229 testInvokePos(ifaceClass, ref, "defaultMethodB", "()I", vm().mirrorOf(RESULT_A)); 230 231 // invoke the inherited and overridden "defaultMethodC" 232 testInvokePos(ifaceClass, ref, "defaultMethodC", "()I", vm().mirrorOf(RESULT_B)); 233 234 // invoke InterfaceB only "defaultMethodD" 235 testInvokePos(ifaceClass, ref, "defaultMethodD", "()I", vm().mirrorOf(RESULT_B)); 236 237 // "implementedMethod" is not present in InterfaceB 238 testInvokeNeg(ifaceClass, ref, "implementedMethod", "()I", vm().mirrorOf(RESULT_TARGET), 239 "Invocation of non-default methods is not supported"); 240 241 242 /* Static method calls*/ 243 244 // "staticMethodA" must not be inherited by InterfaceB 245 testInvokeNeg(ifaceClass, null, "staticMethodA", "()I", vm().mirrorOf(RESULT_A), 246 "Static interface methods are not inheritable"); 247 248 // however it is possible to call "staticMethodA" on the actual instance 249 testInvokeNeg(ifaceClass, ref, "staticMethodA", "()I", vm().mirrorOf(RESULT_A), 250 "Static interface methods are not inheritable"); 251 252 // "staticMethodB" is overridden in InterfaceB 253 testInvokePos(ifaceClass, null, "staticMethodB", "()I", vm().mirrorOf(RESULT_B)); 254 255 // the instance invokes the overriden form of "staticMethodB" from InterfaceB 256 // This should fail because ref is of type InterfacemethodsTest$TargetClass 257 // which is not a subtype of the class containing staticMethodB. 258 testInvokeNeg(ifaceClass, ref, "staticMethodB", "()I", vm().mirrorOf(RESULT_B), 259 "Invalid MethodID"); 260 261 // "staticMethodC" is present only in InterfaceB 262 testInvokePos(ifaceClass, null, "staticMethodC", "()I", vm().mirrorOf(RESULT_B)); 263 264 // "staticMethodC" should not be reachable from the instance because ref is of 265 // type InterfacemethodsTest$TargetClass which is not a subtype of the class 266 // containing staticMethodC. 267 testInvokeNeg(ifaceClass, ref, "staticMethodC", "()I", vm().mirrorOf(RESULT_B), 268 "Invalid MethodID"); 269 } 270 271 private void testImplementationClass(ReferenceType targetClass, ObjectReference thisObject) { 272 // Test invocations on the implementation object 273 274 /* Default method calls */ 275 276 // "defaultMethodA" is accessible and not overridden 277 testInvokePos(targetClass, thisObject, "defaultMethodA", "()I", vm().mirrorOf(RESULT_TARGET)); 278 279 // "defaultMethodB" is accessible and overridden in TargetClass 280 testInvokePos(targetClass, thisObject, "defaultMethodB", "()I", vm().mirrorOf(RESULT_TARGET)); 281 282 // "defaultMethodC" is accessible and overridden in InterfaceB 283 testInvokePos(targetClass, thisObject, "defaultMethodC", "()I", vm().mirrorOf(RESULT_TARGET)); 284 285 // "defaultMethodD" is accessible 286 testInvokePos(targetClass, thisObject, "defaultMethodD", "()I", vm().mirrorOf(RESULT_TARGET)); 287 288 289 /* Non-default instance method calls */ 290 291 // "classMethod" declared in TargetClass is accessible 292 testInvokePos(targetClass, thisObject, "classMethod", "()I", vm().mirrorOf(RESULT_TARGET)); 293 294 // the abstract "implementedMethod" has been implemented in TargetClass 295 testInvokePos(targetClass, thisObject, "implementedMethod", "()I", vm().mirrorOf(RESULT_TARGET)); 296 297 298 /* Static method calls */ 299 300 // All the static methods declared by the interfaces are not reachable from the instance of the implementor class 301 testInvokeNeg(targetClass, thisObject, "staticMethodA", "()I", vm().mirrorOf(RESULT_A), 302 "Static interface methods are not inheritable"); 303 304 testInvokeNeg(targetClass, thisObject, "staticMethodB", "()I", vm().mirrorOf(RESULT_B), 305 "Static interface methods are not inheritable"); 306 307 testInvokeNeg(targetClass, thisObject, "staticMethodC", "()I", vm().mirrorOf(RESULT_B), 308 "Static interface methods are not inheritable"); 309 310 // All the static methods declared by the interfaces are not reachable through the implementor class 311 testInvokeNeg(targetClass, null, "staticMethodA", "()I", vm().mirrorOf(RESULT_A), 312 "Static interface methods are not inheritable"); 313 314 testInvokeNeg(targetClass, null, "staticMethodB", "()I", vm().mirrorOf(RESULT_B), 315 "Static interface methods are not inheritable"); 316 317 testInvokeNeg(targetClass, null, "staticMethodC", "()I", vm().mirrorOf(RESULT_B), 318 "Static interface methods are not inheritable"); 319 } 320 321 private void testInvokePos(ReferenceType targetClass, ObjectReference ref, String methodName, 322 String methodSig, Value value) { 323 testInvokePos(targetClass, ref, methodName, methodSig, value, false); 324 } 325 326 private void testInvokePos(ReferenceType targetClass, ObjectReference ref, String methodName, 327 String methodSig, Value value, boolean virtual) { 328 logInvocation(ref, methodName, methodSig, targetClass); 329 try { 330 invoke(targetClass, ref, methodName, methodSig, value, virtual); 331 System.err.println("--- PASSED"); 332 } catch (Exception e) { 333 System.err.println("--- FAILED"); 334 failure("FAILED: Invocation failed with error message " + e.getLocalizedMessage()); 335 } 336 } 337 338 private void testInvokeNeg(ReferenceType targetClass, ObjectReference ref, String methodName, 339 String methodSig, Value value, String msg) { 340 testInvokeNeg(targetClass, ref, methodName, methodSig, value, msg, false); 341 } 342 343 private void testInvokeNeg(ReferenceType targetClass, ObjectReference ref, String methodName, 344 String methodSig, Value value, String msg, boolean virtual) { 345 logInvocation(ref, methodName, methodSig, targetClass); 346 try { 347 invoke(targetClass, ref, methodName, methodSig, value, virtual); 348 System.err.println("--- FAILED"); 349 failure("FAILED: " + msg); 350 } catch (Exception e) { 351 System.err.println("--- PASSED"); 352 353 } 354 } 355 356 private void invoke(ReferenceType targetClass, ObjectReference ref, String methodName, 357 String methodSig, Value value, boolean virtual) 358 throws Exception { 359 Method method = getMethod(targetClass, methodName, methodSig); 360 if (method == null) { 361 throw new Exception("Can't find method: " + methodName + " for class = " + targetClass); 362 } 363 364 println("Invoking " + (method.isAbstract() ? "abstract " : " ") + "method: " + method); 365 println(method.declaringType().toString()); 366 367 Value returnValue = null; 368 if (ref != null) { 369 if (virtual) { 370 returnValue = invokeVirtual(ref, method); 371 } else { 372 returnValue = invokeInstance(ref, method); 373 } 374 } else { 375 returnValue = invokeStatic(targetClass, method); 376 } 377 378 println(" return val = " + returnValue); 379 // It has to be the same value as what we passed in! 380 if (returnValue.equals(value)) { 381 println(" " + method.name() + " return value matches: " 382 + value); 383 } else { 384 if (value != null) { 385 throw new Exception(method.name() + " returned: " + returnValue + 386 " expected: " + value ); 387 } else { 388 println(" " + method.name() + " return value : " + returnValue); 389 } 390 391 } 392 } 393 394 private Value invokeInstance(ObjectReference ref, Method method) throws Exception { 395 return ref.invokeMethod(mainThread, method, Collections.emptyList(), ObjectReference.INVOKE_NONVIRTUAL); 396 } 397 398 private Value invokeVirtual(ObjectReference ref, Method method) throws Exception { 399 return ref.invokeMethod(mainThread, method, Collections.emptyList(), 0); 400 } 401 402 private Value invokeStatic(ReferenceType refType, Method method) throws Exception { 403 if (refType instanceof ClassType) { 404 return ((ClassType)refType).invokeMethod(mainThread, method, Collections.emptyList(), ObjectReference.INVOKE_NONVIRTUAL); 405 } else { 406 return ((InterfaceType)refType).invokeMethod(mainThread, method, Collections.emptyList(), ObjectReference.INVOKE_NONVIRTUAL); 407 } 408 } 409 410 private Method getMethod(ReferenceType rt, String name, String signature) { 411 if (rt == null) return null; 412 Method m = findMethod(rt, name, signature); 413 if (m == null) { 414 if (rt instanceof ClassType) { 415 for (Object ifc : ((ClassType)rt).interfaces()) { 416 m = getMethod((ReferenceType)ifc, name, signature); 417 if (m != null) { 418 break; 419 } 420 } 421 if (m == null) { 422 m = getMethod(((ClassType)rt).superclass(), name, signature); 423 } else { 424 if (m.isStatic()) { 425 // interface static methods are not inherited 426 m = null; 427 } 428 } 429 } else if (rt instanceof InterfaceType) { 430 for(Object ifc : ((InterfaceType)rt).superinterfaces()) { 431 m = getMethod((ReferenceType)ifc, name, signature); 432 if (m != null) { 433 if (m.isStatic()) { 434 // interface static methods are not inherited 435 m = null; 436 } 437 break; 438 } 439 } 440 } 441 } 442 443 return m; 444 } 445 446 private void logInvocation(ObjectReference ref, String methodName, String methodSig, ReferenceType targetClass) { 447 if (ref != null) { 448 System.err.println("Invoking: " + ref.referenceType().name() + "." + 449 methodName + methodSig + " with target of type " + 450 targetClass.name()); 451 } else { 452 System.err.println("Invoking static : " + targetClass.name() + "." + 453 methodName + methodSig); 454 } 455 } 456 }