| /* |
| * Copyright (C) 2008 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| /* |
| * Basic reflection calls and utility functions. |
| */ |
| #include "Dalvik.h" |
| |
| #include <stdlib.h> |
| |
| /* |
| * Cache some classes. |
| */ |
| bool dvmReflectStartup(void) |
| { |
| gDvm.classJavaLangReflectAccessibleObject = |
| dvmFindSystemClassNoInit("Ljava/lang/reflect/AccessibleObject;"); |
| gDvm.classJavaLangReflectConstructor = |
| dvmFindSystemClassNoInit("Ljava/lang/reflect/Constructor;"); |
| gDvm.classJavaLangReflectConstructorArray = |
| dvmFindArrayClass("[Ljava/lang/reflect/Constructor;", NULL); |
| gDvm.classJavaLangReflectField = |
| dvmFindSystemClassNoInit("Ljava/lang/reflect/Field;"); |
| gDvm.classJavaLangReflectFieldArray = |
| dvmFindArrayClass("[Ljava/lang/reflect/Field;", NULL); |
| gDvm.classJavaLangReflectMethod = |
| dvmFindSystemClassNoInit("Ljava/lang/reflect/Method;"); |
| gDvm.classJavaLangReflectMethodArray = |
| dvmFindArrayClass("[Ljava/lang/reflect/Method;", NULL); |
| gDvm.classJavaLangReflectProxy = |
| dvmFindSystemClassNoInit("Ljava/lang/reflect/Proxy;"); |
| if (gDvm.classJavaLangReflectAccessibleObject == NULL || |
| gDvm.classJavaLangReflectConstructor == NULL || |
| gDvm.classJavaLangReflectConstructorArray == NULL || |
| gDvm.classJavaLangReflectField == NULL || |
| gDvm.classJavaLangReflectFieldArray == NULL || |
| gDvm.classJavaLangReflectMethod == NULL || |
| gDvm.classJavaLangReflectMethodArray == NULL || |
| gDvm.classJavaLangReflectProxy == NULL) |
| { |
| LOGE("Could not find one or more reflection classes\n"); |
| return false; |
| } |
| |
| gDvm.methJavaLangReflectConstructor_init = |
| dvmFindDirectMethodByDescriptor(gDvm.classJavaLangReflectConstructor, "<init>", |
| "(Ljava/lang/Class;[Ljava/lang/Class;[Ljava/lang/Class;I)V"); |
| gDvm.methJavaLangReflectField_init = |
| dvmFindDirectMethodByDescriptor(gDvm.classJavaLangReflectField, "<init>", |
| "(Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/String;I)V"); |
| gDvm.methJavaLangReflectMethod_init = |
| dvmFindDirectMethodByDescriptor(gDvm.classJavaLangReflectMethod, "<init>", |
| "(Ljava/lang/Class;[Ljava/lang/Class;[Ljava/lang/Class;Ljava/lang/Class;Ljava/lang/String;I)V"); |
| if (gDvm.methJavaLangReflectConstructor_init == NULL || |
| gDvm.methJavaLangReflectField_init == NULL || |
| gDvm.methJavaLangReflectMethod_init == NULL) |
| { |
| LOGE("Could not find reflection constructors\n"); |
| return false; |
| } |
| |
| gDvm.classJavaLangClassArray = |
| dvmFindArrayClass("[Ljava/lang/Class;", NULL); |
| gDvm.classJavaLangObjectArray = |
| dvmFindArrayClass("[Ljava/lang/Object;", NULL); |
| if (gDvm.classJavaLangClassArray == NULL || |
| gDvm.classJavaLangObjectArray == NULL) |
| { |
| LOGE("Could not find class-array or object-array class\n"); |
| return false; |
| } |
| |
| gDvm.offJavaLangReflectAccessibleObject_flag = |
| dvmFindFieldOffset(gDvm.classJavaLangReflectAccessibleObject, "flag", |
| "Z"); |
| |
| gDvm.offJavaLangReflectConstructor_slot = |
| dvmFindFieldOffset(gDvm.classJavaLangReflectConstructor, "slot", "I"); |
| gDvm.offJavaLangReflectConstructor_declClass = |
| dvmFindFieldOffset(gDvm.classJavaLangReflectConstructor, |
| "declaringClass", "Ljava/lang/Class;"); |
| |
| gDvm.offJavaLangReflectField_slot = |
| dvmFindFieldOffset(gDvm.classJavaLangReflectField, "slot", "I"); |
| gDvm.offJavaLangReflectField_declClass = |
| dvmFindFieldOffset(gDvm.classJavaLangReflectField, |
| "declaringClass", "Ljava/lang/Class;"); |
| |
| gDvm.offJavaLangReflectMethod_slot = |
| dvmFindFieldOffset(gDvm.classJavaLangReflectMethod, "slot", "I"); |
| gDvm.offJavaLangReflectMethod_declClass = |
| dvmFindFieldOffset(gDvm.classJavaLangReflectMethod, |
| "declaringClass", "Ljava/lang/Class;"); |
| |
| if (gDvm.offJavaLangReflectAccessibleObject_flag < 0 || |
| gDvm.offJavaLangReflectConstructor_slot < 0 || |
| gDvm.offJavaLangReflectConstructor_declClass < 0 || |
| gDvm.offJavaLangReflectField_slot < 0 || |
| gDvm.offJavaLangReflectField_declClass < 0 || |
| gDvm.offJavaLangReflectMethod_slot < 0 || |
| gDvm.offJavaLangReflectMethod_declClass < 0) |
| { |
| LOGE("Could not find reflection fields\n"); |
| return false; |
| } |
| |
| if (!dvmReflectProxyStartup()) |
| return false; |
| if (!dvmReflectAnnotationStartup()) |
| return false; |
| |
| return true; |
| } |
| |
| /* |
| * Clean up. |
| */ |
| void dvmReflectShutdown(void) |
| { |
| // nothing to do |
| } |
| |
| /* |
| * For some of the reflection stuff we need to un-box primitives, e.g. |
| * convert a java/lang/Integer to int or even a float. We assume that |
| * the first instance field holds the value. |
| * |
| * To verify this, we either need to ensure that the class has only one |
| * instance field, or we need to look up the field by name and verify |
| * that it comes first. The former is simpler, and should work. |
| */ |
| bool dvmValidateBoxClasses() |
| { |
| static const char* classes[] = { |
| "Ljava/lang/Boolean;", |
| "Ljava/lang/Character;", |
| "Ljava/lang/Float;", |
| "Ljava/lang/Double;", |
| "Ljava/lang/Byte;", |
| "Ljava/lang/Short;", |
| "Ljava/lang/Integer;", |
| "Ljava/lang/Long;", |
| NULL |
| }; |
| const char** ccp; |
| |
| for (ccp = classes; *ccp != NULL; ccp++) { |
| ClassObject* clazz; |
| |
| clazz = dvmFindClassNoInit(*ccp, NULL); |
| if (clazz == NULL) { |
| LOGE("Couldn't find '%s'\n", *ccp); |
| return false; |
| } |
| |
| if (clazz->ifieldCount != 1) { |
| LOGE("Found %d instance fields in '%s'\n", |
| clazz->ifieldCount, *ccp); |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| |
| /* |
| * Find the named class object. We have to trim "*pSignature" down to just |
| * the first token, do the lookup, and then restore anything important |
| * that we've stomped on. |
| * |
| * "pSig" will be advanced to the start of the next token. |
| */ |
| static ClassObject* convertSignaturePartToClass(char** pSignature, |
| const ClassObject* defClass) |
| { |
| ClassObject* clazz = NULL; |
| char* signature = *pSignature; |
| |
| if (*signature == '[') { |
| /* looks like "[[[Landroid/debug/Stuff;"; we want the whole thing */ |
| char savedChar; |
| |
| while (*++signature == '[') |
| ; |
| if (*signature == 'L') { |
| while (*++signature != ';') |
| ; |
| } |
| |
| /* advance past ';', and stomp on whatever comes next */ |
| savedChar = *++signature; |
| *signature = '\0'; |
| clazz = dvmFindArrayClass(*pSignature, defClass->classLoader); |
| *signature = savedChar; |
| } else if (*signature == 'L') { |
| /* looks like 'Landroid/debug/Stuff;"; we want the whole thing */ |
| char savedChar; |
| while (*++signature != ';') |
| ; |
| savedChar = *++signature; |
| *signature = '\0'; |
| clazz = dvmFindClassNoInit(*pSignature, defClass->classLoader); |
| *signature = savedChar; |
| } else { |
| clazz = dvmFindPrimitiveClass(*signature++); |
| } |
| |
| if (clazz == NULL) { |
| LOGW("Unable to match class for part: '%s'\n", *pSignature); |
| dvmClearException(dvmThreadSelf()); |
| dvmThrowException("Ljava/lang/NoSuchMethodException;", NULL); |
| } |
| *pSignature = signature; |
| return clazz; |
| } |
| |
| /* |
| * Convert the method signature to an array of classes. |
| * |
| * The tokenization process may mangle "*pSignature". On return, it will |
| * be pointing at the closing ')'. |
| * |
| * "defClass" is the method's class, which is needed to make class loaders |
| * happy. |
| */ |
| static ArrayObject* convertSignatureToClassArray(char** pSignature, |
| ClassObject* defClass) |
| { |
| ArrayObject* classArray; |
| ClassObject** classes; |
| char* signature = *pSignature; |
| char* cp; |
| int i, count; |
| |
| assert(*signature == '('); |
| signature++; |
| |
| /* count up the number of parameters */ |
| count = 0; |
| cp = signature; |
| while (*cp != ')') { |
| count++; |
| |
| if (*cp == '[') { |
| while (*++cp == '[') |
| ; |
| } |
| if (*cp == 'L') { |
| while (*++cp != ';') |
| ; |
| } |
| cp++; |
| } |
| LOGVV("REFLECT found %d parameters in '%s'\n", count, *pSignature); |
| |
| /* create an array to hold them */ |
| classArray = dvmAllocArray(gDvm.classJavaLangClassArray, count, |
| kObjectArrayRefWidth, ALLOC_DEFAULT); |
| if (classArray == NULL) |
| return NULL; |
| |
| /* fill it in */ |
| classes = (ClassObject**) classArray->contents; |
| cp = signature; |
| for (i = 0; i < count; i++) { |
| ClassObject* clazz; |
| |
| clazz = convertSignaturePartToClass(&cp, defClass); |
| if (clazz == NULL) { |
| assert(dvmCheckException(dvmThreadSelf())); |
| return NULL; |
| } |
| LOGVV("REFLECT %d: '%s'\n", i, clazz->descriptor); |
| |
| *classes++ = clazz; |
| } |
| |
| *pSignature = cp; |
| |
| /* caller must call dvmReleaseTrackedAlloc */ |
| return classArray; |
| } |
| |
| |
| /* |
| * Convert a field pointer to a slot number. |
| * |
| * We use positive values starting from 0 for instance fields, negative |
| * values starting from -1 for static fields. |
| */ |
| static int fieldToSlot(const Field* field, const ClassObject* clazz) |
| { |
| int slot; |
| |
| if (dvmIsStaticField(field)) { |
| slot = (StaticField*)field - clazz->sfields; |
| assert(slot >= 0 && slot < clazz->sfieldCount); |
| slot = -(slot+1); |
| } else { |
| slot = (InstField*)field - clazz->ifields; |
| assert(slot >= 0 && slot < clazz->ifieldCount); |
| } |
| |
| return slot; |
| } |
| |
| /* |
| * Convert a slot number to a field pointer. |
| */ |
| Field* dvmSlotToField(ClassObject* clazz, int slot) |
| { |
| if (slot < 0) { |
| slot = -(slot+1); |
| assert(slot < clazz->sfieldCount); |
| return (Field*) &clazz->sfields[slot]; |
| } else { |
| assert(slot < clazz->ifieldCount); |
| return (Field*) &clazz->ifields[slot]; |
| } |
| } |
| |
| /* |
| * Create a new java.lang.reflect.Field object from "field". |
| * |
| * The Field spec doesn't specify the constructor. We're going to use the |
| * one from our existing class libs: |
| * |
| * private Field(Class declaringClass, Class type, String name, int slot) |
| */ |
| static Object* createFieldObject(Field* field, const ClassObject* clazz) |
| { |
| Object* result = NULL; |
| Object* fieldObj = NULL; |
| StringObject* nameObj = NULL; |
| ClassObject* type; |
| char* mangle; |
| char* cp; |
| int slot; |
| |
| assert(dvmIsClassInitialized(gDvm.classJavaLangReflectField)); |
| |
| fieldObj = dvmAllocObject(gDvm.classJavaLangReflectField, ALLOC_DEFAULT); |
| if (fieldObj == NULL) |
| goto bail; |
| |
| cp = mangle = strdup(field->signature); |
| type = convertSignaturePartToClass(&cp, clazz); |
| free(mangle); |
| if (type == NULL) |
| goto bail; |
| |
| nameObj = dvmCreateStringFromCstr(field->name, ALLOC_DEFAULT); |
| if (nameObj == NULL) |
| goto bail; |
| |
| slot = fieldToSlot(field, clazz); |
| |
| JValue unused; |
| dvmCallMethod(dvmThreadSelf(), gDvm.methJavaLangReflectField_init, |
| fieldObj, &unused, clazz, type, nameObj, slot); |
| if (dvmCheckException(dvmThreadSelf())) { |
| LOGD("Field class init threw exception\n"); |
| goto bail; |
| } |
| |
| result = fieldObj; |
| |
| bail: |
| dvmReleaseTrackedAlloc((Object*) nameObj, NULL); |
| if (result == NULL) |
| dvmReleaseTrackedAlloc((Object*) fieldObj, NULL); |
| /* caller must dvmReleaseTrackedAlloc(result) */ |
| return result; |
| } |
| |
| /* |
| * |
| * Get an array with all fields declared by a class. |
| * |
| * This includes both static and instance fields. |
| */ |
| ArrayObject* dvmGetDeclaredFields(ClassObject* clazz, bool publicOnly) |
| { |
| ArrayObject* fieldArray = NULL; |
| Object** fields; |
| int i, count; |
| |
| if (!dvmIsClassInitialized(gDvm.classJavaLangReflectField)) |
| dvmInitClass(gDvm.classJavaLangReflectField); |
| |
| /* count #of fields */ |
| if (!publicOnly) |
| count = clazz->sfieldCount + clazz->ifieldCount; |
| else { |
| count = 0; |
| for (i = 0; i < clazz->sfieldCount; i++) { |
| if ((clazz->sfields[i].field.accessFlags & ACC_PUBLIC) != 0) |
| count++; |
| } |
| for (i = 0; i < clazz->ifieldCount; i++) { |
| if ((clazz->ifields[i].field.accessFlags & ACC_PUBLIC) != 0) |
| count++; |
| } |
| } |
| |
| /* create the Field[] array */ |
| fieldArray = dvmAllocArray(gDvm.classJavaLangReflectFieldArray, count, |
| kObjectArrayRefWidth, ALLOC_DEFAULT); |
| if (fieldArray == NULL) |
| return NULL; |
| fields = (Object**) fieldArray->contents; |
| |
| /* populate */ |
| for (i = 0; i < clazz->sfieldCount; i++) { |
| if (!publicOnly || |
| (clazz->sfields[i].field.accessFlags & ACC_PUBLIC) != 0) |
| { |
| *fields = createFieldObject(&clazz->sfields[i].field, clazz); |
| if (*fields == NULL) |
| goto fail; |
| dvmReleaseTrackedAlloc(*fields, NULL); |
| fields++; |
| count--; |
| } |
| } |
| for (i = 0; i < clazz->ifieldCount; i++) { |
| if (!publicOnly || |
| (clazz->ifields[i].field.accessFlags & ACC_PUBLIC) != 0) |
| { |
| *fields = createFieldObject(&clazz->ifields[i].field, clazz); |
| if (*fields == NULL) |
| goto fail; |
| dvmReleaseTrackedAlloc(*fields, NULL); |
| fields++; |
| count--; |
| } |
| } |
| |
| /* caller must call dvmReleaseTrackedAlloc */ |
| return fieldArray; |
| |
| fail: |
| dvmReleaseTrackedAlloc((Object*) fieldArray, NULL); |
| return NULL; |
| } |
| |
| |
| /* |
| * Convert a method pointer to a slot number. |
| * |
| * We use positive values starting from 0 for virtual methods, negative |
| * values starting from -1 for static methods. |
| */ |
| static int methodToSlot(const Method* meth) |
| { |
| ClassObject* clazz = meth->clazz; |
| int slot; |
| |
| if (dvmIsDirectMethod(meth)) { |
| slot = meth - clazz->directMethods; |
| assert(slot >= 0 && slot < clazz->directMethodCount); |
| slot = -(slot+1); |
| } else { |
| slot = meth - clazz->virtualMethods; |
| assert(slot >= 0 && slot < clazz->virtualMethodCount); |
| } |
| |
| return slot; |
| } |
| |
| /* |
| * Convert a slot number to a method pointer. |
| */ |
| Method* dvmSlotToMethod(ClassObject* clazz, int slot) |
| { |
| if (slot < 0) { |
| slot = -(slot+1); |
| assert(slot < clazz->directMethodCount); |
| return &clazz->directMethods[slot]; |
| } else { |
| assert(slot < clazz->virtualMethodCount); |
| return &clazz->virtualMethods[slot]; |
| } |
| } |
| |
| /* |
| * Create a new java/lang/reflect/Constructor object, using the contents of |
| * "meth" to construct it. |
| * |
| * The spec doesn't specify the constructor. We're going to use the |
| * one from our existing class libs: |
| * |
| * private Constructor (Class declaringClass, Class[] ptypes, Class[] extypes, |
| * int slot) |
| */ |
| static Object* createConstructorObject(Method* meth) |
| { |
| Object* result = NULL; |
| ArrayObject* params = NULL; |
| ArrayObject* exceptions = NULL; |
| Object* consObj; |
| DexStringCache mangle; |
| char* cp; |
| int slot; |
| |
| dexStringCacheInit(&mangle); |
| |
| /* parent should guarantee init so we don't have to check on every call */ |
| assert(dvmIsClassInitialized(gDvm.classJavaLangReflectConstructor)); |
| |
| consObj = dvmAllocObject(gDvm.classJavaLangReflectConstructor, |
| ALLOC_DEFAULT); |
| if (consObj == NULL) |
| goto bail; |
| |
| /* |
| * Convert the signature string into an array of classes representing |
| * the arguments. |
| */ |
| cp = dvmCopyDescriptorStringFromMethod(meth, &mangle); |
| params = convertSignatureToClassArray(&cp, meth->clazz); |
| if (params == NULL) |
| goto bail; |
| assert(*cp == ')'); |
| assert(*(cp+1) == 'V'); |
| |
| /* |
| * Create an array with one entry for every exception that the class |
| * is declared to throw. |
| */ |
| exceptions = dvmGetMethodThrows(meth); |
| if (dvmCheckException(dvmThreadSelf())) |
| goto bail; |
| |
| slot = methodToSlot(meth); |
| |
| JValue unused; |
| dvmCallMethod(dvmThreadSelf(), gDvm.methJavaLangReflectConstructor_init, |
| consObj, &unused, meth->clazz, params, exceptions, slot); |
| if (dvmCheckException(dvmThreadSelf())) { |
| LOGD("Constructor class init threw exception\n"); |
| goto bail; |
| } |
| |
| result = consObj; |
| |
| bail: |
| dexStringCacheRelease(&mangle); |
| dvmReleaseTrackedAlloc((Object*) params, NULL); |
| dvmReleaseTrackedAlloc((Object*) exceptions, NULL); |
| if (result == NULL) { |
| assert(dvmCheckException(dvmThreadSelf())); |
| dvmReleaseTrackedAlloc(consObj, NULL); |
| } |
| /* caller must dvmReleaseTrackedAlloc(result) */ |
| return result; |
| } |
| |
| /* |
| * Get an array with all constructors declared by a class. |
| */ |
| ArrayObject* dvmGetDeclaredConstructors(ClassObject* clazz, bool publicOnly) |
| { |
| ArrayObject* consArray; |
| Object** consObjPtr; |
| Method* meth; |
| int i, count; |
| |
| if (!dvmIsClassInitialized(gDvm.classJavaLangReflectConstructor)) |
| dvmInitClass(gDvm.classJavaLangReflectConstructor); |
| |
| /* |
| * Ordinarily we init the class the first time we resolve a method. |
| * We're bypassing the normal resolution mechanism, so we init it here. |
| */ |
| if (!dvmIsClassInitialized(clazz)) |
| dvmInitClass(clazz); |
| |
| /* |
| * Count up the #of relevant methods. |
| */ |
| count = 0; |
| meth = clazz->directMethods; |
| for (i = 0; i < clazz->directMethodCount; i++, meth++) { |
| if ((!publicOnly || dvmIsPublicMethod(meth)) && |
| dvmIsConstructorMethod(meth) && !dvmIsStaticMethod(meth)) |
| { |
| count++; |
| } |
| } |
| |
| /* |
| * Create an array of Constructor objects. |
| */ |
| consArray = dvmAllocArray(gDvm.classJavaLangReflectConstructorArray, count, |
| kObjectArrayRefWidth, ALLOC_DEFAULT); |
| if (consArray == NULL) |
| return NULL; |
| |
| consObjPtr = (Object**) consArray->contents; |
| |
| /* |
| * Fill out the array. |
| */ |
| meth = clazz->directMethods; |
| for (i = 0; i < clazz->directMethodCount; i++, meth++) { |
| if ((!publicOnly || dvmIsPublicMethod(meth)) && |
| dvmIsConstructorMethod(meth) && !dvmIsStaticMethod(meth)) |
| { |
| Object* consObj = createConstructorObject(meth); |
| if (consObj == NULL) |
| goto fail; |
| *consObjPtr++ = consObj; |
| dvmReleaseTrackedAlloc(consObj, NULL); |
| } |
| } |
| |
| assert(consObjPtr - (Object**) consArray->contents == count); |
| |
| /* caller must call dvmReleaseTrackedAlloc */ |
| return consArray; |
| |
| fail: |
| dvmReleaseTrackedAlloc((Object*) consArray, NULL); |
| return NULL; |
| } |
| |
| /* |
| * Create a new java/lang/reflect/Method object, using the contents of |
| * "meth" to construct it. |
| * |
| * The spec doesn't specify the constructor. We're going to use the |
| * one from our existing class libs: |
| * |
| * private Method(Class declaring, Class[] paramTypes, Class[] exceptTypes, |
| * Class returnType, String name, int slot) |
| * |
| * The caller must call dvmReleaseTrackedAlloc() on the result. |
| */ |
| Object* dvmCreateReflectMethodObject(const Method* meth) |
| { |
| Object* result = NULL; |
| ArrayObject* params = NULL; |
| ArrayObject* exceptions = NULL; |
| StringObject* nameObj = NULL; |
| Object* methObj; |
| ClassObject* returnType; |
| DexStringCache mangle; |
| char* cp; |
| int slot; |
| |
| if (dvmCheckException(dvmThreadSelf())) { |
| LOGW("WARNING: dvmCreateReflectMethodObject called with " |
| "exception pending\n"); |
| return NULL; |
| } |
| |
| dexStringCacheInit(&mangle); |
| |
| /* parent should guarantee init so we don't have to check on every call */ |
| assert(dvmIsClassInitialized(gDvm.classJavaLangReflectMethod)); |
| |
| methObj = dvmAllocObject(gDvm.classJavaLangReflectMethod, ALLOC_DEFAULT); |
| if (methObj == NULL) |
| goto bail; |
| |
| /* |
| * Convert the signature string into an array of classes representing |
| * the arguments, and a class for the return type. |
| */ |
| cp = dvmCopyDescriptorStringFromMethod(meth, &mangle); |
| params = convertSignatureToClassArray(&cp, meth->clazz); |
| if (params == NULL) |
| goto bail; |
| assert(*cp == ')'); |
| cp++; |
| returnType = convertSignaturePartToClass(&cp, meth->clazz); |
| if (returnType == NULL) |
| goto bail; |
| |
| /* |
| * Create an array with one entry for every exception that the class |
| * is declared to throw. |
| */ |
| exceptions = dvmGetMethodThrows(meth); |
| if (dvmCheckException(dvmThreadSelf())) |
| goto bail; |
| |
| /* method name */ |
| nameObj = dvmCreateStringFromCstr(meth->name, ALLOC_DEFAULT); |
| if (nameObj == NULL) |
| goto bail; |
| |
| slot = methodToSlot(meth); |
| |
| JValue unused; |
| dvmCallMethod(dvmThreadSelf(), gDvm.methJavaLangReflectMethod_init, |
| methObj, &unused, meth->clazz, params, exceptions, returnType, |
| nameObj, slot); |
| if (dvmCheckException(dvmThreadSelf())) { |
| LOGD("Method class init threw exception\n"); |
| goto bail; |
| } |
| |
| result = methObj; |
| |
| bail: |
| dexStringCacheRelease(&mangle); |
| if (result == NULL) { |
| assert(dvmCheckException(dvmThreadSelf())); |
| } |
| dvmReleaseTrackedAlloc((Object*) nameObj, NULL); |
| dvmReleaseTrackedAlloc((Object*) params, NULL); |
| dvmReleaseTrackedAlloc((Object*) exceptions, NULL); |
| if (result == NULL) |
| dvmReleaseTrackedAlloc(methObj, NULL); |
| return result; |
| } |
| |
| /* |
| * 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) |
| { |
| ArrayObject* methodArray; |
| Object** methObjPtr; |
| Method* meth; |
| int i, count; |
| |
| if (!dvmIsClassInitialized(gDvm.classJavaLangReflectMethod)) |
| dvmInitClass(gDvm.classJavaLangReflectMethod); |
| |
| /* |
| * Count up the #of relevant methods. |
| * |
| * Ignore virtual Miranda methods and direct class/object constructors. |
| */ |
| count = 0; |
| meth = clazz->virtualMethods; |
| for (i = 0; i < clazz->virtualMethodCount; i++, meth++) { |
| if ((!publicOnly || dvmIsPublicMethod(meth)) && |
| !dvmIsMirandaMethod(meth)) |
| { |
| count++; |
| } |
| } |
| meth = clazz->directMethods; |
| for (i = 0; i < clazz->directMethodCount; i++, meth++) { |
| if ((!publicOnly || dvmIsPublicMethod(meth)) && |
| meth->name[0] != '<') |
| { |
| count++; |
| } |
| } |
| |
| /* |
| * Create an array of Method objects. |
| */ |
| methodArray = dvmAllocArray(gDvm.classJavaLangReflectMethodArray, count, |
| kObjectArrayRefWidth, ALLOC_DEFAULT); |
| if (methodArray == NULL) |
| return NULL; |
| |
| methObjPtr = (Object**) methodArray->contents; |
| |
| /* |
| * Fill out the array. |
| */ |
| meth = clazz->virtualMethods; |
| for (i = 0; i < clazz->virtualMethodCount; i++, meth++) { |
| if ((!publicOnly || dvmIsPublicMethod(meth)) && |
| !dvmIsMirandaMethod(meth)) |
| { |
| Object* methObj = dvmCreateReflectMethodObject(meth); |
| if (methObj == NULL) |
| goto fail; |
| *methObjPtr++ = methObj; |
| dvmReleaseTrackedAlloc(methObj, NULL); |
| } |
| } |
| meth = clazz->directMethods; |
| for (i = 0; i < clazz->directMethodCount; i++, meth++) { |
| if ((!publicOnly || dvmIsPublicMethod(meth)) && |
| meth->name[0] != '<') |
| { |
| Object* methObj = dvmCreateReflectMethodObject(meth); |
| if (methObj == NULL) |
| goto fail; |
| *methObjPtr++ = methObj; |
| dvmReleaseTrackedAlloc(methObj, NULL); |
| } |
| } |
| |
| assert(methObjPtr - (Object**) methodArray->contents == count); |
| |
| /* caller must call dvmReleaseTrackedAlloc */ |
| return methodArray; |
| |
| fail: |
| dvmReleaseTrackedAlloc((Object*) methodArray, NULL); |
| return NULL; |
| } |
| |
| /* |
| * Get all interfaces a class implements. If this is unable to allocate |
| * the result array, this raises an OutOfMemoryError and returns NULL. |
| */ |
| ArrayObject* dvmGetInterfaces(ClassObject* clazz) |
| { |
| ArrayObject* interfaceArray; |
| |
| if (!dvmIsClassInitialized(gDvm.classJavaLangReflectMethod)) |
| dvmInitClass(gDvm.classJavaLangReflectMethod); |
| |
| /* |
| * Create an array of Class objects. |
| */ |
| int count = clazz->interfaceCount; |
| interfaceArray = dvmAllocArray(gDvm.classJavaLangClassArray, count, |
| kObjectArrayRefWidth, ALLOC_DEFAULT); |
| if (interfaceArray == NULL) |
| return NULL; |
| |
| /* |
| * Fill out the array. |
| */ |
| Object** interfaceObjPtr = (Object**) interfaceArray->contents; |
| int i; |
| for (i = 0; i < count; i++) { |
| *interfaceObjPtr++ = (Object*) clazz->interfaces[i]; |
| } |
| |
| /* caller must call dvmReleaseTrackedAlloc */ |
| return interfaceArray; |
| } |
| |
| /* |
| * Given a boxed primitive type, such as java/lang/Integer, return the |
| * primitive type index. |
| * |
| * Returns PRIM_NOT for void, since we never "box" that. |
| */ |
| static PrimitiveType getBoxedType(DataObject* arg) |
| { |
| static const int kJavaLangLen = 11; // strlen("Ljava/lang/") |
| const char* name; |
| |
| if (arg == NULL) |
| return PRIM_NOT; |
| |
| name = arg->obj.clazz->descriptor; |
| |
| if (strncmp(name, "Ljava/lang/", kJavaLangLen) != 0) |
| return PRIM_NOT; |
| |
| if (strcmp(name + kJavaLangLen, "Boolean;") == 0) |
| return PRIM_BOOLEAN; |
| if (strcmp(name + kJavaLangLen, "Character;") == 0) |
| return PRIM_CHAR; |
| if (strcmp(name + kJavaLangLen, "Float;") == 0) |
| return PRIM_FLOAT; |
| if (strcmp(name + kJavaLangLen, "Double;") == 0) |
| return PRIM_DOUBLE; |
| if (strcmp(name + kJavaLangLen, "Byte;") == 0) |
| return PRIM_BYTE; |
| if (strcmp(name + kJavaLangLen, "Short;") == 0) |
| return PRIM_SHORT; |
| if (strcmp(name + kJavaLangLen, "Integer;") == 0) |
| return PRIM_INT; |
| if (strcmp(name + kJavaLangLen, "Long;") == 0) |
| return PRIM_LONG; |
| return PRIM_NOT; |
| } |
| |
| /* |
| * Convert primitive, boxed data from "srcPtr" to "dstPtr". |
| * |
| * Section v2 2.6 lists the various conversions and promotions. We |
| * allow the "widening" and "identity" conversions, but don't allow the |
| * "narrowing" conversions. |
| * |
| * Allowed: |
| * byte to short, int, long, float, double |
| * short to int, long, float double |
| * char to int, long, float, double |
| * int to long, float, double |
| * long to float, double |
| * float to double |
| * Values of types byte, char, and short are "internally" widened to int. |
| * |
| * Returns the width in bytes of the destination primitive, or -1 if the |
| * conversion is not allowed. |
| * |
| * TODO? use JValue rather than u4 pointers |
| */ |
| int dvmConvertPrimitiveValue(PrimitiveType srcType, |
| PrimitiveType dstType, const s4* srcPtr, s4* dstPtr) |
| { |
| enum { |
| OK4, OK8, ItoJ, |
| ItoD, JtoD, FtoD, |
| ItoF, JtoF, |
| bad, kMax |
| }; |
| /* [src][dst] */ |
| static const int kConvMode[kMax][kMax] = { |
| /*FROM *TO: bool char float double byte short int long */ |
| /*bool */ { OK4, bad, bad, bad, bad, bad, bad, bad }, |
| /*char */ { bad, OK4, ItoF, ItoD, bad, bad, OK4, ItoJ }, |
| /*float*/ { bad, bad, OK4, FtoD, bad, bad, bad, bad }, |
| /*doubl*/ { bad, bad, bad, OK8, bad, bad, bad, bad }, |
| /*byte */ { bad, bad, ItoF, ItoD, OK4, OK4, OK4, ItoJ }, |
| /*short*/ { bad, bad, ItoF, ItoD, bad, OK4, OK4, ItoJ }, |
| /*int */ { bad, bad, ItoF, ItoD, bad, bad, OK4, ItoJ }, |
| /*long */ { bad, bad, JtoF, JtoD, bad, bad, bad, OK8 }, |
| }; |
| int result; |
| |
| assert(srcType != PRIM_NOT && dstType != PRIM_NOT && |
| srcType != PRIM_VOID && dstType != PRIM_VOID); |
| result = kConvMode[srcType][dstType]; |
| |
| //LOGV("+++ convprim: src=%d dst=%d result=%d\n", srcType, dstType, result); |
| |
| switch (result) { |
| case OK4: |
| *dstPtr = *srcPtr; |
| return 1; |
| case OK8: |
| *(s8*)dstPtr = *(s8*)srcPtr; |
| return 2; |
| case ItoJ: |
| *(s8*)dstPtr = (s8) (*(s4*) srcPtr); |
| return 2; |
| case ItoD: |
| *(double*)dstPtr = (double) (*(s4*) srcPtr); |
| return 2; |
| case JtoD: |
| *(double*)dstPtr = (double) (*(long long*) srcPtr); |
| return 2; |
| case FtoD: |
| *(double*)dstPtr = (double) (*(float*) srcPtr); |
| return 2; |
| case ItoF: |
| *(float*)dstPtr = (float) (*(int*) srcPtr); |
| return 1; |
| case JtoF: |
| *(float*)dstPtr = (float) (*(long long*) srcPtr); |
| return 1; |
| case bad: |
| LOGV("convert primitive: prim %d to %d not allowed\n", |
| srcType, dstType); |
| return -1; |
| default: |
| assert(false); |
| return -1; |
| } |
| } |
| |
| /* |
| * Convert types and widen primitives. Puts the value of "arg" into |
| * "destPtr". |
| * |
| * Returns the width of the argument in 32-bit words (1 or 2), or -1 on error. |
| */ |
| int dvmConvertArgument(DataObject* arg, ClassObject* type, s4* destPtr) |
| { |
| int retVal; |
| |
| if (dvmIsPrimitiveClass(type)) { |
| /* e.g.: "arg" is java/lang/Float instance, "type" is VM float class */ |
| PrimitiveType srcType; |
| s4* valuePtr; |
| |
| srcType = getBoxedType(arg); |
| if (srcType < 0) { // didn't pass a boxed primitive in |
| LOGVV("conv arg: type '%s' not boxed primitive\n", |
| arg->obj.clazz->descriptor); |
| return -1; |
| } |
| |
| /* assumes value is stored in first instance field */ |
| valuePtr = (s4*) arg->instanceData; |
| |
| retVal = dvmConvertPrimitiveValue(srcType, type->primitiveType, |
| valuePtr, destPtr); |
| } else { |
| /* verify object is compatible */ |
| if ((arg == NULL) || dvmInstanceof(arg->obj.clazz, type)) { |
| *destPtr = (s4) arg; |
| retVal = 1; |
| } else { |
| LOGVV("Arg %p (%s) not compatible with %s\n", |
| arg, arg->obj.clazz->descriptor, type->descriptor); |
| retVal = -1; |
| } |
| } |
| |
| return retVal; |
| } |
| |
| /* |
| * Create a wrapper object for a primitive data type. If "returnType" is |
| * not primitive, this just casts "value" to an object and returns it. |
| * |
| * We could invoke the "toValue" method on the box types to take |
| * advantage of pre-created values, but running that through the |
| * interpreter is probably less efficient than just allocating storage here. |
| * |
| * The caller must call dvmReleaseTrackedAlloc on the result. |
| */ |
| DataObject* dvmWrapPrimitive(JValue value, ClassObject* returnType) |
| { |
| static const char* boxTypes[] = { // order from enum PrimitiveType |
| "Ljava/lang/Boolean;", |
| "Ljava/lang/Character;", |
| "Ljava/lang/Float;", |
| "Ljava/lang/Double;", |
| "Ljava/lang/Byte;", |
| "Ljava/lang/Short;", |
| "Ljava/lang/Integer;", |
| "Ljava/lang/Long;" |
| }; |
| ClassObject* wrapperClass; |
| DataObject* wrapperObj; |
| s4* dataPtr; |
| PrimitiveType typeIndex = returnType->primitiveType; |
| const char* classDescriptor; |
| |
| if (typeIndex == PRIM_NOT) { |
| /* add to tracking table so return value is always in table */ |
| if (value.l != NULL) |
| dvmAddTrackedAlloc(value.l, NULL); |
| return (DataObject*) value.l; |
| } |
| |
| assert(typeIndex >= 0 && typeIndex < PRIM_MAX); |
| if (typeIndex == PRIM_VOID) |
| return NULL; |
| |
| classDescriptor = boxTypes[typeIndex]; |
| |
| wrapperClass = dvmFindSystemClass(classDescriptor); |
| if (wrapperClass == NULL) { |
| LOGW("Unable to find '%s'\n", classDescriptor); |
| assert(dvmCheckException(dvmThreadSelf())); |
| return NULL; |
| } |
| |
| wrapperObj = (DataObject*) dvmAllocObject(wrapperClass, ALLOC_DEFAULT); |
| if (wrapperObj == NULL) |
| return NULL; |
| dataPtr = (s4*) wrapperObj->instanceData; |
| |
| /* assumes value is stored in first instance field */ |
| /* (see dvmValidateBoxClasses) */ |
| if (typeIndex == PRIM_LONG || typeIndex == PRIM_DOUBLE) |
| *(s8*)dataPtr = value.j; |
| else |
| *dataPtr = value.i; |
| |
| return wrapperObj; |
| } |
| |
| /* |
| * Unwrap a primitive data type, if necessary. |
| * |
| * If "returnType" is not primitive, we just tuck "value" into JValue and |
| * return it after verifying that it's the right type of object. |
| * |
| * Fails if the field is primitive and "value" is either not a boxed |
| * primitive or is of a type that cannot be converted. |
| * |
| * Returns "true" on success, "false" on failure. |
| */ |
| bool dvmUnwrapPrimitive(Object* value, ClassObject* returnType, |
| JValue* pResult) |
| { |
| JValue result; |
| PrimitiveType typeIndex = returnType->primitiveType; |
| PrimitiveType valueIndex; |
| //const u4* dataPtr; |
| |
| if (typeIndex == PRIM_NOT) { |
| if (value != NULL && !dvmInstanceof(value->clazz, returnType)) { |
| LOGD("wrong object type: %s %s\n", |
| value->clazz->descriptor, returnType->descriptor); |
| return false; |
| } |
| pResult->l = value; |
| return true; |
| } else if (typeIndex == PRIM_VOID) { |
| /* can't put anything into a void */ |
| return false; |
| } |
| |
| valueIndex = getBoxedType((DataObject*)value); |
| if (valueIndex == PRIM_NOT) |
| return false; |
| |
| /* assumes value is stored in first instance field of "value" */ |
| /* (see dvmValidateBoxClasses) */ |
| if (dvmConvertPrimitiveValue(valueIndex, typeIndex, |
| (s4*) ((DataObject*)value)->instanceData, (s4*)pResult) < 0) |
| { |
| LOGV("Prim conversion failed\n"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| |
| /* |
| * Find the return type in the signature, and convert it to a class |
| * object. For primitive types we use a boxed class, for reference types |
| * we do a name lookup. |
| * |
| * On failure, we return NULL with an exception raised. |
| */ |
| ClassObject* dvmGetBoxedReturnType(const Method* meth) |
| { |
| const char* sig = dexProtoGetReturnType(&meth->prototype); |
| |
| switch (*sig) { |
| case 'Z': |
| case 'C': |
| case 'F': |
| case 'D': |
| case 'B': |
| case 'S': |
| case 'I': |
| case 'J': |
| case 'V': |
| return dvmFindPrimitiveClass(*sig); |
| case '[': |
| case 'L': |
| return dvmFindClass(sig, meth->clazz->classLoader); |
| default: { |
| /* should not have passed verification */ |
| char* desc = dexProtoCopyMethodDescriptor(&meth->prototype); |
| LOGE("Bad return type in signature '%s'\n", desc); |
| free(desc); |
| dvmThrowException("Ljava/lang/InternalError;", NULL); |
| return NULL; |
| } |
| } |
| } |
| |
| |
| /* |
| * JNI reflection support: convert reflection object to Field ptr. |
| */ |
| Field* dvmGetFieldFromReflectObj(Object* obj) |
| { |
| ClassObject* clazz; |
| int slot; |
| |
| assert(obj->clazz == gDvm.classJavaLangReflectField); |
| clazz = (ClassObject*)dvmGetFieldObject(obj, |
| gDvm.offJavaLangReflectField_declClass); |
| slot = dvmGetFieldInt(obj, gDvm.offJavaLangReflectField_slot); |
| |
| /* must initialize the class before returning a field ID */ |
| if (!dvmInitClass(clazz)) |
| return NULL; |
| |
| return dvmSlotToField(clazz, slot); |
| } |
| |
| /* |
| * JNI reflection support: convert reflection object to Method ptr. |
| */ |
| Method* dvmGetMethodFromReflectObj(Object* obj) |
| { |
| ClassObject* clazz; |
| int slot; |
| |
| if (obj->clazz == gDvm.classJavaLangReflectConstructor) { |
| clazz = (ClassObject*)dvmGetFieldObject(obj, |
| gDvm.offJavaLangReflectConstructor_declClass); |
| slot = dvmGetFieldInt(obj, gDvm.offJavaLangReflectConstructor_slot); |
| } else if (obj->clazz == gDvm.classJavaLangReflectMethod) { |
| clazz = (ClassObject*)dvmGetFieldObject(obj, |
| gDvm.offJavaLangReflectMethod_declClass); |
| slot = dvmGetFieldInt(obj, gDvm.offJavaLangReflectMethod_slot); |
| } else { |
| assert(false); |
| return NULL; |
| } |
| |
| /* must initialize the class before returning a method ID */ |
| if (!dvmInitClass(clazz)) |
| return NULL; |
| |
| return dvmSlotToMethod(clazz, slot); |
| } |
| |
| /* |
| * JNI reflection support: convert Field to reflection object. |
| * |
| * The return value is a java.lang.reflect.Field. |
| * |
| * Caller must call dvmReleaseTrackedAlloc(). |
| */ |
| Object* dvmCreateReflectObjForField(const ClassObject* clazz, Field* field) |
| { |
| if (!dvmIsClassInitialized(gDvm.classJavaLangReflectField)) |
| dvmInitClass(gDvm.classJavaLangReflectField); |
| |
| /* caller must dvmReleaseTrackedAlloc(result) */ |
| return createFieldObject(field, clazz); |
| } |
| |
| /* |
| * 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); |
| } |
| } |
| |