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