1 /* 2 * Copyright (c) 2012, 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 * @requires (vm.simpleArch == "x64" | vm.simpleArch == "sparcv9" | vm.simpleArch == "aarch64") 27 * @library ../../../../../ 28 * @modules jdk.vm.ci/jdk.vm.ci.meta 29 * jdk.vm.ci/jdk.vm.ci.runtime 30 * java.base/jdk.internal.misc 31 * @build jdk.vm.ci.runtime.test.TestResolvedJavaMethod 32 * @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI jdk.vm.ci.runtime.test.TestResolvedJavaMethod 33 */ 34 35 package jdk.vm.ci.runtime.test; 36 37 import static org.junit.Assert.assertEquals; 38 import static org.junit.Assert.assertFalse; 39 import static org.junit.Assert.assertNotNull; 40 import static org.junit.Assert.assertTrue; 41 42 import java.lang.annotation.Annotation; 43 import java.lang.annotation.ElementType; 44 import java.lang.annotation.Retention; 45 import java.lang.annotation.RetentionPolicy; 46 import java.lang.annotation.Target; 47 import java.lang.invoke.MethodHandle; 48 import java.lang.reflect.Constructor; 49 import java.lang.reflect.Member; 50 import java.lang.reflect.Method; 51 import java.lang.reflect.Modifier; 52 import java.lang.reflect.Type; 53 import java.util.Arrays; 54 import java.util.HashMap; 55 import java.util.HashSet; 56 import java.util.Map; 57 import java.util.Set; 58 59 import jdk.vm.ci.meta.ConstantPool; 60 import jdk.vm.ci.meta.ExceptionHandler; 61 import jdk.vm.ci.meta.ResolvedJavaMethod; 62 import jdk.vm.ci.meta.ResolvedJavaType; 63 64 import org.junit.Assert; 65 import org.junit.Test; 66 67 /** 68 * Tests for {@link ResolvedJavaMethod}. 69 */ 70 public class TestResolvedJavaMethod extends MethodUniverse { 71 72 public TestResolvedJavaMethod() { 73 } 74 75 /** 76 * @see ResolvedJavaMethod#getCode() 77 */ 78 @Test 79 public void getCodeTest() { 80 for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) { 81 ResolvedJavaMethod m = e.getValue(); 82 byte[] code = m.getCode(); 83 if (code == null) { 84 assertTrue(m.getCodeSize() == 0); 85 } else { 86 if (m.isAbstract()) { 87 assertTrue(code.length == 0); 88 } else if (!m.isNative()) { 89 assertTrue(code.length > 0); 90 } 91 } 92 } 93 } 94 95 /** 96 * @see ResolvedJavaMethod#getCodeSize() 97 */ 98 @Test 99 public void getCodeSizeTest() { 100 for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) { 101 ResolvedJavaMethod m = e.getValue(); 102 int codeSize = m.getCodeSize(); 103 if (m.isAbstract()) { 104 assertTrue(codeSize == 0); 105 } else if (!m.isNative()) { 106 assertTrue(codeSize > 0); 107 } 108 } 109 } 110 111 @Test 112 public void getModifiersTest() { 113 for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) { 114 ResolvedJavaMethod m = e.getValue(); 115 int expected = e.getKey().getModifiers(); 116 int actual = m.getModifiers(); 117 assertEquals(String.format("%s: 0x%x != 0x%x", m, expected, actual), expected, actual); 118 } 119 for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) { 120 ResolvedJavaMethod m = e.getValue(); 121 int expected = e.getKey().getModifiers(); 122 int actual = m.getModifiers(); 123 assertEquals(String.format("%s: 0x%x != 0x%x", m, expected, actual), expected, actual); 124 } 125 } 126 127 /** 128 * @see ResolvedJavaMethod#isClassInitializer() 129 */ 130 @Test 131 public void isClassInitializerTest() { 132 for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) { 133 // Class initializers are hidden from reflection 134 ResolvedJavaMethod m = e.getValue(); 135 assertFalse(m.isClassInitializer()); 136 } 137 for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) { 138 ResolvedJavaMethod m = e.getValue(); 139 assertFalse(m.isClassInitializer()); 140 } 141 } 142 143 @Test 144 public void isConstructorTest() { 145 for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) { 146 ResolvedJavaMethod m = e.getValue(); 147 assertFalse(m.isConstructor()); 148 } 149 for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) { 150 ResolvedJavaMethod m = e.getValue(); 151 assertTrue(m.isConstructor()); 152 } 153 } 154 155 @Test 156 public void isSyntheticTest() { 157 for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) { 158 ResolvedJavaMethod m = e.getValue(); 159 assertEquals(e.getKey().isSynthetic(), m.isSynthetic()); 160 } 161 for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) { 162 ResolvedJavaMethod m = e.getValue(); 163 assertEquals(e.getKey().isSynthetic(), m.isSynthetic()); 164 } 165 } 166 167 @Test 168 public void isBridgeTest() { 169 for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) { 170 ResolvedJavaMethod m = e.getValue(); 171 assertEquals(e.getKey().isBridge(), m.isBridge()); 172 } 173 for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) { 174 ResolvedJavaMethod m = e.getValue(); 175 assertEquals(false, m.isBridge()); 176 } 177 } 178 179 @Test 180 public void isVarArgsTest() { 181 for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) { 182 ResolvedJavaMethod m = e.getValue(); 183 assertEquals(e.getKey().isVarArgs(), m.isVarArgs()); 184 } 185 for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) { 186 ResolvedJavaMethod m = e.getValue(); 187 assertEquals(e.getKey().isVarArgs(), m.isVarArgs()); 188 } 189 } 190 191 @Test 192 public void isSynchronizedTest() { 193 for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) { 194 ResolvedJavaMethod m = e.getValue(); 195 assertEquals(Modifier.isSynchronized(e.getKey().getModifiers()), m.isSynchronized()); 196 } 197 for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) { 198 ResolvedJavaMethod m = e.getValue(); 199 assertEquals(Modifier.isSynchronized(e.getKey().getModifiers()), m.isSynchronized()); 200 } 201 } 202 203 @Test 204 public void canBeStaticallyBoundTest() { 205 for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) { 206 ResolvedJavaMethod m = e.getValue(); 207 assertEquals(m.canBeStaticallyBound(), canBeStaticallyBound(e.getKey())); 208 } 209 for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) { 210 ResolvedJavaMethod m = e.getValue(); 211 assertEquals(m.canBeStaticallyBound(), canBeStaticallyBound(e.getKey())); 212 } 213 } 214 215 private static boolean canBeStaticallyBound(Member method) { 216 int modifiers = method.getModifiers(); 217 return (Modifier.isFinal(modifiers) || Modifier.isPrivate(modifiers) || Modifier.isStatic(modifiers) || Modifier.isFinal(method.getDeclaringClass().getModifiers())) && 218 !Modifier.isAbstract(modifiers); 219 } 220 221 private static String methodWithExceptionHandlers(String p1, Object o2) { 222 try { 223 return p1.substring(100) + o2.toString(); 224 } catch (IndexOutOfBoundsException e) { 225 e.printStackTrace(); 226 } catch (NullPointerException e) { 227 e.printStackTrace(); 228 } catch (RuntimeException e) { 229 e.printStackTrace(); 230 } 231 return null; 232 } 233 234 @Test 235 public void getExceptionHandlersTest() throws NoSuchMethodException { 236 ResolvedJavaMethod method = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("methodWithExceptionHandlers", String.class, Object.class)); 237 ExceptionHandler[] handlers = method.getExceptionHandlers(); 238 assertNotNull(handlers); 239 assertEquals(handlers.length, 3); 240 handlers[0].getCatchType().equals(metaAccess.lookupJavaType(IndexOutOfBoundsException.class)); 241 handlers[1].getCatchType().equals(metaAccess.lookupJavaType(NullPointerException.class)); 242 handlers[2].getCatchType().equals(metaAccess.lookupJavaType(RuntimeException.class)); 243 } 244 245 private static String nullPointerExceptionOnFirstLine(Object o, String ignored) { 246 return o.toString() + ignored; 247 } 248 249 @Test 250 public void asStackTraceElementTest() throws NoSuchMethodException { 251 try { 252 nullPointerExceptionOnFirstLine(null, "ignored"); 253 Assert.fail("should not reach here"); 254 } catch (NullPointerException e) { 255 StackTraceElement expected = e.getStackTrace()[0]; 256 ResolvedJavaMethod method = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("nullPointerExceptionOnFirstLine", Object.class, String.class)); 257 StackTraceElement actual = method.asStackTraceElement(0); 258 assertEquals(expected, actual); 259 } 260 } 261 262 @Test 263 public void getConstantPoolTest() { 264 for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) { 265 ResolvedJavaMethod m = e.getValue(); 266 ConstantPool cp = m.getConstantPool(); 267 assertTrue(cp.length() > 0); 268 } 269 } 270 271 @Retention(RetentionPolicy.RUNTIME) 272 @Target(ElementType.METHOD) 273 @interface TestAnnotation { 274 long value(); 275 } 276 277 @Test 278 @TestAnnotation(value = 1000L) 279 public void getAnnotationTest() throws NoSuchMethodException { 280 ResolvedJavaMethod method = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("getAnnotationTest")); 281 TestAnnotation annotation = method.getAnnotation(TestAnnotation.class); 282 assertNotNull(annotation); 283 assertEquals(1000L, annotation.value()); 284 } 285 286 @Test 287 @TestAnnotation(value = 1000L) 288 public void getAnnotationsTest() throws NoSuchMethodException { 289 ResolvedJavaMethod method = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("getAnnotationsTest")); 290 Annotation[] annotations = method.getAnnotations(); 291 assertNotNull(annotations); 292 assertEquals(2, annotations.length); 293 TestAnnotation annotation = null; 294 for (Annotation a : annotations) { 295 if (a instanceof TestAnnotation) { 296 annotation = (TestAnnotation) a; 297 break; 298 } 299 } 300 assertNotNull(annotation); 301 assertEquals(1000L, annotation.value()); 302 } 303 304 @Retention(RetentionPolicy.RUNTIME) 305 @Target(ElementType.PARAMETER) 306 @interface NonNull { 307 } 308 309 @Retention(RetentionPolicy.RUNTIME) 310 @Target(ElementType.PARAMETER) 311 @interface Special { 312 } 313 314 private static native void methodWithAnnotatedParameters(@NonNull HashMap<String, String> p1, @Special @NonNull Class<? extends Annotation> p2); 315 316 @Test 317 public void getParameterAnnotationsTest() throws NoSuchMethodException { 318 ResolvedJavaMethod method = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("methodWithAnnotatedParameters", HashMap.class, Class.class)); 319 Annotation[][] annotations = method.getParameterAnnotations(); 320 assertEquals(2, annotations.length); 321 assertEquals(1, annotations[0].length); 322 assertEquals(NonNull.class, annotations[0][0].annotationType()); 323 assertEquals(2, annotations[1].length); 324 assertEquals(Special.class, annotations[1][0].annotationType()); 325 assertEquals(NonNull.class, annotations[1][1].annotationType()); 326 } 327 328 @Test 329 public void getGenericParameterTypesTest() throws NoSuchMethodException { 330 ResolvedJavaMethod method = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("methodWithAnnotatedParameters", HashMap.class, Class.class)); 331 Type[] genericParameterTypes = method.getGenericParameterTypes(); 332 assertEquals(2, genericParameterTypes.length); 333 assertEquals("java.util.HashMap<java.lang.String, java.lang.String>", genericParameterTypes[0].toString()); 334 assertEquals("java.lang.Class<? extends java.lang.annotation.Annotation>", genericParameterTypes[1].toString()); 335 } 336 337 @Test 338 public void getMaxLocalsTest() throws NoSuchMethodException { 339 ResolvedJavaMethod method1 = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("methodWithAnnotatedParameters", HashMap.class, Class.class)); 340 ResolvedJavaMethod method2 = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("nullPointerExceptionOnFirstLine", Object.class, String.class)); 341 assertEquals(0, method1.getMaxLocals()); 342 assertEquals(2, method2.getMaxLocals()); 343 344 } 345 346 @Test 347 public void getMaxStackSizeTest() throws NoSuchMethodException { 348 ResolvedJavaMethod method1 = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("methodWithAnnotatedParameters", HashMap.class, Class.class)); 349 ResolvedJavaMethod method2 = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("nullPointerExceptionOnFirstLine", Object.class, String.class)); 350 assertEquals(0, method1.getMaxStackSize()); 351 // some versions of javac produce bytecode with a stacksize of 2 for this method 352 // JSR 292 also sometimes need one more stack slot 353 int method2StackSize = method2.getMaxStackSize(); 354 assertTrue(2 <= method2StackSize && method2StackSize <= 4); 355 } 356 357 @Test 358 public void isDefaultTest() { 359 for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) { 360 ResolvedJavaMethod m = e.getValue(); 361 assertEquals(e.getKey().isDefault(), m.isDefault()); 362 } 363 for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) { 364 ResolvedJavaMethod m = e.getValue(); 365 assertFalse(m.isDefault()); 366 } 367 } 368 369 @Test 370 public void hasReceiverTest() { 371 for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) { 372 ResolvedJavaMethod m = e.getValue(); 373 assertTrue(m.hasReceiver() != Modifier.isStatic(e.getKey().getModifiers())); 374 } 375 for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) { 376 ResolvedJavaMethod m = e.getValue(); 377 assertTrue(m.hasReceiver()); 378 } 379 } 380 381 @Test 382 public void hasBytecodesTest() { 383 for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) { 384 ResolvedJavaMethod m = e.getValue(); 385 assertTrue(m.hasBytecodes() == (m.isConcrete() && !m.isNative())); 386 } 387 for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) { 388 ResolvedJavaMethod m = e.getValue(); 389 assertTrue(m.hasBytecodes()); 390 } 391 } 392 393 @Test 394 public void isJavaLangObjectInitTest() throws NoSuchMethodException { 395 ResolvedJavaMethod method = metaAccess.lookupJavaMethod(Object.class.getConstructor()); 396 assertTrue(method.isJavaLangObjectInit()); 397 for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) { 398 ResolvedJavaMethod m = e.getValue(); 399 assertFalse(m.isJavaLangObjectInit()); 400 } 401 for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) { 402 ResolvedJavaMethod m = e.getValue(); 403 Constructor<?> key = e.getKey(); 404 if (key.getDeclaringClass() == Object.class && key.getParameters().length == 0) { 405 assertTrue(m.isJavaLangObjectInit()); 406 } else { 407 assertFalse(m.isJavaLangObjectInit()); 408 } 409 } 410 } 411 412 @Test 413 public void isSignaturePolymorphicTest() { 414 ResolvedJavaType methodHandleType = metaAccess.lookupJavaType(MethodHandle.class); 415 assertTrue(ResolvedJavaMethod.isSignaturePolymorphic(methodHandleType, "invokeExact", metaAccess)); 416 assertTrue(ResolvedJavaMethod.isSignaturePolymorphic(methodHandleType, "invoke", metaAccess)); 417 assertTrue(ResolvedJavaMethod.isSignaturePolymorphic(methodHandleType, "invokeBasic", metaAccess)); 418 assertTrue(ResolvedJavaMethod.isSignaturePolymorphic(methodHandleType, "linkToVirtual", metaAccess)); 419 assertTrue(ResolvedJavaMethod.isSignaturePolymorphic(methodHandleType, "linkToStatic", metaAccess)); 420 assertTrue(ResolvedJavaMethod.isSignaturePolymorphic(methodHandleType, "linkToSpecial", metaAccess)); 421 assertTrue(ResolvedJavaMethod.isSignaturePolymorphic(methodHandleType, "linkToInterface", metaAccess)); 422 assertFalse(ResolvedJavaMethod.isSignaturePolymorphic(methodHandleType, "type", metaAccess)); 423 assertFalse(ResolvedJavaMethod.isSignaturePolymorphic(metaAccess.lookupJavaType(Object.class), "toString", metaAccess)); 424 } 425 426 private Method findTestMethod(Method apiMethod) { 427 String testName = apiMethod.getName() + "Test"; 428 for (Method m : getClass().getDeclaredMethods()) { 429 if (m.getName().equals(testName) && m.getAnnotation(Test.class) != null) { 430 return m; 431 } 432 } 433 return null; 434 } 435 436 // @formatter:off 437 private static final String[] untestedApiMethods = { 438 "invoke", 439 "newInstance", 440 "getDeclaringClass", 441 "getEncoding", 442 "getProfilingInfo", 443 "reprofile", 444 "getCompilerStorage", 445 "canBeInlined", 446 "shouldBeInlined", 447 "getLineNumberTable", 448 "getLocalVariableTable", 449 "isInVirtualMethodTable", 450 "toParameterTypes", 451 "getParameterAnnotation", 452 "getSpeculationLog", 453 "isFinal", 454 "$jacocoInit" 455 }; 456 // @formatter:on 457 458 /** 459 * Ensures that any new methods added to {@link ResolvedJavaMethod} either have a test written 460 * for them or are added to {@link #untestedApiMethods}. 461 */ 462 @Test 463 public void testCoverage() { 464 Set<String> known = new HashSet<>(Arrays.asList(untestedApiMethods)); 465 for (Method m : ResolvedJavaMethod.class.getDeclaredMethods()) { 466 if (Modifier.isStatic(m.getModifiers())) { 467 continue; 468 } 469 if (findTestMethod(m) == null) { 470 assertTrue("test missing for " + m, known.contains(m.getName())); 471 } else { 472 assertFalse("test should be removed from untestedApiMethods" + m, known.contains(m.getName())); 473 } 474 } 475 } 476 }