/* * 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. */ /* * Implementation of java.lang.reflect.Proxy. * * Traditionally this is implemented entirely in interpreted code, * generating bytecode that defines the proxy class. Dalvik doesn't * currently support this approach, so we generate the class directly. If * we add support for DefineClass with standard classfiles we can * eliminate this. */ #include "Dalvik.h" #include <stdlib.h> // fwd 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); 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 #define kProxySFieldCount 1 /* * Generate a proxy class with the specified name, interfaces, and loader. * "interfaces" is an array of class objects. * * 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. */ ClassObject* dvmGenerateProxyClass(StringObject* str, ArrayObject* interfaces, Object* loader) { int result = -1; ArrayObject* throws = NULL; char* nameStr = dvmCreateCstrFromString(str); if (nameStr == NULL) { dvmThrowIllegalArgumentException("missing name"); return NULL; } LOGV("+++ Generate proxy class '%s' %p from %d interface classes", nameStr, loader, interfaces->length); /* * Characteristics of a Proxy class: * - 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 (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, 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 * all the heavy lifting (notably populating the virtual and interface * method tables). */ /* * Allocate storage for the class object and set some basic fields. */ size_t newClassSize = sizeof(ClassObject) + kProxySFieldCount * sizeof(StaticField); ClassObject* newClass = (ClassObject*) dvmMalloc(newClassSize, ALLOC_NON_MOVING); if (newClass == NULL) goto bail; DVM_OBJECT_INIT(newClass, gDvm.classJavaLangClass); dvmSetClassSerialNumber(newClass); newClass->descriptorAlloc = dvmNameToDescriptor(nameStr); newClass->descriptor = newClass->descriptorAlloc; SET_CLASS_FLAG(newClass, ACC_PUBLIC | ACC_FINAL); dvmSetFieldObject((Object *)newClass, OFFSETOF_MEMBER(ClassObject, super), (Object *)gDvm.classJavaLangReflectProxy); newClass->primitiveType = PRIM_NOT; dvmSetFieldObject((Object *)newClass, OFFSETOF_MEMBER(ClassObject, classLoader), (Object *)loader); /* * Add direct method definitions. We have one (the constructor). */ newClass->directMethodCount = 1; newClass->directMethods = (Method*) dvmLinearAlloc(newClass->classLoader, 1 * sizeof(Method)); createConstructor(newClass, &newClass->directMethods[0]); dvmLinearReadOnly(newClass->classLoader, newClass->directMethods); /* * Add virtual method definitions. */ { /* * Generate a temporary list of virtual methods. */ int methodCount; Method **methods; if (!gatherMethods(interfaces, &methods, &throws, &methodCount)) { goto bail; } newClass->virtualMethodCount = methodCount; size_t virtualMethodsSize = methodCount * sizeof(Method); newClass->virtualMethods = (Method*)dvmLinearAlloc(newClass->classLoader, virtualMethodsSize); for (int i = 0; i < newClass->virtualMethodCount; i++) { createHandlerMethod(newClass, &newClass->virtualMethods[i], methods[i]); } free(methods); dvmLinearReadOnly(newClass->classLoader, newClass->virtualMethods); } /* * Add interface list. */ { size_t interfaceCount = interfaces->length; ClassObject** ifArray = (ClassObject**)(void*)interfaces->contents; newClass->interfaceCount = interfaceCount; size_t interfacesSize = sizeof(ClassObject*) * interfaceCount; newClass->interfaces = (ClassObject**)dvmLinearAlloc(newClass->classLoader, interfacesSize); for (size_t i = 0; i < interfaceCount; i++) newClass->interfaces[i] = ifArray[i]; dvmLinearReadOnly(newClass->classLoader, newClass->interfaces); } /* * Static field list. We have one private field, for our list of * exceptions declared for each method. */ assert(kProxySFieldCount == 1); newClass->sfieldCount = kProxySFieldCount; { StaticField* sfield = &newClass->sfields[kThrowsField]; sfield->clazz = newClass; sfield->name = "throws"; sfield->signature = "[[Ljava/lang/Throwable;"; sfield->accessFlags = ACC_STATIC | ACC_PRIVATE; dvmSetStaticFieldObject(sfield, (Object*)throws); } /* * Everything is ready. This class didn't come out of a DEX file * so we didn't tuck any indexes into the class object. We can * advance to LOADED state immediately. */ newClass->status = CLASS_LOADED; if (!dvmLinkClass(newClass)) { LOGD("Proxy class link failed"); goto bail; } /* * All good. Add it to the hash table. We should NOT see a collision * here; if we do, it means the caller has screwed up and provided us * with a duplicate name. */ if (!dvmAddClassToHash(newClass)) { LOGE("ERROR: attempted to generate %s more than once", newClass->descriptor); goto bail; } result = 0; bail: free(nameStr); if (result != 0) { /* must free innards explicitly if we didn't finish linking */ dvmFreeClassInnards(newClass); newClass = NULL; if (!dvmCheckException(dvmThreadSelf())) { /* throw something */ dvmThrowRuntimeException(NULL); } } /* 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, ArrayObject** pThrows, int* pMethodCount) { ClassObject** classes; ArrayObject* throws = NULL; Method** methods = NULL; Method** allMethods = NULL; int numInterfaces, maxCount, actualCount, allCount; bool result = false; int i; /* * Get a maximum count so we can allocate storage. We need the * methods declared by each interface and all of its superinterfaces. */ maxCount = 3; // 3 methods in java.lang.Object numInterfaces = interfaces->length; classes = (ClassObject**)(void*)interfaces->contents; for (i = 0; i < numInterfaces; i++, classes++) { ClassObject* clazz = *classes; LOGVV("--- %s virtualMethodCount=%d", clazz->descriptor, clazz->virtualMethodCount); maxCount += clazz->virtualMethodCount; int j; for (j = 0; j < clazz->iftableCount; j++) { ClassObject* iclass = clazz->iftable[j].clazz; LOGVV("--- +%s %d", iclass->descriptor, iclass->virtualMethodCount); maxCount += iclass->virtualMethodCount; } } methods = (Method**) malloc(maxCount * sizeof(*methods)); 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; 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. */ classes = (ClassObject**)(void*)interfaces->contents; for (i = 0; i < numInterfaces; i++, classes++) { ClassObject* clazz = *classes; int j; for (j = 0; j < clazz->virtualMethodCount; j++) { allMethods[allCount++] = &clazz->virtualMethods[j]; } for (j = 0; j < clazz->iftableCount; j++) { ClassObject* iclass = clazz->iftable[j].clazz; int k; for (k = 0; k < iclass->virtualMethodCount; k++) { 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:"); //for (i = 0; i < actualCount; i++) { // LOGI(" %d: %s.%s", // i, methods[i]->clazz->descriptor, methods[i]->name); //} *pMethods = methods; *pMethodCount = actualCount; *pThrows = throws; result = true; bail: free(allMethods); if (!result) { free(methods); dvmReleaseTrackedAlloc((Object*)throws, NULL); } return result; } /* * 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. * * 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 int copyWithoutDuplicates(Method** allMethods, int allCount, Method** outMethods, ArrayObject* throwLists) { int outCount = 0; int i, j; /* * 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", 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", 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", 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"); return -1; } contents = (Object**)(void*)throwArray->contents; for (ent = 0; ent < commonCount; ent++) { contents[ent] = (Object*) dvmPointerSetGetEntry(commonThrows, ent); } /* add it to the array of arrays */ contents = (Object**)(void*)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", i); } } else { /* * Singleton. Copy the entry and NULL it out. */ LOGV("COPY singleton %d %s.%s -> %d", 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**)(void*)throwLists->contents; contents[outCount] = (Object*) exceptionArray; dvmReleaseTrackedAlloc((Object*) exceptionArray, NULL); } outMethods[outCount++] = allMethods[i]; allMethods[i] = NULL; } } /* * 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", i, allMethods[i]->clazz->descriptor, allMethods[i]->name); dvmThrowIllegalArgumentException( "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**)(void*)exceptionArray->contents; /* * Consider all pairs of classes. If one is the subclass of the other, * null out the subclass. */ size_t len = exceptionArray->length; for (size_t i = 0; i < len - 1; i++) { if (classes[i] == NULL) continue; for (size_t 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; contents = (const ClassObject**)(void*)exceptionArray->contents; for (size_t i = 0; i < 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); size_t mixLen = dvmPointerSetGetCount(throws); const ClassObject* mixSet[mixLen]; size_t declLen = exceptionArray->length; const ClassObject** declSet = (const ClassObject**)(void*)exceptionArray->contents; /* grab a local copy to work on */ for (size_t i = 0; i < mixLen; i++) { mixSet[i] = (ClassObject*)dvmPointerSetGetEntry(throws, i); } for (size_t i = 0; i < mixLen; i++) { size_t j; 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 (size_t 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; } /* * Create a constructor for our Proxy class. The constructor takes one * argument, a java.lang.reflect.InvocationHandler. */ static void createConstructor(ClassObject* clazz, Method* meth) { /* * The constructor signatures (->prototype and ->shorty) need to * be cloned from a method in a "real" DEX file. We declared the * otherwise unused method Proxy.constructorPrototype() just for * this purpose. */ meth->clazz = clazz; meth->accessFlags = ACC_PUBLIC | ACC_NATIVE; meth->name = "<init>"; meth->prototype = gDvm.methJavaLangReflectProxy_constructorPrototype->prototype; meth->shorty = gDvm.methJavaLangReflectProxy_constructorPrototype->shorty; // no pDexCode or pDexMethod int argsSize = dvmComputeMethodArgsSize(meth) + 1; meth->registersSize = meth->insSize = argsSize; meth->nativeFunc = proxyConstructor; } /* * Create a method in our Proxy class with the name and signature of * the interface method it implements. */ static void createHandlerMethod(ClassObject* clazz, Method* dstMeth, const Method* srcMeth) { dstMeth->clazz = clazz; dstMeth->insns = (u2*) srcMeth; dstMeth->accessFlags = ACC_PUBLIC | ACC_NATIVE; dstMeth->name = srcMeth->name; dstMeth->prototype = srcMeth->prototype; dstMeth->shorty = srcMeth->shorty; // no pDexCode or pDexMethod int argsSize = dvmComputeMethodArgsSize(dstMeth) + 1; dstMeth->registersSize = dstMeth->insSize = argsSize; dstMeth->nativeFunc = proxyInvoker; } /* * Return a new Object[] array with the contents of "args". We determine * the number and types of values in "args" based on the method signature. * Primitive types are boxed. * * Returns NULL if the method takes no arguments. * * The caller must call dvmReleaseTrackedAlloc() on the return value. * * On failure, returns with an appropriate exception raised. */ static ArrayObject* boxMethodArgs(const Method* method, const u4* args) { const char* desc = &method->shorty[1]; // [0] is the return type. /* count args */ size_t argCount = dexProtoGetParameterCount(&method->prototype); /* allocate storage */ ArrayObject* argArray = dvmAllocArrayByClass(gDvm.classJavaLangObjectArray, argCount, ALLOC_DEFAULT); if (argArray == NULL) return NULL; Object** argObjects = (Object**)(void*)argArray->contents; /* * Fill in the array. */ size_t srcIndex = 0; size_t dstIndex = 0; while (*desc != '\0') { char descChar = *(desc++); JValue value; switch (descChar) { case 'Z': case 'C': case 'F': case 'B': case 'S': case 'I': value.i = args[srcIndex++]; argObjects[dstIndex] = (Object*) dvmBoxPrimitive(value, dvmFindPrimitiveClass(descChar)); /* argObjects is tracked, don't need to hold this too */ dvmReleaseTrackedAlloc(argObjects[dstIndex], NULL); dstIndex++; break; case 'D': case 'J': value.j = dvmGetArgLong(args, srcIndex); srcIndex += 2; argObjects[dstIndex] = (Object*) dvmBoxPrimitive(value, dvmFindPrimitiveClass(descChar)); dvmReleaseTrackedAlloc(argObjects[dstIndex], NULL); dstIndex++; break; case '[': case 'L': argObjects[dstIndex++] = (Object*) args[srcIndex++]; break; } } return argArray; } /* * 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]; dvmSetFieldObject(obj, gDvm.offJavaLangReflectProxy_h, handler); } /* * This is the common message body for proxy methods. * * The method we're calling looks like: * public Object invoke(Object proxy, Method method, Object[] args) * * This means we have to create a Method object, box our arguments into * a new Object[] array, make the call, and unbox the return value if * necessary. */ static void proxyInvoker(const u4* args, JValue* pResult, const Method* method, Thread* self) { Object* thisObj = (Object*) args[0]; Object* methodObj = NULL; ArrayObject* argArray = NULL; Object* handler; Method* invoke; ClassObject* returnType; JValue invokeResult; /* * Retrieve handler object for this proxy instance. The field is * defined in the superclass (Proxy). */ handler = dvmGetFieldObject(thisObj, gDvm.offJavaLangReflectProxy_h); /* * Find the invoke() method, looking in "this"s class. (Because we * start here we don't have to convert it to a vtable index and then * index into this' vtable.) */ invoke = dvmFindVirtualMethodHierByDescriptor(handler->clazz, "invoke", "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;"); if (invoke == NULL) { LOGE("Unable to find invoke()"); dvmAbort(); } LOGV("invoke: %s.%s, this=%p, handler=%s", method->clazz->descriptor, method->name, thisObj, handler->clazz->descriptor); /* * Create a java.lang.reflect.Method object for this method. * * We don't want to use "method", because that's the concrete * implementation in the proxy class. We want the abstract Method * from the declaring interface. We have a pointer to it tucked * away in the "insns" field. * * TODO: this could be cached for performance. */ methodObj = dvmCreateReflectMethodObject((Method*) method->insns); if (methodObj == NULL) { assert(dvmCheckException(self)); goto bail; } /* * Determine the return type from the signature. * * TODO: this could be cached for performance. */ returnType = dvmGetBoxedReturnType(method); if (returnType == NULL) { char* desc = dexProtoCopyMethodDescriptor(&method->prototype); LOGE("Could not determine return type for '%s'", desc); free(desc); assert(dvmCheckException(self)); goto bail; } LOGV(" return type will be %s", returnType->descriptor); /* * Convert "args" array into Object[] array, using the method * signature to determine types. If the method takes no arguments, * we must pass null. */ argArray = boxMethodArgs(method, args+1); if (dvmCheckException(self)) goto bail; /* * Call h.invoke(proxy, method, args). * * We don't need to repackage exceptions, so if one has been thrown * just jump to the end. * * We're not adding invokeResult.l to the tracked allocation list, but * since we're just unboxing it or returning it to interpreted code * that shouldn't be a problem. */ dvmCallMethod(self, invoke, handler, &invokeResult, thisObj, methodObj, argArray); 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 * ClassCastException. If it's a null pointer and we need a * primitive type, throw a NullPointerException. */ if (returnType->primitiveType == PRIM_VOID) { LOGVV("+++ ignoring return to void"); } else if (invokeResult.l == NULL) { if (dvmIsPrimitiveClass(returnType)) { dvmThrowNullPointerException( "null result when primitive expected"); goto bail; } pResult->l = NULL; } else { if (!dvmUnboxPrimitive((Object*)invokeResult.l, returnType, pResult)) { dvmThrowClassCastException(((Object*)invokeResult.l)->clazz, returnType); goto bail; } } bail: dvmReleaseTrackedAlloc(methodObj, self); 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) { if (!dvmIsCheckedException(throwable)) return false; const StaticField* sfield = &method->clazz->sfields[kThrowsField]; const ArrayObject* throws = (ArrayObject*) dvmGetStaticFieldObject(sfield); int methodIndex = method - method->clazz->virtualMethods; assert(methodIndex >= 0 && methodIndex < method->clazz->virtualMethodCount); const Object** contents = (const Object**)(void*)throws->contents; const ArrayObject* methodThrows = (ArrayObject*) contents[methodIndex]; if (methodThrows == NULL) { /* no throws declared, must wrap all checked exceptions */ return true; } size_t throwCount = methodThrows->length; const ClassObject** classes = (const ClassObject**)(void*)methodThrows->contents; for (size_t 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; }