/*
* 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.
*/
/*
* Annotations.
*
* We're not expecting to make much use of runtime annotations, so speed vs.
* space choices are weighted heavily toward small size.
*
* It would have been nice to treat "system" annotations in the same way
* we do "real" annotations, but that doesn't work. The chief difficulty
* is that some of them have member types that are not legal in annotations,
* such as Method and Annotation. Another source of pain comes from the
* AnnotationDefault annotation, which by virtue of being an annotation
* could itself have default values, requiring some additional checks to
* prevent recursion.
*
* It's simpler, and more efficient, to handle the system annotations
* entirely inside the VM. There are empty classes defined for the system
* annotation types, but their only purpose is to allow the system
* annotations to share name space with standard annotations.
*/
#include "Dalvik.h"
// fwd
static Object* processEncodedAnnotation(const ClassObject* clazz,\
const u1** pPtr);
static bool skipEncodedAnnotation(const ClassObject* clazz, const u1** pPtr);
/*
* System annotation descriptors.
*/
static const char* kDescrAnnotationDefault
= "Ldalvik/annotation/AnnotationDefault;";
static const char* kDescrEnclosingClass
= "Ldalvik/annotation/EnclosingClass;";
static const char* kDescrEnclosingMethod
= "Ldalvik/annotation/EnclosingMethod;";
static const char* kDescrInnerClass = "Ldalvik/annotation/InnerClass;";
static const char* kDescrMemberClasses
= "Ldalvik/annotation/MemberClasses;";
static const char* kDescrSignature = "Ldalvik/annotation/Signature;";
static const char* kDescrThrows = "Ldalvik/annotation/Throws;";
/*
* Perform Annotation setup.
*/
bool dvmReflectAnnotationStartup(void)
{
Method* meth;
/*
* Find some standard Annotation classes.
*/
gDvm.classJavaLangAnnotationAnnotationArray =
dvmFindArrayClass("[Ljava/lang/annotation/Annotation;", NULL);
gDvm.classJavaLangAnnotationAnnotationArrayArray =
dvmFindArrayClass("[[Ljava/lang/annotation/Annotation;", NULL);
if (gDvm.classJavaLangAnnotationAnnotationArray == NULL ||
gDvm.classJavaLangAnnotationAnnotationArrayArray == NULL)
{
LOGE("Could not find Annotation-array classes\n");
return false;
}
/*
* VM-specific annotation classes.
*/
gDvm.classOrgApacheHarmonyLangAnnotationAnnotationFactory =
dvmFindSystemClassNoInit("Lorg/apache/harmony/lang/annotation/AnnotationFactory;");
gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMember =
dvmFindSystemClassNoInit("Lorg/apache/harmony/lang/annotation/AnnotationMember;");
gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMemberArray =
dvmFindArrayClass("[Lorg/apache/harmony/lang/annotation/AnnotationMember;", NULL);
if (gDvm.classOrgApacheHarmonyLangAnnotationAnnotationFactory == NULL ||
gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMember == NULL ||
gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMemberArray == NULL)
{
LOGE("Could not find android.lang annotation classes\n");
return false;
}
meth = dvmFindDirectMethodByDescriptor(gDvm.classOrgApacheHarmonyLangAnnotationAnnotationFactory,
"createAnnotation",
"(Ljava/lang/Class;[Lorg/apache/harmony/lang/annotation/AnnotationMember;)Ljava/lang/annotation/Annotation;");
if (meth == NULL) {
LOGE("Unable to find createAnnotation() in android AnnotationFactory\n");
return false;
}
gDvm.methOrgApacheHarmonyLangAnnotationAnnotationFactory_createAnnotation = meth;
meth = dvmFindDirectMethodByDescriptor(gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMember,
"<init>",
"(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/reflect/Method;)V");
if (meth == NULL) {
LOGE("Unable to find 4-arg constructor in android AnnotationMember\n");
return false;
}
gDvm.methOrgApacheHarmonyLangAnnotationAnnotationMember_init = meth;
return true;
}
/*
* Read an unsigned LEB128 value from a buffer. Advances "pBuf".
*/
static u4 readUleb128(const u1** pBuf)
{
u4 result = 0;
int shift = 0;
const u1* buf = *pBuf;
u1 val;
do {
/*
* Worst-case on bad data is we read too much data and return a bogus
* result. Safe to assume that we will encounter a byte with its
* high bit clear before the end of the mapped file.
*/
assert(shift < 32);
val = *buf++;
result |= (val & 0x7f) << shift;
shift += 7;
} while ((val & 0x80) != 0);
*pBuf = buf;
return result;
}
/*
* Get the annotations directory item.
*/
static const DexAnnotationsDirectoryItem* getAnnoDirectory(DexFile* pDexFile,
const ClassObject* clazz)
{
const DexClassDef* pClassDef;
/*
* Find the class def in the DEX file. For better performance we should
* stash this in the ClassObject.
*/
pClassDef = dexFindClass(pDexFile, clazz->descriptor);
assert(pClassDef != NULL);
return dexGetAnnotationsDirectoryItem(pDexFile, pClassDef);
}
/*
* Return a zero-length array of Annotation objects.
*
* TODO: this currently allocates a new array each time, but I think we
* can get away with returning a canonical copy.
*
* Caller must call dvmReleaseTrackedAlloc().
*/
static ArrayObject* emptyAnnoArray(void)
{
return dvmAllocArrayByClass(
gDvm.classJavaLangAnnotationAnnotationArray, 0, ALLOC_DEFAULT);
}
/*
* Return an array of empty arrays of Annotation objects.
*
* Caller must call dvmReleaseTrackedAlloc().
*/
static ArrayObject* emptyAnnoArrayArray(int numElements)
{
Thread* self = dvmThreadSelf();
ArrayObject* arr;
int i;
arr = dvmAllocArrayByClass(gDvm.classJavaLangAnnotationAnnotationArrayArray,
numElements, ALLOC_DEFAULT);
if (arr != NULL) {
ArrayObject** elems = (ArrayObject**) arr->contents;
for (i = 0; i < numElements; i++) {
elems[i] = emptyAnnoArray();
dvmReleaseTrackedAlloc((Object*)elems[i], self);
}
}
return arr;
}
/*
* Read a signed integer. "zwidth" is the zero-based byte count.
*/
static s4 readSignedInt(const u1* ptr, int zwidth)
{
s4 val = 0;
int i;
for (i = zwidth; i >= 0; --i)
val = ((u4)val >> 8) | (((s4)*ptr++) << 24);
val >>= (3 - zwidth) * 8;
return val;
}
/*
* Read an unsigned integer. "zwidth" is the zero-based byte count,
* "fillOnRight" indicates which side we want to zero-fill from.
*/
static u4 readUnsignedInt(const u1* ptr, int zwidth, bool fillOnRight)
{
u4 val = 0;
int i;
if (!fillOnRight) {
for (i = zwidth; i >= 0; --i)
val = (val >> 8) | (((u4)*ptr++) << 24);
val >>= (3 - zwidth) * 8;
} else {
for (i = zwidth; i >= 0; --i)
val = (val >> 8) | (((u4)*ptr++) << 24);
}
return val;
}
/*
* Read a signed long. "zwidth" is the zero-based byte count.
*/
static s8 readSignedLong(const u1* ptr, int zwidth)
{
s8 val = 0;
int i;
for (i = zwidth; i >= 0; --i)
val = ((u8)val >> 8) | (((s8)*ptr++) << 56);
val >>= (7 - zwidth) * 8;
return val;
}
/*
* Read an unsigned long. "zwidth" is the zero-based byte count,
* "fillOnRight" indicates which side we want to zero-fill from.
*/
static u8 readUnsignedLong(const u1* ptr, int zwidth, bool fillOnRight)
{
u8 val = 0;
int i;
if (!fillOnRight) {
for (i = zwidth; i >= 0; --i)
val = (val >> 8) | (((u8)*ptr++) << 56);
val >>= (7 - zwidth) * 8;
} else {
for (i = zwidth; i >= 0; --i)
val = (val >> 8) | (((u8)*ptr++) << 56);
}
return val;
}
/*
* ===========================================================================
* Element extraction
* ===========================================================================
*/
/*
* An annotation in "clazz" refers to a method by index. This just gives
* us the name of the class and the name and signature of the method. We
* need to find the method's class, and then find the method within that
* class. If the method has been resolved before, we can just use the
* results of the previous lookup.
*
* Normally we do this as part of method invocation in the interpreter, which
* provides us with a bit of context: is it virtual or direct, do we need
* to initialize the class because it's a static method, etc. We don't have
* that information here, so we have to do a bit of searching.
*
* Returns NULL if the method was not found (exception may be pending).
*/
static Method* resolveAmbiguousMethod(const ClassObject* referrer, u4 methodIdx)
{
DexFile* pDexFile;
ClassObject* resClass;
Method* resMethod;
const DexMethodId* pMethodId;
const char* name;
/* if we've already resolved this method, return it */
resMethod = dvmDexGetResolvedMethod(referrer->pDvmDex, methodIdx);
if (resMethod != NULL)
return resMethod;
pDexFile = referrer->pDvmDex->pDexFile;
pMethodId = dexGetMethodId(pDexFile, methodIdx);
resClass = dvmResolveClass(referrer, pMethodId->classIdx, true);
if (resClass == NULL) {
/* note exception will be pending */
LOGD("resolveAmbiguousMethod: unable to find class %d\n", methodIdx);
return NULL;
}
if (dvmIsInterfaceClass(resClass)) {
/* method is part of an interface -- not expecting that */
LOGD("resolveAmbiguousMethod: method in interface?\n");
return NULL;
}
// TODO - consider a method access flag that indicates direct vs. virtual
name = dexStringById(pDexFile, pMethodId->nameIdx);
DexProto proto;
dexProtoSetFromMethodId(&proto, pDexFile, pMethodId);
if (name[0] == '<') {
/*
* Constructor or class initializer. Only need to examine the
* "direct" list, and don't need to look up the class hierarchy.
*/
resMethod = dvmFindDirectMethod(resClass, name, &proto);
} else {
/*
* Do a hierarchical scan for direct and virtual methods.
*
* This uses the search order from the VM spec (v2 5.4.3.3), which
* seems appropriate here.
*/
resMethod = dvmFindMethodHier(resClass, name, &proto);
}
return resMethod;
}
/*
* constants for processAnnotationValue indicating what style of
* result is wanted
*/
typedef enum {
kAllObjects, /* return everything as an object */
kAllRaw, /* return everything as a raw value or index */
kPrimitivesOrObjects /* return primitives as-is but the rest as objects */
} AnnotationResultStyle;
/*
* Recursively process an annotation value.
*
* "clazz" is the class on which the annotations are defined. It may be
* NULL when "resultStyle" is "kAllRaw".
*
* If "resultStyle" is "kAllObjects", the result will always be an Object of an
* appropriate type (in pValue->value.l). For primitive types, the usual
* wrapper objects will be created.
*
* If "resultStyle" is "kAllRaw", numeric constants are stored directly into
* "pValue", and indexed values like String and Method are returned as
* indexes. Complex values like annotations and arrays are not handled.
*
* If "resultStyle" is "kPrimitivesOrObjects", numeric constants are stored
* directly into "pValue", and everything else is constructed as an Object
* of appropriate type (in pValue->value.l).
*
* The caller must call dvmReleaseTrackedAlloc on returned objects, when
* using "kAllObjects" or "kPrimitivesOrObjects".
*
* Returns "true" on success, "false" if the value could not be processed
* or an object could not be allocated. On allocation failure an exception
* will be raised.
*/
static bool processAnnotationValue(const ClassObject* clazz,
const u1** pPtr, AnnotationValue* pValue,
AnnotationResultStyle resultStyle)
{
Thread* self = dvmThreadSelf();
Object* elemObj = NULL;
bool setObject = false;
const u1* ptr = *pPtr;
u1 valueType, valueArg;
int width;
u4 idx;
valueType = *ptr++;
valueArg = valueType >> kDexAnnotationValueArgShift;
width = valueArg + 1; /* assume, correct later */
LOGV("----- type is 0x%02x %d, ptr=%p [0x%06x]\n",
valueType & kDexAnnotationValueTypeMask, valueArg, ptr-1,
(ptr-1) - (u1*)clazz->pDvmDex->pDexFile->baseAddr);
pValue->type = valueType & kDexAnnotationValueTypeMask;
switch (valueType & kDexAnnotationValueTypeMask) {
case kDexAnnotationByte:
pValue->value.i = (s1) readSignedInt(ptr, valueArg);
if (resultStyle == kAllObjects) {
elemObj = (Object*) dvmWrapPrimitive(pValue->value,
dvmFindPrimitiveClass('B'));
setObject = true;
}
break;
case kDexAnnotationShort:
pValue->value.i = (s2) readSignedInt(ptr, valueArg);
if (resultStyle == kAllObjects) {
elemObj = (Object*) dvmWrapPrimitive(pValue->value,
dvmFindPrimitiveClass('S'));
setObject = true;
}
break;
case kDexAnnotationChar:
pValue->value.i = (u2) readUnsignedInt(ptr, valueArg, false);
if (resultStyle == kAllObjects) {
elemObj = (Object*) dvmWrapPrimitive(pValue->value,
dvmFindPrimitiveClass('C'));
setObject = true;
}
break;
case kDexAnnotationInt:
pValue->value.i = readSignedInt(ptr, valueArg);
if (resultStyle == kAllObjects) {
elemObj = (Object*) dvmWrapPrimitive(pValue->value,
dvmFindPrimitiveClass('I'));
setObject = true;
}
break;
case kDexAnnotationLong:
pValue->value.j = readSignedLong(ptr, valueArg);
if (resultStyle == kAllObjects) {
elemObj = (Object*) dvmWrapPrimitive(pValue->value,
dvmFindPrimitiveClass('J'));
setObject = true;
}
break;
case kDexAnnotationFloat:
pValue->value.i = readUnsignedInt(ptr, valueArg, true);
if (resultStyle == kAllObjects) {
elemObj = (Object*) dvmWrapPrimitive(pValue->value,
dvmFindPrimitiveClass('F'));
setObject = true;
}
break;
case kDexAnnotationDouble:
pValue->value.j = readUnsignedLong(ptr, valueArg, true);
if (resultStyle == kAllObjects) {
elemObj = (Object*) dvmWrapPrimitive(pValue->value,
dvmFindPrimitiveClass('D'));
setObject = true;
}
break;
case kDexAnnotationBoolean:
pValue->value.i = (valueArg != 0);
if (resultStyle == kAllObjects) {
elemObj = (Object*) dvmWrapPrimitive(pValue->value,
dvmFindPrimitiveClass('Z'));
setObject = true;
}
width = 0;
break;
case kDexAnnotationString:
idx = readUnsignedInt(ptr, valueArg, false);
if (resultStyle == kAllRaw) {
pValue->value.i = idx;
} else {
elemObj = (Object*) dvmResolveString(clazz, idx);
setObject = true;
if (elemObj == NULL)
return false;
dvmAddTrackedAlloc(elemObj, self); // balance the Release
}
break;
case kDexAnnotationType:
idx = readUnsignedInt(ptr, valueArg, false);
if (resultStyle == kAllRaw) {
pValue->value.i = idx;
} else {
elemObj = (Object*) dvmResolveClass(clazz, idx, true);
setObject = true;
if (elemObj == NULL) {
/* we're expected to throw a TypeNotPresentException here */
DexFile* pDexFile = clazz->pDvmDex->pDexFile;
const char* desc = dexStringByTypeIdx(pDexFile, idx);
dvmClearException(self);
dvmThrowExceptionWithClassMessage(
"Ljava/lang/TypeNotPresentException;", desc);
return false;
} else {
dvmAddTrackedAlloc(elemObj, self); // balance the Release
}
}
break;
case kDexAnnotationMethod:
idx = readUnsignedInt(ptr, valueArg, false);
if (resultStyle == kAllRaw) {
pValue->value.i = idx;
} else {
Method* meth = resolveAmbiguousMethod(clazz, idx);
if (meth == NULL)
return false;
elemObj = dvmCreateReflectObjForMethod(clazz, meth);
setObject = true;
if (elemObj == NULL)
return false;
}
break;
case kDexAnnotationField:
idx = readUnsignedInt(ptr, valueArg, false);
assert(false); // TODO
break;
case kDexAnnotationEnum:
/* enum values are the contents of a static field */
idx = readUnsignedInt(ptr, valueArg, false);
if (resultStyle == kAllRaw) {
pValue->value.i = idx;
} else {
StaticField* sfield;
sfield = dvmResolveStaticField(clazz, idx);
if (sfield == NULL) {
return false;
} else {
assert(sfield->field.clazz->descriptor[0] == 'L');
elemObj = sfield->value.l;
setObject = true;
dvmAddTrackedAlloc(elemObj, self); // balance the Release
}
}
break;
case kDexAnnotationArray:
/*
* encoded_array format, which is a size followed by a stream
* of annotation_value.
*
* We create an array of Object, populate it, and return it.
*/
if (resultStyle == kAllRaw) {
return false;
} else {
ArrayObject* newArray;
u4 size, count;
size = readUleb128(&ptr);
LOGVV("--- annotation array, size is %u at %p\n", size, ptr);
newArray = dvmAllocArrayByClass(gDvm.classJavaLangObjectArray,
size, ALLOC_DEFAULT);
if (newArray == NULL) {
LOGE("annotation element array alloc failed (%d)\n", size);
return false;
}
AnnotationValue avalue;
for (count = 0; count < size; count++) {
if (!processAnnotationValue(clazz, &ptr, &avalue,
kAllObjects)) {
dvmReleaseTrackedAlloc((Object*)newArray, self);
return false;
}
Object* obj = avalue.value.l;
dvmSetObjectArrayElement(newArray, count, obj);
dvmReleaseTrackedAlloc(obj, self);
}
elemObj = (Object*) newArray;
setObject = true;
}
width = 0;
break;
case kDexAnnotationAnnotation:
/* encoded_annotation format */
if (resultStyle == kAllRaw)
return false;
elemObj = processEncodedAnnotation(clazz, &ptr);
setObject = true;
if (elemObj == NULL)
return false;
dvmAddTrackedAlloc(elemObj, self); // balance the Release
width = 0;
break;
case kDexAnnotationNull:
if (resultStyle == kAllRaw) {
pValue->value.i = 0;
} else {
assert(elemObj == NULL);
setObject = true;
}
width = 0;
break;
default:
LOGE("Bad annotation element value byte 0x%02x (0x%02x)\n",
valueType, valueType & kDexAnnotationValueTypeMask);
assert(false);
return false;
}
ptr += width;
*pPtr = ptr;
if (setObject)
pValue->value.l = elemObj;
return true;
}
/*
* For most object types, we have nothing to do here, and we just return
* "valueObj".
*
* For an array annotation, the type of the extracted object will always
* be java.lang.Object[], but we want it to match the type that the
* annotation member is expected to return. In some cases this may
* involve un-boxing primitive values.
*
* We allocate a second array with the correct type, then copy the data
* over. This releases the tracked allocation on "valueObj" and returns
* a new, tracked object.
*
* On failure, this releases the tracking on "valueObj" and returns NULL
* (allowing the call to say "foo = convertReturnType(foo, ..)").
*/
static Object* convertReturnType(Object* valueObj, ClassObject* methodReturn)
{
if (valueObj == NULL ||
!dvmIsArray((ArrayObject*)valueObj) || !dvmIsArrayClass(methodReturn))
{
return valueObj;
}
Thread* self = dvmThreadSelf();
ClassObject* srcElemClass;
ClassObject* dstElemClass;
/*
* We always extract kDexAnnotationArray into Object[], so we expect to
* find that here. This means we can skip the FindClass on
* (valueObj->clazz->descriptor+1, valueObj->clazz->classLoader).
*/
if (strcmp(valueObj->clazz->descriptor, "[Ljava/lang/Object;") != 0) {
LOGE("Unexpected src type class (%s)\n", valueObj->clazz->descriptor);
return NULL;
}
srcElemClass = gDvm.classJavaLangObject;
/*
* Skip past the '[' to get element class name. Note this is not always
* the same as methodReturn->elementClass.
*/
char firstChar = methodReturn->descriptor[1];
if (firstChar == 'L' || firstChar == '[') {
dstElemClass = dvmFindClass(methodReturn->descriptor+1,
methodReturn->classLoader);
} else {
dstElemClass = dvmFindPrimitiveClass(firstChar);
}
LOGV("HEY: converting valueObj from [%s to [%s\n",
srcElemClass->descriptor, dstElemClass->descriptor);
ArrayObject* srcArray = (ArrayObject*) valueObj;
u4 length = srcArray->length;
ArrayObject* newArray;
newArray = dvmAllocArrayByClass(methodReturn, length, ALLOC_DEFAULT);
if (newArray == NULL) {
LOGE("Failed creating duplicate annotation class (%s %d)\n",
methodReturn->descriptor, length);
goto bail;
}
bool success;
if (dstElemClass->primitiveType == PRIM_NOT) {
success = dvmCopyObjectArray(newArray, srcArray, dstElemClass);
} else {
success = dvmUnboxObjectArray(newArray, srcArray, dstElemClass);
}
if (!success) {
LOGE("Annotation array copy failed\n");
dvmReleaseTrackedAlloc((Object*)newArray, self);
newArray = NULL;
goto bail;
}
bail:
/* replace old, return new */
dvmReleaseTrackedAlloc(valueObj, self);
return (Object*) newArray;
}
/*
* Create a new AnnotationMember.
*
* "clazz" is the class on which the annotations are defined. "pPtr"
* points to a pointer into the annotation data. "annoClass" is the
* annotation's class.
*
* We extract the annotation's value, create a new AnnotationMember object,
* and construct it.
*
* Returns NULL on failure; an exception may or may not be raised.
*/
static Object* createAnnotationMember(const ClassObject* clazz,
const ClassObject* annoClass, const u1** pPtr)
{
Thread* self = dvmThreadSelf();
const DexFile* pDexFile = clazz->pDvmDex->pDexFile;
StringObject* nameObj = NULL;
Object* valueObj = NULL;
Object* newMember = NULL;
Object* methodObj = NULL;
ClassObject* methodReturn = NULL;
u4 elementNameIdx;
const char* name;
AnnotationValue avalue;
JValue result;
bool failed = true;
elementNameIdx = readUleb128(pPtr);
if (!processAnnotationValue(clazz, pPtr, &avalue, kAllObjects)) {
LOGW("Failed processing annotation value\n");
goto bail;
}
valueObj = avalue.value.l;
/* new member to hold the element */
newMember =
dvmAllocObject(gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMember,
ALLOC_DEFAULT);
name = dexStringById(pDexFile, elementNameIdx);
nameObj = dvmCreateStringFromCstr(name);
/* find the method in the annotation class, given only the name */
if (name != NULL) {
Method* annoMeth = dvmFindVirtualMethodByName(annoClass, name);
if (annoMeth == NULL) {
LOGW("WARNING: could not find annotation member %s in %s\n",
name, annoClass->descriptor);
} else {
methodObj = dvmCreateReflectMethodObject(annoMeth);
methodReturn = dvmGetBoxedReturnType(annoMeth);
}
}
if (newMember == NULL || nameObj == NULL || methodObj == NULL ||
methodReturn == NULL)
{
LOGE("Failed creating annotation element (m=%p n=%p a=%p r=%p)\n",
newMember, nameObj, methodObj, methodReturn);
goto bail;
}
/* convert the return type, if necessary */
valueObj = convertReturnType(valueObj, methodReturn);
if (valueObj == NULL)
goto bail;
/* call 4-argument constructor */
dvmCallMethod(self, gDvm.methOrgApacheHarmonyLangAnnotationAnnotationMember_init,
newMember, &result, nameObj, valueObj, methodReturn, methodObj);
if (dvmCheckException(self)) {
LOGD("Failed constructing annotation element\n");
goto bail;
}
failed = false;
bail:
/* release tracked allocations */
dvmReleaseTrackedAlloc(newMember, self);
dvmReleaseTrackedAlloc((Object*)nameObj, self);
dvmReleaseTrackedAlloc(valueObj, self);
dvmReleaseTrackedAlloc(methodObj, self);
if (failed)
return NULL;
else
return newMember;
}
/*
* Create a new Annotation object from what we find in the annotation item.
*
* "clazz" is the class on which the annotations are defined. "pPtr"
* points to a pointer into the annotation data.
*
* We use the AnnotationFactory class to create the annotation for us. The
* method we call is:
*
* public static Annotation createAnnotation(
* Class<? extends Annotation> annotationType,
* AnnotationMember[] elements)
*
* Returns a new Annotation, which will NOT be in the local ref table and
* not referenced elsewhere, so store it away soon. On failure, returns NULL
* with an exception raised.
*/
static Object* processEncodedAnnotation(const ClassObject* clazz,
const u1** pPtr)
{
Thread* self = dvmThreadSelf();
Object* newAnno = NULL;
ArrayObject* elementArray = NULL;
const ClassObject* annoClass;
const u1* ptr;
u4 typeIdx, size, count;
ptr = *pPtr;
typeIdx = readUleb128(&ptr);
size = readUleb128(&ptr);
LOGVV("----- processEnc ptr=%p type=%d size=%d\n", ptr, typeIdx, size);
annoClass = dvmDexGetResolvedClass(clazz->pDvmDex, typeIdx);
if (annoClass == NULL) {
annoClass = dvmResolveClass(clazz, typeIdx, true);
if (annoClass == NULL) {
LOGE("Unable to resolve %s annotation class %d\n",
clazz->descriptor, typeIdx);
assert(dvmCheckException(self));
return NULL;
}
}
LOGV("----- processEnc ptr=%p [0x%06x] typeIdx=%d size=%d class=%s\n",
*pPtr, *pPtr - (u1*) clazz->pDvmDex->pDexFile->baseAddr,
typeIdx, size, annoClass->descriptor);
/*
* Elements are parsed out and stored in an array. The Harmony
* constructor wants an array with just the declared elements --
* default values get merged in later.
*/
JValue result;
if (size > 0) {
elementArray = dvmAllocArrayByClass(
gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMemberArray,
size, ALLOC_DEFAULT);
if (elementArray == NULL) {
LOGE("failed to allocate annotation member array (%d elements)\n",
size);
goto bail;
}
}
/*
* "ptr" points to a byte stream with "size" occurrences of
* annotation_element.
*/
for (count = 0; count < size; count++) {
Object* newMember = createAnnotationMember(clazz, annoClass, &ptr);
if (newMember == NULL)
goto bail;
/* add it to the array */
dvmSetObjectArrayElement(elementArray, count, newMember);
}
dvmCallMethod(self,
gDvm.methOrgApacheHarmonyLangAnnotationAnnotationFactory_createAnnotation,
NULL, &result, annoClass, elementArray);
if (dvmCheckException(self)) {
LOGD("Failed creating an annotation\n");
//dvmLogExceptionStackTrace();
goto bail;
}
newAnno = result.l;
bail:
dvmReleaseTrackedAlloc((Object*) elementArray, NULL);
*pPtr = ptr;
if (newAnno == NULL && !dvmCheckException(self)) {
/* make sure an exception is raised */
dvmThrowException("Ljava/lang/RuntimeException;",
"failure in processEncodedAnnotation");
}
return newAnno;
}
/*
* Run through an annotation set and convert each entry into an Annotation
* object.
*
* Returns an array of Annotation objects, or NULL with an exception raised
* on alloc failure.
*/
static ArrayObject* processAnnotationSet(const ClassObject* clazz,
const DexAnnotationSetItem* pAnnoSet, int visibility)
{
DexFile* pDexFile = clazz->pDvmDex->pDexFile;
const DexAnnotationItem* pAnnoItem;
ArrayObject* annoArray;
int i, count;
u4 dstIndex;
/* we need these later; make sure they're initialized */
if (!dvmIsClassInitialized(gDvm.classOrgApacheHarmonyLangAnnotationAnnotationFactory))
dvmInitClass(gDvm.classOrgApacheHarmonyLangAnnotationAnnotationFactory);
if (!dvmIsClassInitialized(gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMember))
dvmInitClass(gDvm.classOrgApacheHarmonyLangAnnotationAnnotationMember);
/* count up the number of visible elements */
for (i = count = 0; i < (int) pAnnoSet->size; i++) {
pAnnoItem = dexGetAnnotationItem(pDexFile, pAnnoSet, i);
if (pAnnoItem->visibility == visibility)
count++;
}
annoArray =
dvmAllocArrayByClass(gDvm.classJavaLangAnnotationAnnotationArray,
count, ALLOC_DEFAULT);
if (annoArray == NULL)
return NULL;
/*
* Generate Annotation objects. We must put them into the array
* immediately (or add them to the tracked ref table).
*/
dstIndex = 0;
for (i = 0; i < (int) pAnnoSet->size; i++) {
pAnnoItem = dexGetAnnotationItem(pDexFile, pAnnoSet, i);
if (pAnnoItem->visibility != visibility)
continue;
const u1* ptr = pAnnoItem->annotation;
Object *anno = processEncodedAnnotation(clazz, &ptr);
if (anno == NULL) {
dvmReleaseTrackedAlloc((Object*) annoArray, NULL);
return NULL;
}
dvmSetObjectArrayElement(annoArray, dstIndex, anno);
++dstIndex;
}
return annoArray;
}
/*
* ===========================================================================
* Skipping and scanning
* ===========================================================================
*/
/*
* Skip past an annotation value.
*
* "clazz" is the class on which the annotations are defined.
*
* Returns "true" on success, "false" on parsing failure.
*/
static bool skipAnnotationValue(const ClassObject* clazz, const u1** pPtr)
{
const u1* ptr = *pPtr;
u1 valueType, valueArg;
int width;
valueType = *ptr++;
valueArg = valueType >> kDexAnnotationValueArgShift;
width = valueArg + 1; /* assume */
LOGV("----- type is 0x%02x %d, ptr=%p [0x%06x]\n",
valueType & kDexAnnotationValueTypeMask, valueArg, ptr-1,
(ptr-1) - (u1*)clazz->pDvmDex->pDexFile->baseAddr);
switch (valueType & kDexAnnotationValueTypeMask) {
case kDexAnnotationByte: break;
case kDexAnnotationShort: break;
case kDexAnnotationChar: break;
case kDexAnnotationInt: break;
case kDexAnnotationLong: break;
case kDexAnnotationFloat: break;
case kDexAnnotationDouble: break;
case kDexAnnotationString: break;
case kDexAnnotationType: break;
case kDexAnnotationMethod: break;
case kDexAnnotationField: break;
case kDexAnnotationEnum: break;
case kDexAnnotationArray:
/* encoded_array format */
{
u4 size = readUleb128(&ptr);
while (size--) {
if (!skipAnnotationValue(clazz, &ptr))
return false;
}
}
width = 0;
break;
case kDexAnnotationAnnotation:
/* encoded_annotation format */
if (!skipEncodedAnnotation(clazz, &ptr))
return false;
width = 0;
break;
case kDexAnnotationBoolean:
case kDexAnnotationNull:
width = 0;
break;
default:
LOGE("Bad annotation element value byte 0x%02x\n", valueType);
assert(false);
return false;
}
ptr += width;
*pPtr = ptr;
return true;
}
/*
* Skip past an encoded annotation. Mainly useful for annotations embedded
* in other annotations.
*/
static bool skipEncodedAnnotation(const ClassObject* clazz, const u1** pPtr)
{
const u1* ptr;
u4 size;
ptr = *pPtr;
(void) readUleb128(&ptr);
size = readUleb128(&ptr);
/*
* "ptr" points to a byte stream with "size" occurrences of
* annotation_element.
*/
while (size--) {
(void) readUleb128(&ptr);
if (!skipAnnotationValue(clazz, &ptr))
return false;
}
*pPtr = ptr;
return true;
}
/*
* Compare the name of the class in the DEX file to the supplied descriptor.
* Return value is equivalent to strcmp.
*/
static int compareClassDescriptor(DexFile* pDexFile, u4 typeIdx,
const char* descriptor)
{
const char* str = dexStringByTypeIdx(pDexFile, typeIdx);
return strcmp(str, descriptor);
}
/*
* Search through the annotation set for an annotation with a matching
* descriptor.
*
* Comparing the string descriptor is slower than comparing an integer class
* index. If annotation lists are expected to be long, we could look up
* the class' index by name from the DEX file, rather than doing a class
* lookup and string compare on each entry. (Note the index will be
* different for each DEX file, so we can't cache annotation class indices
* globally.)
*/
static const DexAnnotationItem* searchAnnotationSet(const ClassObject* clazz,
const DexAnnotationSetItem* pAnnoSet, const char* descriptor,
int visibility)
{
DexFile* pDexFile = clazz->pDvmDex->pDexFile;
const DexAnnotationItem* result = NULL;
u4 typeIdx;
int i;
//printf("##### searchAnnotationSet %s %d\n", descriptor, visibility);
for (i = 0; i < (int) pAnnoSet->size; i++) {
const DexAnnotationItem* pAnnoItem;
pAnnoItem = dexGetAnnotationItem(pDexFile, pAnnoSet, i);
if (pAnnoItem->visibility != visibility)
continue;
const u1* ptr = pAnnoItem->annotation;
typeIdx = readUleb128(&ptr);
if (compareClassDescriptor(pDexFile, typeIdx, descriptor) == 0) {
//printf("##### match on %x/%p at %d\n", typeIdx, pDexFile, i);
result = pAnnoItem;
break;
}
}
return result;
}
/*
* Find an annotation value in the annotation_item whose name matches "name".
* A pointer to the annotation_value is returned, or NULL if it's not found.
*/
static const u1* searchEncodedAnnotation(const ClassObject* clazz,
const u1* ptr, const char* name)
{
DexFile* pDexFile = clazz->pDvmDex->pDexFile;
u4 typeIdx, size;
typeIdx = readUleb128(&ptr);
size = readUleb128(&ptr);
//printf("##### searching ptr=%p type=%u size=%u\n", ptr, typeIdx, size);
while (size--) {
u4 elementNameIdx;
const char* elemName;
elementNameIdx = readUleb128(&ptr);
elemName = dexStringById(pDexFile, elementNameIdx);
if (strcmp(name, elemName) == 0) {
//printf("##### item match on %s\n", name);
return ptr; /* points to start of value */
}
skipAnnotationValue(clazz, &ptr);
}
//printf("##### no item match on %s\n", name);
return NULL;
}
#define GAV_FAILED ((Object*) 0x10000001)
/*
* Extract an encoded annotation value from the field specified by "annoName".
*
* "expectedType" is an annotation value type, e.g. kDexAnnotationString.
* "debugAnnoName" is only used in debug messages.
*
* Returns GAV_FAILED on failure. If an allocation failed, an exception
* will be raised.
*/
static Object* getAnnotationValue(const ClassObject* clazz,
const DexAnnotationItem* pAnnoItem, const char* annoName,
int expectedType, const char* debugAnnoName)
{
const u1* ptr;
AnnotationValue avalue;
/* find the annotation */
ptr = searchEncodedAnnotation(clazz, pAnnoItem->annotation, annoName);
if (ptr == NULL) {
LOGW("%s annotation lacks '%s' member\n", debugAnnoName, annoName);
return GAV_FAILED;
}
if (!processAnnotationValue(clazz, &ptr, &avalue, kAllObjects))
return GAV_FAILED;
/* make sure it has the expected format */
if (avalue.type != expectedType) {
LOGW("%s %s has wrong type (0x%02x, expected 0x%02x)\n",
debugAnnoName, annoName, avalue.type, expectedType);
return GAV_FAILED;
}
return avalue.value.l;
}
/*
* Find the Signature attribute and extract its value. (Signatures can
* be found in annotations on classes, constructors, methods, and fields.)
*
* Caller must call dvmReleaseTrackedAlloc().
*
* Returns NULL if not found. On memory alloc failure, returns NULL with an
* exception raised.
*/
static ArrayObject* getSignatureValue(const ClassObject* clazz,
const DexAnnotationSetItem* pAnnoSet)
{
const DexAnnotationItem* pAnnoItem;
Object* obj;
pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrSignature,
kDexVisibilitySystem);
if (pAnnoItem == NULL)
return NULL;
/*
* The Signature annotation has one member, "String value".
*/
obj = getAnnotationValue(clazz, pAnnoItem, "value", kDexAnnotationArray,
"Signature");
if (obj == GAV_FAILED)
return NULL;
assert(obj->clazz == gDvm.classJavaLangObjectArray);
return (ArrayObject*)obj;
}
/*
* ===========================================================================
* Class
* ===========================================================================
*/
/*
* Find the DexAnnotationSetItem for this class.
*/
static const DexAnnotationSetItem* findAnnotationSetForClass(
const ClassObject* clazz)
{
DexFile* pDexFile;
const DexAnnotationsDirectoryItem* pAnnoDir;
if (clazz->pDvmDex == NULL) /* generated class (Proxy, array) */
return NULL;
pDexFile = clazz->pDvmDex->pDexFile;
pAnnoDir = getAnnoDirectory(pDexFile, clazz);
if (pAnnoDir != NULL)
return dexGetClassAnnotationSet(pDexFile, pAnnoDir);
else
return NULL;
}
/*
* Return an array of Annotation objects for the class. Returns an empty
* array if there are no annotations.
*
* Caller must call dvmReleaseTrackedAlloc().
*
* On allocation failure, this returns NULL with an exception raised.
*/
ArrayObject* dvmGetClassAnnotations(const ClassObject* clazz)
{
ArrayObject* annoArray;
const DexAnnotationSetItem* pAnnoSet = NULL;
pAnnoSet = findAnnotationSetForClass(clazz);
if (pAnnoSet == NULL) {
/* no annotations for anything in class, or no class annotations */
annoArray = emptyAnnoArray();
} else {
annoArray = processAnnotationSet(clazz, pAnnoSet,
kDexVisibilityRuntime);
}
return annoArray;
}
/*
* Retrieve the Signature annotation, if any. Returns NULL if no signature
* exists.
*
* Caller must call dvmReleaseTrackedAlloc().
*/
ArrayObject* dvmGetClassSignatureAnnotation(const ClassObject* clazz)
{
ArrayObject* signature = NULL;
const DexAnnotationSetItem* pAnnoSet;
pAnnoSet = findAnnotationSetForClass(clazz);
if (pAnnoSet != NULL)
signature = getSignatureValue(clazz, pAnnoSet);
return signature;
}
/*
* Get the EnclosingMethod attribute from an annotation. Returns a Method
* object, or NULL.
*
* Caller must call dvmReleaseTrackedAlloc().
*/
Object* dvmGetEnclosingMethod(const ClassObject* clazz)
{
const DexAnnotationItem* pAnnoItem;
const DexAnnotationSetItem* pAnnoSet;
Object* obj;
pAnnoSet = findAnnotationSetForClass(clazz);
if (pAnnoSet == NULL)
return NULL;
pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrEnclosingMethod,
kDexVisibilitySystem);
if (pAnnoItem == NULL)
return NULL;
/*
* The EnclosingMethod annotation has one member, "Method value".
*/
obj = getAnnotationValue(clazz, pAnnoItem, "value", kDexAnnotationMethod,
"EnclosingMethod");
if (obj == GAV_FAILED)
return NULL;
assert(obj->clazz == gDvm.classJavaLangReflectConstructor ||
obj->clazz == gDvm.classJavaLangReflectMethod);
return obj;
}
/*
* Find a class' enclosing class. We return what we find in the
* EnclosingClass attribute.
*
* Returns a Class object, or NULL.
*
* Caller must call dvmReleaseTrackedAlloc().
*/
ClassObject* dvmGetDeclaringClass(const ClassObject* clazz)
{
const DexAnnotationItem* pAnnoItem;
const DexAnnotationSetItem* pAnnoSet;
Object* obj;
pAnnoSet = findAnnotationSetForClass(clazz);
if (pAnnoSet == NULL)
return NULL;
pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrEnclosingClass,
kDexVisibilitySystem);
if (pAnnoItem == NULL)
return NULL;
/*
* The EnclosingClass annotation has one member, "Class value".
*/
obj = getAnnotationValue(clazz, pAnnoItem, "value", kDexAnnotationType,
"EnclosingClass");
if (obj == GAV_FAILED)
return NULL;
assert(obj->clazz == gDvm.classJavaLangClass);
return (ClassObject*)obj;
}
/*
* Find a class' enclosing class. We first search for an EnclosingClass
* attribute, and if that's not found we look for an EnclosingMethod.
*
* Returns a Class object, or NULL.
*
* Caller must call dvmReleaseTrackedAlloc().
*/
ClassObject* dvmGetEnclosingClass(const ClassObject* clazz)
{
const DexAnnotationItem* pAnnoItem;
const DexAnnotationSetItem* pAnnoSet;
Object* obj;
pAnnoSet = findAnnotationSetForClass(clazz);
if (pAnnoSet == NULL)
return NULL;
pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrEnclosingClass,
kDexVisibilitySystem);
if (pAnnoItem != NULL) {
/*
* The EnclosingClass annotation has one member, "Class value".
*/
obj = getAnnotationValue(clazz, pAnnoItem, "value", kDexAnnotationType,
"EnclosingClass");
if (obj != GAV_FAILED) {
assert(obj->clazz == gDvm.classJavaLangClass);
return (ClassObject*)obj;
}
}
/*
* That didn't work. Look for an EnclosingMethod.
*
* We could create a java.lang.reflect.Method object and extract the
* declaringClass from it, but that's more work than we want to do.
* Instead, we find the "value" item and parse the index out ourselves.
*/
pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrEnclosingMethod,
kDexVisibilitySystem);
if (pAnnoItem == NULL)
return NULL;
/* find the value member */
const u1* ptr;
ptr = searchEncodedAnnotation(clazz, pAnnoItem->annotation, "value");
if (ptr == NULL) {
LOGW("EnclosingMethod annotation lacks 'value' member\n");
return NULL;
}
/* parse it, verify the type */
AnnotationValue avalue;
if (!processAnnotationValue(clazz, &ptr, &avalue, kAllRaw)) {
LOGW("EnclosingMethod parse failed\n");
return NULL;
}
if (avalue.type != kDexAnnotationMethod) {
LOGW("EnclosingMethod value has wrong type (0x%02x, expected 0x%02x)\n",
avalue.type, kDexAnnotationMethod);
return NULL;
}
/* pull out the method index and resolve the method */
Method* meth = resolveAmbiguousMethod(clazz, avalue.value.i);
if (meth == NULL)
return NULL;
ClassObject* methClazz = meth->clazz;
dvmAddTrackedAlloc((Object*) methClazz, NULL); // balance the Release
return methClazz;
}
/*
* Get the EnclosingClass attribute from an annotation. If found, returns
* "true". A String with the original name of the class and the original
* access flags are returned through the arguments. (The name will be NULL
* for an anonymous inner class.)
*
* Caller must call dvmReleaseTrackedAlloc().
*/
bool dvmGetInnerClass(const ClassObject* clazz, StringObject** pName,
int* pAccessFlags)
{
const DexAnnotationItem* pAnnoItem;
const DexAnnotationSetItem* pAnnoSet;
pAnnoSet = findAnnotationSetForClass(clazz);
if (pAnnoSet == NULL)
return false;
pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrInnerClass,
kDexVisibilitySystem);
if (pAnnoItem == NULL)
return false;
/*
* The InnerClass annotation has two members, "String name" and
* "int accessFlags". We don't want to get the access flags as an
* Integer, so we process that as a simple value.
*/
const u1* ptr;
ptr = searchEncodedAnnotation(clazz, pAnnoItem->annotation, "name");
if (ptr == NULL) {
LOGW("InnerClass annotation lacks 'name' member\n");
return false;
}
/* parse it into an Object */
AnnotationValue avalue;
if (!processAnnotationValue(clazz, &ptr, &avalue, kAllObjects)) {
LOGD("processAnnotationValue failed on InnerClass member 'name'\n");
return false;
}
/* make sure it has the expected format */
if (avalue.type != kDexAnnotationNull &&
avalue.type != kDexAnnotationString)
{
LOGW("InnerClass name has bad type (0x%02x, expected STRING or NULL)\n",
avalue.type);
return false;
}
*pName = (StringObject*) avalue.value.l;
assert(*pName == NULL || (*pName)->obj.clazz == gDvm.classJavaLangString);
ptr = searchEncodedAnnotation(clazz, pAnnoItem->annotation, "accessFlags");
if (ptr == NULL) {
LOGW("InnerClass annotation lacks 'accessFlags' member\n");
return false;
}
/* parse it, verify the type */
if (!processAnnotationValue(clazz, &ptr, &avalue, kAllRaw)) {
LOGW("InnerClass accessFlags parse failed\n");
return false;
}
if (avalue.type != kDexAnnotationInt) {
LOGW("InnerClass value has wrong type (0x%02x, expected 0x%02x)\n",
avalue.type, kDexAnnotationInt);
return false;
}
*pAccessFlags = avalue.value.i;
return true;
}
/*
* Extract an array of Class objects from the MemberClasses annotation
* for this class.
*
* Caller must call dvmReleaseTrackedAlloc().
*
* Returns NULL if we don't find any member classes.
*/
ArrayObject* dvmGetDeclaredClasses(const ClassObject* clazz)
{
const DexAnnotationSetItem* pAnnoSet;
const DexAnnotationItem* pAnnoItem;
Object* obj;
pAnnoSet = findAnnotationSetForClass(clazz);
if (pAnnoSet == NULL)
return NULL;
pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrMemberClasses,
kDexVisibilitySystem);
if (pAnnoItem == NULL)
return NULL;
/*
* The MemberClasses annotation has one member, "Class[] value".
*/
obj = getAnnotationValue(clazz, pAnnoItem, "value",
kDexAnnotationArray, "MemberClasses");
if (obj == GAV_FAILED)
return NULL;
assert(dvmIsArray((ArrayObject*)obj));
obj = convertReturnType(obj, gDvm.classJavaLangClassArray);
return (ArrayObject*)obj;
}
/*
* ===========================================================================
* Method (and Constructor)
* ===========================================================================
*/
/*
* Compare the attributes (class name, method name, method signature) of
* the specified method to "method".
*/
static int compareMethodStr(DexFile* pDexFile, u4 methodIdx,
const Method* method)
{
const DexMethodId* pMethodId = dexGetMethodId(pDexFile, methodIdx);
const char* str = dexStringByTypeIdx(pDexFile, pMethodId->classIdx);
int result = strcmp(str, method->clazz->descriptor);
if (result == 0) {
str = dexStringById(pDexFile, pMethodId->nameIdx);
result = strcmp(str, method->name);
if (result == 0) {
DexProto proto;
dexProtoSetFromMethodId(&proto, pDexFile, pMethodId);
result = dexProtoCompare(&proto, &method->prototype);
}
}
return result;
}
/*
* Given a method, determine the method's index.
*
* We could simply store this in the Method*, but that would cost 4 bytes
* per method. Instead we plow through the DEX data.
*
* We have two choices: look through the class method data, or look through
* the global method_ids table. The former is awkward because the method
* could have been defined in a superclass or interface. The latter works
* out reasonably well because it's in sorted order, though we're still left
* doing a fair number of string comparisons.
*/
static u4 getMethodIdx(const Method* method)
{
DexFile* pDexFile = method->clazz->pDvmDex->pDexFile;
u4 hi = pDexFile->pHeader->methodIdsSize -1;
u4 lo = 0;
u4 cur;
while (hi >= lo) {
int cmp;
cur = (lo + hi) / 2;
cmp = compareMethodStr(pDexFile, cur, method);
if (cmp < 0) {
lo = cur + 1;
} else if (cmp > 0) {
hi = cur - 1;
} else {
break;
}
}
if (hi < lo) {
/* this should be impossible -- the method came out of this DEX */
char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
LOGE("Unable to find method %s.%s %s in DEX file!\n",
method->clazz->descriptor, method->name, desc);
free(desc);
dvmAbort();
}
return cur;
}
/*
* Find the DexAnnotationSetItem for this method.
*
* Returns NULL if none found.
*/
static const DexAnnotationSetItem* findAnnotationSetForMethod(
const Method* method)
{
ClassObject* clazz = method->clazz;
DexFile* pDexFile;
const DexAnnotationsDirectoryItem* pAnnoDir;
const DexMethodAnnotationsItem* pMethodList;
const DexAnnotationSetItem* pAnnoSet = NULL;
if (clazz->pDvmDex == NULL) /* generated class (Proxy, array) */
return NULL;
pDexFile = clazz->pDvmDex->pDexFile;
pAnnoDir = getAnnoDirectory(pDexFile, clazz);
if (pAnnoDir != NULL) {
pMethodList = dexGetMethodAnnotations(pDexFile, pAnnoDir);
if (pMethodList != NULL) {
/*
* Run through the list and find a matching method. We compare the
* method ref indices in the annotation list with the method's DEX
* method_idx value.
*
* TODO: use a binary search for long lists
*
* Alternate approach: for each entry in the annotations list,
* find the method definition in the DEX file and perform string
* comparisons on class name, method name, and signature.
*/
u4 methodIdx = getMethodIdx(method);
u4 count = dexGetMethodAnnotationsSize(pDexFile, pAnnoDir);
u4 idx;
for (idx = 0; idx < count; idx++) {
if (pMethodList[idx].methodIdx == methodIdx) {
/* found! */
pAnnoSet = dexGetMethodAnnotationSetItem(pDexFile,
&pMethodList[idx]);
break;
}
}
}
}
return pAnnoSet;
}
/*
* Return an array of Annotation objects for the method. Returns an empty
* array if there are no annotations.
*
* Caller must call dvmReleaseTrackedAlloc().
*
* On allocation failure, this returns NULL with an exception raised.
*/
ArrayObject* dvmGetMethodAnnotations(const Method* method)
{
ClassObject* clazz = method->clazz;
const DexAnnotationSetItem* pAnnoSet;
ArrayObject* annoArray = NULL;
pAnnoSet = findAnnotationSetForMethod(method);
if (pAnnoSet == NULL) {
/* no matching annotations found */
annoArray = emptyAnnoArray();
} else {
annoArray = processAnnotationSet(clazz, pAnnoSet,kDexVisibilityRuntime);
}
return annoArray;
}
/*
* Retrieve the Signature annotation, if any. Returns NULL if no signature
* exists.
*
* Caller must call dvmReleaseTrackedAlloc().
*/
ArrayObject* dvmGetMethodSignatureAnnotation(const Method* method)
{
ClassObject* clazz = method->clazz;
const DexAnnotationSetItem* pAnnoSet;
ArrayObject* signature = NULL;
pAnnoSet = findAnnotationSetForMethod(method);
if (pAnnoSet != NULL)
signature = getSignatureValue(clazz, pAnnoSet);
return signature;
}
/*
* Extract an array of exception classes from the "system" annotation list
* for this method.
*
* Caller must call dvmReleaseTrackedAlloc().
*
* Returns NULL if we don't find any exceptions for this method.
*/
ArrayObject* dvmGetMethodThrows(const Method* method)
{
ClassObject* clazz = method->clazz;
const DexAnnotationSetItem* pAnnoSet;
const DexAnnotationItem* pAnnoItem;
/* find the set for this method */
pAnnoSet = findAnnotationSetForMethod(method);
if (pAnnoSet == NULL)
return NULL; /* nothing for this method */
/* find the "Throws" annotation, if any */
pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrThrows,
kDexVisibilitySystem);
if (pAnnoItem == NULL)
return NULL; /* no Throws */
/*
* The Throws annotation has one member, "Class[] value".
*/
Object* obj = getAnnotationValue(clazz, pAnnoItem, "value",
kDexAnnotationArray, "Throws");
if (obj == GAV_FAILED)
return NULL;
assert(dvmIsArray((ArrayObject*)obj));
obj = convertReturnType(obj, gDvm.classJavaLangClassArray);
return (ArrayObject*)obj;
}
/*
* Given an Annotation's method, find the default value, if any.
*
* If this is a CLASS annotation, and we can't find a match for the
* default class value, we need to throw a TypeNotPresentException.
*
* Caller must call dvmReleaseTrackedAlloc().
*/
Object* dvmGetAnnotationDefaultValue(const Method* method)
{
const ClassObject* clazz = method->clazz;
DexFile* pDexFile = clazz->pDvmDex->pDexFile;
const DexAnnotationsDirectoryItem* pAnnoDir;
const DexAnnotationSetItem* pAnnoSet = NULL;
/*
* The method's declaring class (the annotation) will have an
* AnnotationDefault "system" annotation associated with it if any
* of its methods have default values. Start by finding the
* DexAnnotationItem associated with the class.
*/
pAnnoDir = getAnnoDirectory(pDexFile, clazz);
if (pAnnoDir != NULL)
pAnnoSet = dexGetClassAnnotationSet(pDexFile, pAnnoDir);
if (pAnnoSet == NULL) {
/* no annotations for anything in class, or no class annotations */
return NULL;
}
/* find the "AnnotationDefault" annotation, if any */
const DexAnnotationItem* pAnnoItem;
pAnnoItem = searchAnnotationSet(clazz, pAnnoSet, kDescrAnnotationDefault,
kDexVisibilitySystem);
if (pAnnoItem == NULL) {
/* no default values for any member in this annotation */
//printf("##### no default annotations for %s.%s\n",
// method->clazz->descriptor, method->name);
return NULL;
}
/*
* The AnnotationDefault annotation has one member, "Annotation value".
* We need to pull that out.
*/
const u1* ptr;
ptr = searchEncodedAnnotation(clazz, pAnnoItem->annotation, "value");
if (ptr == NULL) {
LOGW("AnnotationDefault annotation lacks 'value'\n");
return NULL;
}
if ((*ptr & kDexAnnotationValueTypeMask) != kDexAnnotationAnnotation) {
LOGW("AnnotationDefault value has wrong type (0x%02x)\n",
*ptr & kDexAnnotationValueTypeMask);
return NULL;
}
/*
* The value_type byte for VALUE_ANNOTATION is followed by
* encoded_annotation data. We want to scan through it to find an
* entry whose name matches our method name.
*/
ptr++;
ptr = searchEncodedAnnotation(clazz, ptr, method->name);
if (ptr == NULL)
return NULL; /* no default annotation for this method */
/* got it, pull it out */
AnnotationValue avalue;
if (!processAnnotationValue(clazz, &ptr, &avalue, kAllObjects)) {
LOGD("processAnnotationValue failed on default for '%s'\n",
method->name);
return NULL;
}
/* convert the return type, if necessary */
ClassObject* methodReturn = dvmGetBoxedReturnType(method);
Object* obj = avalue.value.l;
obj = convertReturnType(obj, methodReturn);
return obj;
}
/*
* ===========================================================================
* Field
* ===========================================================================
*/
/*
* Compare the attributes (class name, field name, field signature) of
* the specified field to "field".
*/
static int compareFieldStr(DexFile* pDexFile, u4 idx, const Field* field)
{
const DexFieldId* pFieldId = dexGetFieldId(pDexFile, idx);
const char* str = dexStringByTypeIdx(pDexFile, pFieldId->classIdx);
int result = strcmp(str, field->clazz->descriptor);
if (result == 0) {
str = dexStringById(pDexFile, pFieldId->nameIdx);
result = strcmp(str, field->name);
if (result == 0) {
str = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx);
result = strcmp(str, field->signature);
}
}
return result;
}
/*
* Given a field, determine the field's index.
*
* This has the same tradeoffs as getMethodIdx.
*/
static u4 getFieldIdx(const Field* field)
{
DexFile* pDexFile = field->clazz->pDvmDex->pDexFile;
u4 hi = pDexFile->pHeader->fieldIdsSize -1;
u4 lo = 0;
u4 cur;
while (hi >= lo) {
int cmp;
cur = (lo + hi) / 2;
cmp = compareFieldStr(pDexFile, cur, field);
if (cmp < 0) {
lo = cur + 1;
} else if (cmp > 0) {
hi = cur - 1;
} else {
break;
}
}
if (hi < lo) {
/* this should be impossible -- the field came out of this DEX */
LOGE("Unable to find field %s.%s %s in DEX file!\n",
field->clazz->descriptor, field->name, field->signature);
dvmAbort();
}
return cur;
}
/*
* Find the DexAnnotationSetItem for this field.
*
* Returns NULL if none found.
*/
static const DexAnnotationSetItem* findAnnotationSetForField(const Field* field)
{
ClassObject* clazz = field->clazz;
DexFile* pDexFile = clazz->pDvmDex->pDexFile;
const DexAnnotationsDirectoryItem* pAnnoDir;
const DexFieldAnnotationsItem* pFieldList;
pAnnoDir = getAnnoDirectory(pDexFile, clazz);
if (pAnnoDir == NULL)
return NULL;
pFieldList = dexGetFieldAnnotations(pDexFile, pAnnoDir);
if (pFieldList == NULL)
return NULL;
/*
* Run through the list and find a matching field. We compare the
* field ref indices in the annotation list with the field's DEX
* field_idx value.
*
* TODO: use a binary search for long lists
*
* Alternate approach: for each entry in the annotations list,
* find the field definition in the DEX file and perform string
* comparisons on class name, field name, and signature.
*/
u4 fieldIdx = getFieldIdx(field);
u4 count = dexGetFieldAnnotationsSize(pDexFile, pAnnoDir);
u4 idx;
for (idx = 0; idx < count; idx++) {
if (pFieldList[idx].fieldIdx == fieldIdx) {
/* found! */
return dexGetFieldAnnotationSetItem(pDexFile, &pFieldList[idx]);
}
}
return NULL;
}
/*
* Return an array of Annotation objects for the field. Returns an empty
* array if there are no annotations.
*
* Caller must call dvmReleaseTrackedAlloc().
*
* On allocation failure, this returns NULL with an exception raised.
*/
ArrayObject* dvmGetFieldAnnotations(const Field* field)
{
ClassObject* clazz = field->clazz;
ArrayObject* annoArray = NULL;
const DexAnnotationSetItem* pAnnoSet = NULL;
pAnnoSet = findAnnotationSetForField(field);
if (pAnnoSet == NULL) {
/* no matching annotations found */
annoArray = emptyAnnoArray();
} else {
annoArray = processAnnotationSet(clazz, pAnnoSet,
kDexVisibilityRuntime);
}
return annoArray;
}
/*
* Retrieve the Signature annotation, if any. Returns NULL if no signature
* exists.
*
* Caller must call dvmReleaseTrackedAlloc().
*/
ArrayObject* dvmGetFieldSignatureAnnotation(const Field* field)
{
ClassObject* clazz = field->clazz;
const DexAnnotationSetItem* pAnnoSet;
ArrayObject* signature = NULL;
pAnnoSet = findAnnotationSetForField(field);
if (pAnnoSet != NULL)
signature = getSignatureValue(clazz, pAnnoSet);
return signature;
}
/*
* ===========================================================================
* Parameter
* ===========================================================================
*/
/*
* We have an annotation_set_ref_list, which is essentially a list of
* entries that we pass to processAnnotationSet().
*
* The returned object must be released with dvmReleaseTrackedAlloc.
*/
static ArrayObject* processAnnotationSetRefList(const ClassObject* clazz,
const DexAnnotationSetRefList* pAnnoSetList, u4 count)
{
DexFile* pDexFile = clazz->pDvmDex->pDexFile;
ArrayObject* annoArrayArray = NULL;
u4 idx;
/* allocate an array of Annotation arrays to hold results */
annoArrayArray = dvmAllocArrayByClass(
gDvm.classJavaLangAnnotationAnnotationArrayArray, count, ALLOC_DEFAULT);
if (annoArrayArray == NULL) {
LOGW("annotation set ref array alloc failed\n");
goto bail;
}
for (idx = 0; idx < count; idx++) {
Thread* self = dvmThreadSelf();
const DexAnnotationSetRefItem* pItem;
const DexAnnotationSetItem* pAnnoSet;
Object *annoSet;
pItem = dexGetParameterAnnotationSetRef(pAnnoSetList, idx);
pAnnoSet = dexGetSetRefItemItem(pDexFile, pItem);
annoSet = (Object *)processAnnotationSet(clazz,
pAnnoSet,
kDexVisibilityRuntime);
if (annoSet == NULL) {
LOGW("processAnnotationSet failed\n");
annoArrayArray = NULL;
goto bail;
}
dvmSetObjectArrayElement(annoArrayArray, idx, annoSet);
dvmReleaseTrackedAlloc((Object*) annoSet, self);
}
bail:
return annoArrayArray;
}
/*
* Find the DexAnnotationSetItem for this parameter.
*
* Returns NULL if none found.
*/
static const DexParameterAnnotationsItem* findAnnotationsItemForMethod(
const Method* method)
{
ClassObject* clazz = method->clazz;
DexFile* pDexFile;
const DexAnnotationsDirectoryItem* pAnnoDir;
const DexParameterAnnotationsItem* pParameterList;
if (clazz->pDvmDex == NULL) /* generated class (Proxy, array) */
return NULL;
pDexFile = clazz->pDvmDex->pDexFile;
pAnnoDir = getAnnoDirectory(pDexFile, clazz);
if (pAnnoDir == NULL)
return NULL;
pParameterList = dexGetParameterAnnotations(pDexFile, pAnnoDir);
if (pParameterList == NULL)
return NULL;
/*
* Run through the list and find a matching method. We compare the
* method ref indices in the annotation list with the method's DEX
* method_idx value.
*
* TODO: use a binary search for long lists
*
* Alternate approach: for each entry in the annotations list,
* find the method definition in the DEX file and perform string
* comparisons on class name, method name, and signature.
*/
u4 methodIdx = getMethodIdx(method);
u4 count = dexGetParameterAnnotationsSize(pDexFile, pAnnoDir);
u4 idx;
for (idx = 0; idx < count; idx++) {
if (pParameterList[idx].methodIdx == methodIdx) {
/* found! */
return &pParameterList[idx];
}
}
return NULL;
}
/*
* Count up the number of arguments the method takes. The "this" pointer
* doesn't count.
*/
static int countMethodArguments(const Method* method)
{
/* method->shorty[0] is the return type */
return strlen(method->shorty + 1);
}
/*
* Return an array of arrays of Annotation objects. The outer array has
* one entry per method parameter, the inner array has the list of annotations
* associated with that parameter.
*
* If the method has no parameters, we return an array of length zero. If
* the method has one or more parameters, we return an array whose length
* is equal to the number of parameters; if a given parameter does not have
* an annotation, the corresponding entry will be null.
*
* Caller must call dvmReleaseTrackedAlloc().
*/
ArrayObject* dvmGetParameterAnnotations(const Method* method)
{
ClassObject* clazz = method->clazz;
const DexParameterAnnotationsItem* pItem;
ArrayObject* annoArrayArray = NULL;
pItem = findAnnotationsItemForMethod(method);
if (pItem != NULL) {
DexFile* pDexFile = clazz->pDvmDex->pDexFile;
const DexAnnotationSetRefList* pAnnoSetList;
u4 size;
size = dexGetParameterAnnotationSetRefSize(pDexFile, pItem);
pAnnoSetList = dexGetParameterAnnotationSetRefList(pDexFile, pItem);
annoArrayArray = processAnnotationSetRefList(clazz, pAnnoSetList, size);
} else {
/* no matching annotations found */
annoArrayArray = emptyAnnoArrayArray(countMethodArguments(method));
}
return annoArrayArray;
}
/*
* ===========================================================================
* DexEncodedArray interpretation
* ===========================================================================
*/
/**
* Initializes an encoded array iterator.
*
* @param iterator iterator to initialize
* @param encodedArray encoded array to iterate over
* @param clazz class to use when resolving strings and types
*/
void dvmEncodedArrayIteratorInitialize(EncodedArrayIterator* iterator,
const DexEncodedArray* encodedArray, const ClassObject* clazz) {
iterator->encodedArray = encodedArray;
iterator->cursor = encodedArray->array;
iterator->size = readUleb128(&iterator->cursor);
iterator->elementsLeft = iterator->size;
iterator->clazz = clazz;
}
/**
* Returns whether there are more elements to be read.
*/
bool dvmEncodedArrayIteratorHasNext(const EncodedArrayIterator* iterator) {
return (iterator->elementsLeft != 0);
}
/**
* Returns the next decoded value from the iterator, advancing its
* cursor. This returns primitive values in their corresponding union
* slots, and returns everything else (including nulls) as object
* references in the "l" union slot.
*
* The caller must call dvmReleaseTrackedAlloc() on any returned reference.
*
* @param value pointer to store decoded value into
* @returns true if a value was decoded and the cursor advanced; false if
* the last value had already been decoded or if there was a problem decoding
*/
bool dvmEncodedArrayIteratorGetNext(EncodedArrayIterator* iterator,
AnnotationValue* value) {
bool processed;
if (iterator->elementsLeft == 0) {
return false;
}
processed = processAnnotationValue(iterator->clazz, &iterator->cursor,
value, kPrimitivesOrObjects);
if (! processed) {
LOGE("Failed to process array element %d from %p",
iterator->size - iterator->elementsLeft,
iterator->encodedArray);
iterator->elementsLeft = 0;
return false;
}
iterator->elementsLeft--;
return true;
}