summaryrefslogtreecommitdiffstats
path: root/libart/src/main/java/libcore
diff options
context:
space:
mode:
Diffstat (limited to 'libart/src/main/java/libcore')
-rw-r--r--libart/src/main/java/libcore/reflect/AnnotationAccess.java794
-rw-r--r--libart/src/main/java/libcore/reflect/AnnotationFactory.java314
-rw-r--r--libart/src/main/java/libcore/reflect/GenericSignatureParser.java496
-rw-r--r--libart/src/main/java/libcore/reflect/ListOfVariables.java35
-rw-r--r--libart/src/main/java/libcore/reflect/TypeVariableImpl.java136
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;
+ }
+}