• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  */
dvmReflectProxyStartup()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  */
dvmGenerateProxyClass(StringObject * str,ArrayObject * interfaces,Object * loader)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  */
gatherMethods(ArrayObject * interfaces,Method *** pMethods,ArrayObject ** pThrows,int * pMethodCount)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  */
copyWithoutDuplicates(Method ** allMethods,int allCount,Method ** outMethods,ArrayObject * throwLists)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  */
reduceExceptionClassList(ArrayObject * exceptionArray)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  */
createExceptionClassList(const Method * method,PointerSet ** pThrows)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  */
updateExceptionClassList(const Method * method,PointerSet * throws)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  */
returnTypesAreCompatible(Method * subMethod,Method * baseMethod)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  */
createConstructor(ClassObject * clazz,Method * meth)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  */
createHandlerMethod(ClassObject * clazz,Method * dstMeth,const Method * srcMeth)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  */
boxMethodArgs(const Method * method,const u4 * args)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  */
proxyConstructor(const u4 * args,JValue * pResult,const Method * method,Thread * self)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  */
proxyInvoker(const u4 * args,JValue * pResult,const Method * method,Thread * self)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  */
mustWrapException(const Method * method,const Object * throwable)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