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