/* * 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); } }