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