| /* |
| * 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> |
| |
| /* |
| * 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'", *ccp); |
| return false; |
| } |
| |
| if (clazz->ifieldCount != 1) { |
| LOGE("Found %d instance fields in '%s'", |
| 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'", *pSignature); |
| } |
| *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) |
| { |
| char* signature = *pSignature; |
| |
| assert(*signature == '('); |
| signature++; |
| |
| /* count up the number of parameters */ |
| size_t count = 0; |
| char* cp = signature; |
| while (*cp != ')') { |
| count++; |
| |
| if (*cp == '[') { |
| while (*++cp == '[') |
| ; |
| } |
| if (*cp == 'L') { |
| while (*++cp != ';') |
| ; |
| } |
| cp++; |
| } |
| LOGVV("REFLECT found %d parameters in '%s'", count, *pSignature); |
| |
| /* create an array to hold them */ |
| ArrayObject* classArray = dvmAllocArrayByClass(gDvm.classJavaLangClassArray, |
| count, ALLOC_DEFAULT); |
| if (classArray == NULL) |
| return NULL; |
| |
| /* fill it in */ |
| cp = signature; |
| for (size_t i = 0; i < count; i++) { |
| ClassObject* clazz = convertSignaturePartToClass(&cp, defClass); |
| if (clazz == NULL) { |
| assert(dvmCheckException(dvmThreadSelf())); |
| return NULL; |
| } |
| LOGVV("REFLECT %d: '%s'", i, clazz->descriptor); |
| dvmSetObjectArrayElement(classArray, i, (Object *)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[0]; |
| 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*)(void*)&clazz->sfields[slot]; |
| } else { |
| assert(slot < clazz->ifieldCount); |
| return (Field*)(void*)&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); |
| 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"); |
| 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) |
| { |
| if (!dvmIsClassInitialized(gDvm.classJavaLangReflectField)) |
| dvmInitClass(gDvm.classJavaLangReflectField); |
| |
| /* count #of fields */ |
| size_t count; |
| if (!publicOnly) |
| count = clazz->sfieldCount + clazz->ifieldCount; |
| else { |
| count = 0; |
| for (int i = 0; i < clazz->sfieldCount; i++) { |
| if ((clazz->sfields[i].accessFlags & ACC_PUBLIC) != 0) |
| count++; |
| } |
| for (int i = 0; i < clazz->ifieldCount; i++) { |
| if ((clazz->ifields[i].accessFlags & ACC_PUBLIC) != 0) |
| count++; |
| } |
| } |
| |
| /* create the Field[] array */ |
| ArrayObject* fieldArray = |
| dvmAllocArrayByClass(gDvm.classJavaLangReflectFieldArray, count, ALLOC_DEFAULT); |
| if (fieldArray == NULL) |
| return NULL; |
| |
| /* populate */ |
| size_t fieldCount = 0; |
| for (int i = 0; i < clazz->sfieldCount; i++) { |
| if (!publicOnly || |
| (clazz->sfields[i].accessFlags & ACC_PUBLIC) != 0) |
| { |
| Object* field = createFieldObject(&clazz->sfields[i], clazz); |
| if (field == NULL) { |
| goto fail; |
| } |
| dvmSetObjectArrayElement(fieldArray, fieldCount, field); |
| dvmReleaseTrackedAlloc(field, NULL); |
| ++fieldCount; |
| } |
| } |
| for (int i = 0; i < clazz->ifieldCount; i++) { |
| if (!publicOnly || |
| (clazz->ifields[i].accessFlags & ACC_PUBLIC) != 0) |
| { |
| Object* field = createFieldObject(&clazz->ifields[i], clazz); |
| if (field == NULL) { |
| goto fail; |
| } |
| dvmSetObjectArrayElement(fieldArray, fieldCount, field); |
| dvmReleaseTrackedAlloc(field, NULL); |
| ++fieldCount; |
| } |
| } |
| |
| assert(fieldCount == fieldArray->length); |
| |
| /* 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"); |
| 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) |
| { |
| 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. |
| */ |
| size_t count = 0; |
| for (int i = 0; i < clazz->directMethodCount; ++i) { |
| Method* meth = &clazz->directMethods[i]; |
| if ((!publicOnly || dvmIsPublicMethod(meth)) && |
| dvmIsConstructorMethod(meth) && !dvmIsStaticMethod(meth)) |
| { |
| count++; |
| } |
| } |
| |
| /* |
| * Create an array of Constructor objects. |
| */ |
| ClassObject* arrayClass = gDvm.classJavaLangReflectConstructorArray; |
| ArrayObject* ctorArray = dvmAllocArrayByClass(arrayClass, count, ALLOC_DEFAULT); |
| if (ctorArray == NULL) |
| return NULL; |
| |
| /* |
| * Fill out the array. |
| */ |
| size_t ctorObjCount = 0; |
| for (int i = 0; i < clazz->directMethodCount; ++i) { |
| Method* meth = &clazz->directMethods[i]; |
| if ((!publicOnly || dvmIsPublicMethod(meth)) && |
| dvmIsConstructorMethod(meth) && !dvmIsStaticMethod(meth)) |
| { |
| Object* ctorObj = createConstructorObject(meth); |
| if (ctorObj == NULL) { |
| dvmReleaseTrackedAlloc((Object*) ctorArray, NULL); |
| return NULL; |
| } |
| dvmSetObjectArrayElement(ctorArray, ctorObjCount, ctorObj); |
| ++ctorObjCount; |
| dvmReleaseTrackedAlloc(ctorObj, NULL); |
| } |
| } |
| |
| assert(ctorObjCount == ctorArray->length); |
| |
| /* caller must call dvmReleaseTrackedAlloc */ |
| return ctorArray; |
| } |
| |
| /* |
| * 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"); |
| 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); |
| 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"); |
| 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) |
| { |
| 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; |
| } |
| |
| /* |
| * Fills targetDescriptorCache with the descriptors of the classes in args. |
| * This is the concatenation of the descriptors with no other adornment, |
| * consistent with dexProtoGetParameterDescriptors. |
| */ |
| static void createTargetDescriptor(ArrayObject* args, |
| DexStringCache* targetDescriptorCache) |
| { |
| ClassObject** argsArray = (ClassObject**)(void*)args->contents; |
| size_t length = 1; /* +1 for the terminating '\0' */ |
| for (size_t i = 0; i < args->length; ++i) { |
| length += strlen(argsArray[i]->descriptor); |
| } |
| |
| dexStringCacheAlloc(targetDescriptorCache, length); |
| |
| char* at = (char*) targetDescriptorCache->value; |
| for (size_t i = 0; i < args->length; ++i) { |
| const char* descriptor = argsArray[i]->descriptor; |
| strcpy(at, descriptor); |
| at += strlen(descriptor); |
| } |
| } |
| |
| static Object* findConstructorOrMethodInArray(int methodsCount, Method* methods, |
| const char* name, const char* parameterDescriptors) |
| { |
| Method* method = NULL; |
| Method* result = NULL; |
| int i; |
| |
| for (i = 0; i < methodsCount; ++i) { |
| method = &methods[i]; |
| if (strcmp(name, method->name) != 0 |
| || dvmIsMirandaMethod(method) |
| || dexProtoCompareToParameterDescriptors(&method->prototype, |
| parameterDescriptors) != 0) { |
| continue; |
| } |
| |
| result = method; |
| |
| /* |
| * Covariant return types permit the class to define multiple |
| * methods with the same name and parameter types. Prefer to return |
| * a non-synthetic method in such situations. We may still return |
| * a synthetic method to handle situations like escalated visibility. |
| */ |
| if (!dvmIsSyntheticMethod(method)) { |
| break; |
| } |
| } |
| |
| if (result != NULL) { |
| return dvmCreateReflectObjForMethod(result->clazz, result); |
| } |
| |
| return NULL; |
| } |
| |
| /* |
| * Get the named method. |
| */ |
| Object* dvmGetDeclaredConstructorOrMethod(ClassObject* clazz, |
| StringObject* nameObj, ArrayObject* args) |
| { |
| Object* result = NULL; |
| DexStringCache targetDescriptorCache; |
| char* name; |
| const char* targetDescriptor; |
| |
| dexStringCacheInit(&targetDescriptorCache); |
| |
| name = dvmCreateCstrFromString(nameObj); |
| createTargetDescriptor(args, &targetDescriptorCache); |
| targetDescriptor = targetDescriptorCache.value; |
| |
| result = findConstructorOrMethodInArray(clazz->directMethodCount, |
| clazz->directMethods, name, targetDescriptor); |
| if (result == NULL) { |
| result = findConstructorOrMethodInArray(clazz->virtualMethodCount, |
| clazz->virtualMethods, name, targetDescriptor); |
| } |
| |
| free(name); |
| dexStringCacheRelease(&targetDescriptorCache); |
| return result; |
| } |
| |
| /* |
| * Get the named field. |
| */ |
| Object* dvmGetDeclaredField(ClassObject* clazz, StringObject* nameObj) |
| { |
| int i; |
| Object* fieldObj = NULL; |
| char* name = dvmCreateCstrFromString(nameObj); |
| |
| if (!dvmIsClassInitialized(gDvm.classJavaLangReflectField)) |
| dvmInitClass(gDvm.classJavaLangReflectField); |
| |
| for (i = 0; i < clazz->sfieldCount; i++) { |
| Field* field = &clazz->sfields[i]; |
| if (strcmp(name, field->name) == 0) { |
| fieldObj = createFieldObject(field, clazz); |
| break; |
| } |
| } |
| if (fieldObj == NULL) { |
| for (i = 0; i < clazz->ifieldCount; i++) { |
| Field* field = &clazz->ifields[i]; |
| if (strcmp(name, field->name) == 0) { |
| fieldObj = createFieldObject(field, clazz); |
| break; |
| } |
| } |
| } |
| |
| free(name); |
| return fieldObj; |
| } |
| |
| /* |
| * 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) |
| { |
| if (!dvmIsClassInitialized(gDvm.classJavaLangReflectMethod)) |
| dvmInitClass(gDvm.classJavaLangReflectMethod); |
| |
| /* |
| * Create an array of Class objects. |
| */ |
| size_t count = clazz->interfaceCount; |
| ArrayObject* interfaceArray = |
| dvmAllocArrayByClass(gDvm.classJavaLangClassArray, count, ALLOC_DEFAULT); |
| if (interfaceArray == NULL) |
| return NULL; |
| |
| /* |
| * Fill out the array. |
| */ |
| memcpy(interfaceArray->contents, clazz->interfaces, |
| count * sizeof(Object *)); |
| dvmWriteBarrierArray(interfaceArray, 0, count); |
| |
| /* 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/") |
| |
| if (arg == NULL) |
| return PRIM_NOT; |
| |
| const char* name = arg->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 32-bit words 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 Conversion { |
| OK4, OK8, ItoJ, ItoD, JtoD, FtoD, ItoF, JtoF, bad |
| }; |
| |
| enum Conversion conv; |
| #ifdef ARCH_HAVE_ALIGNED_DOUBLES |
| double ret; |
| #endif |
| |
| assert((srcType != PRIM_VOID) && (srcType != PRIM_NOT)); |
| assert((dstType != PRIM_VOID) && (dstType != PRIM_NOT)); |
| |
| switch (dstType) { |
| case PRIM_BOOLEAN: |
| case PRIM_CHAR: |
| case PRIM_BYTE: { |
| conv = (srcType == dstType) ? OK4 : bad; |
| break; |
| } |
| case PRIM_SHORT: { |
| switch (srcType) { |
| case PRIM_BYTE: |
| case PRIM_SHORT: conv = OK4; break; |
| default: conv = bad; break; |
| } |
| break; |
| } |
| case PRIM_INT: { |
| switch (srcType) { |
| case PRIM_BYTE: |
| case PRIM_CHAR: |
| case PRIM_SHORT: |
| case PRIM_INT: conv = OK4; break; |
| default: conv = bad; break; |
| } |
| break; |
| } |
| case PRIM_LONG: { |
| switch (srcType) { |
| case PRIM_BYTE: |
| case PRIM_CHAR: |
| case PRIM_SHORT: |
| case PRIM_INT: conv = ItoJ; break; |
| case PRIM_LONG: conv = OK8; break; |
| default: conv = bad; break; |
| } |
| break; |
| } |
| case PRIM_FLOAT: { |
| switch (srcType) { |
| case PRIM_BYTE: |
| case PRIM_CHAR: |
| case PRIM_SHORT: |
| case PRIM_INT: conv = ItoF; break; |
| case PRIM_LONG: conv = JtoF; break; |
| case PRIM_FLOAT: conv = OK4; break; |
| default: conv = bad; break; |
| } |
| break; |
| } |
| case PRIM_DOUBLE: { |
| switch (srcType) { |
| case PRIM_BYTE: |
| case PRIM_CHAR: |
| case PRIM_SHORT: |
| case PRIM_INT: conv = ItoD; break; |
| case PRIM_LONG: conv = JtoD; break; |
| case PRIM_FLOAT: conv = FtoD; break; |
| case PRIM_DOUBLE: conv = OK8; break; |
| default: conv = bad; break; |
| } |
| break; |
| } |
| case PRIM_VOID: |
| case PRIM_NOT: |
| default: { |
| conv = bad; |
| break; |
| } |
| } |
| |
| switch (conv) { |
| case OK4: *dstPtr = *srcPtr; return 1; |
| case OK8: *(s8*) dstPtr = *(s8*)srcPtr; return 2; |
| case ItoJ: *(s8*) dstPtr = (s8) (*(s4*) srcPtr); return 2; |
| #ifndef ARCH_HAVE_ALIGNED_DOUBLES |
| 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; |
| #else |
| case ItoD: ret = (double) (*(s4*) srcPtr); memcpy(dstPtr, &ret, 8); return 2; |
| case JtoD: ret = (double) (*(long long*) srcPtr); memcpy(dstPtr, &ret, 8); return 2; |
| case FtoD: ret = (double) (*(float*) srcPtr); memcpy(dstPtr, &ret, 8); return 2; |
| #endif |
| case ItoF: *(float*) dstPtr = (float) (*(int*) srcPtr); return 1; |
| case JtoF: *(float*) dstPtr = (float) (*(long long*) srcPtr); return 1; |
| case bad: { |
| LOGV("illegal primitive conversion: '%s' to '%s'", |
| dexGetPrimitiveTypeDescriptor(srcType), |
| dexGetPrimitiveTypeDescriptor(dstType)); |
| return -1; |
| } |
| default: { |
| dvmAbort(); |
| return -1; // Keep the compiler happy. |
| } |
| } |
| } |
| |
| /* |
| * 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 == PRIM_NOT) { // didn't pass a boxed primitive in |
| LOGVV("conv arg: type '%s' not boxed primitive", |
| arg->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->clazz, type)) { |
| *destPtr = (s4) arg; |
| retVal = 1; |
| } else { |
| LOGVV("Arg %p (%s) not compatible with %s", |
| arg, arg->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* dvmBoxPrimitive(JValue value, ClassObject* returnType) |
| { |
| 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((Object*)value.l, NULL); |
| return (DataObject*) value.l; |
| } |
| |
| classDescriptor = dexGetBoxedTypeDescriptor(typeIndex); |
| if (classDescriptor == NULL) { |
| return NULL; |
| } |
| |
| wrapperClass = dvmFindSystemClass(classDescriptor); |
| if (wrapperClass == NULL) { |
| LOGW("Unable to find '%s'", 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 dvmUnboxPrimitive(Object* value, ClassObject* returnType, |
| JValue* pResult) |
| { |
| PrimitiveType typeIndex = returnType->primitiveType; |
| PrimitiveType valueIndex; |
| |
| if (typeIndex == PRIM_NOT) { |
| if (value != NULL && !dvmInstanceof(value->clazz, returnType)) { |
| LOGD("wrong object type: %s %s", |
| 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"); |
| 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'", desc); |
| free(desc); |
| dvmThrowInternalError(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); |
| } |
| } |