blob: 4270894fed1c36da9b5a9176add5c6bcfcbdc2c1 [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{
431 Method* best;
432 int outCount = 0;
433 int i, j;
434
435 /*
436 * The plan is to run through all methods, checking all other methods
437 * for a duplicate. If we find a match, we see if the other methods'
438 * return type is compatible/assignable with ours. If the current
439 * method is assignable from all others, we copy it to the new list,
440 * and NULL out all other entries. If not, we keep looking for a
441 * better version.
442 *
443 * If there are no duplicates, we copy the method and NULL the entry.
444 *
445 * At the end of processing, if we have any non-NULL entries, then we
446 * have bad duplicates and must exit with an exception.
447 */
448 for (i = 0; i < allCount; i++) {
449 bool best, dupe;
450
451 if (allMethods[i] == NULL)
452 continue;
453
454 /*
455 * Find all duplicates. If any of the return types is not
456 * assignable to our return type, then we're not the best.
457 *
458 * We start from 0, not i, because we need to compare assignability
459 * the other direction even if we've compared these before.
460 */
461 dupe = false;
462 best = true;
463 for (j = 0; j < allCount; j++) {
464 if (i == j)
465 continue;
466 if (allMethods[j] == NULL)
467 continue;
468
469 if (dvmCompareMethodNamesAndParameterProtos(allMethods[i],
470 allMethods[j]) == 0)
471 {
472 /*
473 * Duplicate method, check return type. If it's a primitive
474 * type or void, the types must match exactly, or we throw
475 * an exception now.
476 */
477 LOGV("MATCH on %s.%s and %s.%s\n",
478 allMethods[i]->clazz->descriptor, allMethods[i]->name,
479 allMethods[j]->clazz->descriptor, allMethods[j]->name);
480 dupe = true;
481 if (!returnTypesAreCompatible(allMethods[i], allMethods[j]))
482 best = false;
483 }
484 }
485
486 /*
487 * If this is the best of a set of duplicates, copy it over and
488 * nuke all duplicates.
489 *
490 * While we do this, we create the set of exceptions declared to
491 * be thrown by all occurrences of the method.
492 */
493 if (dupe) {
494 if (best) {
495 LOGV("BEST %d %s.%s -> %d\n", i,
496 allMethods[i]->clazz->descriptor, allMethods[i]->name,
497 outCount);
498
499 /* if we have exceptions, make a local copy */
500 PointerSet* commonThrows = NULL;
501 if (!createExceptionClassList(allMethods[i], &commonThrows))
502 return -1;
503
504 /*
505 * Run through one more time, erasing the duplicates. (This
506 * would go faster if we had marked them somehow.)
507 */
508 for (j = 0; j < allCount; j++) {
509 if (i == j)
510 continue;
511 if (allMethods[j] == NULL)
512 continue;
513 if (dvmCompareMethodNamesAndParameterProtos(allMethods[i],
514 allMethods[j]) == 0)
515 {
516 LOGV("DEL %d %s.%s\n", j,
517 allMethods[j]->clazz->descriptor,
518 allMethods[j]->name);
519
520 /*
521 * Update set to hold the intersection of method[i]'s
522 * and method[j]'s throws.
523 */
524 if (commonThrows != NULL) {
525 updateExceptionClassList(allMethods[j],
526 commonThrows);
527 }
528
529 allMethods[j] = NULL;
530 }
531 }
532
533 /*
534 * If the set of Throwable classes isn't empty, create an
535 * array of Class, copy them into it, and put the result
536 * into the "throwLists" array.
537 */
538 if (commonThrows != NULL &&
539 dvmPointerSetGetCount(commonThrows) > 0)
540 {
541 int commonCount = dvmPointerSetGetCount(commonThrows);
542 ArrayObject* throwArray;
543 Object** contents;
544 int ent;
545
546 throwArray = dvmAllocArrayByClass(
547 gDvm.classJavaLangClassArray, commonCount,
548 ALLOC_DEFAULT);
549 if (throwArray == NULL) {
550 LOGE("common-throw array alloc failed\n");
551 return -1;
552 }
553
554 contents = (Object**) throwArray->contents;
555 for (ent = 0; ent < commonCount; ent++) {
556 contents[ent] = (Object*)
557 dvmPointerSetGetEntry(commonThrows, ent);
558 }
559
560 /* add it to the array of arrays */
561 contents = (Object**) throwLists->contents;
562 contents[outCount] = (Object*) throwArray;
563 dvmReleaseTrackedAlloc((Object*) throwArray, NULL);
564 }
565
566 /* copy the winner and NULL it out */
567 outMethods[outCount++] = allMethods[i];
568 allMethods[i] = NULL;
569
570 dvmPointerSetFree(commonThrows);
571 } else {
572 LOGV("BEST not %d\n", i);
573 }
574 } else {
575 /*
576 * Singleton. Copy the entry and NULL it out.
577 */
578 LOGV("COPY singleton %d %s.%s -> %d\n", i,
579 allMethods[i]->clazz->descriptor, allMethods[i]->name,
580 outCount);
581
582 /* keep track of our throwables */
583 ArrayObject* exceptionArray = dvmGetMethodThrows(allMethods[i]);
584 if (exceptionArray != NULL) {
585 Object** contents;
586
587 contents = (Object**) throwLists->contents;
588 contents[outCount] = (Object*) exceptionArray;
589 dvmReleaseTrackedAlloc((Object*) exceptionArray, NULL);
590 }
591
592 outMethods[outCount++] = allMethods[i];
593 allMethods[i] = NULL;
594 }
595 }
596
597 /*
598 * Check for stragglers. If we find any, throw an exception.
599 */
600 for (i = 0; i < allCount; i++) {
601 if (allMethods[i] != NULL) {
602 LOGV("BAD DUPE: %d %s.%s\n", i,
603 allMethods[i]->clazz->descriptor, allMethods[i]->name);
604 dvmThrowException("Ljava/lang/IllegalArgumentException;",
605 "incompatible return types in proxied interfaces");
606 return -1;
607 }
608 }
609
610 return outCount;
611}
612
613
614/*
615 * Classes can declare to throw multiple exceptions in a hierarchy, e.g.
616 * IOException and FileNotFoundException. Since we're only interested in
617 * knowing the set that can be thrown without requiring an extra wrapper,
618 * we can remove anything that is a subclass of something else in the list.
619 *
620 * The "mix" step we do next reduces things toward the most-derived class,
621 * so it's important that we start with the least-derived classes.
622 */
623static void reduceExceptionClassList(ArrayObject* exceptionArray)
624{
625 const ClassObject** classes = (const ClassObject**)exceptionArray->contents;
626 int len = exceptionArray->length;
627 int i, j;
628
629 /*
630 * Consider all pairs of classes. If one is the subclass of the other,
631 * null out the subclass.
632 */
633 for (i = 0; i < len-1; i++) {
634 if (classes[i] == NULL)
635 continue;
636 for (j = i + 1; j < len; j++) {
637 if (classes[j] == NULL)
638 continue;
639
640 if (dvmInstanceof(classes[i], classes[j])) {
641 classes[i] = NULL;
642 break; /* no more comparisons against classes[i] */
643 } else if (dvmInstanceof(classes[j], classes[i])) {
644 classes[j] = NULL;
645 }
646 }
647 }
648}
649
650/*
651 * Create a local array with a copy of the throwable classes declared by
652 * "method". If no throws are declared, "*pSet" will be NULL.
653 *
654 * Returns "false" on allocation failure.
655 */
656static bool createExceptionClassList(const Method* method, PointerSet** pThrows)
657{
658 ArrayObject* exceptionArray = NULL;
659 bool result = false;
660
661 exceptionArray = dvmGetMethodThrows(method);
662 if (exceptionArray != NULL && exceptionArray->length > 0) {
663 /* reduce list, nulling out redundant entries */
664 reduceExceptionClassList(exceptionArray);
665
666 *pThrows = dvmPointerSetAlloc(exceptionArray->length);
667 if (*pThrows == NULL)
668 goto bail;
669
670 const ClassObject** contents;
671 int i;
672
673 contents = (const ClassObject**) exceptionArray->contents;
674 for (i = 0; i < (int) exceptionArray->length; i++) {
675 if (contents[i] != NULL)
676 dvmPointerSetAddEntry(*pThrows, contents[i]);
677 }
678 } else {
679 *pThrows = NULL;
680 }
681
682 result = true;
683
684bail:
685 dvmReleaseTrackedAlloc((Object*) exceptionArray, NULL);
686 return result;
687}
688
689/*
690 * We need to compute the intersection of the arguments, i.e. remove
691 * anything from "throws" that isn't in the method's list of throws.
692 *
693 * If one class is a subclass of another, we want to keep just the subclass,
694 * moving toward the most-restrictive set.
695 *
696 * We assume these are all classes, and don't try to filter out interfaces.
697 */
698static void updateExceptionClassList(const Method* method, PointerSet* throws)
699{
700 int setSize = dvmPointerSetGetCount(throws);
701 if (setSize == 0)
702 return;
703
704 ArrayObject* exceptionArray = dvmGetMethodThrows(method);
705 if (exceptionArray == NULL) {
706 /* nothing declared, so intersection is empty */
707 dvmPointerSetClear(throws);
708 return;
709 }
710
711 /* reduce list, nulling out redundant entries */
712 reduceExceptionClassList(exceptionArray);
713
714 int mixLen = dvmPointerSetGetCount(throws);
715 const ClassObject* mixSet[mixLen];
716
717 int declLen = exceptionArray->length;
718 const ClassObject** declSet = (const ClassObject**)exceptionArray->contents;
719
720 int i, j;
721
722 /* grab a local copy to work on */
723 for (i = 0; i < mixLen; i++) {
724 mixSet[i] = dvmPointerSetGetEntry(throws, i);
725 }
726
727 for (i = 0; i < mixLen; i++) {
728 for (j = 0; j < declLen; j++) {
729 if (declSet[j] == NULL)
730 continue;
731
732 if (mixSet[i] == declSet[j]) {
733 /* match, keep this one */
734 break;
735 } else if (dvmInstanceof(mixSet[i], declSet[j])) {
736 /* mix is a subclass of a declared throwable, keep it */
737 break;
738 } else if (dvmInstanceof(declSet[j], mixSet[i])) {
739 /* mix is a superclass, replace it */
740 mixSet[i] = declSet[j];
741 break;
742 }
743 }
744
745 if (j == declLen) {
746 /* no match, remove entry by nulling it out */
747 mixSet[i] = NULL;
748 }
749 }
750
751 /* copy results back out; this eliminates duplicates as we go */
752 dvmPointerSetClear(throws);
753 for (i = 0; i < mixLen; i++) {
754 if (mixSet[i] != NULL)
755 dvmPointerSetAddEntry(throws, mixSet[i]);
756 }
757
758 dvmReleaseTrackedAlloc((Object*) exceptionArray, NULL);
759}
760
761
762/*
763 * Check to see if the return types are compatible.
764 *
765 * If the return type is primitive or void, it must match exactly.
766 *
767 * If not, the type in "subMethod" must be assignable to the type in
768 * "baseMethod".
769 */
770static bool returnTypesAreCompatible(Method* subMethod, Method* baseMethod)
771{
772 const char* baseSig = dexProtoGetReturnType(&baseMethod->prototype);
773 const char* subSig = dexProtoGetReturnType(&subMethod->prototype);
774 ClassObject* baseClass;
775 ClassObject* subClass;
776
777 if (baseSig[1] == '\0' || subSig[1] == '\0') {
778 /* at least one is primitive type */
779 return (baseSig[0] == subSig[0] && baseSig[1] == subSig[1]);
780 }
781
782 baseClass = dvmFindClass(baseSig, baseMethod->clazz->classLoader);
783 subClass = dvmFindClass(subSig, subMethod->clazz->classLoader);
784 bool result = dvmInstanceof(subClass, baseClass);
785 return result;
786}
787
788/*
789 * Create a constructor for our Proxy class. The constructor takes one
790 * argument, a java.lang.reflect.InvocationHandler.
791 */
792static void createConstructor(ClassObject* clazz, Method* meth)
793{
794 meth->clazz = clazz;
795 meth->accessFlags = ACC_PUBLIC | ACC_NATIVE;
796 meth->name = "<init>";
797 meth->prototype =
798 gDvm.methJavaLangReflectProxy_constructorPrototype->prototype;
799 meth->shorty =
800 gDvm.methJavaLangReflectProxy_constructorPrototype->shorty;
801 // no pDexCode or pDexMethod
802
803 int argsSize = dvmComputeMethodArgsSize(meth) + 1;
804 meth->registersSize = meth->insSize = argsSize;
805
806 meth->nativeFunc = proxyConstructor;
807}
808
809/*
810 * Create a method in our Proxy class with the name and signature of
811 * the interface method it implements.
812 */
813static void createHandlerMethod(ClassObject* clazz, Method* dstMeth,
814 const Method* srcMeth)
815{
816 dstMeth->clazz = clazz;
817 dstMeth->insns = (u2*) srcMeth;
818 dstMeth->accessFlags = ACC_PUBLIC | ACC_NATIVE;
819 dstMeth->name = srcMeth->name;
820 dstMeth->prototype = srcMeth->prototype;
821 dstMeth->shorty = srcMeth->shorty;
822 // no pDexCode or pDexMethod
823
824 int argsSize = dvmComputeMethodArgsSize(dstMeth) + 1;
825 dstMeth->registersSize = dstMeth->insSize = argsSize;
826
827 dstMeth->nativeFunc = proxyInvoker;
828}
829
830/*
831 * Return a new Object[] array with the contents of "args". We determine
832 * the number and types of values in "args" based on the method signature.
833 * Primitive types are boxed.
834 *
835 * Returns NULL if the method takes no arguments.
836 *
837 * The caller must call dvmReleaseTrackedAlloc() on the return value.
838 *
839 * On failure, returns with an appropriate exception raised.
840 */
841static ArrayObject* boxMethodArgs(const Method* method, const u4* args)
842{
843 const char* desc = &method->shorty[1]; // [0] is the return type.
844 ArrayObject* argArray = NULL;
845 int argCount;
846 Object** argObjects;
847 bool failed = true;
848
849 /* count args */
850 argCount = dexProtoGetParameterCount(&method->prototype);
851
852 /* allocate storage */
853 argArray = dvmAllocArray(gDvm.classJavaLangObjectArray, argCount,
854 kObjectArrayRefWidth, ALLOC_DEFAULT);
855 if (argArray == NULL)
856 goto bail;
857 argObjects = (Object**) argArray->contents;
858
859 /*
860 * Fill in the array.
861 */
862
863 int srcIndex = 0;
864
865 argCount = 0;
866 while (*desc != '\0') {
867 char descChar = *(desc++);
868 JValue value;
869
870 switch (descChar) {
871 case 'Z':
872 case 'C':
873 case 'F':
874 case 'B':
875 case 'S':
876 case 'I':
877 value.i = args[srcIndex++];
878 argObjects[argCount] = (Object*) dvmWrapPrimitive(value,
879 dvmFindPrimitiveClass(descChar));
880 /* argObjects is tracked, don't need to hold this too */
881 dvmReleaseTrackedAlloc(argObjects[argCount], NULL);
882 argCount++;
883 break;
884 case 'D':
885 case 'J':
886 value.j = dvmGetArgLong(args, srcIndex);
887 srcIndex += 2;
888 argObjects[argCount] = (Object*) dvmWrapPrimitive(value,
889 dvmFindPrimitiveClass(descChar));
890 dvmReleaseTrackedAlloc(argObjects[argCount], NULL);
891 argCount++;
892 break;
893 case '[':
894 case 'L':
895 argObjects[argCount++] = (Object*) args[srcIndex++];
896 break;
897 }
898 }
899
900 failed = false;
901
902bail:
903 if (failed) {
904 dvmReleaseTrackedAlloc((Object*)argArray, NULL);
905 argArray = NULL;
906 }
907 return argArray;
908}
909
910/*
911 * This is the constructor for a generated proxy object. All we need to
912 * do is stuff "handler" into "h".
913 */
914static void proxyConstructor(const u4* args, JValue* pResult,
915 const Method* method, Thread* self)
916{
917 Object* obj = (Object*) args[0];
918 Object* handler = (Object*) args[1];
919
920 dvmSetFieldObject(obj, gDvm.offJavaLangReflectProxy_h, handler);
921}
922
923/*
924 * This is the common message body for proxy methods.
925 *
926 * The method we're calling looks like:
927 * public Object invoke(Object proxy, Method method, Object[] args)
928 *
929 * This means we have to create a Method object, box our arguments into
930 * a new Object[] array, make the call, and unbox the return value if
931 * necessary.
932 */
933static void proxyInvoker(const u4* args, JValue* pResult,
934 const Method* method, Thread* self)
935{
936 Object* thisObj = (Object*) args[0];
937 Object* methodObj = NULL;
938 ArrayObject* argArray = NULL;
939 Object* handler;
940 Method* invoke;
941 ClassObject* returnType;
942 int hOffset;
943 JValue invokeResult;
944
945 /*
946 * Retrieve handler object for this proxy instance. The field is
947 * defined in the superclass (Proxy).
948 */
949 handler = dvmGetFieldObject(thisObj, gDvm.offJavaLangReflectProxy_h);
950
951 /*
952 * Find the invoke() method, looking in "this"s class. (Because we
953 * start here we don't have to convert it to a vtable index and then
954 * index into this' vtable.)
955 */
956 invoke = dvmFindVirtualMethodHierByDescriptor(handler->clazz, "invoke",
957 "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;");
958 if (invoke == NULL) {
959 LOGE("Unable to find invoke()\n");
960 dvmAbort();
961 }
962
963 LOGV("invoke: %s.%s, this=%p, handler=%s\n",
964 method->clazz->descriptor, method->name,
965 thisObj, handler->clazz->descriptor);
966
967 /*
968 * Create a java.lang.reflect.Method object for this method.
969 *
970 * We don't want to use "method", because that's the concrete
971 * implementation in the proxy class. We want the abstract Method
972 * from the declaring interface. We have a pointer to it tucked
973 * away in the "insns" field.
974 *
975 * TODO: this could be cached for performance.
976 */
977 methodObj = dvmCreateReflectMethodObject((Method*) method->insns);
978 if (methodObj == NULL) {
979 assert(dvmCheckException(self));
980 goto bail;
981 }
982
983 /*
984 * Determine the return type from the signature.
985 *
986 * TODO: this could be cached for performance.
987 */
988 returnType = dvmGetBoxedReturnType(method);
989 if (returnType == NULL) {
990 char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
991 LOGE("Could not determine return type for '%s'\n", desc);
992 free(desc);
993 assert(dvmCheckException(self));
994 goto bail;
995 }
996 LOGV(" return type will be %s\n", returnType->descriptor);
997
998 /*
999 * Convert "args" array into Object[] array, using the method
1000 * signature to determine types. If the method takes no arguments,
1001 * we must pass null.
1002 */
1003 argArray = boxMethodArgs(method, args+1);
1004 if (dvmCheckException(self))
1005 goto bail;
1006
1007 /*
1008 * Call h.invoke(proxy, method, args).
1009 *
1010 * We don't need to repackage exceptions, so if one has been thrown
1011 * just jump to the end.
1012 */
1013 dvmCallMethod(self, invoke, handler, &invokeResult,
1014 thisObj, methodObj, argArray);
1015 if (dvmCheckException(self)) {
1016 Object* excep = dvmGetException(self);
1017 if (mustWrapException(method, excep)) {
1018 /* wrap with UndeclaredThrowableException */
1019 dvmWrapException("Ljava/lang/reflect/UndeclaredThrowableException;");
1020 }
1021 goto bail;
1022 }
1023
1024 /*
1025 * Unbox the return value. If it's the wrong type, throw a
1026 * ClassCastException. If it's a null pointer and we need a
1027 * primitive type, throw a NullPointerException.
1028 */
1029 if (returnType->primitiveType == PRIM_VOID) {
1030 LOGVV("+++ ignoring return to void\n");
1031 } else if (invokeResult.l == NULL) {
1032 if (dvmIsPrimitiveClass(returnType)) {
1033 dvmThrowException("Ljava/lang/NullPointerException;",
1034 "null result when primitive expected");
1035 goto bail;
1036 }
1037 pResult->l = NULL;
1038 } else {
1039 if (!dvmUnwrapPrimitive(invokeResult.l, returnType, pResult)) {
1040 dvmThrowExceptionWithClassMessage("Ljava/lang/ClassCastException;",
1041 ((Object*)invokeResult.l)->clazz->descriptor);
1042 goto bail;
1043 }
1044 }
1045
1046bail:
1047 dvmReleaseTrackedAlloc(methodObj, self);
1048 dvmReleaseTrackedAlloc((Object*)argArray, self);
1049}
1050
1051/*
1052 * Determine if it's okay for this method to throw this exception. If
1053 * an unchecked exception was thrown we immediately return false. If
1054 * checked, we have to ensure that this method and all of its duplicates
1055 * have declared that they throw it.
1056 */
1057static bool mustWrapException(const Method* method, const Object* throwable)
1058{
1059 const ArrayObject* throws;
1060 const ArrayObject* methodThrows;
1061 const Object** contents;
1062 const ClassObject** classes;
1063
1064 if (!dvmIsCheckedException(throwable))
1065 return false;
1066
1067 const StaticField* sfield = &method->clazz->sfields[kThrowsField];
1068 throws = (ArrayObject*) dvmGetStaticFieldObject(sfield);
1069
1070 int methodIndex = method - method->clazz->virtualMethods;
1071 assert(methodIndex >= 0 && methodIndex < method->clazz->virtualMethodCount);
1072
1073 contents = (const Object**) throws->contents;
1074 methodThrows = (ArrayObject*) contents[methodIndex];
1075
1076 if (methodThrows == NULL) {
1077 /* no throws declared, must wrap all checked exceptions */
1078 //printf("+++ methodThrows[%d] is null, wrapping all\n", methodIndex);
1079 return true;
1080 }
1081
1082 int throwCount = methodThrows->length;
1083 classes = (const ClassObject**) methodThrows->contents;
1084 int i;
1085
1086 //printf("%s.%s list:\n", method->clazz->descriptor, method->name);
1087 //for (i = 0; i < throwCount; i++)
1088 // printf(" %d: %s\n", i, classes[i]->descriptor);
1089
1090 for (i = 0; i < throwCount; i++) {
1091 if (dvmInstanceof(throwable->clazz, classes[i])) {
1092 /* this was declared, okay to throw */
1093 return false;
1094 }
1095 }
1096
1097 /* no match in declared throws */
1098 return true;
1099}