1 /* 2 * Copyright (c) 2012, 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 * @requires vm.jvmci 27 * @library ../../../../../ 28 * @modules jdk.internal.vm.ci/jdk.vm.ci.meta 29 * jdk.internal.vm.ci/jdk.vm.ci.runtime 30 * java.base/jdk.internal.misc 31 * @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI jdk.vm.ci.runtime.test.TestResolvedJavaMethod 32 */ 33 34 package jdk.vm.ci.runtime.test; 35 36 import static org.junit.Assert.assertArrayEquals; 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.reflect.Constructor; 48 import java.lang.reflect.Member; 49 import java.lang.reflect.Method; 50 import java.lang.reflect.Modifier; 51 import java.lang.reflect.Type; 52 import java.util.Arrays; 53 import java.util.HashMap; 54 import java.util.HashSet; 55 import java.util.Map; 56 import java.util.Set; 57 58 import org.junit.Assert; 59 import org.junit.Test; 60 61 import jdk.vm.ci.meta.ConstantPool; 62 import jdk.vm.ci.meta.ExceptionHandler; 63 import jdk.vm.ci.meta.ResolvedJavaMethod; 64 import jdk.vm.ci.meta.ResolvedJavaMethod.Parameter; 65 import jdk.vm.ci.meta.ResolvedJavaType; 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 @Test 272 public void getParametersTest() { 273 for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) { 274 java.lang.reflect.Parameter[] expected = e.getKey().getParameters(); 275 Parameter[] actual = e.getValue().getParameters(); 276 assertEquals(actual.length, expected.length); 277 for (int i = 0; i < actual.length; i++) { 278 java.lang.reflect.Parameter exp = expected[i]; 279 Parameter act = actual[i]; 280 assertEquals(exp.getName(), act.getName()); 281 assertEquals(exp.isNamePresent(), act.isNamePresent()); 282 assertEquals(exp.getModifiers(), act.getModifiers()); 283 assertArrayEquals(exp.getAnnotations(), act.getAnnotations()); 284 assertEquals(exp.getType().getName(), act.getType().toClassName()); 285 assertEquals(exp.getParameterizedType(), act.getParameterizedType()); 286 assertEquals(metaAccess.lookupJavaMethod(exp.getDeclaringExecutable()), act.getDeclaringMethod()); 287 } 288 } 289 } 290 291 @Retention(RetentionPolicy.RUNTIME) 292 @Target(ElementType.METHOD) 293 @interface TestAnnotation { 294 long value(); 295 } 296 297 @Test 298 @TestAnnotation(value = 1000L) 299 public void getAnnotationTest() throws NoSuchMethodException { 300 ResolvedJavaMethod method = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("getAnnotationTest")); 301 TestAnnotation annotation = method.getAnnotation(TestAnnotation.class); 302 assertNotNull(annotation); 303 assertEquals(1000L, annotation.value()); 304 } 305 306 @Test 307 @TestAnnotation(value = 1000L) 308 public void getAnnotationsTest() throws NoSuchMethodException { 309 ResolvedJavaMethod method = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("getAnnotationsTest")); 310 Annotation[] annotations = method.getAnnotations(); 311 assertNotNull(annotations); 312 assertEquals(2, annotations.length); 313 TestAnnotation annotation = null; 314 for (Annotation a : annotations) { 315 if (a instanceof TestAnnotation) { 316 annotation = (TestAnnotation) a; 317 break; 318 } 319 } 320 assertNotNull(annotation); 321 assertEquals(1000L, annotation.value()); 322 } 323 324 @Retention(RetentionPolicy.RUNTIME) 325 @Target(ElementType.PARAMETER) 326 @interface NonNull { 327 } 328 329 @Retention(RetentionPolicy.RUNTIME) 330 @Target(ElementType.PARAMETER) 331 @interface Special { 332 } 333 334 private static native void methodWithAnnotatedParameters(@NonNull HashMap<String, String> p1, @Special @NonNull Class<? extends Annotation> p2); 335 336 @Test 337 public void getParameterAnnotationsTest() throws NoSuchMethodException { 338 ResolvedJavaMethod method = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("methodWithAnnotatedParameters", HashMap.class, Class.class)); 339 Annotation[][] annotations = method.getParameterAnnotations(); 340 assertEquals(2, annotations.length); 341 assertEquals(1, annotations[0].length); 342 assertEquals(NonNull.class, annotations[0][0].annotationType()); 343 assertEquals(2, annotations[1].length); 344 assertEquals(Special.class, annotations[1][0].annotationType()); 345 assertEquals(NonNull.class, annotations[1][1].annotationType()); 346 } 347 348 @Test 349 public void getGenericParameterTypesTest() throws NoSuchMethodException { 350 ResolvedJavaMethod method = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("methodWithAnnotatedParameters", HashMap.class, Class.class)); 351 Type[] genericParameterTypes = method.getGenericParameterTypes(); 352 assertEquals(2, genericParameterTypes.length); 353 assertEquals("java.util.HashMap<java.lang.String, java.lang.String>", genericParameterTypes[0].toString()); 354 assertEquals("java.lang.Class<? extends java.lang.annotation.Annotation>", genericParameterTypes[1].toString()); 355 } 356 357 @Test 358 public void getMaxLocalsTest() throws NoSuchMethodException { 359 ResolvedJavaMethod method1 = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("methodWithAnnotatedParameters", HashMap.class, Class.class)); 360 ResolvedJavaMethod method2 = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("nullPointerExceptionOnFirstLine", Object.class, String.class)); 361 assertEquals(0, method1.getMaxLocals()); 362 assertEquals(2, method2.getMaxLocals()); 363 364 } 365 366 @Test 367 public void getMaxStackSizeTest() throws NoSuchMethodException { 368 ResolvedJavaMethod method1 = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("methodWithAnnotatedParameters", HashMap.class, Class.class)); 369 ResolvedJavaMethod method2 = metaAccess.lookupJavaMethod(getClass().getDeclaredMethod("nullPointerExceptionOnFirstLine", Object.class, String.class)); 370 assertEquals(0, method1.getMaxStackSize()); 371 // some versions of javac produce bytecode with a stacksize of 2 for this method 372 // JSR 292 also sometimes need one more stack slot 373 int method2StackSize = method2.getMaxStackSize(); 374 assertTrue(2 <= method2StackSize && method2StackSize <= 4); 375 } 376 377 @Test 378 public void isDefaultTest() { 379 for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) { 380 ResolvedJavaMethod m = e.getValue(); 381 assertEquals(e.getKey().isDefault(), m.isDefault()); 382 } 383 for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) { 384 ResolvedJavaMethod m = e.getValue(); 385 assertFalse(m.isDefault()); 386 } 387 } 388 389 @Test 390 public void hasReceiverTest() { 391 for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) { 392 ResolvedJavaMethod m = e.getValue(); 393 assertTrue(m.hasReceiver() != Modifier.isStatic(e.getKey().getModifiers())); 394 } 395 for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) { 396 ResolvedJavaMethod m = e.getValue(); 397 assertTrue(m.hasReceiver()); 398 } 399 } 400 401 @Test 402 public void hasBytecodesTest() { 403 for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) { 404 ResolvedJavaMethod m = e.getValue(); 405 assertTrue(m.hasBytecodes() == (m.isConcrete() && !m.isNative())); 406 } 407 for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) { 408 ResolvedJavaMethod m = e.getValue(); 409 assertTrue(m.hasBytecodes()); 410 } 411 } 412 413 @Test 414 public void isJavaLangObjectInitTest() throws NoSuchMethodException { 415 ResolvedJavaMethod method = metaAccess.lookupJavaMethod(Object.class.getConstructor()); 416 assertTrue(method.isJavaLangObjectInit()); 417 for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) { 418 ResolvedJavaMethod m = e.getValue(); 419 assertFalse(m.isJavaLangObjectInit()); 420 } 421 for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) { 422 ResolvedJavaMethod m = e.getValue(); 423 Constructor<?> key = e.getKey(); 424 if (key.getDeclaringClass() == Object.class && key.getParameters().length == 0) { 425 assertTrue(m.isJavaLangObjectInit()); 426 } else { 427 assertFalse(m.isJavaLangObjectInit()); 428 } 429 } 430 } 431 432 /** 433 * All public non-final methods should be available in the vtable. 434 */ 435 @Test 436 public void testVirtualMethodTableAccess() { 437 for (Class<?> c : classes) { 438 if (c.isPrimitive() || c.isInterface()) { 439 continue; 440 } 441 ResolvedJavaType receiverType = metaAccess.lookupJavaType(c); 442 for (Method m : c.getMethods()) { 443 ResolvedJavaMethod method = metaAccess.lookupJavaMethod(m); 444 if (!method.isStatic() && !method.isFinal() && !method.getDeclaringClass().isLeaf() && !method.getDeclaringClass().isInterface()) { 445 assertTrue(method + " not available in " + receiverType, method.isInVirtualMethodTable(receiverType)); 446 } 447 } 448 } 449 } 450 451 private Method findTestMethod(Method apiMethod) { 452 String testName = apiMethod.getName() + "Test"; 453 for (Method m : getClass().getDeclaredMethods()) { 454 if (m.getName().equals(testName) && m.getAnnotation(Test.class) != null) { 455 return m; 456 } 457 } 458 return null; 459 } 460 461 // @formatter:off 462 private static final String[] untestedApiMethods = { 463 "newInstance", 464 "getDeclaringClass", 465 "getEncoding", 466 "getProfilingInfo", 467 "reprofile", 468 "getCompilerStorage", 469 "hasNeverInlineDirective", 470 "canBeInlined", 471 "shouldBeInlined", 472 "getLineNumberTable", 473 "getLocalVariableTable", 474 "isInVirtualMethodTable", 475 "toParameterTypes", 476 "getParameterAnnotation", 477 "getSpeculationLog", 478 "isFinal", 479 "$jacocoInit" 480 }; 481 // @formatter:on 482 483 /** 484 * Ensures that any new methods added to {@link ResolvedJavaMethod} either have a test written 485 * for them or are added to {@link #untestedApiMethods}. 486 */ 487 @Test 488 public void testCoverage() { 489 Set<String> known = new HashSet<>(Arrays.asList(untestedApiMethods)); 490 for (Method m : ResolvedJavaMethod.class.getDeclaredMethods()) { 491 if (Modifier.isStatic(m.getModifiers())) { 492 continue; 493 } 494 if (findTestMethod(m) == null) { 495 assertTrue("test missing for " + m, known.contains(m.getName())); 496 } else { 497 assertFalse("test should be removed from untestedApiMethods" + m, known.contains(m.getName())); 498 } 499 } 500 } 501 }