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