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 }