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 * @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 201 testInvokePos(ifaceClass, ref, "staticMethodA", "()I", vm().mirrorOf(RESULT_A)); 202 203 // invoke interface static method B 204 testInvokePos(ifaceClass, null, "staticMethodB", "()I", vm().mirrorOf(RESULT_A)); 205 206 // try to invoke static method B on the instance 207 testInvokePos(ifaceClass, ref, "staticMethodB", "()I", vm().mirrorOf(RESULT_A)); 208 209 // try to invoke a virtual method 210 testInvokePos(ifaceClass, ref, "implementedMethod", "()I", vm().mirrorOf(RESULT_A), true); 211 } 212 213 private void testInterfaceB(ObjectReference ref) { 214 // Test non-virtual calls on InterfaceB 215 ReferenceType ifaceClass = (ReferenceType)vm().classesByName(INTERFACEB_NAME).get(0); 216 217 /* Default method calls */ 218 219 // invoke the inherited "defaultMethodA" 220 testInvokePos(ifaceClass, ref, "defaultMethodA", "()I", vm().mirrorOf(RESULT_A)); 221 222 // invoke the inherited "defaultMethodB" 223 testInvokePos(ifaceClass, ref, "defaultMethodB", "()I", vm().mirrorOf(RESULT_A)); 224 225 // invoke the inherited and overridden "defaultMethodC" 226 testInvokePos(ifaceClass, ref, "defaultMethodC", "()I", vm().mirrorOf(RESULT_B)); 227 228 // invoke InterfaceB only "defaultMethodD" 229 testInvokePos(ifaceClass, ref, "defaultMethodD", "()I", vm().mirrorOf(RESULT_B)); 230 231 // "implementedMethod" is not present in InterfaceB 232 testInvokeNeg(ifaceClass, ref, "implementedMethod", "()I", vm().mirrorOf(RESULT_TARGET), 233 "Invocation of non-default methods is not supported"); 234 235 236 /* Static method calls*/ 237 238 // "staticMethodA" must not be inherited by InterfaceB 239 testInvokeNeg(ifaceClass, null, "staticMethodA", "()I", vm().mirrorOf(RESULT_A), 240 "Static interface methods are not inheritable"); 241 242 // however it is possible to call "staticMethodA" on the actual instance 243 testInvokeNeg(ifaceClass, ref, "staticMethodA", "()I", vm().mirrorOf(RESULT_A), 244 "Static interface methods are not inheritable"); 245 246 // "staticMethodB" is overridden in InterfaceB 247 testInvokePos(ifaceClass, null, "staticMethodB", "()I", vm().mirrorOf(RESULT_B)); 248 249 // the instance invokes the overriden form of "staticMethodB" from InterfaceB 250 testInvokePos(ifaceClass, ref, "staticMethodB", "()I", vm().mirrorOf(RESULT_B)); 251 252 // "staticMethodC" is present only in InterfaceB 253 testInvokePos(ifaceClass, null, "staticMethodC", "()I", vm().mirrorOf(RESULT_B)); 254 255 // "staticMethodC" should be reachable from the instance too 256 testInvokePos(ifaceClass, ref, "staticMethodC", "()I", vm().mirrorOf(RESULT_B)); 257 } 258 259 private void testImplementationClass(ReferenceType targetClass, ObjectReference thisObject) { 260 // Test invocations on the implementation object 261 262 /* Default method calls */ 263 264 // "defaultMethodA" is accessible and not overridden 265 testInvokePos(targetClass, thisObject, "defaultMethodA", "()I", vm().mirrorOf(RESULT_TARGET)); 266 267 // "defaultMethodB" is accessible and overridden in TargetClass 268 testInvokePos(targetClass, thisObject, "defaultMethodB", "()I", vm().mirrorOf(RESULT_TARGET)); 269 270 // "defaultMethodC" is accessible and overridden in InterfaceB 271 testInvokePos(targetClass, thisObject, "defaultMethodC", "()I", vm().mirrorOf(RESULT_TARGET)); 272 273 // "defaultMethodD" is accessible 274 testInvokePos(targetClass, thisObject, "defaultMethodD", "()I", vm().mirrorOf(RESULT_TARGET)); 275 276 277 /* Non-default instance method calls */ 278 279 // "classMethod" declared in TargetClass is accessible 280 testInvokePos(targetClass, thisObject, "classMethod", "()I", vm().mirrorOf(RESULT_TARGET)); 281 282 // the abstract "implementedMethod" has been implemented in TargetClass 283 testInvokePos(targetClass, thisObject, "implementedMethod", "()I", vm().mirrorOf(RESULT_TARGET)); 284 285 286 /* Static method calls */ 287 288 // All the static methods declared by the interfaces are not reachable from the instance of the implementor class 289 testInvokeNeg(targetClass, thisObject, "staticMethodA", "()I", vm().mirrorOf(RESULT_A), 290 "Static interface methods are not inheritable"); 291 292 testInvokeNeg(targetClass, thisObject, "staticMethodB", "()I", vm().mirrorOf(RESULT_B), 293 "Static interface methods are not inheritable"); 294 295 testInvokeNeg(targetClass, thisObject, "staticMethodC", "()I", vm().mirrorOf(RESULT_B), 296 "Static interface methods are not inheritable"); 297 298 // All the static methods declared by the interfaces are not reachable through the implementor class 299 testInvokeNeg(targetClass, null, "staticMethodA", "()I", vm().mirrorOf(RESULT_A), 300 "Static interface methods are not inheritable"); 301 302 testInvokeNeg(targetClass, null, "staticMethodB", "()I", vm().mirrorOf(RESULT_B), 303 "Static interface methods are not inheritable"); 304 305 testInvokeNeg(targetClass, null, "staticMethodC", "()I", vm().mirrorOf(RESULT_B), 306 "Static interface methods are not inheritable"); 307 } 308 309 private void testInvokePos(ReferenceType targetClass, ObjectReference ref, String methodName, 310 String methodSig, Value value) { 311 testInvokePos(targetClass, ref, methodName, methodSig, value, false); 312 } 313 314 private void testInvokePos(ReferenceType targetClass, ObjectReference ref, String methodName, 315 String methodSig, Value value, boolean virtual) { 316 logInvocation(ref, methodName, methodSig, targetClass); 317 try { 318 invoke(targetClass, ref, methodName, methodSig, value, virtual); 319 System.err.println("--- PASSED"); 320 } catch (Exception e) { 321 System.err.println("--- FAILED"); 322 failure("FAILED: Invocation failed with error message " + e.getLocalizedMessage()); 323 } 324 } 325 326 private void testInvokeNeg(ReferenceType targetClass, ObjectReference ref, String methodName, 327 String methodSig, Value value, String msg) { 328 testInvokeNeg(targetClass, ref, methodName, methodSig, value, msg, false); 329 } 330 331 private void testInvokeNeg(ReferenceType targetClass, ObjectReference ref, String methodName, 332 String methodSig, Value value, String msg, boolean virtual) { 333 logInvocation(ref, methodName, methodSig, targetClass); 334 try { 335 invoke(targetClass, ref, methodName, methodSig, value, virtual); 336 System.err.println("--- FAILED"); 337 failure("FAILED: " + msg); 338 } catch (Exception e) { 339 System.err.println("--- PASSED"); 340 341 } 342 } 343 344 private void invoke(ReferenceType targetClass, ObjectReference ref, String methodName, 345 String methodSig, Value value, boolean virtual) 346 throws Exception { 347 Method method = getMethod(targetClass, methodName, methodSig); 348 if (method == null) { 349 throw new Exception("Can't find method: " + methodName + " for class = " + targetClass); 350 } 351 352 println("Invoking " + (method.isAbstract() ? "abstract " : " ") + "method: " + method); 353 println(method.declaringType().toString()); 354 355 Value returnValue = null; 356 if (ref != null) { 357 if (virtual) { 358 returnValue = invokeVirtual(ref, method); 359 } else { 360 returnValue = invokeInstance(ref, method); 361 } 362 } else { 363 returnValue = invokeStatic(targetClass, method); 364 } 365 366 println(" return val = " + returnValue); 367 // It has to be the same value as what we passed in! 368 if (returnValue.equals(value)) { 369 println(" " + method.name() + " return value matches: " 370 + value); 371 } else { 372 if (value != null) { 373 throw new Exception(method.name() + " returned: " + returnValue + 374 " expected: " + value ); 375 } else { 376 println(" " + method.name() + " return value : " + returnValue); 377 } 378 379 } 380 } 381 382 private Value invokeInstance(ObjectReference ref, Method method) throws Exception { 383 return ref.invokeMethod(mainThread, method, Collections.emptyList(), ObjectReference.INVOKE_NONVIRTUAL); 384 } 385 386 private Value invokeVirtual(ObjectReference ref, Method method) throws Exception { 387 return ref.invokeMethod(mainThread, method, Collections.emptyList(), 0); 388 } 389 390 private Value invokeStatic(ReferenceType refType, Method method) throws Exception { 391 if (refType instanceof ClassType) { 392 return ((ClassType)refType).invokeMethod(mainThread, method, Collections.emptyList(), ObjectReference.INVOKE_NONVIRTUAL); 393 } else { 394 return ((InterfaceType)refType).invokeMethod(mainThread, method, Collections.emptyList(), ObjectReference.INVOKE_NONVIRTUAL); 395 } 396 } 397 398 private Method getMethod(ReferenceType rt, String name, String signature) { 399 if (rt == null) return null; 400 Method m = findMethod(rt, name, signature); 401 if (m == null) { 402 if (rt instanceof ClassType) { 403 for (Object ifc : ((ClassType)rt).interfaces()) { 404 m = getMethod((ReferenceType)ifc, name, signature); 405 if (m != null) { 406 break; 407 } 408 } 409 if (m == null) { 410 m = getMethod(((ClassType)rt).superclass(), name, signature); 411 } else { 412 if (m.isStatic()) { 413 // interface static methods are not inherited 414 m = null; 415 } 416 } 417 } else if (rt instanceof InterfaceType) { 418 for(Object ifc : ((InterfaceType)rt).superinterfaces()) { 419 m = getMethod((ReferenceType)ifc, name, signature); 420 if (m != null) { 421 if (m.isStatic()) { 422 // interface static methods are not inherited 423 m = null; 424 } 425 break; 426 } 427 } 428 } 429 } 430 431 return m; 432 } 433 434 private void logInvocation(ObjectReference ref, String methodName, String methodSig, ReferenceType targetClass) { 435 if (ref != null) { 436 System.err.println("Invoking: " + ref.referenceType().name() + "." + 437 methodName + methodSig + " with target of type " + 438 targetClass.name()); 439 } else { 440 System.err.println("Invoking static : " + targetClass.name() + "." + 441 methodName + methodSig); 442 } 443 } 444 }