2 * Copyright (C) 2008 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 * Implementation of java.lang.reflect.Proxy.
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
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);
48 /* private static fields in the Proxy class */
49 #define kThrowsField 0
50 #define kProxySFieldCount 1
53 * Generate a proxy class with the specified name, interfaces, and loader.
54 * "interfaces" is an array of class objects.
56 * The Proxy.getProxyClass() code has done the following:
57 * - Verified that "interfaces" contains only interfaces
58 * - Verified that no interface appears twice
59 * - Prepended the package name to the class name if one or more
60 * interfaces are non-public
61 * - Searched for an existing instance of an appropriate Proxy class
63 * On failure we leave a partially-created class object sitting around,
64 * but the garbage collector will take care of it.
66 ClassObject* dvmGenerateProxyClass(StringObject* str, ArrayObject* interfaces,
70 ArrayObject* throws = NULL;
72 char* nameStr = dvmCreateCstrFromString(str);
73 if (nameStr == NULL) {
74 dvmThrowIllegalArgumentException("missing name");
78 ALOGV("+++ Generate proxy class '%s' %p from %d interface classes",
79 nameStr, loader, interfaces->length);
83 * Characteristics of a Proxy class:
84 * - concrete class, public and final
85 * - superclass is java.lang.reflect.Proxy
86 * - implements all listed interfaces (req'd for instanceof)
87 * - has one method for each method in the interfaces (for duplicates,
88 * the method in the earliest interface wins)
89 * - has one constructor (takes an InvocationHandler arg)
90 * - has overrides for hashCode, equals, and toString (these come first)
91 * - has one field, a reference to the InvocationHandler object, inherited
94 * TODO: set protection domain so it matches bootstrap classes.
96 * The idea here is to create a class object and fill in the details
97 * as we would in loadClassFromDex(), and then call dvmLinkClass() to do
98 * all the heavy lifting (notably populating the virtual and interface
103 * Allocate storage for the class object and set some basic fields.
105 size_t newClassSize =
106 sizeof(ClassObject) + kProxySFieldCount * sizeof(StaticField);
107 ClassObject* newClass =
108 (ClassObject*) dvmMalloc(newClassSize, ALLOC_NON_MOVING);
109 if (newClass == NULL)
111 DVM_OBJECT_INIT(newClass, gDvm.classJavaLangClass);
112 dvmSetClassSerialNumber(newClass);
113 newClass->descriptorAlloc = dvmNameToDescriptor(nameStr);
114 newClass->descriptor = newClass->descriptorAlloc;
115 SET_CLASS_FLAG(newClass, ACC_PUBLIC | ACC_FINAL);
116 dvmSetFieldObject((Object *)newClass,
117 OFFSETOF_MEMBER(ClassObject, super),
118 (Object *)gDvm.classJavaLangReflectProxy);
119 newClass->primitiveType = PRIM_NOT;
120 dvmSetFieldObject((Object *)newClass,
121 OFFSETOF_MEMBER(ClassObject, classLoader),
125 * Add direct method definitions. We have one (the constructor).
127 newClass->directMethodCount = 1;
128 newClass->directMethods = (Method*) dvmLinearAlloc(newClass->classLoader,
130 createConstructor(newClass, &newClass->directMethods[0]);
131 dvmLinearReadOnly(newClass->classLoader, newClass->directMethods);
134 * Add virtual method definitions.
138 * Generate a temporary list of virtual methods.
142 if (!gatherMethods(interfaces, &methods, &throws, &methodCount)) {
145 newClass->virtualMethodCount = methodCount;
146 size_t virtualMethodsSize = methodCount * sizeof(Method);
147 newClass->virtualMethods =
148 (Method*)dvmLinearAlloc(newClass->classLoader, virtualMethodsSize);
149 for (int i = 0; i < newClass->virtualMethodCount; i++) {
150 createHandlerMethod(newClass, &newClass->virtualMethods[i], methods[i]);
153 dvmLinearReadOnly(newClass->classLoader, newClass->virtualMethods);
157 * Add interface list.
160 size_t interfaceCount = interfaces->length;
161 ClassObject** ifArray = (ClassObject**)(void*)interfaces->contents;
162 newClass->interfaceCount = interfaceCount;
163 size_t interfacesSize = sizeof(ClassObject*) * interfaceCount;
164 newClass->interfaces =
165 (ClassObject**)dvmLinearAlloc(newClass->classLoader, interfacesSize);
166 for (size_t i = 0; i < interfaceCount; i++)
167 newClass->interfaces[i] = ifArray[i];
168 dvmLinearReadOnly(newClass->classLoader, newClass->interfaces);
172 * Static field list. We have one private field, for our list of
173 * exceptions declared for each method.
175 assert(kProxySFieldCount == 1);
176 newClass->sfieldCount = kProxySFieldCount;
178 StaticField* sfield = &newClass->sfields[kThrowsField];
179 sfield->clazz = newClass;
180 sfield->name = "throws";
181 sfield->signature = "[[Ljava/lang/Throwable;";
182 sfield->accessFlags = ACC_STATIC | ACC_PRIVATE;
183 dvmSetStaticFieldObject(sfield, (Object*)throws);
187 * Everything is ready. This class didn't come out of a DEX file
188 * so we didn't tuck any indexes into the class object. We can
189 * advance to LOADED state immediately.
191 newClass->status = CLASS_LOADED;
192 if (!dvmLinkClass(newClass)) {
193 LOGD("Proxy class link failed");
198 * All good. Add it to the hash table. We should NOT see a collision
199 * here; if we do, it means the caller has screwed up and provided us
200 * with a duplicate name.
202 if (!dvmAddClassToHash(newClass)) {
203 LOGE("ERROR: attempted to generate %s more than once",
204 newClass->descriptor);
213 /* must free innards explicitly if we didn't finish linking */
214 dvmFreeClassInnards(newClass);
216 if (!dvmCheckException(dvmThreadSelf())) {
217 /* throw something */
218 dvmThrowRuntimeException(NULL);
222 /* allow the GC to free these when nothing else has a reference */
223 dvmReleaseTrackedAlloc((Object*) throws, NULL);
224 dvmReleaseTrackedAlloc((Object*) newClass, NULL);
231 * Generate a list of methods. The Method pointers returned point to the
232 * abstract method definition from the appropriate interface, or to the
233 * virtual method definition in java.lang.Object.
235 * We also allocate an array of arrays of throwable classes, one for each
236 * method,so we can do some special handling of checked exceptions. The
237 * caller must call ReleaseTrackedAlloc() on *pThrows.
239 static bool gatherMethods(ArrayObject* interfaces, Method*** pMethods,
240 ArrayObject** pThrows, int* pMethodCount)
242 ClassObject** classes;
243 ArrayObject* throws = NULL;
244 Method** methods = NULL;
245 Method** allMethods = NULL;
246 int numInterfaces, maxCount, actualCount, allCount;
251 * Get a maximum count so we can allocate storage. We need the
252 * methods declared by each interface and all of its superinterfaces.
254 maxCount = 3; // 3 methods in java.lang.Object
255 numInterfaces = interfaces->length;
256 classes = (ClassObject**)(void*)interfaces->contents;
258 for (i = 0; i < numInterfaces; i++, classes++) {
259 ClassObject* clazz = *classes;
261 LOGVV("--- %s virtualMethodCount=%d",
262 clazz->descriptor, clazz->virtualMethodCount);
263 maxCount += clazz->virtualMethodCount;
266 for (j = 0; j < clazz->iftableCount; j++) {
267 ClassObject* iclass = clazz->iftable[j].clazz;
270 iclass->descriptor, iclass->virtualMethodCount);
271 maxCount += iclass->virtualMethodCount;
275 methods = (Method**) malloc(maxCount * sizeof(*methods));
276 allMethods = (Method**) malloc(maxCount * sizeof(*methods));
277 if (methods == NULL || allMethods == NULL)
281 * First three entries are the java.lang.Object methods.
284 ClassObject* obj = gDvm.classJavaLangObject;
285 allMethods[0] = obj->vtable[gDvm.voffJavaLangObject_equals];
286 allMethods[1] = obj->vtable[gDvm.voffJavaLangObject_hashCode];
287 allMethods[2] = obj->vtable[gDvm.voffJavaLangObject_toString];
292 * Add the methods from each interface, in order.
294 classes = (ClassObject**)(void*)interfaces->contents;
295 for (i = 0; i < numInterfaces; i++, classes++) {
296 ClassObject* clazz = *classes;
299 for (j = 0; j < clazz->virtualMethodCount; j++) {
300 allMethods[allCount++] = &clazz->virtualMethods[j];
303 for (j = 0; j < clazz->iftableCount; j++) {
304 ClassObject* iclass = clazz->iftable[j].clazz;
307 for (k = 0; k < iclass->virtualMethodCount; k++) {
308 allMethods[allCount++] = &iclass->virtualMethods[k];
312 assert(allCount == maxCount);
315 * Allocate some storage to hold the lists of throwables. We need
316 * one entry per unique method, but it's convenient to allocate it
317 * ahead of the duplicate processing.
319 ClassObject* arrArrClass;
320 arrArrClass = dvmFindArrayClass("[[Ljava/lang/Throwable;", NULL);
321 if (arrArrClass == NULL)
323 throws = dvmAllocArrayByClass(arrArrClass, allCount, ALLOC_DEFAULT);
326 * Identify and remove duplicates.
328 actualCount = copyWithoutDuplicates(allMethods, allCount, methods, throws);
332 //LOGI("gathered methods:");
333 //for (i = 0; i < actualCount; i++) {
334 // LOGI(" %d: %s.%s",
335 // i, methods[i]->clazz->descriptor, methods[i]->name);
339 *pMethodCount = actualCount;
347 dvmReleaseTrackedAlloc((Object*)throws, NULL);
353 * Identify and remove duplicates, where "duplicate" means it has the
354 * same name and arguments, but not necessarily the same return type.
356 * If duplicate methods have different return types, we want to use the
357 * first method whose return type is assignable from all other duplicate
358 * methods. That is, if we have:
360 * class sub extends base {...}
361 * class subsub extends sub {...}
362 * Then we want to return the method that returns subsub, since callers
363 * to any form of the method will get a usable object back.
365 * All other duplicate methods are stripped out.
367 * This also populates the "throwLists" array with arrays of Class objects,
368 * one entry per method in "outMethods". Methods that don't declare any
369 * throwables (or have no common throwables with duplicate methods) will
372 * Returns the number of methods copied into "methods", or -1 on failure.
374 static int copyWithoutDuplicates(Method** allMethods, int allCount,
375 Method** outMethods, ArrayObject* throwLists)
381 * The plan is to run through all methods, checking all other methods
382 * for a duplicate. If we find a match, we see if the other methods'
383 * return type is compatible/assignable with ours. If the current
384 * method is assignable from all others, we copy it to the new list,
385 * and NULL out all other entries. If not, we keep looking for a
388 * If there are no duplicates, we copy the method and NULL the entry.
390 * At the end of processing, if we have any non-NULL entries, then we
391 * have bad duplicates and must exit with an exception.
393 for (i = 0; i < allCount; i++) {
396 if (allMethods[i] == NULL)
400 * Find all duplicates. If any of the return types is not
401 * assignable to our return type, then we're not the best.
403 * We start from 0, not i, because we need to compare assignability
404 * the other direction even if we've compared these before.
408 for (j = 0; j < allCount; j++) {
411 if (allMethods[j] == NULL)
414 if (dvmCompareMethodNamesAndParameterProtos(allMethods[i],
418 * Duplicate method, check return type. If it's a primitive
419 * type or void, the types must match exactly, or we throw
422 ALOGV("MATCH on %s.%s and %s.%s",
423 allMethods[i]->clazz->descriptor, allMethods[i]->name,
424 allMethods[j]->clazz->descriptor, allMethods[j]->name);
426 if (!returnTypesAreCompatible(allMethods[i], allMethods[j]))
432 * If this is the best of a set of duplicates, copy it over and
433 * nuke all duplicates.
435 * While we do this, we create the set of exceptions declared to
436 * be thrown by all occurrences of the method.
440 ALOGV("BEST %d %s.%s -> %d", i,
441 allMethods[i]->clazz->descriptor, allMethods[i]->name,
444 /* if we have exceptions, make a local copy */
445 PointerSet* commonThrows = NULL;
446 if (!createExceptionClassList(allMethods[i], &commonThrows))
450 * Run through one more time, erasing the duplicates. (This
451 * would go faster if we had marked them somehow.)
453 for (j = 0; j < allCount; j++) {
456 if (allMethods[j] == NULL)
458 if (dvmCompareMethodNamesAndParameterProtos(allMethods[i],
461 ALOGV("DEL %d %s.%s", j,
462 allMethods[j]->clazz->descriptor,
463 allMethods[j]->name);
466 * Update set to hold the intersection of method[i]'s
467 * and method[j]'s throws.
469 if (commonThrows != NULL) {
470 updateExceptionClassList(allMethods[j],
474 allMethods[j] = NULL;
479 * If the set of Throwable classes isn't empty, create an
480 * array of Class, copy them into it, and put the result
481 * into the "throwLists" array.
483 if (commonThrows != NULL &&
484 dvmPointerSetGetCount(commonThrows) > 0)
486 int commonCount = dvmPointerSetGetCount(commonThrows);
487 ArrayObject* throwArray;
491 throwArray = dvmAllocArrayByClass(
492 gDvm.classJavaLangClassArray, commonCount,
494 if (throwArray == NULL) {
495 LOGE("common-throw array alloc failed");
499 contents = (Object**)(void*)throwArray->contents;
500 for (ent = 0; ent < commonCount; ent++) {
501 contents[ent] = (Object*)
502 dvmPointerSetGetEntry(commonThrows, ent);
505 /* add it to the array of arrays */
506 contents = (Object**)(void*)throwLists->contents;
507 contents[outCount] = (Object*) throwArray;
508 dvmReleaseTrackedAlloc((Object*) throwArray, NULL);
511 /* copy the winner and NULL it out */
512 outMethods[outCount++] = allMethods[i];
513 allMethods[i] = NULL;
515 dvmPointerSetFree(commonThrows);
517 ALOGV("BEST not %d", i);
521 * Singleton. Copy the entry and NULL it out.
523 ALOGV("COPY singleton %d %s.%s -> %d", i,
524 allMethods[i]->clazz->descriptor, allMethods[i]->name,
527 /* keep track of our throwables */
528 ArrayObject* exceptionArray = dvmGetMethodThrows(allMethods[i]);
529 if (exceptionArray != NULL) {
532 contents = (Object**)(void*)throwLists->contents;
533 contents[outCount] = (Object*) exceptionArray;
534 dvmReleaseTrackedAlloc((Object*) exceptionArray, NULL);
537 outMethods[outCount++] = allMethods[i];
538 allMethods[i] = NULL;
543 * Check for stragglers. If we find any, throw an exception.
545 for (i = 0; i < allCount; i++) {
546 if (allMethods[i] != NULL) {
547 ALOGV("BAD DUPE: %d %s.%s", i,
548 allMethods[i]->clazz->descriptor, allMethods[i]->name);
549 dvmThrowIllegalArgumentException(
550 "incompatible return types in proxied interfaces");
560 * Classes can declare to throw multiple exceptions in a hierarchy, e.g.
561 * IOException and FileNotFoundException. Since we're only interested in
562 * knowing the set that can be thrown without requiring an extra wrapper,
563 * we can remove anything that is a subclass of something else in the list.
565 * The "mix" step we do next reduces things toward the most-derived class,
566 * so it's important that we start with the least-derived classes.
568 static void reduceExceptionClassList(ArrayObject* exceptionArray)
570 const ClassObject** classes =
571 (const ClassObject**)(void*)exceptionArray->contents;
574 * Consider all pairs of classes. If one is the subclass of the other,
575 * null out the subclass.
577 size_t len = exceptionArray->length;
578 for (size_t i = 0; i < len - 1; i++) {
579 if (classes[i] == NULL)
581 for (size_t j = i + 1; j < len; j++) {
582 if (classes[j] == NULL)
585 if (dvmInstanceof(classes[i], classes[j])) {
587 break; /* no more comparisons against classes[i] */
588 } else if (dvmInstanceof(classes[j], classes[i])) {
596 * Create a local array with a copy of the throwable classes declared by
597 * "method". If no throws are declared, "*pSet" will be NULL.
599 * Returns "false" on allocation failure.
601 static bool createExceptionClassList(const Method* method, PointerSet** pThrows)
603 ArrayObject* exceptionArray = NULL;
606 exceptionArray = dvmGetMethodThrows(method);
607 if (exceptionArray != NULL && exceptionArray->length > 0) {
608 /* reduce list, nulling out redundant entries */
609 reduceExceptionClassList(exceptionArray);
611 *pThrows = dvmPointerSetAlloc(exceptionArray->length);
612 if (*pThrows == NULL)
615 const ClassObject** contents;
617 contents = (const ClassObject**)(void*)exceptionArray->contents;
618 for (size_t i = 0; i < exceptionArray->length; i++) {
619 if (contents[i] != NULL)
620 dvmPointerSetAddEntry(*pThrows, contents[i]);
629 dvmReleaseTrackedAlloc((Object*) exceptionArray, NULL);
634 * We need to compute the intersection of the arguments, i.e. remove
635 * anything from "throws" that isn't in the method's list of throws.
637 * If one class is a subclass of another, we want to keep just the subclass,
638 * moving toward the most-restrictive set.
640 * We assume these are all classes, and don't try to filter out interfaces.
642 static void updateExceptionClassList(const Method* method, PointerSet* throws)
644 int setSize = dvmPointerSetGetCount(throws);
648 ArrayObject* exceptionArray = dvmGetMethodThrows(method);
649 if (exceptionArray == NULL) {
650 /* nothing declared, so intersection is empty */
651 dvmPointerSetClear(throws);
655 /* reduce list, nulling out redundant entries */
656 reduceExceptionClassList(exceptionArray);
658 size_t mixLen = dvmPointerSetGetCount(throws);
659 const ClassObject* mixSet[mixLen];
661 size_t declLen = exceptionArray->length;
662 const ClassObject** declSet = (const ClassObject**)(void*)exceptionArray->contents;
664 /* grab a local copy to work on */
665 for (size_t i = 0; i < mixLen; i++) {
666 mixSet[i] = (ClassObject*)dvmPointerSetGetEntry(throws, i);
669 for (size_t i = 0; i < mixLen; i++) {
671 for (j = 0; j < declLen; j++) {
672 if (declSet[j] == NULL)
675 if (mixSet[i] == declSet[j]) {
676 /* match, keep this one */
678 } else if (dvmInstanceof(mixSet[i], declSet[j])) {
679 /* mix is a subclass of a declared throwable, keep it */
681 } else if (dvmInstanceof(declSet[j], mixSet[i])) {
682 /* mix is a superclass, replace it */
683 mixSet[i] = declSet[j];
689 /* no match, remove entry by nulling it out */
694 /* copy results back out; this eliminates duplicates as we go */
695 dvmPointerSetClear(throws);
696 for (size_t i = 0; i < mixLen; i++) {
697 if (mixSet[i] != NULL)
698 dvmPointerSetAddEntry(throws, mixSet[i]);
701 dvmReleaseTrackedAlloc((Object*) exceptionArray, NULL);
706 * Check to see if the return types are compatible.
708 * If the return type is primitive or void, it must match exactly.
710 * If not, the type in "subMethod" must be assignable to the type in
713 static bool returnTypesAreCompatible(Method* subMethod, Method* baseMethod)
715 const char* baseSig = dexProtoGetReturnType(&baseMethod->prototype);
716 const char* subSig = dexProtoGetReturnType(&subMethod->prototype);
717 ClassObject* baseClass;
718 ClassObject* subClass;
720 if (baseSig[1] == '\0' || subSig[1] == '\0') {
721 /* at least one is primitive type */
722 return (baseSig[0] == subSig[0] && baseSig[1] == subSig[1]);
725 baseClass = dvmFindClass(baseSig, baseMethod->clazz->classLoader);
726 subClass = dvmFindClass(subSig, subMethod->clazz->classLoader);
727 bool result = dvmInstanceof(subClass, baseClass);
732 * Create a constructor for our Proxy class. The constructor takes one
733 * argument, a java.lang.reflect.InvocationHandler.
735 static void createConstructor(ClassObject* clazz, Method* meth)
738 * The constructor signatures (->prototype and ->shorty) need to
739 * be cloned from a method in a "real" DEX file. We declared the
740 * otherwise unused method Proxy.constructorPrototype() just for
745 meth->accessFlags = ACC_PUBLIC | ACC_NATIVE;
746 meth->name = "<init>";
748 gDvm.methJavaLangReflectProxy_constructorPrototype->prototype;
750 gDvm.methJavaLangReflectProxy_constructorPrototype->shorty;
751 // no pDexCode or pDexMethod
753 int argsSize = dvmComputeMethodArgsSize(meth) + 1;
754 meth->registersSize = meth->insSize = argsSize;
756 meth->nativeFunc = proxyConstructor;
760 * Create a method in our Proxy class with the name and signature of
761 * the interface method it implements.
763 static void createHandlerMethod(ClassObject* clazz, Method* dstMeth,
764 const Method* srcMeth)
766 dstMeth->clazz = clazz;
767 dstMeth->insns = (u2*) srcMeth;
768 dstMeth->accessFlags = ACC_PUBLIC | ACC_NATIVE;
769 dstMeth->name = srcMeth->name;
770 dstMeth->prototype = srcMeth->prototype;
771 dstMeth->shorty = srcMeth->shorty;
772 // no pDexCode or pDexMethod
774 int argsSize = dvmComputeMethodArgsSize(dstMeth) + 1;
775 dstMeth->registersSize = dstMeth->insSize = argsSize;
777 dstMeth->nativeFunc = proxyInvoker;
781 * Return a new Object[] array with the contents of "args". We determine
782 * the number and types of values in "args" based on the method signature.
783 * Primitive types are boxed.
785 * Returns NULL if the method takes no arguments.
787 * The caller must call dvmReleaseTrackedAlloc() on the return value.
789 * On failure, returns with an appropriate exception raised.
791 static ArrayObject* boxMethodArgs(const Method* method, const u4* args)
793 const char* desc = &method->shorty[1]; // [0] is the return type.
796 size_t argCount = dexProtoGetParameterCount(&method->prototype);
798 /* allocate storage */
799 ArrayObject* argArray = dvmAllocArrayByClass(gDvm.classJavaLangObjectArray,
800 argCount, ALLOC_DEFAULT);
801 if (argArray == NULL)
803 Object** argObjects = (Object**)(void*)argArray->contents;
811 while (*desc != '\0') {
812 char descChar = *(desc++);
822 value.i = args[srcIndex++];
823 argObjects[dstIndex] = (Object*) dvmBoxPrimitive(value,
824 dvmFindPrimitiveClass(descChar));
825 /* argObjects is tracked, don't need to hold this too */
826 dvmReleaseTrackedAlloc(argObjects[dstIndex], NULL);
831 value.j = dvmGetArgLong(args, srcIndex);
833 argObjects[dstIndex] = (Object*) dvmBoxPrimitive(value,
834 dvmFindPrimitiveClass(descChar));
835 dvmReleaseTrackedAlloc(argObjects[dstIndex], NULL);
840 argObjects[dstIndex++] = (Object*) args[srcIndex++];
849 * This is the constructor for a generated proxy object. All we need to
850 * do is stuff "handler" into "h".
852 static void proxyConstructor(const u4* args, JValue* pResult,
853 const Method* method, Thread* self)
855 Object* obj = (Object*) args[0];
856 Object* handler = (Object*) args[1];
858 dvmSetFieldObject(obj, gDvm.offJavaLangReflectProxy_h, handler);
862 * This is the common message body for proxy methods.
864 * The method we're calling looks like:
865 * public Object invoke(Object proxy, Method method, Object[] args)
867 * This means we have to create a Method object, box our arguments into
868 * a new Object[] array, make the call, and unbox the return value if
871 static void proxyInvoker(const u4* args, JValue* pResult,
872 const Method* method, Thread* self)
874 Object* thisObj = (Object*) args[0];
875 Object* methodObj = NULL;
876 ArrayObject* argArray = NULL;
879 ClassObject* returnType;
883 * Retrieve handler object for this proxy instance. The field is
884 * defined in the superclass (Proxy).
886 handler = dvmGetFieldObject(thisObj, gDvm.offJavaLangReflectProxy_h);
889 * Find the invoke() method, looking in "this"s class. (Because we
890 * start here we don't have to convert it to a vtable index and then
891 * index into this' vtable.)
893 invoke = dvmFindVirtualMethodHierByDescriptor(handler->clazz, "invoke",
894 "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;");
895 if (invoke == NULL) {
896 LOGE("Unable to find invoke()");
900 ALOGV("invoke: %s.%s, this=%p, handler=%s",
901 method->clazz->descriptor, method->name,
902 thisObj, handler->clazz->descriptor);
905 * Create a java.lang.reflect.Method object for this method.
907 * We don't want to use "method", because that's the concrete
908 * implementation in the proxy class. We want the abstract Method
909 * from the declaring interface. We have a pointer to it tucked
910 * away in the "insns" field.
912 * TODO: this could be cached for performance.
914 methodObj = dvmCreateReflectMethodObject((Method*) method->insns);
915 if (methodObj == NULL) {
916 assert(dvmCheckException(self));
921 * Determine the return type from the signature.
923 * TODO: this could be cached for performance.
925 returnType = dvmGetBoxedReturnType(method);
926 if (returnType == NULL) {
927 char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
928 LOGE("Could not determine return type for '%s'", desc);
930 assert(dvmCheckException(self));
933 ALOGV(" return type will be %s", returnType->descriptor);
936 * Convert "args" array into Object[] array, using the method
937 * signature to determine types. If the method takes no arguments,
940 argArray = boxMethodArgs(method, args+1);
941 if (dvmCheckException(self))
945 * Call h.invoke(proxy, method, args).
947 * We don't need to repackage exceptions, so if one has been thrown
948 * just jump to the end.
950 * We're not adding invokeResult.l to the tracked allocation list, but
951 * since we're just unboxing it or returning it to interpreted code
952 * that shouldn't be a problem.
954 dvmCallMethod(self, invoke, handler, &invokeResult,
955 thisObj, methodObj, argArray);
956 if (dvmCheckException(self)) {
957 Object* excep = dvmGetException(self);
958 if (mustWrapException(method, excep)) {
959 /* wrap with UndeclaredThrowableException */
960 dvmWrapException("Ljava/lang/reflect/UndeclaredThrowableException;");
966 * Unbox the return value. If it's the wrong type, throw a
967 * ClassCastException. If it's a null pointer and we need a
968 * primitive type, throw a NullPointerException.
970 if (returnType->primitiveType == PRIM_VOID) {
971 LOGVV("+++ ignoring return to void");
972 } else if (invokeResult.l == NULL) {
973 if (dvmIsPrimitiveClass(returnType)) {
974 dvmThrowNullPointerException(
975 "null result when primitive expected");
980 if (!dvmUnboxPrimitive((Object*)invokeResult.l, returnType, pResult)) {
981 dvmThrowClassCastException(((Object*)invokeResult.l)->clazz,
988 dvmReleaseTrackedAlloc(methodObj, self);
989 dvmReleaseTrackedAlloc((Object*)argArray, self);
993 * Determine if it's okay for this method to throw this exception. If
994 * an unchecked exception was thrown we immediately return false. If
995 * checked, we have to ensure that this method and all of its duplicates
996 * have declared that they throw it.
998 static bool mustWrapException(const Method* method, const Object* throwable)
1000 if (!dvmIsCheckedException(throwable))
1003 const StaticField* sfield = &method->clazz->sfields[kThrowsField];
1004 const ArrayObject* throws = (ArrayObject*) dvmGetStaticFieldObject(sfield);
1006 int methodIndex = method - method->clazz->virtualMethods;
1007 assert(methodIndex >= 0 && methodIndex < method->clazz->virtualMethodCount);
1009 const Object** contents = (const Object**)(void*)throws->contents;
1010 const ArrayObject* methodThrows = (ArrayObject*) contents[methodIndex];
1012 if (methodThrows == NULL) {
1013 /* no throws declared, must wrap all checked exceptions */
1017 size_t throwCount = methodThrows->length;
1018 const ClassObject** classes =
1019 (const ClassObject**)(void*)methodThrows->contents;
1021 for (size_t i = 0; i < throwCount; i++) {
1022 if (dvmInstanceof(throwable->clazz, classes[i])) {
1023 /* this was declared, okay to throw */
1028 /* no match in declared throws */