Mercurial > hg > truffle
diff graal/com.oracle.graal.api.meta.test/src/com/oracle/graal/api/meta/test/TestResolvedJavaType.java @ 7037:dd81042f4eb1
added unit tests for ResolvedJavaType
replaced some CompilerToVM methods used by HotSpotResolvedJavaType with pure Java code
author | Doug Simon <doug.simon@oracle.com> |
---|---|
date | Tue, 27 Nov 2012 11:21:48 +0100 |
parents | |
children | 947de43c68d6 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/graal/com.oracle.graal.api.meta.test/src/com/oracle/graal/api/meta/test/TestResolvedJavaType.java Tue Nov 27 11:21:48 2012 +0100 @@ -0,0 +1,540 @@ +/* + * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package com.oracle.graal.api.meta.test; + +import static com.oracle.graal.api.meta.test.TestMetaAccessProvider.*; +import static java.lang.reflect.Modifier.*; +import static org.junit.Assert.*; + +import java.lang.annotation.*; +import java.lang.reflect.*; +import java.util.*; + +import org.junit.*; + +import sun.reflect.ConstantPool; + +import com.oracle.graal.api.meta.*; + +public class TestResolvedJavaType { + + public TestResolvedJavaType() { + } + + @Test + public void isInterfaceTest() { + for (Class c : classes) { + ResolvedJavaType type = runtime.lookupJavaType(c); + boolean expected = c.isInterface(); + boolean actual = type.isInterface(); + assertEquals(expected, actual); + } + } + + + @Test + public void isInstanceClassTest() { + for (Class c : classes) { + ResolvedJavaType type = runtime.lookupJavaType(c); + boolean expected = !c.isArray() && !c.isPrimitive() && !c.isInterface(); + boolean actual = type.isInstanceClass(); + assertEquals(expected, actual); + } + } + + @Test + public void isArrayClassTest() { + for (Class c : classes) { + ResolvedJavaType type = runtime.lookupJavaType(c); + boolean expected = c.isArray(); + boolean actual = type.isArrayClass(); + assertEquals(expected, actual); + } + } + + @Test + public void getModifiersTest() { + for (Class c : classes) { + ResolvedJavaType type = runtime.lookupJavaType(c); + int expected = c.getModifiers(); + int actual = type.getModifiers(); + assertEquals(expected, actual); + } + } + + @Test + public void isAssignableToTest() { + Class[] all = classes.toArray(new Class[classes.size()]); + for (int i = 0; i < all.length; i++) { + Class<?> c1 = all[i]; + for (int j = i; j < all.length; j++) { + Class<?> c2 = all[j]; + ResolvedJavaType t1 = runtime.lookupJavaType(c1); + ResolvedJavaType t2 = runtime.lookupJavaType(c2); + boolean expected = c1.isAssignableFrom(c2); + boolean actual = t2.isAssignableTo(t1); + assertEquals(expected, actual); + if (expected && t1 != t2) { + assertFalse(t1.isAssignableTo(t2)); + } + } + } + } + + @Test + public void isInstanceTest() { + for (Constant c : constants) { + if (c.getKind().isObject() && !c.isNull()) { + Object o = c.asObject(); + Class< ? extends Object> cls = o.getClass(); + while (cls != null) { + ResolvedJavaType type = runtime.lookupJavaType(cls); + boolean expected = cls.isInstance(o); + boolean actual = type.isInstance(c); + assertEquals(expected, actual); + cls = cls.getSuperclass(); + } + } + } + } + + private static Class asExactClass(Class c) { + if (c.isArray()) { + if (asExactClass(c.getComponentType()) != null) { + return c; + } + } else { + if (c.isPrimitive() || Modifier.isFinal(c.getModifiers())) { + return c; + } + } + return null; + } + + @Test + public void asExactTypeTest() { + for (Class c : classes) { + ResolvedJavaType type = runtime.lookupJavaType(c); + ResolvedJavaType exactType = type.asExactType(); + Class expected = asExactClass(c); + if (expected == null) { + assertTrue("exact(" + c.getName() + ") != null", exactType == null); + } else { + assertNotNull(exactType); + assertTrue(exactType.isClass(expected)); + } + } + } + + @Test + public void getSuperclassTest() { + for (Class c : classes) { + ResolvedJavaType type = runtime.lookupJavaType(c); + Class expected = c.getSuperclass(); + ResolvedJavaType actual = type.getSuperclass(); + if (expected == null) { + assertTrue(actual == null); + } else { + assertNotNull(actual); + assertTrue(actual.isClass(expected)); + } + } + } + + @Test + public void getInterfacesTest() { + for (Class c : classes) { + ResolvedJavaType type = runtime.lookupJavaType(c); + Class[] expected = c.getInterfaces(); + ResolvedJavaType[] actual = type.getInterfaces(); + assertEquals(expected.length, actual.length); + for (int i = 0; i < expected.length; i++) { + assertTrue(actual[i].isClass(expected[i])); + } + } + } + + public static Class getSupertype(Class c) { + assert !c.isPrimitive(); + if (c.isArray()) { + Class componentType = c.getComponentType(); + if (componentType.isPrimitive() || componentType == Object.class) { + return Object.class; + } + return getArrayClass(getSupertype(componentType)); + } + if (c.isInterface()) { + return Object.class; + } + return c.getSuperclass(); + } + + public static Class findLeastCommonAncestor(Class<?> c1Initial, Class<?> c2Initial) { + if (c1Initial.isPrimitive() || c2Initial.isPrimitive()) { + return null; + } else { + Class<?> c1 = c1Initial; + Class<?> c2 = c2Initial; + while (true) { + if (c1.isAssignableFrom(c2)) { + return c1; + } + if (c2.isAssignableFrom(c1)) { + return c2; + } + c1 = getSupertype(c1); + c2 = getSupertype(c2); + } + } + } + + @Test + public void findLeastCommonAncestorTest() { + Class[] all = classes.toArray(new Class[classes.size()]); + for (int i = 0; i < all.length; i++) { + Class<?> c1 = all[i]; + for (int j = i; j < all.length; j++) { + Class<?> c2 = all[j]; + ResolvedJavaType t1 = runtime.lookupJavaType(c1); + ResolvedJavaType t2 = runtime.lookupJavaType(c2); + Class expected = findLeastCommonAncestor(c1, c2); + ResolvedJavaType actual = t1.findLeastCommonAncestor(t2); + if (expected == null) { + assertTrue(actual == null); + } else { + assertNotNull(actual); + assertTrue(actual.isClass(expected)); + } + } + } + } + + private static class Base {} + abstract static class Abstract1 extends Base {} + interface Interface1 {} + static class Concrete1 extends Abstract1 {} + static class Concrete2 extends Abstract1 implements Interface1 {} + static class Concrete3 extends Concrete2 {} + abstract static class Abstract4 extends Concrete3 {} + + static void checkConcreteSubtype(ResolvedJavaType type, Class expected) { + ResolvedJavaType subtype = type.findUniqueConcreteSubtype(); + if (type.isInterface() && subtype == null) { + // A runtime may not record the subtype tree for interfaces in which case + // findUniqueConcreteSubtype() will return null for interfaces. + return; + } + + if (expected == null) { + assertNull(subtype); + } else { + assertTrue(subtype.isClass(expected)); + } + if (!type.isArrayClass()) { + ResolvedJavaType arrayType = type.getArrayClass(); + if (subtype == type) { + assertEquals(arrayType.findUniqueConcreteSubtype(), arrayType); + } else { + assertNull(arrayType.findUniqueConcreteSubtype()); + } + } + } + + @Test + public void findUniqueConcreteSubtypeTest() { + ResolvedJavaType base = runtime.lookupJavaType(Base.class); + checkConcreteSubtype(base, Base.class); + + ResolvedJavaType a1 = runtime.lookupJavaType(Abstract1.class); + ResolvedJavaType c1 = runtime.lookupJavaType(Concrete1.class); + + checkConcreteSubtype(base, null); + checkConcreteSubtype(a1, Concrete1.class); + checkConcreteSubtype(c1, Concrete1.class); + + ResolvedJavaType i1 = runtime.lookupJavaType(Interface1.class); + ResolvedJavaType c2 = runtime.lookupJavaType(Concrete2.class); + + checkConcreteSubtype(base, null); + checkConcreteSubtype(a1, null); + checkConcreteSubtype(c1, Concrete1.class); + checkConcreteSubtype(i1, Concrete2.class); + checkConcreteSubtype(c2, Concrete2.class); + + ResolvedJavaType c3 = runtime.lookupJavaType(Concrete3.class); + checkConcreteSubtype(c2, null); + checkConcreteSubtype(c3, Concrete3.class); + + ResolvedJavaType a4 = runtime.lookupJavaType(Abstract4.class); + checkConcreteSubtype(c3, null); + checkConcreteSubtype(a4, null); + } + + @Test + public void getComponentTypeTest() { + for (Class c : classes) { + ResolvedJavaType type = runtime.lookupJavaType(c); + Class expected = c.getComponentType(); + ResolvedJavaType actual = type.getComponentType(); + if (expected == null) { + assertNull(actual); + } else { + assertTrue(actual.isClass(expected)); + } + } + } + + @Test + public void getArrayClassTest() { + for (Class c : classes) { + if (c != void.class) { + ResolvedJavaType type = runtime.lookupJavaType(c); + Class expected = getArrayClass(c); + ResolvedJavaType actual = type.getArrayClass(); + assertTrue(actual.isClass(expected)); + } + } + } + + static class Declarations { + final Method implementation; + final Set<Method> declarations; + + public Declarations(Method impl) { + this.implementation = impl; + declarations = new HashSet<>(); + } + } + + /** + * See <a href="http://docs.oracle.com/javase/specs/jvms/se7/html/jvms-5.html#jvms-5.4.5">Method overriding</a>. + */ + static boolean isOverriderOf(Method impl, Method m) { + if (!isPrivate(m.getModifiers()) && !isFinal(m.getModifiers())) { + if (m.getName().equals(impl.getName())) { + if (m.getReturnType() == impl.getReturnType()) { + if (Arrays.equals(m.getParameterTypes(), impl.getParameterTypes())) { + if (isPublic(m.getModifiers()) || isProtected(m.getModifiers())) { + // m is public or protected + return isPublic(impl.getModifiers()) || isProtected(impl.getModifiers()); + } else { + // m is package-private + return impl.getDeclaringClass().getPackage() == m.getDeclaringClass().getPackage(); + } + } + } + } + } + return false; + } + + static final Map<Class, VTable> vtables = new HashMap<>(); + + static class NameAndSig { + final String name; + final Class returnType; + final Class[] parameterTypes; + public NameAndSig(Method m) { + this.name = m.getName(); + this.returnType = m.getReturnType(); + this.parameterTypes = m.getParameterTypes(); + } + @Override + public boolean equals(Object obj) { + if (obj instanceof NameAndSig) { + NameAndSig s = (NameAndSig) obj; + return s.returnType == returnType && name.equals(s.name) && Arrays.equals(s.parameterTypes, parameterTypes); + } + return false; + } + + @Override + public int hashCode() { + return name.hashCode(); + } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(name + "("); + String sep = ""; + for (Class p : parameterTypes) { + sb.append(sep); + sep = ", "; + sb.append(p.getName()); + } + return sb.append(')').append(returnType.getName()).toString(); + } + } + + static class VTable { + final Map<NameAndSig, Method> methods = new HashMap<>(); + } + + static synchronized VTable getVTable(Class c) { + VTable vtable = vtables.get(c); + if (vtable == null) { + vtable = new VTable(); + if (c != Object.class) { + VTable superVtable = getVTable(c.getSuperclass()); + vtable.methods.putAll(superVtable.methods); + } + for (Method m : c.getDeclaredMethods()) { + if (!isStatic(m.getModifiers()) && !isPrivate(m.getModifiers())) { + Method overridden = vtable.methods.put(new NameAndSig(m), m); + if (overridden != null) { + //System.out.println(m + " overrides " + overridden); + } + } + } + vtables.put(c, vtable); + } + return vtable; + } + + static Set<Method> findDeclarations(Method impl, Class c) { + Set<Method> declarations = new HashSet<>(); + NameAndSig implSig = new NameAndSig(impl); + if (c != null) { + for (Method m : c.getDeclaredMethods()) { + if (new NameAndSig(m).equals(implSig)) { + declarations.add(m); + break; + } + } + if (!c.isInterface()) { + declarations.addAll(findDeclarations(impl, c.getSuperclass())); + } + for (Class i : c.getInterfaces()) { + declarations.addAll(findDeclarations(impl, i)); + } + } + return declarations; + } + + private static void checkResolveMethod(ResolvedJavaType type, ResolvedJavaMethod decl, ResolvedJavaMethod expected) { + ResolvedJavaMethod impl = type.resolveMethod(decl); + assertEquals(expected, impl); + } + + @Test + public void resolveMethodTest() { + for (Class c : classes) { + if (!c.isPrimitive() && !c.isInterface()) { + ResolvedJavaType type = runtime.lookupJavaType(c); + VTable vtable = getVTable(c); + for (Method impl : vtable.methods.values()) { + Set<Method> decls = findDeclarations(impl, c); + for (Method decl : decls) { + ResolvedJavaMethod m = runtime.lookupJavaMethod(decl); + ResolvedJavaMethod i = runtime.lookupJavaMethod(impl); + checkResolveMethod(type, m, i); + } + } + } + } + } + + @Test + public void findUniqueConcreteMethodTest() { + // TODO + } + + public static Set<Field> getInstanceFields(Class c, boolean includeSuperclasses) { + if (c.isArray() || c.isPrimitive() || c.isInterface()) { + return Collections.emptySet(); + } + Set<Field> result = new HashSet<>(); + for (Field f : c.getDeclaredFields()) { + if (!Modifier.isStatic(f.getModifiers())) { + result.add(f); + } + } + if (includeSuperclasses && c != Object.class) { + result.addAll(getInstanceFields(c.getSuperclass(), true)); + } + return result; + } + + public static boolean containsField(ResolvedJavaField[] fields, Field f) { + for (ResolvedJavaField rf : fields) { + if (rf.getName().equals(f.getName()) && rf.getType().resolve(rf.getDeclaringClass()).isClass(f.getType())) { + assert f.getModifiers() == rf.getModifiers() : f; + return true; + } + } + return false; + } + + public static boolean containsField(Set<Field> fields, ResolvedJavaField rf) { + for (Field f : fields) { + if (f.getName().equals(rf.getName()) && rf.getType().resolve(rf.getDeclaringClass()).isClass(f.getType())) { + assert rf.getModifiers() == f.getModifiers() : rf; + return true; + } + } + return false; + } + + private static boolean isHiddenFromReflection(ResolvedJavaField f) { + if (f.getDeclaringClass().isClass(Throwable.class) && f.getName().equals("backtrace")) { + return true; + } + if (f.getDeclaringClass().isClass(ConstantPool.class) && f.getName().equals("constantPoolOop")) { + return true; + } + return false; + } + + @Test + public void getInstanceFieldsTest() { + for (Class c : classes) { + ResolvedJavaType type = runtime.lookupJavaType(c); + for (boolean includeSuperclasses : new boolean[] {true, false}) { + Set<Field> expected = getInstanceFields(c, includeSuperclasses); + ResolvedJavaField[] actual = type.getInstanceFields(includeSuperclasses); + for (Field f : expected) { + assertTrue(containsField(actual, f)); + } + for (ResolvedJavaField rf : actual) { + if (!isHiddenFromReflection(rf)) { + assertEquals(containsField(expected, rf), !rf.isInternal()); + } + } + + // Test stability of getInstanceFields + ResolvedJavaField[] actual2 = type.getInstanceFields(includeSuperclasses); + assertArrayEquals(actual, actual2); + } + } + } + + @Test + public void getAnnotationTest() { + for (Class c : classes) { + ResolvedJavaType type = runtime.lookupJavaType(c); + for (Annotation a : c.getAnnotations()) { + assertEquals(a, type.getAnnotation(a.annotationType())); + } + } + } +}