diff options
author | Brian Carlstrom <bdc@google.com> | 2014-08-05 12:46:17 -0700 |
---|---|---|
committer | Brian Carlstrom <bdc@google.com> | 2014-08-05 12:51:13 -0700 |
commit | 870b4f2d70d67d6dbb7d0881d101c61bed8caad2 (patch) | |
tree | 7487dad3970556a040f88a49852a3dc9ed19bebd /vm/reflect/Proxy.cpp | |
parent | 76e15e367ae1189b6f641ba8d16ca92bd179dac0 (diff) | |
download | android_dalvik-870b4f2d70d67d6dbb7d0881d101c61bed8caad2.tar.gz android_dalvik-870b4f2d70d67d6dbb7d0881d101c61bed8caad2.tar.bz2 android_dalvik-870b4f2d70d67d6dbb7d0881d101c61bed8caad2.zip |
Dalvik is dead, long live Dalvik!
croot
cd dalvik
repo start dalvik-is-dead-long-live-dalvik .
repo sync -c .
git rm -r README.txt
git rm -r dexopt
git rm -r tools/deadcode.py
git rm -r tools/dex-preopt
git rm -r tools/dexcheck
git rm -r tools/gdbjithelper
git rm -r unit-tests
git rm -r vm
git checkout HEAD vm/Common.h (needed by libdex)
git checkout HEAD vm/DalvikVersion.h (needed by libdex)
git checkout HEAD vm/Profile.h (needed by dmtracedump)
git add Android.mk (after removing vm, dexopt, and unit-tests references)
git commit -a -m 'Dalvik is dead, long live Dalvik!'
Bug: 14298175
Change-Id: I9dd13053677629d13496d4238af4374452cda415
Diffstat (limited to 'vm/reflect/Proxy.cpp')
-rw-r--r-- | vm/reflect/Proxy.cpp | 1029 |
1 files changed, 0 insertions, 1029 deletions
diff --git a/vm/reflect/Proxy.cpp b/vm/reflect/Proxy.cpp deleted file mode 100644 index 57d32e793..000000000 --- a/vm/reflect/Proxy.cpp +++ /dev/null @@ -1,1029 +0,0 @@ -/* - * 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) -{ - ClassObject* result = NULL; - ArrayObject* throws = NULL; - - char* nameStr = dvmCreateCstrFromString(str); - if (nameStr == NULL) { - dvmThrowIllegalArgumentException("missing name"); - return NULL; - } - - ALOGV("+++ 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)) { - ALOGD("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)) { - ALOGE("ERROR: attempted to generate %s more than once", - newClass->descriptor); - goto bail; - } - - result = newClass; - -bail: - free(nameStr); - if (result == NULL) { - /* must free innards explicitly if we didn't finish linking */ - dvmFreeClassInnards(newClass); - 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 result; -} - - -/* - * 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; - - //ALOGI("gathered methods:"); - //for (i = 0; i < actualCount; i++) { - // ALOGI(" %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. - */ - ALOGV("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) { - ALOGV("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) - { - ALOGV("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) { - ALOGE("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 { - ALOGV("BEST not %d", i); - } - } else { - /* - * Singleton. Copy the entry and NULL it out. - */ - ALOGV("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) { - ALOGV("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) { - ALOGE("Unable to find invoke()"); - dvmAbort(); - } - - ALOGV("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); - ALOGE("Could not determine return type for '%s'", desc); - free(desc); - assert(dvmCheckException(self)); - goto bail; - } - ALOGV(" 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; -} |