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