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 static class TestClassLoader extends ClassLoader { 146 147 @Override 148 protected Class<?> findClass(final String name) { 149 if (!name.equals(TypeWithUnresolvedFieldType.class.getName())) { 150 try { 151 return super.findClass(name); 152 } catch (ClassNotFoundException e) { 153 throw new AssertionError("unexpected: " + e); 154 } 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, "XXXXXXXXXXX"); 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() { 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 } 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 }