/* * JNI reflection support: convert Method to reflection object. * * The returned object will be either a java.lang.reflect.Method or * .Constructor, depending on whether "method" is a constructor. * * This is also used for certain "system" annotations. * * Caller must call dvmReleaseTrackedAlloc(). */ Object* dvmCreateReflectObjForMethod(const ClassObject* clazz, Method* method) { UNUSED_PARAMETER(clazz); if (strcmp(method->name, "<init>") == 0) { if (!dvmIsClassInitialized(gDvm.classJavaLangReflectConstructor)) dvmInitClass(gDvm.classJavaLangReflectConstructor); return createConstructorObject(method); } else { if (!dvmIsClassInitialized(gDvm.classJavaLangReflectMethod)) dvmInitClass(gDvm.classJavaLangReflectMethod); return dvmCreateReflectMethodObject(method); } }
/* * Get an array with all methods declared by a class. * * This includes both static and virtual methods, and can include private * members if "publicOnly" is false. It does not include Miranda methods, * since those weren't declared in the class, or constructors. */ ArrayObject* dvmGetDeclaredMethods(ClassObject* clazz, bool publicOnly) { if (!dvmIsClassInitialized(gDvm.classJavaLangReflectMethod)) dvmInitClass(gDvm.classJavaLangReflectMethod); /* * Count up the #of relevant methods. * * Ignore virtual Miranda methods and direct class/object constructors. */ size_t count = 0; Method* meth = clazz->virtualMethods; for (int i = 0; i < clazz->virtualMethodCount; i++, meth++) { if ((!publicOnly || dvmIsPublicMethod(meth)) && !dvmIsMirandaMethod(meth)) { count++; } } meth = clazz->directMethods; for (int i = 0; i < clazz->directMethodCount; i++, meth++) { if ((!publicOnly || dvmIsPublicMethod(meth)) && meth->name[0] != '<') { count++; } } /* * Create an array of Method objects. */ ArrayObject* methodArray = dvmAllocArrayByClass(gDvm.classJavaLangReflectMethodArray, count, ALLOC_DEFAULT); if (methodArray == NULL) return NULL; /* * Fill out the array. */ meth = clazz->virtualMethods; size_t methObjCount = 0; for (int i = 0; i < clazz->virtualMethodCount; i++, meth++) { if ((!publicOnly || dvmIsPublicMethod(meth)) && !dvmIsMirandaMethod(meth)) { Object* methObj = dvmCreateReflectMethodObject(meth); if (methObj == NULL) goto fail; dvmSetObjectArrayElement(methodArray, methObjCount, methObj); ++methObjCount; dvmReleaseTrackedAlloc(methObj, NULL); } } meth = clazz->directMethods; for (int i = 0; i < clazz->directMethodCount; i++, meth++) { if ((!publicOnly || dvmIsPublicMethod(meth)) && meth->name[0] != '<') { Object* methObj = dvmCreateReflectMethodObject(meth); if (methObj == NULL) goto fail; dvmSetObjectArrayElement(methodArray, methObjCount, methObj); ++methObjCount; dvmReleaseTrackedAlloc(methObj, NULL); } } assert(methObjCount == methodArray->length); /* caller must call dvmReleaseTrackedAlloc */ return methodArray; fail: dvmReleaseTrackedAlloc((Object*) methodArray, NULL); return NULL; }
/* * This is the common message body for proxy methods. * * The method we're calling looks like: * public Object invoke(Object proxy, Method method, Object[] args) * * This means we have to create a Method object, box our arguments into * a new Object[] array, make the call, and unbox the return value if * necessary. */ static void proxyInvoker(const u4* args, JValue* pResult, const Method* method, Thread* self) { Object* thisObj = (Object*) args[0]; Object* methodObj = NULL; ArrayObject* argArray = NULL; Object* handler; Method* invoke; ClassObject* returnType; int hOffset; JValue invokeResult; /* * Retrieve handler object for this proxy instance. */ hOffset = dvmFindFieldOffset(thisObj->clazz, "h", "Ljava/lang/reflect/InvocationHandler;"); if (hOffset < 0) { LOGE("Unable to find 'h' in Proxy object\n"); dvmAbort(); } handler = dvmGetFieldObject(thisObj, hOffset); /* * Find the invoke() method, looking in "this"s class. (Because we * start here we don't have to convert it to a vtable index and then * index into this' vtable.) */ invoke = dvmFindVirtualMethodHierByDescriptor(handler->clazz, "invoke", "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;"); if (invoke == NULL) { LOGE("Unable to find invoke()\n"); dvmAbort(); } LOGV("invoke: %s.%s, this=%p, handler=%s\n", method->clazz->descriptor, method->name, thisObj, handler->clazz->descriptor); /* * Create a java.lang.reflect.Method object for this method. * * We don't want to use "method", because that's the concrete * implementation in the proxy class. We want the abstract Method * from the declaring interface. We have a pointer to it tucked * away in the "insns" field. * * TODO: this could be cached for performance. */ methodObj = dvmCreateReflectMethodObject((Method*) method->insns); if (methodObj == NULL) { assert(dvmCheckException(self)); goto bail; } /* * Determine the return type from the signature. * * TODO: this could be cached for performance. */ returnType = dvmGetBoxedReturnType(method); if (returnType == NULL) { char* desc = dexProtoCopyMethodDescriptor(&method->prototype); LOGE("Could not determine return type for '%s'\n", desc); free(desc); assert(dvmCheckException(self)); goto bail; } LOGV(" return type will be %s\n", returnType->descriptor); /* * Convert "args" array into Object[] array, using the method * signature to determine types. If the method takes no arguments, * we must pass null. */ argArray = boxMethodArgs(method, args+1); if (dvmCheckException(self)) goto bail; /* * Call h.invoke(proxy, method, args). * * We don't need to repackage exceptions, so if one has been thrown * just jump to the end. */ dvmCallMethod(self, invoke, handler, &invokeResult, thisObj, methodObj, argArray); if (dvmCheckException(self)) goto bail; /* * Unbox the return value. If it's the wrong type, throw a * ClassCastException. If it's a null pointer and we need a * primitive type, throw a NullPointerException. */ if (returnType->primitiveType == PRIM_VOID) { LOGVV("+++ ignoring return to void\n"); } else if (invokeResult.l == NULL) { if (dvmIsPrimitiveClass(returnType)) { dvmThrowException("Ljava/lang/NullPointerException;", "null result when primitive expected"); goto bail; } pResult->l = NULL; } else { if (!dvmUnwrapPrimitive(invokeResult.l, returnType, pResult)) { dvmThrowExceptionWithClassMessage("Ljava/lang/ClassCastException;", ((Object*)invokeResult.l)->clazz->descriptor); goto bail; } } bail: dvmReleaseTrackedAlloc(methodObj, self); dvmReleaseTrackedAlloc((Object*)argArray, self); }