OSDN Git Service

am c4102d66: (-s ours) Merge "DO NOT MERGE: Optimize IndirectRefTable." into ics-mr1
[android-x86/dalvik.git] / vm / reflect / Proxy.cpp
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     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         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                 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  */
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     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
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 }