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