diff options
Diffstat (limited to 'libart/src/main/java/libcore')
5 files changed, 1775 insertions, 0 deletions
diff --git a/libart/src/main/java/libcore/reflect/AnnotationAccess.java b/libart/src/main/java/libcore/reflect/AnnotationAccess.java new file mode 100644 index 0000000..fe740de --- /dev/null +++ b/libart/src/main/java/libcore/reflect/AnnotationAccess.java @@ -0,0 +1,794 @@ +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package libcore.reflect; + +import com.android.dex.ClassDef; +import com.android.dex.Dex; +import com.android.dex.EncodedValueReader; +import com.android.dex.FieldId; +import com.android.dex.MethodId; +import com.android.dex.ProtoId; +import com.android.dex.TypeList; +import java.lang.annotation.Annotation; +import java.lang.annotation.Inherited; +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.AnnotatedElement; +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Member; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import libcore.util.EmptyArray; + +/** + * Look up annotations from a dex file. + */ +public final class AnnotationAccess { + private AnnotationAccess() { + } + + /* + * Classes like arrays, primitives and proxies will not have a Dex file. + * Such classes never have annotations. + */ + + private static final Class<?>[] NO_ARGUMENTS = null; + @SuppressWarnings("unused") + private static final byte VISIBILITY_BUILD = 0x00; + private static final byte VISIBILITY_RUNTIME = 0x01; + @SuppressWarnings("unused") + private static final byte VISIBILITY_SYSTEM = 0x02; + + /* + * Class annotations. This includes declared class annotations plus + * annotations on the superclass that have @Inherited. + */ + + public static <A extends java.lang.annotation.Annotation> A getAnnotation( + Class<?> c, Class<A> annotationType) { + if (annotationType == null) { + throw new NullPointerException("annotationType == null"); + } + + A annotation = getDeclaredAnnotation(c, annotationType); + if (annotation != null) { + return annotation; + } + + if (isInherited(annotationType)) { + for (Class<?> sup = c.getSuperclass(); sup != null; sup = sup.getSuperclass()) { + annotation = getDeclaredAnnotation(sup, annotationType); + if (annotation != null) { + return annotation; + } + } + } + + return null; + } + + /** + * Returns true if {@code annotationType} annotations on the superclass + * apply to subclasses that don't have another annotation of the same + * type. + */ + private static boolean isInherited(Class<? extends Annotation> annotationType) { + return isDeclaredAnnotationPresent(annotationType, Inherited.class); + } + + public static Annotation[] getAnnotations(Class<?> c) { + /* + * We need to get the annotations declared on this class, plus the + * annotations from superclasses that have the "@Inherited" annotation + * set. We create a temporary map to use while we accumulate the + * annotations and convert it to an array at the end. + * + * It's possible to have duplicates when annotations are inherited. + * We use a Map to filter those out. + * + * HashMap might be overkill here. + */ + HashMap<Class<?>, Annotation> map = new HashMap<Class<?>, Annotation>(); + for (Annotation declaredAnnotation : getDeclaredAnnotations(c)) { + map.put(declaredAnnotation.annotationType(), declaredAnnotation); + } + for (Class<?> sup = c.getSuperclass(); sup != null; sup = sup.getSuperclass()) { + for (Annotation declaredAnnotation : getDeclaredAnnotations(sup)) { + Class<? extends Annotation> clazz = declaredAnnotation.annotationType(); + if (!map.containsKey(clazz) && isInherited(clazz)) { + map.put(clazz, declaredAnnotation); + } + } + } + + /* convert annotation values from HashMap to array */ + Collection<Annotation> coll = map.values(); + return coll.toArray(new Annotation[coll.size()]); + } + + /** + * Returns true if {@code c} is annotated by {@code annotationType}. + */ + public static boolean isAnnotationPresent( + Class<?> c, Class<? extends Annotation> annotationType) { + if (annotationType == null) { + throw new NullPointerException("annotationType == null"); + } + + if (isDeclaredAnnotationPresent(c, annotationType)) { + return true; + } + + if (isInherited(annotationType)) { + for (Class<?> sup = c.getSuperclass(); sup != null; sup = sup.getSuperclass()) { + if (isDeclaredAnnotationPresent(sup, annotationType)) { + return true; + } + } + } + + return false; + } + + /* + * Class, Field, Method, Constructor and Parameter annotations + */ + + /** + * Returns the annotations on {@code element}. + */ + public static List<Annotation> getDeclaredAnnotations(AnnotatedElement element) { + int offset = getAnnotationSetOffset(element); + return annotationSetToAnnotations(getDexClass(element), offset); + } + + /** + * Returns the annotation if it exists. + */ + public static <A extends Annotation> A getDeclaredAnnotation( + AnnotatedElement element, Class<A> annotationClass) { + com.android.dex.Annotation a = getMethodAnnotation(element, annotationClass); + return a != null + ? toAnnotationInstance(getDexClass(element), annotationClass, a) + : null; + } + + /** + * Returns true if the annotation exists. + */ + public static boolean isDeclaredAnnotationPresent( + AnnotatedElement element, Class<? extends Annotation> annotationClass) { + return getMethodAnnotation(element, annotationClass) != null; + } + + private static com.android.dex.Annotation getMethodAnnotation( + AnnotatedElement element, Class<? extends Annotation> annotationClass) { + Class<?> dexClass = getDexClass(element); + Dex dex = dexClass.getDex(); + int annotationTypeIndex = getTypeIndex(dex, annotationClass); + if (annotationTypeIndex == -1) { + return null; // the dex file doesn't use this annotation + } + + int annotationSetOffset = getAnnotationSetOffset(element); + return getAnnotationFromAnnotationSet(dex, annotationSetOffset, annotationTypeIndex); + } + + /** + * @param element a class, a field, a method or a constructor. + */ + private static int getAnnotationSetOffset(AnnotatedElement element) { + Class<?> dexClass = getDexClass(element); + int directoryOffset = getDirectoryOffset(dexClass); + if (directoryOffset == 0) { + return 0; // nothing on this class has annotations + } + + Dex.Section directoryIn = dexClass.getDex().open(directoryOffset); + int classSetOffset = directoryIn.readInt(); + if (element instanceof Class) { + return classSetOffset; + } + + int fieldsSize = directoryIn.readInt(); + int methodsSize = directoryIn.readInt(); + directoryIn.readInt(); // parameters size + + int fieldIndex = element instanceof Field ? ((Field) element).getDexFieldIndex() : -1; + for (int i = 0; i < fieldsSize; i++) { + int candidateFieldIndex = directoryIn.readInt(); + int annotationSetOffset = directoryIn.readInt(); + if (candidateFieldIndex == fieldIndex) { + return annotationSetOffset; + } + } + // we must read all fields prior to methods, if we were searching for a field then we missed + if (element instanceof Field) { + return 0; + } + + int methodIndex= element instanceof Method ? ((Method) element).getDexMethodIndex() + : ((Constructor<?>) element).getDexMethodIndex(); + for (int i = 0; i < methodsSize; i++) { + int candidateMethodIndex = directoryIn.readInt(); + int annotationSetOffset = directoryIn.readInt(); + if (candidateMethodIndex == methodIndex) { + return annotationSetOffset; + } + } + + return 0; + } + + /** + * Returns {@code element} if it is a class; and the class declaring + * {@code element} otherwise. The dex file of the returned class also + * defines {@code element}. + */ + private static Class<?> getDexClass(AnnotatedElement element) { + return element instanceof Class + ? ((Class<?>) element) + : ((Member) element).getDeclaringClass(); + } + + public static int getFieldIndex(Class<?> declaringClass, Class<?> type, String name) { + Dex dex = declaringClass.getDex(); + int declaringClassIndex = getTypeIndex(dex, declaringClass); + int typeIndex = getTypeIndex(dex, type); + int nameIndex = getStringIndex(dex, name); + FieldId fieldId = new FieldId(dex, declaringClassIndex, typeIndex, nameIndex); + return Collections.binarySearch(dex.fieldIds(), fieldId); + } + + public static int getMethodIndex(Class<?> declaringClass, String name, int protoIndex) { + Dex dex = declaringClass.getDex(); + int declaringClassIndex = getTypeIndex(dex, declaringClass); + int nameIndex = getStringIndex(dex, name); + MethodId methodId = new MethodId(dex, declaringClassIndex, protoIndex, nameIndex); + return Collections.binarySearch(dex.methodIds(), methodId); + } + + /** + * Returns the parameter annotations on {@code member}. + */ + public static Annotation[][] getParameterAnnotations(Member member) { + Class<?> declaringClass = member.getDeclaringClass(); + Dex dex = declaringClass.getDex(); + int methodDexIndex; + if (member instanceof Method) { + methodDexIndex = ((Method) member).getDexMethodIndex(); + } else { + methodDexIndex = ((Constructor<?>) member).getDexMethodIndex(); + } + int protoIndex = dex.methodIds().get(methodDexIndex).getProtoIndex(); + ProtoId proto = dex.protoIds().get(protoIndex); + TypeList parametersList = dex.readTypeList(proto.getParametersOffset()); + short[] types = parametersList.getTypes(); + int typesCount = types.length; + + int directoryOffset = getDirectoryOffset(declaringClass); + if (directoryOffset == 0) { + return new Annotation[typesCount][0]; // nothing on this class has annotations + } + + Dex.Section directoryIn = dex.open(directoryOffset); + directoryIn.readInt(); // class annotations + int fieldsSize = directoryIn.readInt(); + int methodsSize = directoryIn.readInt(); + int parametersSize = directoryIn.readInt(); + + for (int i = 0; i < fieldsSize; i++) { + directoryIn.readInt(); // field_index + directoryIn.readInt(); // annotation_set + } + + for (int i = 0; i < methodsSize; i++) { + directoryIn.readInt(); // method_index + directoryIn.readInt(); // annotation_set + } + + for (int i = 0; i < parametersSize; i++) { + int candidateMethodDexIndex = directoryIn.readInt(); + int annotationSetRefListOffset = directoryIn.readInt(); + if (candidateMethodDexIndex != methodDexIndex) { + continue; + } + + Dex.Section refList = dex.open(annotationSetRefListOffset); + int parameterCount = refList.readInt(); + Annotation[][] result = new Annotation[parameterCount][]; + for (int p = 0; p < parameterCount; p++) { + int annotationSetOffset = refList.readInt(); + List<Annotation> annotations + = annotationSetToAnnotations(declaringClass, annotationSetOffset); + result[p] = annotations.toArray(new Annotation[annotations.size()]); + } + return result; + } + + return new Annotation[typesCount][0]; + } + + /* + * System annotations. + */ + + public static Object getDefaultValue(Method method) { + /* + * Dex represents this with @AnnotationDefault on annotations that have + * default values: + * + * @AnnotationDefault(value=@Foo(a=7)) + * public @interface Foo { + * int a() default 7; + * int b(); + * } + */ + + Class<?> annotationClass = method.getDeclaringClass(); + Dex dex = annotationClass.getDex(); + EncodedValueReader reader = getOnlyAnnotationValue( + dex, annotationClass, "Ldalvik/annotation/AnnotationDefault;"); + if (reader == null) { + return null; + } + + int fieldCount = reader.readAnnotation(); + if (reader.getAnnotationType() != getTypeIndex(dex, annotationClass)) { + throw new AssertionError("annotation value type != annotation class"); + } + + int methodNameIndex = Collections.binarySearch(dex.strings(), method.getName()); + for (int i = 0; i < fieldCount; i++) { + int candidateNameIndex = reader.readAnnotationName(); + if (candidateNameIndex == methodNameIndex) { + Class<?> returnType = method.getReturnType(); + return decodeValue(annotationClass, returnType, dex, reader); + } else { + reader.skipValue(); + } + } + + return null; + } + + /** + * Returns the class of which {@code c} is a direct member. If {@code c} is + * defined in a method or constructor, this is not transitive. + */ + public static Class<?> getDeclaringClass(Class<?> c) { + /* + * public class Bar { + * @EnclosingClass(value=Bar) + * public class Foo {} + * } + */ + Dex dex = c.getDex(); + EncodedValueReader reader = getOnlyAnnotationValue( + dex, c, "Ldalvik/annotation/EnclosingClass;"); + if (reader == null) { + return null; + } + return indexToType(c, dex, reader.readType()); + } + + public static AccessibleObject getEnclosingMethodOrConstructor(Class<?> c) { + /* + * public class Bar { + * public void quux(String s, int i) { + * @EnclosingMethod(value=Bar.quux(String,int)) + * class Foo {} + * } + * } + */ + Dex dex = c.getDex(); + EncodedValueReader reader = getOnlyAnnotationValue( + dex, c, "Ldalvik/annotation/EnclosingMethod;"); + if (reader == null) { + return null; + } + return indexToMethod(c, dex, reader.readMethod()); + } + + public static Class<?>[] getMemberClasses(Class<?> c) { + /* + * @MemberClasses(value=[Bar, Baz]) + * public class Foo { + * class Bar {} + * class Baz {} + * } + */ + Dex dex = c.getDex(); + EncodedValueReader reader = getOnlyAnnotationValue( + dex, c, "Ldalvik/annotation/MemberClasses;"); + if (reader == null) { + return EmptyArray.CLASS; + } + return (Class[]) decodeValue(c, Class[].class, dex, reader); + } + + /** + * @param element a class, a field, a method or a constructor. + */ + public static String getSignature(AnnotatedElement element) { + /* + * @Signature(value=["Ljava/util/List", "<", "Ljava/lang/String;", ">;"]) + * List<String> foo; + */ + Class<?> dexClass = getDexClass(element); + Dex dex = dexClass.getDex(); + EncodedValueReader reader = getOnlyAnnotationValue( + dex, element, "Ldalvik/annotation/Signature;"); + if (reader == null) { + return null; + } + String[] array = (String[]) decodeValue(dexClass, String[].class, dex, reader); + StringBuilder result = new StringBuilder(); + for (String s : array) { + result.append(s); + } + return result.toString(); + } + + /** + * @param element a method or a constructor. + */ + public static Class<?>[] getExceptions(AnnotatedElement element) { + /* + * @Throws(value=[IOException.class]) + * void foo() throws IOException; + */ + Class<?> dexClass = getDexClass(element); + Dex dex = dexClass.getDex(); + EncodedValueReader reader = getOnlyAnnotationValue( + dex, element, "Ldalvik/annotation/Throws;"); + if (reader == null) { + return EmptyArray.CLASS; + } + return (Class<?>[]) decodeValue(dexClass, Class[].class, dex, reader); + } + + public static int getInnerClassFlags(Class<?> c, int defaultValue) { + /* + * @InnerClass(accessFlags=0x01,name="Foo") + * class Foo {}; + */ + Dex dex = c.getDex(); + EncodedValueReader reader = getAnnotationReader( + dex, c, "Ldalvik/annotation/InnerClass;", 2); + if (reader == null) { + return defaultValue; + } + reader.readAnnotationName(); // accessFlags + return reader.readInt(); + } + + public static String getInnerClassName(Class<?> c) { + /* + * @InnerClass(accessFlags=0x01,name="Foo") + * class Foo {}; + */ + Dex dex = c.getDex(); + EncodedValueReader reader = getAnnotationReader( + dex, c, "Ldalvik/annotation/InnerClass;", 2); + if (reader == null) { + return null; + } + reader.readAnnotationName(); // accessFlags + reader.readInt(); + reader.readAnnotationName(); // name + return reader.peek() == EncodedValueReader.ENCODED_NULL + ? null + : (String) decodeValue(c, String.class, dex, reader); + } + + public static boolean isAnonymousClass(Class<?> c) { + /* + * @InnerClass(accessFlags=0x01,name="Foo") + * class Foo {}; + */ + Dex dex = c.getDex(); + EncodedValueReader reader = getAnnotationReader( + dex, c, "Ldalvik/annotation/InnerClass;", 2); + if (reader == null) { + return false; + } + reader.readAnnotationName(); // accessFlags + reader.readInt(); + reader.readAnnotationName(); // name + return reader.peek() == EncodedValueReader.ENCODED_NULL; + } + + /* + * Dex support. + * + * Different classes come from different Dex files. This class is careful + * to guarantee that Dex-relative indices and encoded values are interpreted + * using the Dex that they were read from. Methods that use Dex-relative + * values accept that Dex as a parameter or the class from which that Dex + * was derived. + */ + + private static int getTypeIndex(Dex dex, Class<?> c) { + return dex == c.getDex() ? c.getTypeIndex() : computeTypeIndex(dex, c); + } + + public static int computeTypeIndex(Dex dex, Class<?> c) { + if (dex == null) { + return -1; + } + int typeIndex = Collections.binarySearch(dex.typeNames(), InternalNames.getInternalName(c)); + if (typeIndex < 0) { + typeIndex = -1; + } + return typeIndex; + } + + private static int getStringIndex(Dex dex, String string) { + return Collections.binarySearch(dex.strings(), string); + } + + private static int getDirectoryOffset(Class<?> c) { + return c.getAnnotationDirectoryOffset(); + } + + private static com.android.dex.Annotation getAnnotationFromAnnotationSet( + Dex dex, int annotationSetOffset, int annotationType) { + if (annotationSetOffset == 0) { + return null; // no annotation + } + + Dex.Section setIn = dex.open(annotationSetOffset); // annotation_set_item + for (int i = 0, size = setIn.readInt(); i < size; i++) { + int annotationOffset = setIn.readInt(); + Dex.Section annotationIn = dex.open(annotationOffset); // annotation_item + com.android.dex.Annotation candidate = annotationIn.readAnnotation(); + if (candidate.getTypeIndex() == annotationType) { + return candidate; + } + } + + return null; // this set doesn't carry the annotation + } + + private static EncodedValueReader getAnnotationReader( + Dex dex, AnnotatedElement element, String annotationName, int expectedFieldCount) { + int annotationSetOffset = getAnnotationSetOffset(element); + if (annotationSetOffset == 0) { + return null; // no annotations on the class + } + + int annotationTypeIndex = Collections.binarySearch(dex.typeNames(), annotationName); + com.android.dex.Annotation annotation = getAnnotationFromAnnotationSet( + dex, annotationSetOffset, annotationTypeIndex); + if (annotation == null) { + return null; // no annotation + } + + EncodedValueReader reader = annotation.getReader(); + int fieldCount = reader.readAnnotation(); + if (reader.getAnnotationType() != annotationTypeIndex) { + throw new AssertionError(); + } + if (fieldCount != expectedFieldCount) { + return null; // not the expected values on this annotation; give up + } + + return reader; + } + + /** + * Returns a reader ready to read the only value of the annotation on + * {@code element}, or null if that annotation doesn't exist. + */ + private static EncodedValueReader getOnlyAnnotationValue( + Dex dex, AnnotatedElement element, String annotationName) { + EncodedValueReader reader = getAnnotationReader(dex, element, annotationName, 1); + if (reader == null) { + return null; + } + reader.readAnnotationName(); // skip the name + return reader; + } + + private static Class<? extends Annotation> getAnnotationClass(Class<?> context, Dex dex, + int typeIndex) { + try { + @SuppressWarnings("unchecked") // we do a runtime check + Class<? extends Annotation> result = (Class<? extends Annotation>) indexToType(context, + dex, typeIndex); + if (!result.isAnnotation()) { + throw new IncompatibleClassChangeError("Expected annotation: " + result.getName()); + } + return result; + } catch (NoClassDefFoundError ncdfe) { + return null; + } + } + + private static Class<?> indexToType(Class<?> context, Dex dex, int typeIndex) { + String internalName = dex.typeNames().get(typeIndex); + return InternalNames.getClass(context.getClassLoader(), internalName); + } + + private static AccessibleObject indexToMethod(Class<?> context, Dex dex, int methodIndex) { + MethodId methodId = dex.methodIds().get(methodIndex); + Class<?> declaringClass = indexToType(context, dex, methodId.getDeclaringClassIndex()); + String name = dex.strings().get(methodId.getNameIndex()); + Class<?>[] parametersArray = protoIndexToParameters(context, dex, methodId.getProtoIndex()); + try { + return name.equals("<init>") + ? declaringClass.getDeclaredConstructor(parametersArray) + : declaringClass.getDeclaredMethod(name, parametersArray); + } catch (NoSuchMethodException e) { + throw new IncompatibleClassChangeError("Couldn't find " + declaringClass.getName() + + "." + name + Arrays.toString(parametersArray)); + } + } + + public static Class<?>[] protoIndexToParameters(Class<?> context, Dex dex, int protoIndex) { + ProtoId proto = dex.protoIds().get(protoIndex); + TypeList parametersList = dex.readTypeList(proto.getParametersOffset()); + short[] types = parametersList.getTypes(); + Class<?>[] parametersArray = new Class[types.length]; + for (int i = 0; i < types.length; i++) { + parametersArray[i] = indexToType(context, dex, types[i]); + } + return parametersArray; + } + + public static Class<?>[] typeIndexToInterfaces(Class<?> context, Dex dex, int typeIndex) { + ClassDef def = getClassDef(dex, typeIndex); + if (def == null) { + return EmptyArray.CLASS; + } + short[] interfaces = def.getInterfaces(); + Class<?>[] result = new Class<?>[interfaces.length]; + for (int i = 0; i < interfaces.length; i++) { + result[i] = indexToType(context, dex, interfaces[i]); + } + return result; + } + + public static int typeIndexToAnnotationDirectoryOffset(Dex dex, int typeIndex) { + ClassDef def = getClassDef(dex, typeIndex); + return def == null ? 0 : def.getAnnotationsOffset(); + } + + private static ClassDef getClassDef(Dex dex, int typeIndex) { + if (typeIndex == -1) { + return null; + } + for (ClassDef def : dex.classDefs()) { + if (def.getTypeIndex() == typeIndex) { + return def; + } + } + throw new AssertionError(); + } + + private static List<Annotation> annotationSetToAnnotations(Class<?> context, int offset) { + if (offset == 0) { + return Collections.emptyList(); // no annotations in the set + } + + Dex dex = context.getDex(); + Dex.Section setIn = dex.open(offset); // annotation_set_item + int size = setIn.readInt(); + List<Annotation> result = new ArrayList<Annotation>(size); + + for (int i = 0; i < size; i++) { + int annotationOffset = setIn.readInt(); + Dex.Section annotationIn = dex.open(annotationOffset); // annotation_item + com.android.dex.Annotation annotation = annotationIn.readAnnotation(); + if (annotation.getVisibility() != VISIBILITY_RUNTIME) { + continue; + } + Class<? extends Annotation> annotationClass = + getAnnotationClass(context, dex, annotation.getTypeIndex()); + if (annotationClass != null) { + result.add(toAnnotationInstance(context, dex, annotationClass, annotation.getReader())); + } + } + return result; + } + + private static <A extends Annotation> A toAnnotationInstance(Class<?> context, + Class<A> annotationClass, com.android.dex.Annotation annotation) { + return toAnnotationInstance(context, context.getDex(), annotationClass, + annotation.getReader()); + } + + private static <A extends Annotation> A toAnnotationInstance(Class<?> context, Dex dex, + Class<A> annotationClass, EncodedValueReader reader) { + int fieldCount = reader.readAnnotation(); + if (annotationClass != indexToType(context, dex, reader.getAnnotationType())) { + throw new AssertionError("annotation value type != return type"); + } + AnnotationMember[] members = new AnnotationMember[fieldCount]; + for (int i = 0; i < fieldCount; i++) { + int name = reader.readAnnotationName(); + String nameString = dex.strings().get(name); + Method method; + try { + method = annotationClass.getMethod(nameString, NO_ARGUMENTS); + } catch (NoSuchMethodException e) { + throw new IncompatibleClassChangeError( + "Couldn't find " + annotationClass.getName() + "." + nameString); + } + Class<?> returnType = method.getReturnType(); + Object value = decodeValue(context, returnType, dex, reader); + members[i] = new AnnotationMember(nameString, value, returnType, method); + } + return AnnotationFactory.createAnnotation(annotationClass, members); + } + + private static Object decodeValue(Class<?> context, Class<?> type, + Dex dex, EncodedValueReader reader) { + if (type.isArray()) { + int size = reader.readArray(); + Class<?> componentType = type.getComponentType(); + Object array = Array.newInstance(componentType, size); + for (int i = 0; i < size; i++) { + Array.set(array, i, decodeValue(context, componentType, dex, reader)); + } + return array; + } else if (type.isEnum()) { + int fieldIndex = reader.readEnum(); + FieldId fieldId = dex.fieldIds().get(fieldIndex); + String enumName = dex.strings().get(fieldId.getNameIndex()); + @SuppressWarnings({"unchecked", "rawtypes"}) // Class.isEnum is the runtime check + Class<? extends Enum> enumType = (Class<? extends Enum>) type; + return Enum.valueOf(enumType, enumName); + } else if (type.isAnnotation()) { + @SuppressWarnings("unchecked") // Class.isAnnotation is the runtime check + Class<? extends Annotation> annotationClass = (Class<? extends Annotation>) type; + return toAnnotationInstance(context, dex, annotationClass, reader); + } else if (type == String.class) { + int index = reader.readString(); + return dex.strings().get(index); + } else if (type == Class.class) { + int index = reader.readType(); + return indexToType(context, dex, index); + } else if (type == byte.class) { + return reader.readByte(); + } else if (type == short.class) { + return reader.readShort(); + } else if (type == int.class) { + return reader.readInt(); + } else if (type == long.class) { + return reader.readLong(); + } else if (type == float.class) { + return reader.readFloat(); + } else if (type == double.class) { + return reader.readDouble(); + } else if (type == char.class) { + return reader.readChar(); + } else if (type == boolean.class) { + return reader.readBoolean(); + } else { + // is null legit? + throw new AssertionError("Unexpected annotation value type: " + type); + } + } +} diff --git a/libart/src/main/java/libcore/reflect/AnnotationFactory.java b/libart/src/main/java/libcore/reflect/AnnotationFactory.java new file mode 100644 index 0000000..f14a9e8 --- /dev/null +++ b/libart/src/main/java/libcore/reflect/AnnotationFactory.java @@ -0,0 +1,314 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package libcore.reflect; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.lang.annotation.Annotation; +import java.lang.annotation.IncompleteAnnotationException; +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; + +/** + * The annotation implementation based on dynamically generated proxy instances. + * It conforms to all requirements stated in public APIs, see in particular + * {@link java.lang.reflect.AnnotatedElement java.lang.reflect.AnnotatedElement} + * and {@link java.lang.annotation.Annotation java.lang.annotation.Annotation}. + * Namely, annotation instances are immutable and serializable; they provide + * conforming access to annotation member values and required implementations of + * methods declared in Annotation interface. + * + * @see AnnotationMember + * @see java.lang.annotation.Annotation + * + * @author Alexey V. Varlamov, Serguei S. Zapreyev + * @version $Revision$ + */ +@SuppressWarnings({"serial"}) +public final class AnnotationFactory implements InvocationHandler, Serializable { + + private static final transient + Map<Class<? extends Annotation>, AnnotationMember[]> + cache = new WeakHashMap<Class<? extends Annotation>, AnnotationMember[]>(); + + /** + * Reflects specified annotation type and returns an array + * of member element definitions with default values. + */ + public static AnnotationMember[] getElementsDescription(Class<? extends Annotation> annotationType) { + AnnotationMember[] desc = cache.get(annotationType); + if (desc == null) { + if (!annotationType.isAnnotation()) { + throw new IllegalArgumentException("Type is not annotation: " + + annotationType.getName()); + } + Method[] m = annotationType.getDeclaredMethods(); + desc = new AnnotationMember[m.length]; + int idx = 0; + for (Method element : m) { + String name = element.getName(); + Class<?> type = element.getReturnType(); + try { + desc[idx] = new AnnotationMember(name, + element.getDefaultValue(), type, element); + } catch (Throwable t) { + desc[idx] = new AnnotationMember(name, t, type, element); + } + idx++; + } + cache.put(annotationType, desc); + } + return desc; + } + + /** + * Provides a new annotation instance. + * @param annotationType the annotation type definition + * @param elements name-value pairs representing elements of the annotation + * @return a new annotation instance + */ + @SuppressWarnings("unchecked") // newProxyInstance returns the type of its interfaces + public static <A extends Annotation> A createAnnotation( + Class<A> annotationType, AnnotationMember[] elements) { + AnnotationFactory factory = new AnnotationFactory(annotationType, elements); + return (A) Proxy.newProxyInstance( annotationType.getClassLoader(), + new Class[]{annotationType}, factory); + } + + private final Class<? extends Annotation> klazz; + private AnnotationMember[] elements; + + /** + * New instances should not be created directly, use factory method + * {@link #createAnnotation(Class, AnnotationMember[]) createAnnotation()} + * instead. + * + * @param klzz class defining the annotation type + * @param values actual element values + */ + private AnnotationFactory(Class<? extends Annotation> klzz, AnnotationMember[] values) { + klazz = klzz; + AnnotationMember[] defs = getElementsDescription(klazz); + if (values == null) { + elements = defs; + } else { + //merge default and actual values + elements = new AnnotationMember[defs.length]; + next: for (int i = elements.length - 1; i >= 0; i-- ){ + for (AnnotationMember val : values){ + if (val.name.equals(defs[i].name)) { + elements[i] = val.setDefinition(defs[i]); + continue next; + } + } + elements[i] = defs[i]; + } + } + } + + /** + * Reads the object, obtains actual member definitions for the annotation type, + * and merges deserialized values with the new definitions. + */ + private void readObject(ObjectInputStream os) throws IOException, + ClassNotFoundException { + os.defaultReadObject(); + // Annotation type members can be changed arbitrarily + // So there may be zombi elements from the previous life; + // they hardly fit into this new annotation's incarnation, + // as we have no defining methods for them. + // Reasonably just drop such elements, + // but seems better to keep them for compatibility + AnnotationMember[] defs = getElementsDescription(klazz); + AnnotationMember[] old = elements; + List<AnnotationMember> merged = new ArrayList<AnnotationMember>( + defs.length + old.length); + nextOld: for (AnnotationMember el1 : old) { + for (AnnotationMember el2 : defs) { + if (el2.name.equals(el1.name)) { + continue nextOld; + } + } + merged.add(el1); //phantom element + } + nextNew: for (AnnotationMember def : defs){ + for (AnnotationMember val : old){ + if (val.name.equals(def.name)) { + // nothing to do about cached errors (if any) + // anyway they remain relevant to values + merged.add(val.setDefinition(def)); + continue nextNew; + } + } + merged.add(def); // brand new element + } + elements = merged.toArray(new AnnotationMember[merged.size()]); + } + + /** + * Returns true if the specified object represents the same annotation instance. + * That is, if it implements the same annotation type and + * returns the same element values. + * <br>Note, actual underlying implementation mechanism does not matter - it may + * differ completely from this class. + * @return true if the passed object is equivalent annotation instance, + * false otherwise. + * @see AnnotationMember#equals(Object) + */ + public boolean equals(Object obj) { + if (obj == this) { + return true; + } + if (!klazz.isInstance(obj)) { + return false; + } + Object handler = null; + if (Proxy.isProxyClass(obj.getClass()) + && (handler = Proxy.getInvocationHandler(obj)) instanceof AnnotationFactory ) { + AnnotationFactory other = (AnnotationFactory) handler; + if (elements.length != other.elements.length) { + return false; + } + next: for (AnnotationMember el1 : elements){ + for (AnnotationMember el2 : other.elements) { + if (el1.equals(el2)) { + continue next; + } + } + return false; + } + return true; + } else { + // encountered foreign annotation implementaton + // so have to obtain element values via invocation + // of corresponding methods + for (final AnnotationMember el : elements) { + if (el.tag == AnnotationMember.ERROR) { + // undefined value is incomparable (transcendent) + return false; + } + try { + if (!el.definingMethod.isAccessible()) { + el.definingMethod.setAccessible(true); + } + Object otherValue = el.definingMethod.invoke(obj); + if (otherValue != null ) { + if (el.tag == AnnotationMember.ARRAY) { + if (!el.equalArrayValue(otherValue)) { + return false; + } + } else { + if (!el.value.equals(otherValue)) { + return false; + } + } + } else if (el.value != AnnotationMember.NO_VALUE) { + return false; + } + } catch (Throwable e) { + return false; + } + } + return true; + } + } + + /** + * Returns a hash code composed as a sum of hash codes of member elements, + * including elements with default values. + * @see AnnotationMember#hashCode() + */ + public int hashCode() { + int hash = 0; + for (AnnotationMember element : elements) { + hash += element.hashCode(); + } + return hash; + } + + /** + * Provides detailed description of this annotation instance, + * including all member name-values pairs. + * @return string representation of this annotation + */ + public String toString() { + StringBuilder result = new StringBuilder(); + result.append('@'); + result.append(klazz.getName()); + result.append('('); + for (int i = 0; i < elements.length; ++i) { + if (i != 0) { + result.append(", "); + } + result.append(elements[i]); + } + result.append(')'); + return result.toString(); + } + + /** + * Processes a method invocation request to this annotation instance. + * Recognizes the methods declared in the + * {@link java.lang.annotation.Annotation java.lang.annotation.Annotation} + * interface, and member-defining methods of the implemented annotation type. + * @throws IllegalArgumentException If the specified method is none of the above + * @return the invocation result + */ + public Object invoke(Object proxy, Method method, Object[] args) throws Throwable + { + String name = method.getName(); + Class[] params = method.getParameterTypes(); + if (params.length == 0) { + if ("annotationType".equals(name)) { + return klazz; + } else if ("toString".equals(name)) { + return toString(); + } else if ("hashCode".equals(name)) { + return hashCode(); + } + + // this must be element value request + AnnotationMember element = null; + for (AnnotationMember el : elements) { + if (name.equals(el.name)) { + element = el; + break; + } + } + if (element == null || !method.equals(element.definingMethod)) { + throw new IllegalArgumentException(method.toString()); + } else { + Object value = element.validateValue(); + if (value == null) { + throw new IncompleteAnnotationException(klazz, name); + } + return value; + } + } else if (params.length == 1 && params[0] == Object.class && "equals".equals(name)){ + return Boolean.valueOf(equals(args[0])); + } + throw new IllegalArgumentException( + "Invalid method for annotation type: " + method); + } +} diff --git a/libart/src/main/java/libcore/reflect/GenericSignatureParser.java b/libart/src/main/java/libcore/reflect/GenericSignatureParser.java new file mode 100644 index 0000000..0c94eba --- /dev/null +++ b/libart/src/main/java/libcore/reflect/GenericSignatureParser.java @@ -0,0 +1,496 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package libcore.reflect; + +import java.lang.reflect.Constructor; +import java.lang.reflect.GenericDeclaration; +import java.lang.reflect.GenericSignatureFormatError; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; + +/** + * Implements a parser for the generics signature attribute. + * Uses a top-down, recursive descent parsing approach for the following grammar: + * <pre> + * ClassSignature ::= + * OptFormalTypeParams SuperclassSignature {SuperinterfaceSignature}. + * SuperclassSignature ::= ClassTypeSignature. + * SuperinterfaceSignature ::= ClassTypeSignature. + * + * OptFormalTypeParams ::= + * ["<" FormalTypeParameter {FormalTypeParameter} ">"]. + * + * FormalTypeParameter ::= Ident ClassBound {InterfaceBound}. + * ClassBound ::= ":" [FieldTypeSignature]. + * InterfaceBound ::= ":" FieldTypeSignature. + * + * FieldTypeSignature ::= + * ClassTypeSignature | ArrayTypeSignature | TypeVariableSignature. + * ArrayTypeSignature ::= "[" TypSignature. + * + * ClassTypeSignature ::= + * "L" {Ident "/"} Ident OptTypeArguments {"." Ident OptTypeArguments} ";". + * + * OptTypeArguments ::= "<" TypeArgument {TypeArgument} ">". + * + * TypeArgument ::= ([WildcardIndicator] FieldTypeSignature) | "*". + * WildcardIndicator ::= "+" | "-". + * + * TypeVariableSignature ::= "T" Ident ";". + * + * TypSignature ::= FieldTypeSignature | BaseType. + * BaseType ::= "B" | "C" | "D" | "F" | "I" | "J" | "S" | "Z". + * + * MethodTypeSignature ::= + * OptFormalTypeParams "(" {TypeSignature} ")" ReturnType {ThrowsSignature}. + * ThrowsSignature ::= ("^" ClassTypeSignature) | ("^" TypeVariableSignature). + * + * ReturnType ::= TypSignature | VoidDescriptor. + * VoidDescriptor ::= "V". + * </pre> + */ +public final class GenericSignatureParser { + + // TODO: unify this with InternalNames + + public ListOfTypes exceptionTypes; + public ListOfTypes parameterTypes; + public TypeVariable[] formalTypeParameters; + public Type returnType; + public Type fieldType; + public ListOfTypes interfaceTypes; + public Type superclassType; + public ClassLoader loader; + + GenericDeclaration genericDecl; + + /* + * Parser: + */ + char symbol; // 0: eof; else valid term symbol or first char of identifier. + String identifier; + + + /* + * Scanner: + * eof is private to the scan methods + * and it's set only when a scan is issued at the end of the buffer. + */ + private boolean eof; + + char[] buffer; + int pos; + + public GenericSignatureParser(ClassLoader loader) { + this.loader = loader; + } + + void setInput(GenericDeclaration genericDecl, String input) { + if (input != null) { + this.genericDecl = genericDecl; + this.buffer = input.toCharArray(); + this.eof = false; + scanSymbol(); + } + else { + this.eof = true; + } + } + + /** + * Parses the generic signature of a class and creates the data structure + * representing the signature. + * + * @param genericDecl the GenericDeclaration calling this method + * @param signature the generic signature of the class + */ + public void parseForClass(GenericDeclaration genericDecl, + String signature) { + setInput(genericDecl, signature); + if (!eof) { + parseClassSignature(); + } else { + if(genericDecl instanceof Class) { + Class c = (Class) genericDecl; + this.formalTypeParameters = ListOfVariables.EMPTY; + this.superclassType = c.getSuperclass(); + this.interfaceTypes = new ListOfTypes(c.getInterfaces()); + } else { + this.formalTypeParameters = ListOfVariables.EMPTY; + this.superclassType = Object.class; + this.interfaceTypes = ListOfTypes.EMPTY; + } + } + } + + /** + * Parses the generic signature of a method and creates the data structure + * representing the signature. + * + * @param genericDecl the GenericDeclaration calling this method + * @param signature the generic signature of the class + */ + public void parseForMethod(GenericDeclaration genericDecl, + String signature, Class<?>[] rawExceptionTypes) { + setInput(genericDecl, signature); + if (!eof) { + parseMethodTypeSignature(rawExceptionTypes); + } else { + Method m = (Method) genericDecl; + this.formalTypeParameters = ListOfVariables.EMPTY; + this.parameterTypes = new ListOfTypes(m.getParameterTypes()); + this.exceptionTypes = new ListOfTypes(m.getExceptionTypes()); + this.returnType = m.getReturnType(); + } + } + + /** + * Parses the generic signature of a constructor and creates the data + * structure representing the signature. + * + * @param genericDecl the GenericDeclaration calling this method + * @param signature the generic signature of the class + */ + public void parseForConstructor(GenericDeclaration genericDecl, + String signature, Class<?>[] rawExceptionTypes) { + setInput(genericDecl, signature); + if (!eof) { + parseMethodTypeSignature(rawExceptionTypes); + } else { + Constructor c = (Constructor) genericDecl; + this.formalTypeParameters = ListOfVariables.EMPTY; + this.parameterTypes = new ListOfTypes(c.getParameterTypes()); + this.exceptionTypes = new ListOfTypes(c.getExceptionTypes()); + } + } + + /** + * Parses the generic signature of a field and creates the data structure + * representing the signature. + * + * @param genericDecl the GenericDeclaration calling this method + * @param signature the generic signature of the class + */ + public void parseForField(GenericDeclaration genericDecl, + String signature) { + setInput(genericDecl, signature); + if (!eof) { + this.fieldType = parseFieldTypeSignature(); + } + } + + + // + // Parser: + // + + void parseClassSignature() { + // ClassSignature ::= + // OptFormalTypeParameters SuperclassSignature {SuperinterfaceSignature}. + + parseOptFormalTypeParameters(); + + // SuperclassSignature ::= ClassTypeSignature. + this.superclassType = parseClassTypeSignature(); + + interfaceTypes = new ListOfTypes(16); + while (symbol > 0) { + // SuperinterfaceSignature ::= ClassTypeSignature. + interfaceTypes.add(parseClassTypeSignature()); + } + } + + void parseOptFormalTypeParameters() { + // OptFormalTypeParameters ::= + // ["<" FormalTypeParameter {FormalTypeParameter} ">"]. + + ListOfVariables typeParams = new ListOfVariables(); + + if (symbol == '<') { + scanSymbol(); + typeParams.add(parseFormalTypeParameter()); + while ((symbol != '>') && (symbol > 0)) { + typeParams.add(parseFormalTypeParameter()); + } + expect('>'); + } + this.formalTypeParameters = typeParams.getArray(); + } + + TypeVariableImpl<GenericDeclaration> parseFormalTypeParameter() { + // FormalTypeParameter ::= Ident ClassBound {InterfaceBound}. + + scanIdentifier(); + String name = identifier.intern(); // FIXME: is this o.k.? + + ListOfTypes bounds = new ListOfTypes(8); + + // ClassBound ::= ":" [FieldTypeSignature]. + expect(':'); + if (symbol == 'L' || symbol == '[' || symbol == 'T') { + bounds.add(parseFieldTypeSignature()); + } + + while (symbol == ':') { + // InterfaceBound ::= ":" FieldTypeSignature. + scanSymbol(); + bounds.add(parseFieldTypeSignature()); + } + + return new TypeVariableImpl<GenericDeclaration>(genericDecl, name, bounds); + } + + Type parseFieldTypeSignature() { + // FieldTypeSignature ::= ClassTypeSignature | ArrayTypeSignature + // | TypeVariableSignature. + + switch (symbol) { + case 'L': + return parseClassTypeSignature(); + case '[': + // ArrayTypeSignature ::= "[" TypSignature. + scanSymbol(); + return new GenericArrayTypeImpl(parseTypeSignature()); + case 'T': + return parseTypeVariableSignature(); + default: + throw new GenericSignatureFormatError(); + } + } + + Type parseClassTypeSignature() { + // ClassTypeSignature ::= "L" {Ident "/"} Ident + // OptTypeArguments {"." Ident OptTypeArguments} ";". + + expect('L'); + + StringBuilder qualIdent = new StringBuilder(); + scanIdentifier(); + while (symbol == '/') { + scanSymbol(); + qualIdent.append(identifier).append("."); + scanIdentifier(); + } + + qualIdent.append(this.identifier); + + ListOfTypes typeArgs = parseOptTypeArguments(); + ParameterizedTypeImpl parentType = + new ParameterizedTypeImpl(null, qualIdent.toString(), typeArgs, loader); + ParameterizedTypeImpl type = parentType; + + while (symbol == '.') { + // Deal with Member Classes: + scanSymbol(); + scanIdentifier(); + qualIdent.append("$").append(identifier); // FIXME: is "$" correct? + typeArgs = parseOptTypeArguments(); + type = new ParameterizedTypeImpl(parentType, qualIdent.toString(), typeArgs, + loader); + } + + expect(';'); + + return type; + } + + ListOfTypes parseOptTypeArguments() { + // OptTypeArguments ::= "<" TypeArgument {TypeArgument} ">". + + ListOfTypes typeArgs = new ListOfTypes(8); + if (symbol == '<') { + scanSymbol(); + + typeArgs.add(parseTypeArgument()); + while ((symbol != '>') && (symbol > 0)) { + typeArgs.add(parseTypeArgument()); + } + expect('>'); + } + return typeArgs; + } + + Type parseTypeArgument() { + // TypeArgument ::= (["+" | "-"] FieldTypeSignature) | "*". + ListOfTypes extendsBound = new ListOfTypes(1); + ListOfTypes superBound = new ListOfTypes(1); + if (symbol == '*') { + scanSymbol(); + extendsBound.add(Object.class); + return new WildcardTypeImpl(extendsBound, superBound); + } + else if (symbol == '+') { + scanSymbol(); + extendsBound.add(parseFieldTypeSignature()); + return new WildcardTypeImpl(extendsBound, superBound); + } + else if (symbol == '-') { + scanSymbol(); + superBound.add(parseFieldTypeSignature()); + extendsBound.add(Object.class); + return new WildcardTypeImpl(extendsBound, superBound); + } + else { + return parseFieldTypeSignature(); + } + } + + TypeVariableImpl<GenericDeclaration> parseTypeVariableSignature() { + // TypeVariableSignature ::= "T" Ident ";". + expect('T'); + scanIdentifier(); + expect(';'); + // Reference to type variable: + // Note: we don't know the declaring GenericDeclaration yet. + return new TypeVariableImpl<GenericDeclaration>(genericDecl, identifier); + } + + Type parseTypeSignature() { + switch (symbol) { + case 'B': scanSymbol(); return byte.class; + case 'C': scanSymbol(); return char.class; + case 'D': scanSymbol(); return double.class; + case 'F': scanSymbol(); return float.class; + case 'I': scanSymbol(); return int.class; + case 'J': scanSymbol(); return long.class; + case 'S': scanSymbol(); return short.class; + case 'Z': scanSymbol(); return boolean.class; + default: + // Not an elementary type, but a FieldTypeSignature. + return parseFieldTypeSignature(); + } + } + + /** + * @param rawExceptionTypes the non-generic exceptions. This is necessary + * because the signature may omit the exceptions when none are generic. + * May be null for methods that declare no exceptions. + */ + void parseMethodTypeSignature(Class<?>[] rawExceptionTypes) { + // MethodTypeSignature ::= [FormalTypeParameters] + // "(" {TypeSignature} ")" ReturnType {ThrowsSignature}. + + parseOptFormalTypeParameters(); + + parameterTypes = new ListOfTypes(16); + expect('('); + while (symbol != ')' && (symbol > 0)) { + parameterTypes.add(parseTypeSignature()); + } + expect(')'); + + returnType = parseReturnType(); + + if (symbol == '^') { + exceptionTypes = new ListOfTypes(8); + do { + scanSymbol(); + + // ThrowsSignature ::= ("^" ClassTypeSignature) | + // ("^" TypeVariableSignature). + if (symbol == 'T') { + exceptionTypes.add(parseTypeVariableSignature()); + } else { + exceptionTypes.add(parseClassTypeSignature()); + } + } while (symbol == '^'); + } else if (rawExceptionTypes != null) { + exceptionTypes = new ListOfTypes(rawExceptionTypes); + } else { + exceptionTypes = new ListOfTypes(0); + } + } + + Type parseReturnType() { + // ReturnType ::= TypeSignature | "V". + if (symbol != 'V') { return parseTypeSignature(); } + else { scanSymbol(); return void.class; } + } + + + // + // Scanner: + // + + void scanSymbol() { + if (!eof) { + if (pos < buffer.length) { + symbol = buffer[pos]; + pos++; + } else { + symbol = 0; + eof = true; + } + } else { + throw new GenericSignatureFormatError(); + } + } + + void expect(char c) { + if (symbol == c) { + scanSymbol(); + } else { + throw new GenericSignatureFormatError(); + } + } + + boolean isStopSymbol(char ch) { + switch (ch) { + case ':': + case '/': + case ';': + case '<': + case '.': + return true; + } + return false; + } + + // PRE: symbol is the first char of the identifier. + // POST: symbol = the next symbol AFTER the identifier. + void scanIdentifier() { + if (!eof) { + StringBuilder identBuf = new StringBuilder(32); + if (!isStopSymbol(symbol)) { + identBuf.append(symbol); + do { + char ch = buffer[pos]; + if ((ch >= 'a') && (ch <= 'z') || (ch >= 'A') && (ch <= 'Z') + || !isStopSymbol(ch)) { + identBuf.append(buffer[pos]); + pos++; + } else { + identifier = identBuf.toString(); + scanSymbol(); + return; + } + } while (pos != buffer.length); + identifier = identBuf.toString(); + symbol = 0; + eof = true; + } else { + // Ident starts with incorrect char. + symbol = 0; + eof = true; + throw new GenericSignatureFormatError(); + } + } else { + throw new GenericSignatureFormatError(); + } + } +} diff --git a/libart/src/main/java/libcore/reflect/ListOfVariables.java b/libart/src/main/java/libcore/reflect/ListOfVariables.java new file mode 100644 index 0000000..5d96817 --- /dev/null +++ b/libart/src/main/java/libcore/reflect/ListOfVariables.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package libcore.reflect; + +import java.lang.reflect.TypeVariable; +import java.util.ArrayList; + +final class ListOfVariables { + public static final TypeVariable[] EMPTY = new TypeVariableImpl[0]; + + final ArrayList<TypeVariable<?>> array = new ArrayList<TypeVariable<?>>(); + + void add (TypeVariable<?> elem) { + array.add(elem); + } + + TypeVariable<?>[] getArray() { + TypeVariable<?>[] a = new TypeVariable[array.size()]; + return array.toArray(a); + } +} diff --git a/libart/src/main/java/libcore/reflect/TypeVariableImpl.java b/libart/src/main/java/libcore/reflect/TypeVariableImpl.java new file mode 100644 index 0000000..2eb9827 --- /dev/null +++ b/libart/src/main/java/libcore/reflect/TypeVariableImpl.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package libcore.reflect; + +import java.lang.reflect.AccessibleObject; +import java.lang.reflect.Constructor; +import java.lang.reflect.GenericDeclaration; +import java.lang.reflect.Method; +import java.lang.reflect.Type; +import java.lang.reflect.TypeVariable; + + +public final class TypeVariableImpl<D extends GenericDeclaration> implements TypeVariable<D> { + private TypeVariableImpl<D> formalVar; + private final GenericDeclaration declOfVarUser; + private final String name; + private D genericDeclaration; + private ListOfTypes bounds; + + @Override + public boolean equals(Object o) { + if(!(o instanceof TypeVariable)) { + return false; + } + TypeVariable<?> that = (TypeVariable<?>) o; + return getName().equals(that.getName()) && + getGenericDeclaration().equals(that.getGenericDeclaration()); + } + + + @Override + public int hashCode() { + return 31 * getName().hashCode() + getGenericDeclaration().hashCode(); + } + + /** + * @param genericDecl declaration where a type variable is declared + * @param name type variable name + * @param bounds class and interface bounds + */ + TypeVariableImpl(D genericDecl, String name, ListOfTypes bounds) { + this.genericDeclaration = genericDecl; + this.name = name; + this.bounds = bounds; + this.formalVar = this; + this.declOfVarUser = null; + } + + /** + * @param genericDecl declaration where a type variable is used + * @param name type variable name + */ + TypeVariableImpl(D genericDecl, String name) { + this.name = name; + this.declOfVarUser = genericDecl; + } + + static TypeVariable findFormalVar(GenericDeclaration layer, String name) { + TypeVariable[] formalVars = layer.getTypeParameters(); + for (TypeVariable var : formalVars) { + if (name.equals(var.getName())) { + return var; + } + } + // resolve() looks up the next level only, if null is returned + return null; + } + + private static GenericDeclaration nextLayer(GenericDeclaration decl) { + if (decl instanceof Class) { + // FIXME: Is the following hierarchy correct?: + Class cl = (Class)decl; + decl = (GenericDeclaration) AnnotationAccess.getEnclosingMethodOrConstructor(cl); + if (decl != null) { + return decl; + } + return cl.getEnclosingClass(); + } else if (decl instanceof Method) { + return ((Method)decl).getDeclaringClass(); + } else if (decl instanceof Constructor) { + return ((Constructor)decl).getDeclaringClass(); + } else { + throw new AssertionError(); + } + } + + void resolve() { + if (formalVar != null) { + return; + } + GenericDeclaration curLayer = declOfVarUser; + TypeVariable var; + while ((var = findFormalVar(curLayer, name)) == null) { + curLayer = nextLayer(curLayer); + if (curLayer == null) { + throw new AssertionError("illegal type variable reference"); + } + } + formalVar = (TypeVariableImpl<D>) var; + this.genericDeclaration = formalVar.genericDeclaration; + this.bounds = formalVar.bounds; + } + + public Type[] getBounds() { + resolve(); + return bounds.getResolvedTypes().clone(); + } + + public D getGenericDeclaration() { + resolve(); + return genericDeclaration; + } + + public String getName() { + return name; + } + + @Override + public String toString() { + return name; + } +} |