diff options
author | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:03:55 -0800 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2008-12-17 18:03:55 -0800 |
commit | 89c1feb0a69a7707b271086e749975b3f7acacf7 (patch) | |
tree | 003624a03635e05020a47fc72a2c42934e3f0703 /vm/reflect | |
parent | 2ad60cfc28e14ee8f0bb038720836a4696c478ad (diff) | |
download | android_dalvik-89c1feb0a69a7707b271086e749975b3f7acacf7.tar.gz android_dalvik-89c1feb0a69a7707b271086e749975b3f7acacf7.tar.bz2 android_dalvik-89c1feb0a69a7707b271086e749975b3f7acacf7.zip |
Code drop from //branches/cupcake/...@124589
Diffstat (limited to 'vm/reflect')
-rw-r--r-- | vm/reflect/Annotation.c | 99 | ||||
-rw-r--r-- | vm/reflect/Proxy.c | 634 | ||||
-rw-r--r-- | vm/reflect/Reflect.c | 6 |
3 files changed, 633 insertions, 106 deletions
diff --git a/vm/reflect/Annotation.c b/vm/reflect/Annotation.c index cf35b5180..5b2afd937 100644 --- a/vm/reflect/Annotation.c +++ b/vm/reflect/Annotation.c @@ -109,7 +109,7 @@ bool dvmReflectAnnotationStartup(void) LOGE("Unable to find 4-arg constructor in android AnnotationMember\n"); return false; } - + gDvm.methOrgApacheHarmonyLangAnnotationAnnotationMember_init = meth; return true; @@ -174,17 +174,27 @@ static ArrayObject* emptyAnnoArray(void) } /* - * Return a zero-length array of arrays of Annotation objects. - * - * TODO: this currently allocates a new array each time, but I think we - * can get away with returning a canonical copy. + * Return an array of empty arrays of Annotation objects. * * Caller must call dvmReleaseTrackedAlloc(). */ -static ArrayObject* emptyAnnoArrayArray(void) +static ArrayObject* emptyAnnoArrayArray(int numElements) { - return dvmAllocArrayByClass( - gDvm.classJavaLangAnnotationAnnotationArrayArray, 0, ALLOC_DEFAULT); + 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; } /* @@ -276,7 +286,7 @@ static u8 readUnsignedLong(const u1* ptr, int zwidth, bool fillOnRight) * 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. + * Returns NULL if the method was not found (exception may be pending). */ static Method* resolveAmbiguousMethod(const ClassObject* referrer, u4 methodIdx) { @@ -296,6 +306,7 @@ static Method* resolveAmbiguousMethod(const ClassObject* referrer, u4 methodIdx) 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; } @@ -338,7 +349,7 @@ typedef enum { 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. * @@ -380,8 +391,9 @@ static bool processAnnotationValue(const ClassObject* clazz, valueArg = valueType >> kDexAnnotationValueArgShift; width = valueArg + 1; /* assume, correct later */ - LOGV("----- type is 0x%02x %d, ptr=%p\n", - valueType & kDexAnnotationValueTypeMask, valueArg, ptr-1); + 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; @@ -472,6 +484,18 @@ static bool processAnnotationValue(const ClassObject* clazz, elemObj = (Object*) dvmResolveClass(clazz, idx, true); setObject = true; if (elemObj == NULL) { + /* see if it's a primitive type - not common */ + dvmClearException(self); + const char* className = + dexStringByTypeIdx(clazz->pDvmDex->pDexFile, idx); + + if (className != NULL && + className[0] != '\0' && className[1] == '\0') + { + elemObj = (Object*) dvmFindPrimitiveClass(className[0]); + } + } + if (elemObj == NULL) { /* we're expected to throw a TypeNotPresentException here */ DexFile* pDexFile = clazz->pDvmDex->pDexFile; const char* desc = dexStringByTypeIdx(pDexFile, idx); @@ -479,8 +503,9 @@ static bool processAnnotationValue(const ClassObject* clazz, dvmThrowExceptionWithClassMessage( "Ljava/lang/TypeNotPresentException;", desc); return false; - } else + } else { dvmAddTrackedAlloc(elemObj, self); // balance the Release + } } break; case kDexAnnotationMethod: @@ -535,7 +560,7 @@ static bool processAnnotationValue(const ClassObject* clazz, u4 size; size = readUleb128(&ptr); - LOGVV("--- annotation array, size is %u\n", size); + LOGVV("--- annotation array, size is %u at %p\n", size, ptr); newArray = dvmAllocArrayByClass(gDvm.classJavaLangObjectArray, size, ALLOC_DEFAULT); if (newArray == NULL) { @@ -678,7 +703,7 @@ bail: * We extract the annotation's value, create a new AnnotationMember object, * and construct it. * - * Returns NULL on failure. + * Returns NULL on failure; an exception may or may not be raised. */ static Object* createAnnotationMember(const ClassObject* clazz, const ClassObject* annoClass, const u1** pPtr) @@ -771,7 +796,8 @@ bail: * 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. + * 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) @@ -788,7 +814,7 @@ static Object* processEncodedAnnotation(const ClassObject* clazz, typeIdx = readUleb128(&ptr); size = readUleb128(&ptr); - LOGV("----- processEnc ptr=%p type=%d size=%d\n", ptr, typeIdx, size); + LOGVV("----- processEnc ptr=%p type=%d size=%d\n", ptr, typeIdx, size); annoClass = dvmDexGetResolvedClass(clazz->pDvmDex, typeIdx); if (annoClass == NULL) { @@ -801,8 +827,9 @@ static Object* processEncodedAnnotation(const ClassObject* clazz, } } - //LOGI("Found typeIdx=%d size=%d class=%s\n", - // typeIdx, size, annoClass->descriptor); + 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 @@ -830,6 +857,8 @@ static Object* processEncodedAnnotation(const ClassObject* clazz, */ while (size--) { Object* newMember = createAnnotationMember(clazz, annoClass, &ptr); + if (newMember == NULL) + goto bail; /* add it to the array */ *pElement++ = newMember; @@ -849,6 +878,11 @@ static Object* processEncodedAnnotation(const ClassObject* clazz, 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; } @@ -931,8 +965,9 @@ static bool skipAnnotationValue(const ClassObject* clazz, const u1** pPtr) valueArg = valueType >> kDexAnnotationValueArgShift; width = valueArg + 1; /* assume */ - LOGV("----- type is 0x%02x %d, ptr=%p\n", - valueType & kDexAnnotationValueTypeMask, valueArg, ptr-1); + 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; @@ -2043,11 +2078,27 @@ static const DexParameterAnnotationsItem* findAnnotationsItemForMethod( 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) @@ -2061,13 +2112,13 @@ ArrayObject* dvmGetParameterAnnotations(const Method* method) 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(); + annoArrayArray = emptyAnnoArrayArray(countMethodArguments(method)); } return annoArrayArray; @@ -2118,7 +2169,7 @@ bool dvmEncodedArrayIteratorHasNext(const EncodedArrayIterator* iterator) { bool dvmEncodedArrayIteratorGetNext(EncodedArrayIterator* iterator, AnnotationValue* value) { bool processed; - + if (iterator->elementsLeft == 0) { return false; } diff --git a/vm/reflect/Proxy.c b/vm/reflect/Proxy.c index 8641ab95a..9e91ded06 100644 --- a/vm/reflect/Proxy.c +++ b/vm/reflect/Proxy.c @@ -13,6 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ + /* * Implementation of java.lang.reflect.Proxy. * @@ -27,9 +28,14 @@ #include <stdlib.h> // fwd -static bool gatherMethods(ArrayObject* interfaces, Method*** pMethods, - int* pMethodCount); -static bool addMethod(Method* meth, Method** methArray, int slot); +static bool returnTypesAreCompatible(Method* baseMethod, Method* subMethod); +static bool gatherMethods(ArrayObject* interfaces, Method*** pMethods,\ + ArrayObject** pThrows, int* pMethodCount); +static int copyWithoutDuplicates(Method** allMethods, int allCount, + Method** outMethods, ArrayObject* throws); +static bool createExceptionClassList(const Method* method, + PointerSet** pThrows); +static void updateExceptionClassList(const Method* method, PointerSet* throws); static void createConstructor(ClassObject* clazz, Method* meth); static void createHandlerMethod(ClassObject* clazz, Method* dstMeth, const Method* srcMeth); @@ -37,6 +43,11 @@ static void proxyConstructor(const u4* args, JValue* pResult, const Method* method, Thread* self); static void proxyInvoker(const u4* args, JValue* pResult, const Method* method, Thread* self); +static bool mustWrapException(const Method* method, const Object* throwable); + +/* private static fields in the Proxy class */ +#define kThrowsField 0 + /* * Perform Proxy setup. @@ -50,14 +61,14 @@ bool dvmReflectProxyStartup() Method* methH; Method* methT; Method* methF; - methE = dvmFindVirtualMethodByDescriptor(gDvm.classJavaLangObject, "equals", - "(Ljava/lang/Object;)Z"); - methH = dvmFindVirtualMethodByDescriptor(gDvm.classJavaLangObject, "hashCode", - "()I"); - methT = dvmFindVirtualMethodByDescriptor(gDvm.classJavaLangObject, "toString", - "()Ljava/lang/String;"); - methF = dvmFindVirtualMethodByDescriptor(gDvm.classJavaLangObject, "finalize", - "()V"); + methE = dvmFindVirtualMethodByDescriptor(gDvm.classJavaLangObject, + "equals", "(Ljava/lang/Object;)Z"); + methH = dvmFindVirtualMethodByDescriptor(gDvm.classJavaLangObject, + "hashCode", "()I"); + methT = dvmFindVirtualMethodByDescriptor(gDvm.classJavaLangObject, + "toString", "()Ljava/lang/String;"); + methF = dvmFindVirtualMethodByDescriptor(gDvm.classJavaLangObject, + "finalize", "()V"); if (methE == NULL || methH == NULL || methT == NULL || methF == NULL) { LOGE("Could not find equals/hashCode/toString/finalize in Object\n"); return false; @@ -87,6 +98,16 @@ bool dvmReflectProxyStartup() } gDvm.methJavaLangReflectProxy_constructorPrototype = meth; + /* + * Get the offset of the "h" field in Proxy. + */ + gDvm.offJavaLangReflectProxy_h = dvmFindFieldOffset(proxyClass, "h", + "Ljava/lang/reflect/InvocationHandler;"); + if (gDvm.offJavaLangReflectProxy_h < 0) { + LOGE("Unable to find 'h' field in java.lang.Proxy\n"); + return false; + } + return true; } @@ -95,8 +116,12 @@ bool dvmReflectProxyStartup() * Generate a proxy class with the specified name, interfaces, and loader. * "interfaces" is an array of class objects. * - * The interpreted code has done all of the necessary checks, e.g. we know - * that "interfaces" contains only interface classes. + * The Proxy.getProxyClass() code has done the following: + * - Verified that "interfaces" contains only interfaces + * - Verified that no interface appears twice + * - Prepended the package name to the class name if one or more + * interfaces are non-public + * - Searched for an existing instance of an appropriate Proxy class * * On failure we leave a partially-created class object sitting around, * but the garbage collector will take care of it. @@ -107,6 +132,7 @@ ClassObject* dvmGenerateProxyClass(StringObject* str, ArrayObject* interfaces, int result = -1; char* nameStr = NULL; Method** methods = NULL; + ArrayObject* throws = NULL; ClassObject* newClass = NULL; int i; @@ -126,10 +152,14 @@ ClassObject* dvmGenerateProxyClass(StringObject* str, ArrayObject* interfaces, * - concrete class, public and final * - superclass is java.lang.reflect.Proxy * - implements all listed interfaces (req'd for instanceof) - * - has one method for each method in the interfaces (barring duplicates) + * - has one method for each method in the interfaces (for duplicates, + * the method in the earliest interface wins) * - has one constructor (takes an InvocationHandler arg) * - has overrides for hashCode, equals, and toString (these come first) - * - has one field, a reference to the InvocationHandler object + * - has one field, a reference to the InvocationHandler object, inherited + * from Proxy + * + * TODO: set protection domain so it matches bootstrap classes. * * The idea here is to create a class object and fill in the details * as we would in loadClassFromDex(), and then call dvmLinkClass() to do @@ -140,8 +170,8 @@ ClassObject* dvmGenerateProxyClass(StringObject* str, ArrayObject* interfaces, /* * Generate a temporary list of virtual methods. */ - int methodCount; - if (!gatherMethods(interfaces, &methods, &methodCount)) + int methodCount = -1; + if (!gatherMethods(interfaces, &methods, &throws, &methodCount)) goto bail; /* @@ -149,7 +179,7 @@ ClassObject* dvmGenerateProxyClass(StringObject* str, ArrayObject* interfaces, */ newClass = (ClassObject*) dvmMalloc(sizeof(*newClass), ALLOC_DEFAULT); if (newClass == NULL) - return NULL; + goto bail; DVM_OBJECT_INIT(&newClass->obj, gDvm.unlinkedJavaLangClass); newClass->descriptorAlloc = dvmNameToDescriptor(nameStr); newClass->descriptor = newClass->descriptorAlloc; @@ -195,27 +225,24 @@ ClassObject* dvmGenerateProxyClass(StringObject* str, ArrayObject* interfaces, dvmLinearReadOnly(newClass->classLoader, newClass->interfaces); /* - * The class has one instance field, "protected InvocationHandler h", - * which is filled in by the constructor. + * Static field list. We have one private field, for our list of + * exceptions declared for each method. */ - newClass->ifieldCount = 1; - newClass->ifields = (InstField*) dvmLinearAlloc(newClass->classLoader, - 1 * sizeof(InstField)); - InstField* ifield = &newClass->ifields[0]; - ifield->field.clazz = newClass; - ifield->field.name = "h"; - ifield->field.signature = "Ljava/lang/reflect/InvocationHandler;"; - ifield->field.accessFlags = ACC_PROTECTED; - ifield->byteOffset = -1; /* set later */ - dvmLinearReadOnly(newClass->classLoader, newClass->ifields); - + newClass->sfieldCount = 1; + newClass->sfields = (StaticField*) calloc(1, sizeof(StaticField)); + StaticField* sfield = &newClass->sfields[kThrowsField]; + sfield->field.clazz = newClass; + sfield->field.name = "throws"; + sfield->field.signature = "[[Ljava/lang/Throwable;"; + sfield->field.accessFlags = ACC_STATIC | ACC_PRIVATE; + dvmSetStaticFieldObject(sfield, (Object*)throws); /* * Everything is ready. See if the linker will lap it up. */ newClass->status = CLASS_LOADED; if (!dvmLinkClass(newClass, true)) { - LOGI("Proxy class link failed\n"); + LOGD("Proxy class link failed\n"); goto bail; } @@ -239,26 +266,38 @@ bail: /* must free innards explicitly if we didn't finish linking */ dvmFreeClassInnards(newClass); newClass = NULL; - dvmThrowException("Ljava/lang/RuntimeException;", NULL); + if (!dvmCheckException(dvmThreadSelf())) { + /* throw something */ + dvmThrowException("Ljava/lang/RuntimeException;", NULL); + } } - /* this allows the GC to free it */ + /* allow the GC to free these when nothing else has a reference */ + dvmReleaseTrackedAlloc((Object*) throws, NULL); dvmReleaseTrackedAlloc((Object*) newClass, NULL); return newClass; } + /* * Generate a list of methods. The Method pointers returned point to the * abstract method definition from the appropriate interface, or to the * virtual method definition in java.lang.Object. + * + * We also allocate an array of arrays of throwable classes, one for each + * method,so we can do some special handling of checked exceptions. The + * caller must call ReleaseTrackedAlloc() on *pThrows. */ static bool gatherMethods(ArrayObject* interfaces, Method*** pMethods, - int* pMethodCount) + ArrayObject** pThrows, int* pMethodCount) { ClassObject** classes; - Method** methods; - int numInterfaces, maxCount, actualCount; + ArrayObject* throws = NULL; + Method** methods = NULL; + Method** allMethods = NULL; + int numInterfaces, maxCount, actualCount, allCount; + bool result = false; int i; /* @@ -287,21 +326,21 @@ static bool gatherMethods(ArrayObject* interfaces, Method*** pMethods, } methods = (Method**) malloc(maxCount * sizeof(*methods)); - if (methods == NULL) - return false; + allMethods = (Method**) malloc(maxCount * sizeof(*methods)); + if (methods == NULL || allMethods == NULL) + goto bail; /* * First three entries are the java.lang.Object methods. */ ClassObject* obj = gDvm.classJavaLangObject; - methods[0] = obj->vtable[gDvm.voffJavaLangObject_equals]; - methods[1] = obj->vtable[gDvm.voffJavaLangObject_hashCode]; - methods[2] = obj->vtable[gDvm.voffJavaLangObject_toString]; - actualCount = 3; + allMethods[0] = obj->vtable[gDvm.voffJavaLangObject_equals]; + allMethods[1] = obj->vtable[gDvm.voffJavaLangObject_hashCode]; + allMethods[2] = obj->vtable[gDvm.voffJavaLangObject_toString]; + allCount = 3; /* - * Add the methods from each interface, in order, checking for - * duplicates. This is O(n^2), but that should be okay here. + * Add the methods from each interface, in order. */ classes = (ClassObject**) interfaces->contents; for (i = 0; i < numInterfaces; i++, classes++) { @@ -309,8 +348,7 @@ static bool gatherMethods(ArrayObject* interfaces, Method*** pMethods, int j; for (j = 0; j < clazz->virtualMethodCount; j++) { - if (addMethod(&clazz->virtualMethods[j], methods, actualCount)) - actualCount++; + allMethods[allCount++] = &clazz->virtualMethods[j]; } for (j = 0; j < clazz->iftableCount; j++) { @@ -318,12 +356,31 @@ static bool gatherMethods(ArrayObject* interfaces, Method*** pMethods, int k; for (k = 0; k < iclass->virtualMethodCount; k++) { - if (addMethod(&iclass->virtualMethods[k], methods, actualCount)) - actualCount++; + allMethods[allCount++] = &iclass->virtualMethods[k]; } } } + assert(allCount == maxCount); + /* + * Allocate some storage to hold the lists of throwables. We need + * one entry per unique method, but it's convenient to allocate it + * ahead of the duplicate processing. + */ + ClassObject* arrArrClass; + arrArrClass = dvmFindArrayClass("[[Ljava/lang/Throwable;", NULL); + if (arrArrClass == NULL) + goto bail; + throws = dvmAllocArrayByClass(arrArrClass, allCount, ALLOC_DEFAULT); + + /* + * Identify and remove duplicates. + */ + actualCount = copyWithoutDuplicates(allMethods, allCount, methods, throws); + if (actualCount < 0) + goto bail; + + //LOGI("gathered methods:\n"); //for (i = 0; i < actualCount; i++) { // LOGI(" %d: %s.%s\n", // i, methods[i]->clazz->descriptor, methods[i]->name); @@ -331,28 +388,398 @@ static bool gatherMethods(ArrayObject* interfaces, Method*** pMethods, *pMethods = methods; *pMethodCount = actualCount; - return true; + *pThrows = throws; + result = true; + +bail: + free(allMethods); + if (!result) { + free(methods); + dvmReleaseTrackedAlloc((Object*)throws, NULL); + } + return result; } /* - * Add a method to "methArray" if a matching method does not already - * exist. Two methods match if they have the same name and signature. + * Identify and remove duplicates, where "duplicate" means it has the + * same name and arguments, but not necessarily the same return type. + * + * If duplicate methods have different return types, we want to use the + * first method whose return type is assignable from all other duplicate + * methods. That is, if we have: + * class base {...} + * class sub extends base {...} + * class subsub extends sub {...} + * Then we want to return the method that returns subsub, since callers + * to any form of the method will get a usable object back. + * + * All other duplicate methods are stripped out. * - * Returns "true" if the item was added, "false" if a duplicate was - * found and the method was not added. + * This also populates the "throwLists" array with arrays of Class objects, + * one entry per method in "outMethods". Methods that don't declare any + * throwables (or have no common throwables with duplicate methods) will + * have NULL entries. + * + * Returns the number of methods copied into "methods", or -1 on failure. */ -static bool addMethod(Method* meth, Method** methArray, int slot) +static int copyWithoutDuplicates(Method** allMethods, int allCount, + Method** outMethods, ArrayObject* throwLists) { - int i; + Method* best; + int outCount = 0; + int i, j; - for (i = 0; i < slot; i++) { - if (dvmCompareMethodNamesAndProtos(methArray[i], meth) == 0) { - return false; + /* + * The plan is to run through all methods, checking all other methods + * for a duplicate. If we find a match, we see if the other methods' + * return type is compatible/assignable with ours. If the current + * method is assignable from all others, we copy it to the new list, + * and NULL out all other entries. If not, we keep looking for a + * better version. + * + * If there are no duplicates, we copy the method and NULL the entry. + * + * At the end of processing, if we have any non-NULL entries, then we + * have bad duplicates and must exit with an exception. + */ + for (i = 0; i < allCount; i++) { + bool best, dupe; + + if (allMethods[i] == NULL) + continue; + + /* + * Find all duplicates. If any of the return types is not + * assignable to our return type, then we're not the best. + * + * We start from 0, not i, because we need to compare assignability + * the other direction even if we've compared these before. + */ + dupe = false; + best = true; + for (j = 0; j < allCount; j++) { + if (i == j) + continue; + if (allMethods[j] == NULL) + continue; + + if (dvmCompareMethodNamesAndParameterProtos(allMethods[i], + allMethods[j]) == 0) + { + /* + * Duplicate method, check return type. If it's a primitive + * type or void, the types must match exactly, or we throw + * an exception now. + */ + LOGV("MATCH on %s.%s and %s.%s\n", + allMethods[i]->clazz->descriptor, allMethods[i]->name, + allMethods[j]->clazz->descriptor, allMethods[j]->name); + dupe = true; + if (!returnTypesAreCompatible(allMethods[i], allMethods[j])) + best = false; + } + } + + /* + * If this is the best of a set of duplicates, copy it over and + * nuke all duplicates. + * + * While we do this, we create the set of exceptions declared to + * be thrown by all occurrences of the method. + */ + if (dupe) { + if (best) { + LOGV("BEST %d %s.%s -> %d\n", i, + allMethods[i]->clazz->descriptor, allMethods[i]->name, + outCount); + + /* if we have exceptions, make a local copy */ + PointerSet* commonThrows = NULL; + if (!createExceptionClassList(allMethods[i], &commonThrows)) + return -1; + + /* + * Run through one more time, erasing the duplicates. (This + * would go faster if we had marked them somehow.) + */ + for (j = 0; j < allCount; j++) { + if (i == j) + continue; + if (allMethods[j] == NULL) + continue; + if (dvmCompareMethodNamesAndParameterProtos(allMethods[i], + allMethods[j]) == 0) + { + LOGV("DEL %d %s.%s\n", j, + allMethods[j]->clazz->descriptor, + allMethods[j]->name); + + /* + * Update set to hold the intersection of method[i]'s + * and method[j]'s throws. + */ + if (commonThrows != NULL) { + updateExceptionClassList(allMethods[j], + commonThrows); + } + + allMethods[j] = NULL; + } + } + + /* + * If the set of Throwable classes isn't empty, create an + * array of Class, copy them into it, and put the result + * into the "throwLists" array. + */ + if (commonThrows != NULL && + dvmPointerSetGetCount(commonThrows) > 0) + { + int commonCount = dvmPointerSetGetCount(commonThrows); + ArrayObject* throwArray; + Object** contents; + int ent; + + throwArray = dvmAllocArrayByClass( + gDvm.classJavaLangClassArray, commonCount, + ALLOC_DEFAULT); + if (throwArray == NULL) { + LOGE("common-throw array alloc failed\n"); + return -1; + } + + contents = (Object**) throwArray->contents; + for (ent = 0; ent < commonCount; ent++) { + contents[ent] = (Object*) + dvmPointerSetGetEntry(commonThrows, ent); + } + + /* add it to the array of arrays */ + contents = (Object**) throwLists->contents; + contents[outCount] = (Object*) throwArray; + dvmReleaseTrackedAlloc((Object*) throwArray, NULL); + } + + /* copy the winner and NULL it out */ + outMethods[outCount++] = allMethods[i]; + allMethods[i] = NULL; + + dvmPointerSetFree(commonThrows); + } else { + LOGV("BEST not %d\n", i); + } + } else { + /* + * Singleton. Copy the entry and NULL it out. + */ + LOGV("COPY singleton %d %s.%s -> %d\n", i, + allMethods[i]->clazz->descriptor, allMethods[i]->name, + outCount); + + /* keep track of our throwables */ + ArrayObject* exceptionArray = dvmGetMethodThrows(allMethods[i]); + if (exceptionArray != NULL) { + Object** contents; + + contents = (Object**) throwLists->contents; + contents[outCount] = (Object*) exceptionArray; + dvmReleaseTrackedAlloc((Object*) exceptionArray, NULL); + } + + outMethods[outCount++] = allMethods[i]; + allMethods[i] = NULL; } } - methArray[slot] = meth; - return true; + /* + * Check for stragglers. If we find any, throw an exception. + */ + for (i = 0; i < allCount; i++) { + if (allMethods[i] != NULL) { + LOGV("BAD DUPE: %d %s.%s\n", i, + allMethods[i]->clazz->descriptor, allMethods[i]->name); + dvmThrowException("Ljava/lang/IllegalArgumentException;", + "incompatible return types in proxied interfaces"); + return -1; + } + } + + return outCount; +} + + +/* + * Classes can declare to throw multiple exceptions in a hierarchy, e.g. + * IOException and FileNotFoundException. Since we're only interested in + * knowing the set that can be thrown without requiring an extra wrapper, + * we can remove anything that is a subclass of something else in the list. + * + * The "mix" step we do next reduces things toward the most-derived class, + * so it's important that we start with the least-derived classes. + */ +static void reduceExceptionClassList(ArrayObject* exceptionArray) +{ + const ClassObject** classes = (const ClassObject**)exceptionArray->contents; + int len = exceptionArray->length; + int i, j; + + /* + * Consider all pairs of classes. If one is the subclass of the other, + * null out the subclass. + */ + for (i = 0; i < len-1; i++) { + if (classes[i] == NULL) + continue; + for (j = i + 1; j < len; j++) { + if (classes[j] == NULL) + continue; + + if (dvmInstanceof(classes[i], classes[j])) { + classes[i] = NULL; + break; /* no more comparisons against classes[i] */ + } else if (dvmInstanceof(classes[j], classes[i])) { + classes[j] = NULL; + } + } + } +} + +/* + * Create a local array with a copy of the throwable classes declared by + * "method". If no throws are declared, "*pSet" will be NULL. + * + * Returns "false" on allocation failure. + */ +static bool createExceptionClassList(const Method* method, PointerSet** pThrows) +{ + ArrayObject* exceptionArray = NULL; + bool result = false; + + exceptionArray = dvmGetMethodThrows(method); + if (exceptionArray != NULL && exceptionArray->length > 0) { + /* reduce list, nulling out redundant entries */ + reduceExceptionClassList(exceptionArray); + + *pThrows = dvmPointerSetAlloc(exceptionArray->length); + if (*pThrows == NULL) + goto bail; + + const ClassObject** contents; + int i; + + contents = (const ClassObject**) exceptionArray->contents; + for (i = 0; i < (int) exceptionArray->length; i++) { + if (contents[i] != NULL) + dvmPointerSetAddEntry(*pThrows, contents[i]); + } + } else { + *pThrows = NULL; + } + + result = true; + +bail: + dvmReleaseTrackedAlloc((Object*) exceptionArray, NULL); + return result; +} + +/* + * We need to compute the intersection of the arguments, i.e. remove + * anything from "throws" that isn't in the method's list of throws. + * + * If one class is a subclass of another, we want to keep just the subclass, + * moving toward the most-restrictive set. + * + * We assume these are all classes, and don't try to filter out interfaces. + */ +static void updateExceptionClassList(const Method* method, PointerSet* throws) +{ + int setSize = dvmPointerSetGetCount(throws); + if (setSize == 0) + return; + + ArrayObject* exceptionArray = dvmGetMethodThrows(method); + if (exceptionArray == NULL) { + /* nothing declared, so intersection is empty */ + dvmPointerSetClear(throws); + return; + } + + /* reduce list, nulling out redundant entries */ + reduceExceptionClassList(exceptionArray); + + int mixLen = dvmPointerSetGetCount(throws); + const ClassObject* mixSet[mixLen]; + + int declLen = exceptionArray->length; + const ClassObject** declSet = (const ClassObject**)exceptionArray->contents; + + int i, j; + + /* grab a local copy to work on */ + for (i = 0; i < mixLen; i++) { + mixSet[i] = dvmPointerSetGetEntry(throws, i); + } + + for (i = 0; i < mixLen; i++) { + for (j = 0; j < declLen; j++) { + if (declSet[j] == NULL) + continue; + + if (mixSet[i] == declSet[j]) { + /* match, keep this one */ + break; + } else if (dvmInstanceof(mixSet[i], declSet[j])) { + /* mix is a subclass of a declared throwable, keep it */ + break; + } else if (dvmInstanceof(declSet[j], mixSet[i])) { + /* mix is a superclass, replace it */ + mixSet[i] = declSet[j]; + break; + } + } + + if (j == declLen) { + /* no match, remove entry by nulling it out */ + mixSet[i] = NULL; + } + } + + /* copy results back out; this eliminates duplicates as we go */ + dvmPointerSetClear(throws); + for (i = 0; i < mixLen; i++) { + if (mixSet[i] != NULL) + dvmPointerSetAddEntry(throws, mixSet[i]); + } + + dvmReleaseTrackedAlloc((Object*) exceptionArray, NULL); +} + + +/* + * Check to see if the return types are compatible. + * + * If the return type is primitive or void, it must match exactly. + * + * If not, the type in "subMethod" must be assignable to the type in + * "baseMethod". + */ +static bool returnTypesAreCompatible(Method* subMethod, Method* baseMethod) +{ + const char* baseSig = dexProtoGetReturnType(&baseMethod->prototype); + const char* subSig = dexProtoGetReturnType(&subMethod->prototype); + ClassObject* baseClass; + ClassObject* subClass; + + if (baseSig[1] == '\0' || subSig[1] == '\0') { + /* at least one is primitive type */ + return (baseSig[0] == subSig[0] && baseSig[1] == subSig[1]); + } + + baseClass = dvmFindClass(baseSig, baseMethod->clazz->classLoader); + subClass = dvmFindClass(subSig, subMethod->clazz->classLoader); + bool result = dvmInstanceof(subClass, baseClass); + return result; } /* @@ -478,24 +905,16 @@ bail: } /* - * This is the constructor for a generated proxy object. + * This is the constructor for a generated proxy object. All we need to + * do is stuff "handler" into "h". */ static void proxyConstructor(const u4* args, JValue* pResult, const Method* method, Thread* self) { Object* obj = (Object*) args[0]; Object* handler = (Object*) args[1]; - ClassObject* clazz = obj->clazz; - int fieldOffset; - - fieldOffset = dvmFindFieldOffset(clazz, "h", - "Ljava/lang/reflect/InvocationHandler;"); - if (fieldOffset < 0) { - LOGE("Unable to find 'h' in Proxy object\n"); - //dvmDumpClass(clazz, kDumpClassFullDetail); - dvmAbort(); // this should never happen - } - dvmSetFieldObject(obj, fieldOffset, handler); + + dvmSetFieldObject(obj, gDvm.offJavaLangReflectProxy_h, handler); } /* @@ -521,15 +940,10 @@ static void proxyInvoker(const u4* args, JValue* pResult, JValue invokeResult; /* - * Retrieve handler object for this proxy instance. + * Retrieve handler object for this proxy instance. The field is + * defined in the superclass (Proxy). */ - hOffset = dvmFindFieldOffset(thisObj->clazz, "h", - "Ljava/lang/reflect/InvocationHandler;"); - if (hOffset < 0) { - LOGE("Unable to find 'h' in Proxy object\n"); - dvmAbort(); - } - handler = dvmGetFieldObject(thisObj, hOffset); + handler = dvmGetFieldObject(thisObj, gDvm.offJavaLangReflectProxy_h); /* * Find the invoke() method, looking in "this"s class. (Because we @@ -595,8 +1009,14 @@ static void proxyInvoker(const u4* args, JValue* pResult, */ dvmCallMethod(self, invoke, handler, &invokeResult, thisObj, methodObj, argArray); - if (dvmCheckException(self)) + if (dvmCheckException(self)) { + Object* excep = dvmGetException(self); + if (mustWrapException(method, excep)) { + /* wrap with UndeclaredThrowableException */ + dvmWrapException("Ljava/lang/reflect/UndeclaredThrowableException;"); + } goto bail; + } /* * Unbox the return value. If it's the wrong type, throw a @@ -625,3 +1045,53 @@ bail: dvmReleaseTrackedAlloc((Object*)argArray, self); } +/* + * Determine if it's okay for this method to throw this exception. If + * an unchecked exception was thrown we immediately return false. If + * checked, we have to ensure that this method and all of its duplicates + * have declared that they throw it. + */ +static bool mustWrapException(const Method* method, const Object* throwable) +{ + const ArrayObject* throws; + const ArrayObject* methodThrows; + const Object** contents; + const ClassObject** classes; + + if (!dvmIsCheckedException(throwable)) + return false; + + const StaticField* sfield = &method->clazz->sfields[kThrowsField]; + throws = (ArrayObject*) dvmGetStaticFieldObject(sfield); + + int methodIndex = method - method->clazz->virtualMethods; + assert(methodIndex >= 0 && methodIndex < method->clazz->virtualMethodCount); + + contents = (const Object**) throws->contents; + methodThrows = (ArrayObject*) contents[methodIndex]; + + if (methodThrows == NULL) { + /* no throws declared, must wrap all checked exceptions */ + //printf("+++ methodThrows[%d] is null, wrapping all\n", methodIndex); + return true; + } + + int throwCount = methodThrows->length; + classes = (const ClassObject**) methodThrows->contents; + int i; + + //printf("%s.%s list:\n", method->clazz->descriptor, method->name); + //for (i = 0; i < throwCount; i++) + // printf(" %d: %s\n", i, classes[i]->descriptor); + + for (i = 0; i < throwCount; i++) { + if (dvmInstanceof(throwable->clazz, classes[i])) { + /* this was declared, okay to throw */ + return false; + } + } + + /* no match in declared throws */ + return true; +} + diff --git a/vm/reflect/Reflect.c b/vm/reflect/Reflect.c index 19a572c07..b7de934de 100644 --- a/vm/reflect/Reflect.c +++ b/vm/reflect/Reflect.c @@ -662,6 +662,12 @@ Object* dvmCreateReflectMethodObject(const Method* meth) char* cp; int slot; + if (dvmCheckException(dvmThreadSelf())) { + LOGW("WARNING: dvmCreateReflectMethodObject called with " + "exception pending\n"); + return NULL; + } + dexStringCacheInit(&mangle); /* parent should guarantee init so we don't have to check on every call */ |