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