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 }