| /* |
| * 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.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 = getAnnotation(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 getAnnotation(element, annotationClass) != null; |
| } |
| |
| private static com.android.dex.Annotation getAnnotation( |
| AnnotatedElement element, Class<? extends Annotation> annotationClass) { |
| int annotationSetOffset = getAnnotationSetOffset(element); |
| if (annotationSetOffset == 0) { |
| return null; // no annotation |
| } |
| |
| Class<?> dexClass = getDexClass(element); |
| Dex dex = dexClass.getDex(); |
| Dex.Section setIn = dex.open(annotationSetOffset); // annotation_set_item |
| String annotationInternalName = InternalNames.getInternalName(annotationClass); |
| for (int i = 0, size = setIn.readInt(); i < size; i++) { |
| int annotationOffset = setIn.readInt(); |
| Dex.Section annotationIn = dex.open(annotationOffset); // annotation_item |
| // The internal string name of the annotation is compared here and deliberately not |
| // the value of annotationClass.getTypeIndex(). The annotationClass may have been |
| // defined by a different dex file, which would make the indexes incomparable. |
| com.android.dex.Annotation candidate = annotationIn.readAnnotation(); |
| String candidateInternalName = dex.typeNames().get(candidate.getTypeIndex()); |
| if (candidateInternalName.equals(annotationInternalName)) { |
| return candidate; |
| } |
| } |
| |
| return null; // This set doesn't contain the annotation. |
| } |
| |
| /** |
| * @param element a class, a field, a method or a constructor. |
| */ |
| private static int getAnnotationSetOffset(AnnotatedElement element) { |
| Class<?> dexClass = getDexClass(element); |
| int directoryOffset = dexClass.getDexAnnotationDirectoryOffset(); |
| 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 |
| |
| if (element instanceof Field) { |
| int fieldIndex = ((Field) element).getDexFieldIndex(); |
| for (int i = 0; i < fieldsSize; i++) { |
| int candidateFieldIndex = directoryIn.readInt(); |
| int annotationSetOffset = directoryIn.readInt(); |
| if (candidateFieldIndex == fieldIndex) { |
| return annotationSetOffset; |
| } |
| } |
| // if we were searching for a field then we missed |
| return 0; |
| } |
| |
| // Skip through the fields without reading them and look for constructors or methods. |
| directoryIn.skip(8 * fieldsSize); |
| |
| 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(); |
| } |
| |
| /** |
| * Returns the parameter annotations on {@code member}. |
| */ |
| public static Annotation[][] getParameterAnnotations(Class<?> declaringClass, |
| int methodDexIndex) { |
| Dex dex = declaringClass.getDex(); |
| 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 = declaringClass.getDexAnnotationDirectoryOffset(); |
| 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(); |
| // All lookups of type and string indexes are within the Dex that declares the annotation so |
| // the indexes can be compared directly. |
| Dex dex = annotationClass.getDex(); |
| EncodedValueReader reader = getOnlyAnnotationValue( |
| dex, annotationClass, "Ldalvik/annotation/AnnotationDefault;"); |
| if (reader == null) { |
| return null; |
| } |
| |
| int fieldCount = reader.readAnnotation(); |
| if (reader.getAnnotationType() != annotationClass.getDexTypeIndex()) { |
| throw new AssertionError("annotation value type != annotation class"); |
| } |
| |
| int methodNameIndex = dex.findStringIndex(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<?> getEnclosingClass(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 c.getDexCacheType(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 EncodedValueReader getAnnotationReader( |
| Dex dex, AnnotatedElement element, String annotationName, int expectedFieldCount) { |
| int annotationSetOffset = getAnnotationSetOffset(element); |
| if (annotationSetOffset == 0) { |
| return null; // no annotations on the class |
| } |
| |
| Dex.Section setIn = dex.open(annotationSetOffset); // annotation_set_item |
| com.android.dex.Annotation annotation = null; |
| // TODO: is it better to compute the index of the annotation name in the dex file and check |
| // indices below? |
| 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(); |
| String candidateAnnotationName = dex.typeNames().get(candidate.getTypeIndex()); |
| if (annotationName.equals(candidateAnnotationName)) { |
| annotation = candidate; |
| break; |
| } |
| } |
| if (annotation == null) { |
| return null; // no annotation |
| } |
| |
| EncodedValueReader reader = annotation.getReader(); |
| int fieldCount = reader.readAnnotation(); |
| String readerAnnotationName = dex.typeNames().get(reader.getAnnotationType()); |
| if (!readerAnnotationName.equals(annotationName)) { |
| 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>) context.getDexCacheType(dex, typeIndex); |
| if (!result.isAnnotation()) { |
| throw new IncompatibleClassChangeError("Expected annotation: " + result.getName()); |
| } |
| return result; |
| } catch (NoClassDefFoundError ncdfe) { |
| return null; |
| } |
| } |
| |
| private static AccessibleObject indexToMethod(Class<?> context, Dex dex, int methodIndex) { |
| Class<?> declaringClass = |
| context.getDexCacheType(dex, dex.declaringClassIndexFromMethodIndex(methodIndex)); |
| String name = context.getDexCacheString(dex, dex.nameIndexFromMethodIndex(methodIndex)); |
| short[] types = dex.parameterTypeIndicesFromMethodIndex(methodIndex); |
| Class<?>[] parametersArray = new Class[types.length]; |
| for (int i = 0; i < types.length; i++) { |
| parametersArray[i] = context.getDexCacheType(dex, types[i]); |
| } |
| 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)); |
| } |
| } |
| |
| 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 != context.getDexCacheType(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 fieldName = dex.strings().get(fieldId.getNameIndex()); |
| Field field; |
| try { |
| field = type.getDeclaredField(fieldName); |
| return field.get(null); |
| } catch (NoSuchFieldException e) { |
| NoSuchFieldError error = new NoSuchFieldError(); |
| error.initCause(e); |
| throw error; |
| } catch (IllegalAccessException e) { |
| IllegalAccessError error = new IllegalAccessError(); |
| error.initCause(e); |
| throw error; |
| } |
| } 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 context.getDexCacheString(dex, index); |
| } else if (type == Class.class) { |
| int index = reader.readType(); |
| return context.getDexCacheType(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); |
| } |
| } |
| } |