Home | History | Annotate | Download | only in reflect
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 /*
     18  * Implementation of java.lang.reflect.Proxy.
     19  *
     20  * Traditionally this is implemented entirely in interpreted code,
     21  * generating bytecode that defines the proxy class.  Dalvik doesn't
     22  * currently support this approach, so we generate the class directly.  If
     23  * we add support for DefineClass with standard classfiles we can
     24  * eliminate this.
     25  */
     26 #include "Dalvik.h"
     27 
     28 #include <stdlib.h>
     29 
     30 // fwd
     31 static bool returnTypesAreCompatible(Method* baseMethod, Method* subMethod);
     32 static bool gatherMethods(ArrayObject* interfaces, Method*** pMethods,\
     33     ArrayObject** pThrows, int* pMethodCount);
     34 static int copyWithoutDuplicates(Method** allMethods, int allCount,
     35     Method** outMethods, ArrayObject* throws);
     36 static bool createExceptionClassList(const Method* method,
     37     PointerSet** pThrows);
     38 static void updateExceptionClassList(const Method* method, PointerSet* throws);
     39 static void createConstructor(ClassObject* clazz, Method* meth);
     40 static void createHandlerMethod(ClassObject* clazz, Method* dstMeth,
     41     const Method* srcMeth);
     42 static void proxyConstructor(const u4* args, JValue* pResult,
     43     const Method* method, Thread* self);
     44 static void proxyInvoker(const u4* args, JValue* pResult,
     45     const Method* method, Thread* self);
     46 static bool mustWrapException(const Method* method, const Object* throwable);
     47 
     48 /* private static fields in the Proxy class */
     49 #define kThrowsField    0
     50 #define kProxySFieldCount 1
     51 
     52 /*
     53  * Generate a proxy class with the specified name, interfaces, and loader.
     54  * "interfaces" is an array of class objects.
     55  *
     56  * The Proxy.getProxyClass() code has done the following:
     57  *  - Verified that "interfaces" contains only interfaces
     58  *  - Verified that no interface appears twice
     59  *  - Prepended the package name to the class name if one or more
     60  *    interfaces are non-public
     61  *  - Searched for an existing instance of an appropriate Proxy class
     62  *
     63  * On failure we leave a partially-created class object sitting around,
     64  * but the garbage collector will take care of it.
     65  */
     66 ClassObject* dvmGenerateProxyClass(StringObject* str, ArrayObject* interfaces,
     67     Object* loader)
     68 {
     69     int result = -1;
     70     ArrayObject* throws = NULL;
     71 
     72     char* nameStr = dvmCreateCstrFromString(str);
     73     if (nameStr == NULL) {
     74         dvmThrowIllegalArgumentException("missing name");
     75         return NULL;
     76     }
     77 
     78     LOGV("+++ Generate proxy class '%s' %p from %d interface classes",
     79         nameStr, loader, interfaces->length);
     80 
     81 
     82     /*
     83      * Characteristics of a Proxy class:
     84      * - concrete class, public and final
     85      * - superclass is java.lang.reflect.Proxy
     86      * - implements all listed interfaces (req'd for instanceof)
     87      * - has one method for each method in the interfaces (for duplicates,
     88      *   the method in the earliest interface wins)
     89      * - has one constructor (takes an InvocationHandler arg)
     90      * - has overrides for hashCode, equals, and toString (these come first)
     91      * - has one field, a reference to the InvocationHandler object, inherited
     92      *   from Proxy
     93      *
     94      * TODO: set protection domain so it matches bootstrap classes.
     95      *
     96      * The idea here is to create a class object and fill in the details
     97      * as we would in loadClassFromDex(), and then call dvmLinkClass() to do
     98      * all the heavy lifting (notably populating the virtual and interface
     99      * method tables).
    100      */
    101 
    102     /*
    103      * Allocate storage for the class object and set some basic fields.
    104      */
    105     size_t newClassSize =
    106         sizeof(ClassObject) + kProxySFieldCount * sizeof(StaticField);
    107     ClassObject* newClass =
    108         (ClassObject*) dvmMalloc(newClassSize, ALLOC_NON_MOVING);
    109     if (newClass == NULL)
    110         goto bail;
    111     DVM_OBJECT_INIT(newClass, gDvm.classJavaLangClass);
    112     dvmSetClassSerialNumber(newClass);
    113     newClass->descriptorAlloc = dvmNameToDescriptor(nameStr);
    114     newClass->descriptor = newClass->descriptorAlloc;
    115     SET_CLASS_FLAG(newClass, ACC_PUBLIC | ACC_FINAL);
    116     dvmSetFieldObject((Object *)newClass,
    117                       OFFSETOF_MEMBER(ClassObject, super),
    118                       (Object *)gDvm.classJavaLangReflectProxy);
    119     newClass->primitiveType = PRIM_NOT;
    120     dvmSetFieldObject((Object *)newClass,
    121                       OFFSETOF_MEMBER(ClassObject, classLoader),
    122                       (Object *)loader);
    123 
    124     /*
    125      * Add direct method definitions.  We have one (the constructor).
    126      */
    127     newClass->directMethodCount = 1;
    128     newClass->directMethods = (Method*) dvmLinearAlloc(newClass->classLoader,
    129             1 * sizeof(Method));
    130     createConstructor(newClass, &newClass->directMethods[0]);
    131     dvmLinearReadOnly(newClass->classLoader, newClass->directMethods);
    132 
    133     /*
    134      * Add virtual method definitions.
    135      */
    136     {
    137         /*
    138          * Generate a temporary list of virtual methods.
    139          */
    140         int methodCount;
    141         Method **methods;
    142         if (!gatherMethods(interfaces, &methods, &throws, &methodCount)) {
    143             goto bail;
    144         }
    145         newClass->virtualMethodCount = methodCount;
    146         size_t virtualMethodsSize = methodCount * sizeof(Method);
    147         newClass->virtualMethods =
    148             (Method*)dvmLinearAlloc(newClass->classLoader, virtualMethodsSize);
    149         for (int i = 0; i < newClass->virtualMethodCount; i++) {
    150             createHandlerMethod(newClass, &newClass->virtualMethods[i], methods[i]);
    151         }
    152         free(methods);
    153         dvmLinearReadOnly(newClass->classLoader, newClass->virtualMethods);
    154     }
    155 
    156     /*
    157      * Add interface list.
    158      */
    159     {
    160         size_t interfaceCount = interfaces->length;
    161         ClassObject** ifArray = (ClassObject**)(void*)interfaces->contents;
    162         newClass->interfaceCount = interfaceCount;
    163         size_t interfacesSize = sizeof(ClassObject*) * interfaceCount;
    164         newClass->interfaces =
    165             (ClassObject**)dvmLinearAlloc(newClass->classLoader, interfacesSize);
    166         for (size_t i = 0; i < interfaceCount; i++)
    167           newClass->interfaces[i] = ifArray[i];
    168         dvmLinearReadOnly(newClass->classLoader, newClass->interfaces);
    169     }
    170 
    171     /*
    172      * Static field list.  We have one private field, for our list of
    173      * exceptions declared for each method.
    174      */
    175     assert(kProxySFieldCount == 1);
    176     newClass->sfieldCount = kProxySFieldCount;
    177     {
    178         StaticField* sfield = &newClass->sfields[kThrowsField];
    179         sfield->clazz = newClass;
    180         sfield->name = "throws";
    181         sfield->signature = "[[Ljava/lang/Throwable;";
    182         sfield->accessFlags = ACC_STATIC | ACC_PRIVATE;
    183         dvmSetStaticFieldObject(sfield, (Object*)throws);
    184     }
    185 
    186     /*
    187      * Everything is ready. This class didn't come out of a DEX file
    188      * so we didn't tuck any indexes into the class object.  We can
    189      * advance to LOADED state immediately.
    190      */
    191     newClass->status = CLASS_LOADED;
    192     if (!dvmLinkClass(newClass)) {
    193         LOGD("Proxy class link failed");
    194         goto bail;
    195     }
    196 
    197     /*
    198      * All good.  Add it to the hash table.  We should NOT see a collision
    199      * here; if we do, it means the caller has screwed up and provided us
    200      * with a duplicate name.
    201      */
    202     if (!dvmAddClassToHash(newClass)) {
    203         LOGE("ERROR: attempted to generate %s more than once",
    204             newClass->descriptor);
    205         goto bail;
    206     }
    207 
    208     result = 0;
    209 
    210 bail:
    211     free(nameStr);
    212     if (result != 0) {
    213         /* must free innards explicitly if we didn't finish linking */
    214         dvmFreeClassInnards(newClass);
    215         newClass = NULL;
    216         if (!dvmCheckException(dvmThreadSelf())) {
    217             /* throw something */
    218             dvmThrowRuntimeException(NULL);
    219         }
    220     }
    221 
    222     /* allow the GC to free these when nothing else has a reference */
    223     dvmReleaseTrackedAlloc((Object*) throws, NULL);
    224     dvmReleaseTrackedAlloc((Object*) newClass, NULL);
    225 
    226     return newClass;
    227 }
    228 
    229 
    230 /*
    231  * Generate a list of methods.  The Method pointers returned point to the
    232  * abstract method definition from the appropriate interface, or to the
    233  * virtual method definition in java.lang.Object.
    234  *
    235  * We also allocate an array of arrays of throwable classes, one for each
    236  * method,so we can do some special handling of checked exceptions.  The
    237  * caller must call ReleaseTrackedAlloc() on *pThrows.
    238  */
    239 static bool gatherMethods(ArrayObject* interfaces, Method*** pMethods,
    240     ArrayObject** pThrows, int* pMethodCount)
    241 {
    242     ClassObject** classes;
    243     ArrayObject* throws = NULL;
    244     Method** methods = NULL;
    245     Method** allMethods = NULL;
    246     int numInterfaces, maxCount, actualCount, allCount;
    247     bool result = false;
    248     int i;
    249 
    250     /*
    251      * Get a maximum count so we can allocate storage.  We need the
    252      * methods declared by each interface and all of its superinterfaces.
    253      */
    254     maxCount = 3;       // 3 methods in java.lang.Object
    255     numInterfaces = interfaces->length;
    256     classes = (ClassObject**)(void*)interfaces->contents;
    257 
    258     for (i = 0; i < numInterfaces; i++, classes++) {
    259         ClassObject* clazz = *classes;
    260 
    261         LOGVV("---  %s virtualMethodCount=%d",
    262             clazz->descriptor, clazz->virtualMethodCount);
    263         maxCount += clazz->virtualMethodCount;
    264 
    265         int j;
    266         for (j = 0; j < clazz->iftableCount; j++) {
    267             ClassObject* iclass = clazz->iftable[j].clazz;
    268 
    269             LOGVV("---  +%s %d",
    270                 iclass->descriptor, iclass->virtualMethodCount);
    271             maxCount += iclass->virtualMethodCount;
    272         }
    273     }
    274 
    275     methods = (Method**) malloc(maxCount * sizeof(*methods));
    276     allMethods = (Method**) malloc(maxCount * sizeof(*methods));
    277     if (methods == NULL || allMethods == NULL)
    278         goto bail;
    279 
    280     /*
    281      * First three entries are the java.lang.Object methods.
    282      */
    283     {
    284       ClassObject* obj = gDvm.classJavaLangObject;
    285       allMethods[0] = obj->vtable[gDvm.voffJavaLangObject_equals];
    286       allMethods[1] = obj->vtable[gDvm.voffJavaLangObject_hashCode];
    287       allMethods[2] = obj->vtable[gDvm.voffJavaLangObject_toString];
    288       allCount = 3;
    289     }
    290 
    291     /*
    292      * Add the methods from each interface, in order.
    293      */
    294     classes = (ClassObject**)(void*)interfaces->contents;
    295     for (i = 0; i < numInterfaces; i++, classes++) {
    296         ClassObject* clazz = *classes;
    297         int j;
    298 
    299         for (j = 0; j < clazz->virtualMethodCount; j++) {
    300             allMethods[allCount++] = &clazz->virtualMethods[j];
    301         }
    302 
    303         for (j = 0; j < clazz->iftableCount; j++) {
    304             ClassObject* iclass = clazz->iftable[j].clazz;
    305             int k;
    306 
    307             for (k = 0; k < iclass->virtualMethodCount; k++) {
    308                 allMethods[allCount++] = &iclass->virtualMethods[k];
    309             }
    310         }
    311     }
    312     assert(allCount == maxCount);
    313 
    314     /*
    315      * Allocate some storage to hold the lists of throwables.  We need
    316      * one entry per unique method, but it's convenient to allocate it
    317      * ahead of the duplicate processing.
    318      */
    319     ClassObject* arrArrClass;
    320     arrArrClass = dvmFindArrayClass("[[Ljava/lang/Throwable;", NULL);
    321     if (arrArrClass == NULL)
    322         goto bail;
    323     throws = dvmAllocArrayByClass(arrArrClass, allCount, ALLOC_DEFAULT);
    324 
    325     /*
    326      * Identify and remove duplicates.
    327      */
    328     actualCount = copyWithoutDuplicates(allMethods, allCount, methods, throws);
    329     if (actualCount < 0)
    330         goto bail;
    331 
    332     //LOGI("gathered methods:");
    333     //for (i = 0; i < actualCount; i++) {
    334     //    LOGI(" %d: %s.%s",
    335     //        i, methods[i]->clazz->descriptor, methods[i]->name);
    336     //}
    337 
    338     *pMethods = methods;
    339     *pMethodCount = actualCount;
    340     *pThrows = throws;
    341     result = true;
    342 
    343 bail:
    344     free(allMethods);
    345     if (!result) {
    346         free(methods);
    347         dvmReleaseTrackedAlloc((Object*)throws, NULL);
    348     }
    349     return result;
    350 }
    351 
    352 /*
    353  * Identify and remove duplicates, where "duplicate" means it has the
    354  * same name and arguments, but not necessarily the same return type.
    355  *
    356  * If duplicate methods have different return types, we want to use the
    357  * first method whose return type is assignable from all other duplicate
    358  * methods.  That is, if we have:
    359  *   class base {...}
    360  *   class sub extends base {...}
    361  *   class subsub extends sub {...}
    362  * Then we want to return the method that returns subsub, since callers
    363  * to any form of the method will get a usable object back.
    364  *
    365  * All other duplicate methods are stripped out.
    366  *
    367  * This also populates the "throwLists" array with arrays of Class objects,
    368  * one entry per method in "outMethods".  Methods that don't declare any
    369  * throwables (or have no common throwables with duplicate methods) will
    370  * have NULL entries.
    371  *
    372  * Returns the number of methods copied into "methods", or -1 on failure.
    373  */
    374 static int copyWithoutDuplicates(Method** allMethods, int allCount,
    375     Method** outMethods, ArrayObject* throwLists)
    376 {
    377     int outCount = 0;
    378     int i, j;
    379 
    380     /*
    381      * The plan is to run through all methods, checking all other methods
    382      * for a duplicate.  If we find a match, we see if the other methods'
    383      * return type is compatible/assignable with ours.  If the current
    384      * method is assignable from all others, we copy it to the new list,
    385      * and NULL out all other entries.  If not, we keep looking for a
    386      * better version.
    387      *
    388      * If there are no duplicates, we copy the method and NULL the entry.
    389      *
    390      * At the end of processing, if we have any non-NULL entries, then we
    391      * have bad duplicates and must exit with an exception.
    392      */
    393     for (i = 0; i < allCount; i++) {
    394         bool best, dupe;
    395 
    396         if (allMethods[i] == NULL)
    397             continue;
    398 
    399         /*
    400          * Find all duplicates.  If any of the return types is not
    401          * assignable to our return type, then we're not the best.
    402          *
    403          * We start from 0, not i, because we need to compare assignability
    404          * the other direction even if we've compared these before.
    405          */
    406         dupe = false;
    407         best = true;
    408         for (j = 0; j < allCount; j++) {
    409             if (i == j)
    410                 continue;
    411             if (allMethods[j] == NULL)
    412                 continue;
    413 
    414             if (dvmCompareMethodNamesAndParameterProtos(allMethods[i],
    415                     allMethods[j]) == 0)
    416             {
    417                 /*
    418                  * Duplicate method, check return type.  If it's a primitive
    419                  * type or void, the types must match exactly, or we throw
    420                  * an exception now.
    421                  */
    422                 LOGV("MATCH on %s.%s and %s.%s",
    423                     allMethods[i]->clazz->descriptor, allMethods[i]->name,
    424                     allMethods[j]->clazz->descriptor, allMethods[j]->name);
    425                 dupe = true;
    426                 if (!returnTypesAreCompatible(allMethods[i], allMethods[j]))
    427                     best = false;
    428             }
    429         }
    430 
    431         /*
    432          * If this is the best of a set of duplicates, copy it over and
    433          * nuke all duplicates.
    434          *
    435          * While we do this, we create the set of exceptions declared to
    436          * be thrown by all occurrences of the method.
    437          */
    438         if (dupe) {
    439             if (best) {
    440                 LOGV("BEST %d %s.%s -> %d", i,
    441                     allMethods[i]->clazz->descriptor, allMethods[i]->name,
    442                     outCount);
    443 
    444                 /* if we have exceptions, make a local copy */
    445                 PointerSet* commonThrows = NULL;
    446                 if (!createExceptionClassList(allMethods[i], &commonThrows))
    447                     return -1;
    448 
    449                 /*
    450                  * Run through one more time, erasing the duplicates.  (This
    451                  * would go faster if we had marked them somehow.)
    452                  */
    453                 for (j = 0; j < allCount; j++) {
    454                     if (i == j)
    455                         continue;
    456                     if (allMethods[j] == NULL)
    457                         continue;
    458                     if (dvmCompareMethodNamesAndParameterProtos(allMethods[i],
    459                             allMethods[j]) == 0)
    460                     {
    461                         LOGV("DEL %d %s.%s", j,
    462                             allMethods[j]->clazz->descriptor,
    463                             allMethods[j]->name);
    464 
    465                         /*
    466                          * Update set to hold the intersection of method[i]'s
    467                          * and method[j]'s throws.
    468                          */
    469                         if (commonThrows != NULL) {
    470                             updateExceptionClassList(allMethods[j],
    471                                 commonThrows);
    472                         }
    473 
    474                         allMethods[j] = NULL;
    475                     }
    476                 }
    477 
    478                 /*
    479                  * If the set of Throwable classes isn't empty, create an
    480                  * array of Class, copy them into it, and put the result
    481                  * into the "throwLists" array.
    482                  */
    483                 if (commonThrows != NULL &&
    484                     dvmPointerSetGetCount(commonThrows) > 0)
    485                 {
    486                     int commonCount = dvmPointerSetGetCount(commonThrows);
    487                     ArrayObject* throwArray;
    488                     Object** contents;
    489                     int ent;
    490 
    491                     throwArray = dvmAllocArrayByClass(
    492                             gDvm.classJavaLangClassArray, commonCount,
    493                             ALLOC_DEFAULT);
    494                     if (throwArray == NULL) {
    495                         LOGE("common-throw array alloc failed");
    496                         return -1;
    497                     }
    498 
    499                     contents = (Object**)(void*)throwArray->contents;
    500                     for (ent = 0; ent < commonCount; ent++) {
    501                         contents[ent] = (Object*)
    502                             dvmPointerSetGetEntry(commonThrows, ent);
    503                     }
    504 
    505                     /* add it to the array of arrays */
    506                     contents = (Object**)(void*)throwLists->contents;
    507                     contents[outCount] = (Object*) throwArray;
    508                     dvmReleaseTrackedAlloc((Object*) throwArray, NULL);
    509                 }
    510 
    511                 /* copy the winner and NULL it out */
    512                 outMethods[outCount++] = allMethods[i];
    513                 allMethods[i] = NULL;
    514 
    515                 dvmPointerSetFree(commonThrows);
    516             } else {
    517                 LOGV("BEST not %d", i);
    518             }
    519         } else {
    520             /*
    521              * Singleton.  Copy the entry and NULL it out.
    522              */
    523             LOGV("COPY singleton %d %s.%s -> %d", i,
    524                 allMethods[i]->clazz->descriptor, allMethods[i]->name,
    525                 outCount);
    526 
    527             /* keep track of our throwables */
    528             ArrayObject* exceptionArray = dvmGetMethodThrows(allMethods[i]);
    529             if (exceptionArray != NULL) {
    530                 Object** contents;
    531 
    532                 contents = (Object**)(void*)throwLists->contents;
    533                 contents[outCount] = (Object*) exceptionArray;
    534                 dvmReleaseTrackedAlloc((Object*) exceptionArray, NULL);
    535             }
    536 
    537             outMethods[outCount++] = allMethods[i];
    538             allMethods[i] = NULL;
    539         }
    540     }
    541 
    542     /*
    543      * Check for stragglers.  If we find any, throw an exception.
    544      */
    545     for (i = 0; i < allCount; i++) {
    546         if (allMethods[i] != NULL) {
    547             LOGV("BAD DUPE: %d %s.%s", i,
    548                 allMethods[i]->clazz->descriptor, allMethods[i]->name);
    549             dvmThrowIllegalArgumentException(
    550                 "incompatible return types in proxied interfaces");
    551             return -1;
    552         }
    553     }
    554 
    555     return outCount;
    556 }
    557 
    558 
    559 /*
    560  * Classes can declare to throw multiple exceptions in a hierarchy, e.g.
    561  * IOException and FileNotFoundException.  Since we're only interested in
    562  * knowing the set that can be thrown without requiring an extra wrapper,
    563  * we can remove anything that is a subclass of something else in the list.
    564  *
    565  * The "mix" step we do next reduces things toward the most-derived class,
    566  * so it's important that we start with the least-derived classes.
    567  */
    568 static void reduceExceptionClassList(ArrayObject* exceptionArray)
    569 {
    570     const ClassObject** classes =
    571         (const ClassObject**)(void*)exceptionArray->contents;
    572 
    573     /*
    574      * Consider all pairs of classes.  If one is the subclass of the other,
    575      * null out the subclass.
    576      */
    577     size_t len = exceptionArray->length;
    578     for (size_t i = 0; i < len - 1; i++) {
    579         if (classes[i] == NULL)
    580             continue;
    581         for (size_t j = i + 1; j < len; j++) {
    582             if (classes[j] == NULL)
    583                 continue;
    584 
    585             if (dvmInstanceof(classes[i], classes[j])) {
    586                 classes[i] = NULL;
    587                 break;      /* no more comparisons against classes[i] */
    588             } else if (dvmInstanceof(classes[j], classes[i])) {
    589                 classes[j] = NULL;
    590             }
    591         }
    592     }
    593 }
    594 
    595 /*
    596  * Create a local array with a copy of the throwable classes declared by
    597  * "method".  If no throws are declared, "*pSet" will be NULL.
    598  *
    599  * Returns "false" on allocation failure.
    600  */
    601 static bool createExceptionClassList(const Method* method, PointerSet** pThrows)
    602 {
    603     ArrayObject* exceptionArray = NULL;
    604     bool result = false;
    605 
    606     exceptionArray = dvmGetMethodThrows(method);
    607     if (exceptionArray != NULL && exceptionArray->length > 0) {
    608         /* reduce list, nulling out redundant entries */
    609         reduceExceptionClassList(exceptionArray);
    610 
    611         *pThrows = dvmPointerSetAlloc(exceptionArray->length);
    612         if (*pThrows == NULL)
    613             goto bail;
    614 
    615         const ClassObject** contents;
    616 
    617         contents = (const ClassObject**)(void*)exceptionArray->contents;
    618         for (size_t i = 0; i < exceptionArray->length; i++) {
    619             if (contents[i] != NULL)
    620                 dvmPointerSetAddEntry(*pThrows, contents[i]);
    621         }
    622     } else {
    623         *pThrows = NULL;
    624     }
    625 
    626     result = true;
    627 
    628 bail:
    629     dvmReleaseTrackedAlloc((Object*) exceptionArray, NULL);
    630     return result;
    631 }
    632 
    633 /*
    634  * We need to compute the intersection of the arguments, i.e. remove
    635  * anything from "throws" that isn't in the method's list of throws.
    636  *
    637  * If one class is a subclass of another, we want to keep just the subclass,
    638  * moving toward the most-restrictive set.
    639  *
    640  * We assume these are all classes, and don't try to filter out interfaces.
    641  */
    642 static void updateExceptionClassList(const Method* method, PointerSet* throws)
    643 {
    644     int setSize = dvmPointerSetGetCount(throws);
    645     if (setSize == 0)
    646         return;
    647 
    648     ArrayObject* exceptionArray = dvmGetMethodThrows(method);
    649     if (exceptionArray == NULL) {
    650         /* nothing declared, so intersection is empty */
    651         dvmPointerSetClear(throws);
    652         return;
    653     }
    654 
    655     /* reduce list, nulling out redundant entries */
    656     reduceExceptionClassList(exceptionArray);
    657 
    658     size_t mixLen = dvmPointerSetGetCount(throws);
    659     const ClassObject* mixSet[mixLen];
    660 
    661     size_t declLen = exceptionArray->length;
    662     const ClassObject** declSet = (const ClassObject**)(void*)exceptionArray->contents;
    663 
    664     /* grab a local copy to work on */
    665     for (size_t i = 0; i < mixLen; i++) {
    666         mixSet[i] = (ClassObject*)dvmPointerSetGetEntry(throws, i);
    667     }
    668 
    669     for (size_t i = 0; i < mixLen; i++) {
    670         size_t j;
    671         for (j = 0; j < declLen; j++) {
    672             if (declSet[j] == NULL)
    673                 continue;
    674 
    675             if (mixSet[i] == declSet[j]) {
    676                 /* match, keep this one */
    677                 break;
    678             } else if (dvmInstanceof(mixSet[i], declSet[j])) {
    679                 /* mix is a subclass of a declared throwable, keep it */
    680                 break;
    681             } else if (dvmInstanceof(declSet[j], mixSet[i])) {
    682                 /* mix is a superclass, replace it */
    683                 mixSet[i] = declSet[j];
    684                 break;
    685             }
    686         }
    687 
    688         if (j == declLen) {
    689             /* no match, remove entry by nulling it out */
    690             mixSet[i] = NULL;
    691         }
    692     }
    693 
    694     /* copy results back out; this eliminates duplicates as we go */
    695     dvmPointerSetClear(throws);
    696     for (size_t i = 0; i < mixLen; i++) {
    697         if (mixSet[i] != NULL)
    698             dvmPointerSetAddEntry(throws, mixSet[i]);
    699     }
    700 
    701     dvmReleaseTrackedAlloc((Object*) exceptionArray, NULL);
    702 }
    703 
    704 
    705 /*
    706  * Check to see if the return types are compatible.
    707  *
    708  * If the return type is primitive or void, it must match exactly.
    709  *
    710  * If not, the type in "subMethod" must be assignable to the type in
    711  * "baseMethod".
    712  */
    713 static bool returnTypesAreCompatible(Method* subMethod, Method* baseMethod)
    714 {
    715     const char* baseSig = dexProtoGetReturnType(&baseMethod->prototype);
    716     const char* subSig = dexProtoGetReturnType(&subMethod->prototype);
    717     ClassObject* baseClass;
    718     ClassObject* subClass;
    719 
    720     if (baseSig[1] == '\0' || subSig[1] == '\0') {
    721         /* at least one is primitive type */
    722         return (baseSig[0] == subSig[0] && baseSig[1] == subSig[1]);
    723     }
    724 
    725     baseClass = dvmFindClass(baseSig, baseMethod->clazz->classLoader);
    726     subClass = dvmFindClass(subSig, subMethod->clazz->classLoader);
    727     bool result = dvmInstanceof(subClass, baseClass);
    728     return result;
    729 }
    730 
    731 /*
    732  * Create a constructor for our Proxy class.  The constructor takes one
    733  * argument, a java.lang.reflect.InvocationHandler.
    734  */
    735 static void createConstructor(ClassObject* clazz, Method* meth)
    736 {
    737     /*
    738      * The constructor signatures (->prototype and ->shorty) need to
    739      * be cloned from a method in a "real" DEX file. We declared the
    740      * otherwise unused method Proxy.constructorPrototype() just for
    741      * this purpose.
    742      */
    743 
    744     meth->clazz = clazz;
    745     meth->accessFlags = ACC_PUBLIC | ACC_NATIVE;
    746     meth->name = "<init>";
    747     meth->prototype =
    748         gDvm.methJavaLangReflectProxy_constructorPrototype->prototype;
    749     meth->shorty =
    750         gDvm.methJavaLangReflectProxy_constructorPrototype->shorty;
    751     // no pDexCode or pDexMethod
    752 
    753     int argsSize = dvmComputeMethodArgsSize(meth) + 1;
    754     meth->registersSize = meth->insSize = argsSize;
    755 
    756     meth->nativeFunc = proxyConstructor;
    757 }
    758 
    759 /*
    760  * Create a method in our Proxy class with the name and signature of
    761  * the interface method it implements.
    762  */
    763 static void createHandlerMethod(ClassObject* clazz, Method* dstMeth,
    764     const Method* srcMeth)
    765 {
    766     dstMeth->clazz = clazz;
    767     dstMeth->insns = (u2*) srcMeth;
    768     dstMeth->accessFlags = ACC_PUBLIC | ACC_NATIVE;
    769     dstMeth->name = srcMeth->name;
    770     dstMeth->prototype = srcMeth->prototype;
    771     dstMeth->shorty = srcMeth->shorty;
    772     // no pDexCode or pDexMethod
    773 
    774     int argsSize = dvmComputeMethodArgsSize(dstMeth) + 1;
    775     dstMeth->registersSize = dstMeth->insSize = argsSize;
    776 
    777     dstMeth->nativeFunc = proxyInvoker;
    778 }
    779 
    780 /*
    781  * Return a new Object[] array with the contents of "args".  We determine
    782  * the number and types of values in "args" based on the method signature.
    783  * Primitive types are boxed.
    784  *
    785  * Returns NULL if the method takes no arguments.
    786  *
    787  * The caller must call dvmReleaseTrackedAlloc() on the return value.
    788  *
    789  * On failure, returns with an appropriate exception raised.
    790  */
    791 static ArrayObject* boxMethodArgs(const Method* method, const u4* args)
    792 {
    793     const char* desc = &method->shorty[1]; // [0] is the return type.
    794 
    795     /* count args */
    796     size_t argCount = dexProtoGetParameterCount(&method->prototype);
    797 
    798     /* allocate storage */
    799     ArrayObject* argArray = dvmAllocArrayByClass(gDvm.classJavaLangObjectArray,
    800         argCount, ALLOC_DEFAULT);
    801     if (argArray == NULL)
    802         return NULL;
    803     Object** argObjects = (Object**)(void*)argArray->contents;
    804 
    805     /*
    806      * Fill in the array.
    807      */
    808 
    809     size_t srcIndex = 0;
    810     size_t dstIndex = 0;
    811     while (*desc != '\0') {
    812         char descChar = *(desc++);
    813         JValue value;
    814 
    815         switch (descChar) {
    816         case 'Z':
    817         case 'C':
    818         case 'F':
    819         case 'B':
    820         case 'S':
    821         case 'I':
    822             value.i = args[srcIndex++];
    823             argObjects[dstIndex] = (Object*) dvmBoxPrimitive(value,
    824                 dvmFindPrimitiveClass(descChar));
    825             /* argObjects is tracked, don't need to hold this too */
    826             dvmReleaseTrackedAlloc(argObjects[dstIndex], NULL);
    827             dstIndex++;
    828             break;
    829         case 'D':
    830         case 'J':
    831             value.j = dvmGetArgLong(args, srcIndex);
    832             srcIndex += 2;
    833             argObjects[dstIndex] = (Object*) dvmBoxPrimitive(value,
    834                 dvmFindPrimitiveClass(descChar));
    835             dvmReleaseTrackedAlloc(argObjects[dstIndex], NULL);
    836             dstIndex++;
    837             break;
    838         case '[':
    839         case 'L':
    840             argObjects[dstIndex++] = (Object*) args[srcIndex++];
    841             break;
    842         }
    843     }
    844 
    845     return argArray;
    846 }
    847 
    848 /*
    849  * This is the constructor for a generated proxy object.  All we need to
    850  * do is stuff "handler" into "h".
    851  */
    852 static void proxyConstructor(const u4* args, JValue* pResult,
    853     const Method* method, Thread* self)
    854 {
    855     Object* obj = (Object*) args[0];
    856     Object* handler = (Object*) args[1];
    857 
    858     dvmSetFieldObject(obj, gDvm.offJavaLangReflectProxy_h, handler);
    859 }
    860 
    861 /*
    862  * This is the common message body for proxy methods.
    863  *
    864  * The method we're calling looks like:
    865  *   public Object invoke(Object proxy, Method method, Object[] args)
    866  *
    867  * This means we have to create a Method object, box our arguments into
    868  * a new Object[] array, make the call, and unbox the return value if
    869  * necessary.
    870  */
    871 static void proxyInvoker(const u4* args, JValue* pResult,
    872     const Method* method, Thread* self)
    873 {
    874     Object* thisObj = (Object*) args[0];
    875     Object* methodObj = NULL;
    876     ArrayObject* argArray = NULL;
    877     Object* handler;
    878     Method* invoke;
    879     ClassObject* returnType;
    880     JValue invokeResult;
    881 
    882     /*
    883      * Retrieve handler object for this proxy instance.  The field is
    884      * defined in the superclass (Proxy).
    885      */
    886     handler = dvmGetFieldObject(thisObj, gDvm.offJavaLangReflectProxy_h);
    887 
    888     /*
    889      * Find the invoke() method, looking in "this"s class.  (Because we
    890      * start here we don't have to convert it to a vtable index and then
    891      * index into this' vtable.)
    892      */
    893     invoke = dvmFindVirtualMethodHierByDescriptor(handler->clazz, "invoke",
    894             "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;");
    895     if (invoke == NULL) {
    896         LOGE("Unable to find invoke()");
    897         dvmAbort();
    898     }
    899 
    900     LOGV("invoke: %s.%s, this=%p, handler=%s",
    901         method->clazz->descriptor, method->name,
    902         thisObj, handler->clazz->descriptor);
    903 
    904     /*
    905      * Create a java.lang.reflect.Method object for this method.
    906      *
    907      * We don't want to use "method", because that's the concrete
    908      * implementation in the proxy class.  We want the abstract Method
    909      * from the declaring interface.  We have a pointer to it tucked
    910      * away in the "insns" field.
    911      *
    912      * TODO: this could be cached for performance.
    913      */
    914     methodObj = dvmCreateReflectMethodObject((Method*) method->insns);
    915     if (methodObj == NULL) {
    916         assert(dvmCheckException(self));
    917         goto bail;
    918     }
    919 
    920     /*
    921      * Determine the return type from the signature.
    922      *
    923      * TODO: this could be cached for performance.
    924      */
    925     returnType = dvmGetBoxedReturnType(method);
    926     if (returnType == NULL) {
    927         char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
    928         LOGE("Could not determine return type for '%s'", desc);
    929         free(desc);
    930         assert(dvmCheckException(self));
    931         goto bail;
    932     }
    933     LOGV("  return type will be %s", returnType->descriptor);
    934 
    935     /*
    936      * Convert "args" array into Object[] array, using the method
    937      * signature to determine types.  If the method takes no arguments,
    938      * we must pass null.
    939      */
    940     argArray = boxMethodArgs(method, args+1);
    941     if (dvmCheckException(self))
    942         goto bail;
    943 
    944     /*
    945      * Call h.invoke(proxy, method, args).
    946      *
    947      * We don't need to repackage exceptions, so if one has been thrown
    948      * just jump to the end.
    949      *
    950      * We're not adding invokeResult.l to the tracked allocation list, but
    951      * since we're just unboxing it or returning it to interpreted code
    952      * that shouldn't be a problem.
    953      */
    954     dvmCallMethod(self, invoke, handler, &invokeResult,
    955         thisObj, methodObj, argArray);
    956     if (dvmCheckException(self)) {
    957         Object* excep = dvmGetException(self);
    958         if (mustWrapException(method, excep)) {
    959             /* wrap with UndeclaredThrowableException */
    960             dvmWrapException("Ljava/lang/reflect/UndeclaredThrowableException;");
    961         }
    962         goto bail;
    963     }
    964 
    965     /*
    966      * Unbox the return value.  If it's the wrong type, throw a
    967      * ClassCastException.  If it's a null pointer and we need a
    968      * primitive type, throw a NullPointerException.
    969      */
    970     if (returnType->primitiveType == PRIM_VOID) {
    971         LOGVV("+++ ignoring return to void");
    972     } else if (invokeResult.l == NULL) {
    973         if (dvmIsPrimitiveClass(returnType)) {
    974             dvmThrowNullPointerException(
    975                 "null result when primitive expected");
    976             goto bail;
    977         }
    978         pResult->l = NULL;
    979     } else {
    980         if (!dvmUnboxPrimitive((Object*)invokeResult.l, returnType, pResult)) {
    981             dvmThrowClassCastException(((Object*)invokeResult.l)->clazz,
    982                     returnType);
    983             goto bail;
    984         }
    985     }
    986 
    987 bail:
    988     dvmReleaseTrackedAlloc(methodObj, self);
    989     dvmReleaseTrackedAlloc((Object*)argArray, self);
    990 }
    991 
    992 /*
    993  * Determine if it's okay for this method to throw this exception.  If
    994  * an unchecked exception was thrown we immediately return false.  If
    995  * checked, we have to ensure that this method and all of its duplicates
    996  * have declared that they throw it.
    997  */
    998 static bool mustWrapException(const Method* method, const Object* throwable)
    999 {
   1000     if (!dvmIsCheckedException(throwable))
   1001         return false;
   1002 
   1003     const StaticField* sfield = &method->clazz->sfields[kThrowsField];
   1004     const ArrayObject* throws = (ArrayObject*) dvmGetStaticFieldObject(sfield);
   1005 
   1006     int methodIndex = method - method->clazz->virtualMethods;
   1007     assert(methodIndex >= 0 && methodIndex < method->clazz->virtualMethodCount);
   1008 
   1009     const Object** contents = (const Object**)(void*)throws->contents;
   1010     const ArrayObject* methodThrows = (ArrayObject*) contents[methodIndex];
   1011 
   1012     if (methodThrows == NULL) {
   1013         /* no throws declared, must wrap all checked exceptions */
   1014         return true;
   1015     }
   1016 
   1017     size_t throwCount = methodThrows->length;
   1018     const ClassObject** classes =
   1019         (const ClassObject**)(void*)methodThrows->contents;
   1020 
   1021     for (size_t i = 0; i < throwCount; i++) {
   1022         if (dvmInstanceof(throwable->clazz, classes[i])) {
   1023             /* this was declared, okay to throw */
   1024             return false;
   1025         }
   1026     }
   1027 
   1028     /* no match in declared throws */
   1029     return true;
   1030 }
   1031