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