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