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