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