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