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