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