1 /*
   2  * Copyright (c) 2012, 2019, 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.jvmci
  27  * @library ../../../../../
  28  * @ignore Not supported JVMCI API
  29  * @modules jdk.internal.vm.ci/jdk.vm.ci.meta
  30  *          jdk.internal.vm.ci/jdk.vm.ci.runtime
  31  *          java.base/jdk.internal.misc
  32  * @run junit/othervm -XX:+UnlockExperimentalVMOptions -XX:+EnableJVMCI -Djvmci.Compiler=null jdk.vm.ci.runtime.test.TestResolvedJavaField
  33  */
  34 
  35 package jdk.vm.ci.runtime.test;
  36 
  37 import static org.junit.Assert.assertArrayEquals;
  38 import static org.junit.Assert.assertEquals;
  39 import static org.junit.Assert.assertFalse;
  40 import static org.junit.Assert.assertTrue;
  41 
  42 import java.io.ByteArrayOutputStream;
  43 import java.io.IOException;
  44 import java.io.InputStream;
  45 import java.io.PrintStream;
  46 import java.lang.annotation.Annotation;
  47 import java.lang.reflect.Field;
  48 import java.lang.reflect.Method;
  49 import java.util.Arrays;
  50 import java.util.HashSet;
  51 import java.util.Map;
  52 import java.util.Set;
  53 
  54 import org.junit.Assert;
  55 import org.junit.Test;
  56 
  57 import jdk.vm.ci.meta.ResolvedJavaField;
  58 import jdk.vm.ci.meta.ResolvedJavaMethod;
  59 import jdk.vm.ci.meta.ResolvedJavaType;
  60 import jdk.vm.ci.runtime.test.TestResolvedJavaField.TestClassLoader;
  61 
  62 /**
  63  * Tests for {@link ResolvedJavaField}.
  64  */
  65 public class TestResolvedJavaField extends FieldUniverse {
  66 
  67     public TestResolvedJavaField() {
  68     }
  69 
  70     @Test
  71     public void getModifiersTest() {
  72         for (Map.Entry<Field, ResolvedJavaField> e : fields.entrySet()) {
  73             int expected = e.getKey().getModifiers();
  74             int actual = e.getValue().getModifiers();
  75             assertEquals(expected, actual);
  76         }
  77     }
  78 
  79     @Test
  80     public void isSyntheticTest() {
  81         for (Map.Entry<Field, ResolvedJavaField> e : fields.entrySet()) {
  82             boolean expected = e.getKey().isSynthetic();
  83             boolean actual = e.getValue().isSynthetic();
  84             assertEquals(expected, actual);
  85         }
  86     }
  87 
  88     @Test
  89     public void getAnnotationsTest() {
  90         for (Map.Entry<Field, ResolvedJavaField> e : fields.entrySet()) {
  91             Annotation[] expected = e.getKey().getAnnotations();
  92             Annotation[] actual = e.getValue().getAnnotations();
  93             assertArrayEquals(expected, actual);
  94         }
  95     }
  96 
  97     @Test
  98     public void getAnnotationTest() {
  99         for (Map.Entry<Field, ResolvedJavaField> e : fields.entrySet()) {
 100             for (Annotation expected : e.getKey().getAnnotations()) {
 101                 if (expected != null) {
 102                     Annotation actual = e.getValue().getAnnotation(expected.annotationType());
 103                     assertEquals(expected, actual);
 104                 }
 105             }
 106         }
 107     }
 108 
 109     private Method findTestMethod(Method apiMethod) {
 110         String testName = apiMethod.getName() + "Test";
 111         for (Method m : getClass().getDeclaredMethods()) {
 112             if (m.getName().equals(testName) && m.getAnnotation(Test.class) != null) {
 113                 return m;
 114             }
 115         }
 116         return null;
 117     }
 118 
 119     // @formatter:off
 120     private static final String[] untestedApiMethods = {
 121         "getDeclaringClass",
 122         "getOffset",
 123         "isInternal",
 124         "isFinal"
 125     };
 126     // @formatter:on
 127 
 128     /**
 129      * Ensures that any new methods added to {@link ResolvedJavaMethod} either have a test written
 130      * for them or are added to {@link #untestedApiMethods}.
 131      */
 132     @Test
 133     public void testCoverage() {
 134         Set<String> known = new HashSet<>(Arrays.asList(untestedApiMethods));
 135         for (Method m : ResolvedJavaField.class.getDeclaredMethods()) {
 136             if (m.isSynthetic()) {
 137                 continue;
 138             }
 139             if (findTestMethod(m) == null) {
 140                 assertTrue("test missing for " + m, known.contains(m.getName()));
 141             } else {
 142                 assertFalse("test should be removed from untestedApiMethods" + m, known.contains(m.getName()));
 143             }
 144         }
 145     }
 146 
 147     private static final String NON_EXISTENT_CLASS_NAME = "XXXXXXXXXXX";
 148 
 149     static class TestClassLoader extends ClassLoader {
 150 
 151         @Override
 152         protected Class<?> findClass(final String name) throws ClassNotFoundException {
 153             if (!name.equals(TypeWithUnresolvedFieldType.class.getName())) {
 154                 return super.findClass(name);
 155             }
 156             // copy classfile to byte array
 157             byte[] classData = null;
 158             try {
 159                 String simpleName = TypeWithUnresolvedFieldType.class.getSimpleName();
 160                 InputStream is = TypeWithUnresolvedFieldType.class.getResourceAsStream(simpleName + ".class");
 161                 assert is != null;
 162                 ByteArrayOutputStream baos = new ByteArrayOutputStream();
 163 
 164                 byte[] buf = new byte[1024];
 165                 int size;
 166                 while ((size = is.read(buf, 0, buf.length)) != -1) {
 167                     baos.write(buf, 0, size);
 168                 }
 169                 baos.flush();
 170                 classData = baos.toByteArray();
 171             } catch (IOException e) {
 172                 Assert.fail("can't access class: " + name);
 173             }
 174 
 175             // replace all occurrences of "PrintStream" in classfile
 176             int index = -1;
 177 
 178             while ((index = indexOf(classData, index + 1, "PrintStream")) != -1) {
 179                 replace(classData, index, NON_EXISTENT_CLASS_NAME);
 180             }
 181 
 182             Class<?> c = defineClass(null, classData, 0, classData.length);
 183             return c;
 184         }
 185 
 186         private static int indexOf(byte[] b, int index, String find) {
 187             for (int i = index; i < b.length; i++) {
 188                 boolean match = true;
 189                 for (int j = i; j < i + find.length(); j++) {
 190                     if (b[j] != (byte) find.charAt(j - i)) {
 191                         match = false;
 192                         break;
 193                     }
 194                 }
 195                 if (match) {
 196                     return i;
 197                 }
 198             }
 199             return -1;
 200         }
 201 
 202         private static void replace(byte[] b, int index, String replace) {
 203             for (int i = index; i < index + replace.length(); i++) {
 204                 b[i] = (byte) replace.charAt(i - index);
 205             }
 206         }
 207     }
 208 
 209     /**
 210      * Tests that calling {@link ResolvedJavaField#getType()} does not cause a linkage error if the
 211      * type of the field is not resolvable.
 212      */
 213     @Test
 214     public void testGetType() throws ClassNotFoundException {
 215         Class<?> c = new TestClassLoader().findClass(TypeWithUnresolvedFieldType.class.getName());
 216         ResolvedJavaType type = metaAccess.lookupJavaType(c);
 217         for (ResolvedJavaField field : type.getInstanceFields(false)) {
 218             assertTrue(field.getName().equals("fieldWithUnresolvableType"));
 219             field.getType();
 220             field.toString();
 221             field.getAnnotations();
 222         }
 223     }
 224 }
 225 
 226 class TypeWithUnresolvedFieldType {
 227     /**
 228      * {@link TestClassLoader} will rewrite the type of this field to "Ljava/io/XXXXXXXXXXX;".
 229      */
 230     PrintStream fieldWithUnresolvableType;
 231 }