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