Proxy.cpp revision 4308417beec548c2b2c06ecec4f7f4a965b09fb2
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
31static bool returnTypesAreCompatible(Method* baseMethod, Method* subMethod);
32static bool gatherMethods(ArrayObject* interfaces, Method*** pMethods,\
33    ArrayObject** pThrows, int* pMethodCount);
34static int copyWithoutDuplicates(Method** allMethods, int allCount,
35    Method** outMethods, ArrayObject* throws);
36static bool createExceptionClassList(const Method* method,
37    PointerSet** pThrows);
38static void updateExceptionClassList(const Method* method, PointerSet* throws);
39static void createConstructor(ClassObject* clazz, Method* meth);
40static void createHandlerMethod(ClassObject* clazz, Method* dstMeth,
41    const Method* srcMeth);
42static void proxyConstructor(const u4* args, JValue* pResult,
43    const Method* method, Thread* self);
44static void proxyInvoker(const u4* args, JValue* pResult,
45    const Method* method, Thread* self);
46static 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 */
66ClassObject* 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    ALOGV("+++ 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        ALOGD("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
210bail:
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 */
239static 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    //ALOGI("gathered methods:");
333    //for (i = 0; i < actualCount; i++) {
334    //    ALOGI(" %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
343bail:
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 */
374static 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                ALOGV("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                ALOGV("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                        ALOGV("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                ALOGV("BEST not %d", i);
518            }
519        } else {
520            /*
521             * Singleton.  Copy the entry and NULL it out.
522             */
523            ALOGV("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            ALOGV("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 */
568static 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 */
601static 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
628bail:
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 */
642static 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 */
713static 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 */
735static 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 */
763static 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 */
791static 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 */
852static 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 */
871static 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    ALOGV("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    ALOGV("  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
987bail:
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 */
998static 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