blob: 5d201a65d68b1997cb5c983f7dd36bffdb1aac13 [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;
Barry Hayesc49db852010-05-14 13:43:34 -0700186 DVM_OBJECT_INIT(&newClass->obj, gDvm.classJavaLangClass);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800187 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 /*
Barry Hayesc49db852010-05-14 13:43:34 -0700244 * Everything is ready. This class didn't come out of a DEX file
245 * so we didn't tuck any indexes into the class object. We can
246 * advance to LOADED state immediately.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800247 */
248 newClass->status = CLASS_LOADED;
Barry Hayesc49db852010-05-14 13:43:34 -0700249 if (!dvmLinkClass(newClass)) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800250 LOGD("Proxy class link failed\n");
251 goto bail;
252 }
253
254 /*
255 * All good. Add it to the hash table. We should NOT see a collision
256 * here; if we do, it means the caller has screwed up and provided us
257 * with a duplicate name.
258 */
259 if (!dvmAddClassToHash(newClass)) {
260 LOGE("ERROR: attempted to generate %s more than once\n",
261 newClass->descriptor);
262 goto bail;
263 }
264
265 result = 0;
266
267bail:
268 free(nameStr);
269 free(methods);
270 if (result != 0) {
271 /* must free innards explicitly if we didn't finish linking */
272 dvmFreeClassInnards(newClass);
273 newClass = NULL;
274 if (!dvmCheckException(dvmThreadSelf())) {
275 /* throw something */
276 dvmThrowException("Ljava/lang/RuntimeException;", NULL);
277 }
278 }
279
280 /* allow the GC to free these when nothing else has a reference */
281 dvmReleaseTrackedAlloc((Object*) throws, NULL);
282 dvmReleaseTrackedAlloc((Object*) newClass, NULL);
283
284 return newClass;
285}
286
287
288/*
289 * Generate a list of methods. The Method pointers returned point to the
290 * abstract method definition from the appropriate interface, or to the
291 * virtual method definition in java.lang.Object.
292 *
293 * We also allocate an array of arrays of throwable classes, one for each
294 * method,so we can do some special handling of checked exceptions. The
295 * caller must call ReleaseTrackedAlloc() on *pThrows.
296 */
297static bool gatherMethods(ArrayObject* interfaces, Method*** pMethods,
298 ArrayObject** pThrows, int* pMethodCount)
299{
300 ClassObject** classes;
301 ArrayObject* throws = NULL;
302 Method** methods = NULL;
303 Method** allMethods = NULL;
304 int numInterfaces, maxCount, actualCount, allCount;
305 bool result = false;
306 int i;
307
308 /*
309 * Get a maximum count so we can allocate storage. We need the
310 * methods declared by each interface and all of its superinterfaces.
311 */
312 maxCount = 3; // 3 methods in java.lang.Object
313 numInterfaces = interfaces->length;
314 classes = (ClassObject**) interfaces->contents;
315
316 for (i = 0; i < numInterfaces; i++, classes++) {
317 ClassObject* clazz = *classes;
318
319 LOGVV("--- %s virtualMethodCount=%d\n",
320 clazz->descriptor, clazz->virtualMethodCount);
321 maxCount += clazz->virtualMethodCount;
322
323 int j;
324 for (j = 0; j < clazz->iftableCount; j++) {
325 ClassObject* iclass = clazz->iftable[j].clazz;
326
327 LOGVV("--- +%s %d\n",
328 iclass->descriptor, iclass->virtualMethodCount);
329 maxCount += iclass->virtualMethodCount;
330 }
331 }
332
333 methods = (Method**) malloc(maxCount * sizeof(*methods));
334 allMethods = (Method**) malloc(maxCount * sizeof(*methods));
335 if (methods == NULL || allMethods == NULL)
336 goto bail;
337
338 /*
339 * First three entries are the java.lang.Object methods.
340 */
341 ClassObject* obj = gDvm.classJavaLangObject;
342 allMethods[0] = obj->vtable[gDvm.voffJavaLangObject_equals];
343 allMethods[1] = obj->vtable[gDvm.voffJavaLangObject_hashCode];
344 allMethods[2] = obj->vtable[gDvm.voffJavaLangObject_toString];
345 allCount = 3;
346
347 /*
348 * Add the methods from each interface, in order.
349 */
350 classes = (ClassObject**) interfaces->contents;
351 for (i = 0; i < numInterfaces; i++, classes++) {
352 ClassObject* clazz = *classes;
353 int j;
354
355 for (j = 0; j < clazz->virtualMethodCount; j++) {
356 allMethods[allCount++] = &clazz->virtualMethods[j];
357 }
358
359 for (j = 0; j < clazz->iftableCount; j++) {
360 ClassObject* iclass = clazz->iftable[j].clazz;
361 int k;
362
363 for (k = 0; k < iclass->virtualMethodCount; k++) {
364 allMethods[allCount++] = &iclass->virtualMethods[k];
365 }
366 }
367 }
368 assert(allCount == maxCount);
369
370 /*
371 * Allocate some storage to hold the lists of throwables. We need
372 * one entry per unique method, but it's convenient to allocate it
373 * ahead of the duplicate processing.
374 */
375 ClassObject* arrArrClass;
376 arrArrClass = dvmFindArrayClass("[[Ljava/lang/Throwable;", NULL);
377 if (arrArrClass == NULL)
378 goto bail;
379 throws = dvmAllocArrayByClass(arrArrClass, allCount, ALLOC_DEFAULT);
380
381 /*
382 * Identify and remove duplicates.
383 */
384 actualCount = copyWithoutDuplicates(allMethods, allCount, methods, throws);
385 if (actualCount < 0)
386 goto bail;
387
388 //LOGI("gathered methods:\n");
389 //for (i = 0; i < actualCount; i++) {
390 // LOGI(" %d: %s.%s\n",
391 // i, methods[i]->clazz->descriptor, methods[i]->name);
392 //}
393
394 *pMethods = methods;
395 *pMethodCount = actualCount;
396 *pThrows = throws;
397 result = true;
398
399bail:
400 free(allMethods);
401 if (!result) {
402 free(methods);
403 dvmReleaseTrackedAlloc((Object*)throws, NULL);
404 }
405 return result;
406}
407
408/*
409 * Identify and remove duplicates, where "duplicate" means it has the
410 * same name and arguments, but not necessarily the same return type.
411 *
412 * If duplicate methods have different return types, we want to use the
413 * first method whose return type is assignable from all other duplicate
414 * methods. That is, if we have:
415 * class base {...}
416 * class sub extends base {...}
417 * class subsub extends sub {...}
418 * Then we want to return the method that returns subsub, since callers
419 * to any form of the method will get a usable object back.
420 *
421 * All other duplicate methods are stripped out.
422 *
423 * This also populates the "throwLists" array with arrays of Class objects,
424 * one entry per method in "outMethods". Methods that don't declare any
425 * throwables (or have no common throwables with duplicate methods) will
426 * have NULL entries.
427 *
428 * Returns the number of methods copied into "methods", or -1 on failure.
429 */
430static int copyWithoutDuplicates(Method** allMethods, int allCount,
431 Method** outMethods, ArrayObject* throwLists)
432{
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800433 int outCount = 0;
434 int i, j;
435
436 /*
437 * The plan is to run through all methods, checking all other methods
438 * for a duplicate. If we find a match, we see if the other methods'
439 * return type is compatible/assignable with ours. If the current
440 * method is assignable from all others, we copy it to the new list,
441 * and NULL out all other entries. If not, we keep looking for a
442 * better version.
443 *
444 * If there are no duplicates, we copy the method and NULL the entry.
445 *
446 * At the end of processing, if we have any non-NULL entries, then we
447 * have bad duplicates and must exit with an exception.
448 */
449 for (i = 0; i < allCount; i++) {
450 bool best, dupe;
451
452 if (allMethods[i] == NULL)
453 continue;
454
455 /*
456 * Find all duplicates. If any of the return types is not
457 * assignable to our return type, then we're not the best.
458 *
459 * We start from 0, not i, because we need to compare assignability
460 * the other direction even if we've compared these before.
461 */
462 dupe = false;
463 best = true;
464 for (j = 0; j < allCount; j++) {
465 if (i == j)
466 continue;
467 if (allMethods[j] == NULL)
468 continue;
469
470 if (dvmCompareMethodNamesAndParameterProtos(allMethods[i],
471 allMethods[j]) == 0)
472 {
473 /*
474 * Duplicate method, check return type. If it's a primitive
475 * type or void, the types must match exactly, or we throw
476 * an exception now.
477 */
478 LOGV("MATCH on %s.%s and %s.%s\n",
479 allMethods[i]->clazz->descriptor, allMethods[i]->name,
480 allMethods[j]->clazz->descriptor, allMethods[j]->name);
481 dupe = true;
482 if (!returnTypesAreCompatible(allMethods[i], allMethods[j]))
483 best = false;
484 }
485 }
486
487 /*
488 * If this is the best of a set of duplicates, copy it over and
489 * nuke all duplicates.
490 *
491 * While we do this, we create the set of exceptions declared to
492 * be thrown by all occurrences of the method.
493 */
494 if (dupe) {
495 if (best) {
496 LOGV("BEST %d %s.%s -> %d\n", i,
497 allMethods[i]->clazz->descriptor, allMethods[i]->name,
498 outCount);
499
500 /* if we have exceptions, make a local copy */
501 PointerSet* commonThrows = NULL;
502 if (!createExceptionClassList(allMethods[i], &commonThrows))
503 return -1;
504
505 /*
506 * Run through one more time, erasing the duplicates. (This
507 * would go faster if we had marked them somehow.)
508 */
509 for (j = 0; j < allCount; j++) {
510 if (i == j)
511 continue;
512 if (allMethods[j] == NULL)
513 continue;
514 if (dvmCompareMethodNamesAndParameterProtos(allMethods[i],
515 allMethods[j]) == 0)
516 {
517 LOGV("DEL %d %s.%s\n", j,
518 allMethods[j]->clazz->descriptor,
519 allMethods[j]->name);
520
521 /*
522 * Update set to hold the intersection of method[i]'s
523 * and method[j]'s throws.
524 */
525 if (commonThrows != NULL) {
526 updateExceptionClassList(allMethods[j],
527 commonThrows);
528 }
529
530 allMethods[j] = NULL;
531 }
532 }
533
534 /*
535 * If the set of Throwable classes isn't empty, create an
536 * array of Class, copy them into it, and put the result
537 * into the "throwLists" array.
538 */
539 if (commonThrows != NULL &&
540 dvmPointerSetGetCount(commonThrows) > 0)
541 {
542 int commonCount = dvmPointerSetGetCount(commonThrows);
543 ArrayObject* throwArray;
544 Object** contents;
545 int ent;
546
547 throwArray = dvmAllocArrayByClass(
548 gDvm.classJavaLangClassArray, commonCount,
549 ALLOC_DEFAULT);
550 if (throwArray == NULL) {
551 LOGE("common-throw array alloc failed\n");
552 return -1;
553 }
554
555 contents = (Object**) throwArray->contents;
556 for (ent = 0; ent < commonCount; ent++) {
557 contents[ent] = (Object*)
558 dvmPointerSetGetEntry(commonThrows, ent);
559 }
560
561 /* add it to the array of arrays */
562 contents = (Object**) throwLists->contents;
563 contents[outCount] = (Object*) throwArray;
564 dvmReleaseTrackedAlloc((Object*) throwArray, NULL);
565 }
566
567 /* copy the winner and NULL it out */
568 outMethods[outCount++] = allMethods[i];
569 allMethods[i] = NULL;
570
571 dvmPointerSetFree(commonThrows);
572 } else {
573 LOGV("BEST not %d\n", i);
574 }
575 } else {
576 /*
577 * Singleton. Copy the entry and NULL it out.
578 */
579 LOGV("COPY singleton %d %s.%s -> %d\n", i,
580 allMethods[i]->clazz->descriptor, allMethods[i]->name,
581 outCount);
582
583 /* keep track of our throwables */
584 ArrayObject* exceptionArray = dvmGetMethodThrows(allMethods[i]);
585 if (exceptionArray != NULL) {
586 Object** contents;
587
588 contents = (Object**) throwLists->contents;
589 contents[outCount] = (Object*) exceptionArray;
590 dvmReleaseTrackedAlloc((Object*) exceptionArray, NULL);
591 }
592
593 outMethods[outCount++] = allMethods[i];
594 allMethods[i] = NULL;
595 }
596 }
597
598 /*
599 * Check for stragglers. If we find any, throw an exception.
600 */
601 for (i = 0; i < allCount; i++) {
602 if (allMethods[i] != NULL) {
603 LOGV("BAD DUPE: %d %s.%s\n", i,
604 allMethods[i]->clazz->descriptor, allMethods[i]->name);
605 dvmThrowException("Ljava/lang/IllegalArgumentException;",
606 "incompatible return types in proxied interfaces");
607 return -1;
608 }
609 }
610
611 return outCount;
612}
613
614
615/*
616 * Classes can declare to throw multiple exceptions in a hierarchy, e.g.
617 * IOException and FileNotFoundException. Since we're only interested in
618 * knowing the set that can be thrown without requiring an extra wrapper,
619 * we can remove anything that is a subclass of something else in the list.
620 *
621 * The "mix" step we do next reduces things toward the most-derived class,
622 * so it's important that we start with the least-derived classes.
623 */
624static void reduceExceptionClassList(ArrayObject* exceptionArray)
625{
626 const ClassObject** classes = (const ClassObject**)exceptionArray->contents;
627 int len = exceptionArray->length;
628 int i, j;
629
630 /*
631 * Consider all pairs of classes. If one is the subclass of the other,
632 * null out the subclass.
633 */
634 for (i = 0; i < len-1; i++) {
635 if (classes[i] == NULL)
636 continue;
637 for (j = i + 1; j < len; j++) {
638 if (classes[j] == NULL)
639 continue;
640
641 if (dvmInstanceof(classes[i], classes[j])) {
642 classes[i] = NULL;
643 break; /* no more comparisons against classes[i] */
644 } else if (dvmInstanceof(classes[j], classes[i])) {
645 classes[j] = NULL;
646 }
647 }
648 }
649}
650
651/*
652 * Create a local array with a copy of the throwable classes declared by
653 * "method". If no throws are declared, "*pSet" will be NULL.
654 *
655 * Returns "false" on allocation failure.
656 */
657static bool createExceptionClassList(const Method* method, PointerSet** pThrows)
658{
659 ArrayObject* exceptionArray = NULL;
660 bool result = false;
661
662 exceptionArray = dvmGetMethodThrows(method);
663 if (exceptionArray != NULL && exceptionArray->length > 0) {
664 /* reduce list, nulling out redundant entries */
665 reduceExceptionClassList(exceptionArray);
666
667 *pThrows = dvmPointerSetAlloc(exceptionArray->length);
668 if (*pThrows == NULL)
669 goto bail;
670
671 const ClassObject** contents;
672 int i;
673
674 contents = (const ClassObject**) exceptionArray->contents;
675 for (i = 0; i < (int) exceptionArray->length; i++) {
676 if (contents[i] != NULL)
677 dvmPointerSetAddEntry(*pThrows, contents[i]);
678 }
679 } else {
680 *pThrows = NULL;
681 }
682
683 result = true;
684
685bail:
686 dvmReleaseTrackedAlloc((Object*) exceptionArray, NULL);
687 return result;
688}
689
690/*
691 * We need to compute the intersection of the arguments, i.e. remove
692 * anything from "throws" that isn't in the method's list of throws.
693 *
694 * If one class is a subclass of another, we want to keep just the subclass,
695 * moving toward the most-restrictive set.
696 *
697 * We assume these are all classes, and don't try to filter out interfaces.
698 */
699static void updateExceptionClassList(const Method* method, PointerSet* throws)
700{
701 int setSize = dvmPointerSetGetCount(throws);
702 if (setSize == 0)
703 return;
704
705 ArrayObject* exceptionArray = dvmGetMethodThrows(method);
706 if (exceptionArray == NULL) {
707 /* nothing declared, so intersection is empty */
708 dvmPointerSetClear(throws);
709 return;
710 }
711
712 /* reduce list, nulling out redundant entries */
713 reduceExceptionClassList(exceptionArray);
714
715 int mixLen = dvmPointerSetGetCount(throws);
716 const ClassObject* mixSet[mixLen];
717
718 int declLen = exceptionArray->length;
719 const ClassObject** declSet = (const ClassObject**)exceptionArray->contents;
720
721 int i, j;
722
723 /* grab a local copy to work on */
724 for (i = 0; i < mixLen; i++) {
725 mixSet[i] = dvmPointerSetGetEntry(throws, i);
726 }
727
728 for (i = 0; i < mixLen; i++) {
729 for (j = 0; j < declLen; j++) {
730 if (declSet[j] == NULL)
731 continue;
732
733 if (mixSet[i] == declSet[j]) {
734 /* match, keep this one */
735 break;
736 } else if (dvmInstanceof(mixSet[i], declSet[j])) {
737 /* mix is a subclass of a declared throwable, keep it */
738 break;
739 } else if (dvmInstanceof(declSet[j], mixSet[i])) {
740 /* mix is a superclass, replace it */
741 mixSet[i] = declSet[j];
742 break;
743 }
744 }
745
746 if (j == declLen) {
747 /* no match, remove entry by nulling it out */
748 mixSet[i] = NULL;
749 }
750 }
751
752 /* copy results back out; this eliminates duplicates as we go */
753 dvmPointerSetClear(throws);
754 for (i = 0; i < mixLen; i++) {
755 if (mixSet[i] != NULL)
756 dvmPointerSetAddEntry(throws, mixSet[i]);
757 }
758
759 dvmReleaseTrackedAlloc((Object*) exceptionArray, NULL);
760}
761
762
763/*
764 * Check to see if the return types are compatible.
765 *
766 * If the return type is primitive or void, it must match exactly.
767 *
768 * If not, the type in "subMethod" must be assignable to the type in
769 * "baseMethod".
770 */
771static bool returnTypesAreCompatible(Method* subMethod, Method* baseMethod)
772{
773 const char* baseSig = dexProtoGetReturnType(&baseMethod->prototype);
774 const char* subSig = dexProtoGetReturnType(&subMethod->prototype);
775 ClassObject* baseClass;
776 ClassObject* subClass;
777
778 if (baseSig[1] == '\0' || subSig[1] == '\0') {
779 /* at least one is primitive type */
780 return (baseSig[0] == subSig[0] && baseSig[1] == subSig[1]);
781 }
782
783 baseClass = dvmFindClass(baseSig, baseMethod->clazz->classLoader);
784 subClass = dvmFindClass(subSig, subMethod->clazz->classLoader);
785 bool result = dvmInstanceof(subClass, baseClass);
786 return result;
787}
788
789/*
790 * Create a constructor for our Proxy class. The constructor takes one
791 * argument, a java.lang.reflect.InvocationHandler.
792 */
793static void createConstructor(ClassObject* clazz, Method* meth)
794{
795 meth->clazz = clazz;
796 meth->accessFlags = ACC_PUBLIC | ACC_NATIVE;
797 meth->name = "<init>";
798 meth->prototype =
799 gDvm.methJavaLangReflectProxy_constructorPrototype->prototype;
800 meth->shorty =
801 gDvm.methJavaLangReflectProxy_constructorPrototype->shorty;
802 // no pDexCode or pDexMethod
803
804 int argsSize = dvmComputeMethodArgsSize(meth) + 1;
805 meth->registersSize = meth->insSize = argsSize;
806
807 meth->nativeFunc = proxyConstructor;
808}
809
810/*
811 * Create a method in our Proxy class with the name and signature of
812 * the interface method it implements.
813 */
814static void createHandlerMethod(ClassObject* clazz, Method* dstMeth,
815 const Method* srcMeth)
816{
817 dstMeth->clazz = clazz;
818 dstMeth->insns = (u2*) srcMeth;
819 dstMeth->accessFlags = ACC_PUBLIC | ACC_NATIVE;
820 dstMeth->name = srcMeth->name;
821 dstMeth->prototype = srcMeth->prototype;
822 dstMeth->shorty = srcMeth->shorty;
823 // no pDexCode or pDexMethod
824
825 int argsSize = dvmComputeMethodArgsSize(dstMeth) + 1;
826 dstMeth->registersSize = dstMeth->insSize = argsSize;
827
828 dstMeth->nativeFunc = proxyInvoker;
829}
830
831/*
832 * Return a new Object[] array with the contents of "args". We determine
833 * the number and types of values in "args" based on the method signature.
834 * Primitive types are boxed.
835 *
836 * Returns NULL if the method takes no arguments.
837 *
838 * The caller must call dvmReleaseTrackedAlloc() on the return value.
839 *
840 * On failure, returns with an appropriate exception raised.
841 */
842static ArrayObject* boxMethodArgs(const Method* method, const u4* args)
843{
844 const char* desc = &method->shorty[1]; // [0] is the return type.
845 ArrayObject* argArray = NULL;
846 int argCount;
847 Object** argObjects;
848 bool failed = true;
849
850 /* count args */
851 argCount = dexProtoGetParameterCount(&method->prototype);
852
853 /* allocate storage */
854 argArray = dvmAllocArray(gDvm.classJavaLangObjectArray, argCount,
855 kObjectArrayRefWidth, ALLOC_DEFAULT);
856 if (argArray == NULL)
857 goto bail;
858 argObjects = (Object**) argArray->contents;
859
860 /*
861 * Fill in the array.
862 */
863
864 int srcIndex = 0;
865
866 argCount = 0;
867 while (*desc != '\0') {
868 char descChar = *(desc++);
869 JValue value;
870
871 switch (descChar) {
872 case 'Z':
873 case 'C':
874 case 'F':
875 case 'B':
876 case 'S':
877 case 'I':
878 value.i = args[srcIndex++];
879 argObjects[argCount] = (Object*) dvmWrapPrimitive(value,
880 dvmFindPrimitiveClass(descChar));
881 /* argObjects is tracked, don't need to hold this too */
882 dvmReleaseTrackedAlloc(argObjects[argCount], NULL);
883 argCount++;
884 break;
885 case 'D':
886 case 'J':
887 value.j = dvmGetArgLong(args, srcIndex);
888 srcIndex += 2;
889 argObjects[argCount] = (Object*) dvmWrapPrimitive(value,
890 dvmFindPrimitiveClass(descChar));
891 dvmReleaseTrackedAlloc(argObjects[argCount], NULL);
892 argCount++;
893 break;
894 case '[':
895 case 'L':
896 argObjects[argCount++] = (Object*) args[srcIndex++];
897 break;
898 }
899 }
900
901 failed = false;
902
903bail:
904 if (failed) {
905 dvmReleaseTrackedAlloc((Object*)argArray, NULL);
906 argArray = NULL;
907 }
908 return argArray;
909}
910
911/*
912 * This is the constructor for a generated proxy object. All we need to
913 * do is stuff "handler" into "h".
914 */
915static void proxyConstructor(const u4* args, JValue* pResult,
916 const Method* method, Thread* self)
917{
918 Object* obj = (Object*) args[0];
919 Object* handler = (Object*) args[1];
920
921 dvmSetFieldObject(obj, gDvm.offJavaLangReflectProxy_h, handler);
922}
923
924/*
925 * This is the common message body for proxy methods.
926 *
927 * The method we're calling looks like:
928 * public Object invoke(Object proxy, Method method, Object[] args)
929 *
930 * This means we have to create a Method object, box our arguments into
931 * a new Object[] array, make the call, and unbox the return value if
932 * necessary.
933 */
934static void proxyInvoker(const u4* args, JValue* pResult,
935 const Method* method, Thread* self)
936{
937 Object* thisObj = (Object*) args[0];
938 Object* methodObj = NULL;
939 ArrayObject* argArray = NULL;
940 Object* handler;
941 Method* invoke;
942 ClassObject* returnType;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800943 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}