/* * 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 /* * Perform Proxy setup. */ bool dvmReflectProxyStartup() { /* * Standard methods we must provide in our proxy. */ Method* methE; 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"); if (methE == NULL || methH == NULL || methT == NULL || methF == NULL) { LOGE("Could not find equals/hashCode/toString/finalize in Object\n"); return false; } gDvm.voffJavaLangObject_equals = methE->methodIndex; gDvm.voffJavaLangObject_hashCode = methH->methodIndex; gDvm.voffJavaLangObject_toString = methT->methodIndex; gDvm.voffJavaLangObject_finalize = methF->methodIndex; /* * The prototype signature needs to be cloned from a method in a * "real" DEX file. We declared this otherwise unused method just * for this purpose. */ ClassObject* proxyClass; Method* meth; proxyClass = dvmFindSystemClassNoInit("Ljava/lang/reflect/Proxy;"); if (proxyClass == NULL) { LOGE("No java.lang.reflect.Proxy\n"); return false; } meth = dvmFindDirectMethodByDescriptor(proxyClass, "constructorPrototype", "(Ljava/lang/reflect/InvocationHandler;)V"); if (meth == NULL) { LOGE("Could not find java.lang.Proxy.constructorPrototype()\n"); return false; } 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; } /* * 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; char* nameStr = NULL; Method** methods = NULL; ArrayObject* throws = NULL; ClassObject* newClass = NULL; int i; nameStr = dvmCreateCstrFromString(str); if (nameStr == NULL) { dvmThrowException("Ljava/lang/IllegalArgumentException;", "missing name"); goto bail; } LOGV("+++ Generate proxy class '%s' %p from %d interface classes\n", 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). */ /* * Generate a temporary list of virtual methods. */ int methodCount = -1; if (!gatherMethods(interfaces, &methods, &throws, &methodCount)) goto bail; /* * Allocate storage for the class object and set some basic fields. */ newClass = (ClassObject*) dvmMalloc(sizeof(*newClass), ALLOC_DEFAULT); if (newClass == NULL) goto bail; DVM_OBJECT_INIT(&newClass->obj, gDvm.unlinkedJavaLangClass); dvmSetClassSerialNumber(newClass); newClass->descriptorAlloc = dvmNameToDescriptor(nameStr); newClass->descriptor = newClass->descriptorAlloc; newClass->accessFlags = ACC_PUBLIC | ACC_FINAL; newClass->super = gDvm.classJavaLangReflectProxy; newClass->primitiveType = PRIM_NOT; newClass->classLoader = loader; #if WITH_HPROF && WITH_HPROF_STACK hprofFillInStackTrace(newClass); #endif /* * 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. */ newClass->virtualMethodCount = methodCount; newClass->virtualMethods = (Method*) dvmLinearAlloc(newClass->classLoader, newClass->virtualMethodCount * sizeof(Method)); for (i = 0; i < newClass->virtualMethodCount; i++) { createHandlerMethod(newClass, &newClass->virtualMethods[i],methods[i]); } dvmLinearReadOnly(newClass->classLoader, newClass->virtualMethods); /* * Add interface list. */ int interfaceCount = interfaces->length; ClassObject** ifArray = (ClassObject**) interfaces->contents; newClass->interfaceCount = interfaceCount; newClass->interfaces = (ClassObject**)dvmLinearAlloc(newClass->classLoader, sizeof(ClassObject*) * interfaceCount); for (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. */ 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)) { LOGD("Proxy class link failed\n"); 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\n", newClass->descriptor); goto bail; } result = 0; bail: free(nameStr); free(methods); if (result != 0) { /* must free innards explicitly if we didn't finish linking */ dvmFreeClassInnards(newClass); newClass = NULL; if (!dvmCheckException(dvmThreadSelf())) { /* throw something */ dvmThrowException("Ljava/lang/RuntimeException;", 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**) interfaces->contents; for (i = 0; i < numInterfaces; i++, classes++) { ClassObject* clazz = *classes; LOGVV("--- %s virtualMethodCount=%d\n", 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\n", 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**) 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:\n"); //for (i = 0; i < actualCount; i++) { // LOGI(" %d: %s.%s\n", // 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) { Method* best; 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\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; } } /* * 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; } /* * Create a constructor for our Proxy class. The constructor takes one * argument, a java.lang.reflect.InvocationHandler. */ static void createConstructor(ClassObject* clazz, Method* meth) { 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. ArrayObject* argArray = NULL; int argCount; Object** argObjects; bool failed = true; /* count args */ argCount = dexProtoGetParameterCount(&method->prototype); /* allocate storage */ argArray = dvmAllocArray(gDvm.classJavaLangObjectArray, argCount, kObjectArrayRefWidth, ALLOC_DEFAULT); if (argArray == NULL) goto bail; argObjects = (Object**) argArray->contents; /* * Fill in the array. */ int srcIndex = 0; argCount = 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[argCount] = (Object*) dvmWrapPrimitive(value, dvmFindPrimitiveClass(descChar)); /* argObjects is tracked, don't need to hold this too */ dvmReleaseTrackedAlloc(argObjects[argCount], NULL); argCount++; break; case 'D': case 'J': value.j = dvmGetArgLong(args, srcIndex); srcIndex += 2; argObjects[argCount] = (Object*) dvmWrapPrimitive(value, dvmFindPrimitiveClass(descChar)); dvmReleaseTrackedAlloc(argObjects[argCount], NULL); argCount++; break; case '[': case 'L': argObjects[argCount++] = (Object*) args[srcIndex++]; break; } } failed = false; bail: if (failed) { dvmReleaseTrackedAlloc((Object*)argArray, NULL); argArray = NULL; } 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; int hOffset; 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()\n"); dvmAbort(); } LOGV("invoke: %s.%s, this=%p, handler=%s\n", 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'\n", desc); free(desc); assert(dvmCheckException(self)); goto bail; } LOGV(" return type will be %s\n", 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. */ 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\n"); } else if (invokeResult.l == NULL) { if (dvmIsPrimitiveClass(returnType)) { dvmThrowException("Ljava/lang/NullPointerException;", "null result when primitive expected"); goto bail; } pResult->l = NULL; } else { if (!dvmUnwrapPrimitive(invokeResult.l, returnType, pResult)) { dvmThrowExceptionWithClassMessage("Ljava/lang/ClassCastException;", ((Object*)invokeResult.l)->clazz->descriptor); 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) { 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; }