/*
* 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.
*/
/*
* java.lang.reflect.Field
*/
#include "Dalvik.h"
#include "native/InternalNativePriv.h"
/*
* Validate access to a field. Returns a pointer to the Field struct.
*
* "declaringClass" is the class in which the field was declared. For an
* instance field, "obj" is the object that holds the field data; for a
* static field its value is ignored.
*
* "If the underlying field is static, the class that declared the
* field is initialized if it has not already been initialized."
*
* On failure, throws an exception and returns NULL.
*
* The documentation lists exceptional conditions and the exceptions that
* should be thrown, but doesn't say which exception prevails when two or
* more exceptional conditions exist at the same time. For example,
* attempting to set a protected field from an unrelated class causes an
* IllegalAccessException, while passing in a data type that doesn't match
* the field causes an IllegalArgumentException. If code does both at the
* same time, we have to choose one or the other.
*
* The expected order is:
* (1) Check for illegal access. Throw IllegalAccessException.
* (2) Make sure the object actually has the field. Throw
* IllegalArgumentException.
* (3) Make sure the field matches the expected type, e.g. if we issued
* a "getInteger" call make sure the field is an integer or can be
* converted to an int with a widening conversion. Throw
* IllegalArgumentException.
* (4) Make sure "obj" is not null. Throw NullPointerException.
*
* TODO: we're currently handling #3 after #4, because we don't check the
* widening conversion until we're actually extracting the value from the
* object (which won't work well if it's a null reference).
*/
static Field* validateFieldAccess(Object* obj, ClassObject* declaringClass,
int slot, bool isSetOperation, bool noAccessCheck)
{
Field* field;
field = dvmSlotToField(declaringClass, slot);
assert(field != NULL);
/* verify access */
if (!noAccessCheck) {
if (isSetOperation && dvmIsFinalField(field)) {
dvmThrowIllegalAccessException("field is marked 'final'");
return NULL;
}
ClassObject* callerClass =
dvmGetCaller2Class(dvmThreadSelf()->interpSave.curFrame);
/*
* We need to check two things:
* (1) Would an instance of the calling class have access to the field?
* (2) If the field is "protected", is the object an instance of the
* calling class, or is the field's declaring class in the same
* package as the calling class?
*
* #1 is basic access control. #2 ensures that, just because
* you're a subclass of Foo, you can't mess with protected fields
* in arbitrary Foo objects from other packages.
*/
if (!dvmCheckFieldAccess(callerClass, field)) {
dvmThrowIllegalAccessException("access to field not allowed");
return NULL;
}
if (dvmIsProtectedField(field)) {
bool isInstance, samePackage;
if (obj != NULL)
isInstance = dvmInstanceof(obj->clazz, callerClass);
else
isInstance = false;
samePackage = dvmInSamePackage(declaringClass, callerClass);
if (!isInstance && !samePackage) {
dvmThrowIllegalAccessException(
"access to protected field not allowed");
return NULL;
}
}
}
if (dvmIsStaticField(field)) {
/* init class if necessary, then return ptr to storage in "field" */
if (!dvmIsClassInitialized(declaringClass)) {
if (!dvmInitClass(declaringClass)) {
assert(dvmCheckException(dvmThreadSelf()));
return NULL;
}
}
} else {
/*
* Verify object is of correct type (i.e. it actually has the
* expected field in it), then grab a pointer to obj storage.
* The call to dvmVerifyObjectInClass throws an NPE if "obj" is NULL.
*/
if (!dvmVerifyObjectInClass(obj, declaringClass)) {
assert(dvmCheckException(dvmThreadSelf()));
return NULL;
}
}
return field;
}
/*
* Extracts the value of a static field. Provides appropriate barriers
* for volatile fields.
*
* Sub-32-bit values are sign- or zero-extended to fill out 32 bits.
*/
static void getStaticFieldValue(const StaticField* sfield, JValue* value)
{
if (!dvmIsVolatileField(sfield)) {
/* just copy the whole thing */
*value = sfield->value;
} else {
/* need memory barriers and/or 64-bit atomic ops */
switch (sfield->signature[0]) {
case 'Z':
value->i = dvmGetStaticFieldBooleanVolatile(sfield);
break;
case 'B':
value->i = dvmGetStaticFieldByteVolatile(sfield);
break;
case 'S':
value->i = dvmGetStaticFieldShortVolatile(sfield);
break;
case 'C':
value->i = dvmGetStaticFieldCharVolatile(sfield);
break;
case 'I':
value->i = dvmGetStaticFieldIntVolatile(sfield);
break;
case 'F':
value->f = dvmGetStaticFieldFloatVolatile(sfield);
break;
case 'J':
value->j = dvmGetStaticFieldLongVolatile(sfield);
break;
case 'D':
value->d = dvmGetStaticFieldDoubleVolatile(sfield);
break;
case 'L':
case '[':
value->l = dvmGetStaticFieldObjectVolatile(sfield);
break;
default:
LOGE("Unhandled field signature '%s'", sfield->signature);
dvmAbort();
}
}
}
/*
* Extracts the value of an instance field. Provides appropriate barriers
* for volatile fields.
*
* Sub-32-bit values are sign- or zero-extended to fill out 32 bits.
*/
static void getInstFieldValue(const InstField* ifield, Object* obj,
JValue* value)
{
if (!dvmIsVolatileField(ifield)) {
/* use type-specific get; really just 32-bit vs. 64-bit */
switch (ifield->signature[0]) {
case 'Z':
value->i = dvmGetFieldBoolean(obj, ifield->byteOffset);
break;
case 'B':
value->i = dvmGetFieldByte(obj, ifield->byteOffset);
break;
case 'S':
value->i = dvmGetFieldShort(obj, ifield->byteOffset);
break;
case 'C':
value->i = dvmGetFieldChar(obj, ifield->byteOffset);
break;
case 'I':
value->i = dvmGetFieldInt(obj, ifield->byteOffset);
break;
case 'F':
value->f = dvmGetFieldFloat(obj, ifield->byteOffset);
break;
case 'J':
value->j = dvmGetFieldLong(obj, ifield->byteOffset);
break;
case 'D':
value->d = dvmGetFieldDouble(obj, ifield->byteOffset);
break;
case 'L':
case '[':
value->l = dvmGetFieldObject(obj, ifield->byteOffset);
break;
default:
LOGE("Unhandled field signature '%s'", ifield->signature);
dvmAbort();
}
} else {
/* need memory barriers and/or 64-bit atomic ops */
switch (ifield->signature[0]) {
case 'Z':
value->i = dvmGetFieldBooleanVolatile(obj, ifield->byteOffset);
break;
case 'B':
value->i = dvmGetFieldByteVolatile(obj, ifield->byteOffset);
break;
case 'S':
value->i = dvmGetFieldShortVolatile(obj, ifield->byteOffset);
break;
case 'C':
value->i = dvmGetFieldCharVolatile(obj, ifield->byteOffset);
break;
case 'I':
value->i = dvmGetFieldIntVolatile(obj, ifield->byteOffset);
break;
case 'F':
value->f = dvmGetFieldFloatVolatile(obj, ifield->byteOffset);
break;
case 'J':
value->j = dvmGetFieldLongVolatile(obj, ifield->byteOffset);
break;
case 'D':
value->d = dvmGetFieldDoubleVolatile(obj, ifield->byteOffset);
break;
case 'L':
case '[':
value->l = dvmGetFieldObjectVolatile(obj, ifield->byteOffset);
break;
default:
LOGE("Unhandled field signature '%s'", ifield->signature);
dvmAbort();
}
}
}
/*
* Copies the value of the static or instance field into "*value".
*/
static void getFieldValue(const Field* field, Object* obj, JValue* value)
{
if (dvmIsStaticField(field)) {
return getStaticFieldValue((const StaticField*) field, value);
} else {
return getInstFieldValue((const InstField*) field, obj, value);
}
}
/*
* Sets the value of a static field. Provides appropriate barriers
* for volatile fields.
*/
static void setStaticFieldValue(StaticField* sfield, const JValue* value)
{
if (!dvmIsVolatileField(sfield)) {
switch (sfield->signature[0]) {
case 'L':
case '[':
dvmSetStaticFieldObject(sfield, (Object*)value->l);
break;
default:
/* just copy the whole thing */
sfield->value = *value;
break;
}
} else {
/* need memory barriers and/or 64-bit atomic ops */
switch (sfield->signature[0]) {
case 'Z':
dvmSetStaticFieldBooleanVolatile(sfield, value->z);
break;
case 'B':
dvmSetStaticFieldByteVolatile(sfield, value->b);
break;
case 'S':
dvmSetStaticFieldShortVolatile(sfield, value->s);
break;
case 'C':
dvmSetStaticFieldCharVolatile(sfield, value->c);
break;
case 'I':
dvmSetStaticFieldIntVolatile(sfield, value->i);
break;
case 'F':
dvmSetStaticFieldFloatVolatile(sfield, value->f);
break;
case 'J':
dvmSetStaticFieldLongVolatile(sfield, value->j);
break;
case 'D':
dvmSetStaticFieldDoubleVolatile(sfield, value->d);
break;
case 'L':
case '[':
dvmSetStaticFieldObjectVolatile(sfield, (Object*)value->l);
break;
default:
LOGE("Unhandled field signature '%s'", sfield->signature);
dvmAbort();
}
}
}
/*
* Sets the value of an instance field. Provides appropriate barriers
* for volatile fields.
*/
static void setInstFieldValue(InstField* ifield, Object* obj,
const JValue* value)
{
if (!dvmIsVolatileField(ifield)) {
/* use type-specific set; really just 32-bit vs. 64-bit */
switch (ifield->signature[0]) {
case 'Z':
dvmSetFieldBoolean(obj, ifield->byteOffset, value->z);
break;
case 'B':
dvmSetFieldByte(obj, ifield->byteOffset, value->b);
break;
case 'S':
dvmSetFieldShort(obj, ifield->byteOffset, value->s);
break;
case 'C':
dvmSetFieldChar(obj, ifield->byteOffset, value->c);
break;
case 'I':
dvmSetFieldInt(obj, ifield->byteOffset, value->i);
break;
case 'F':
dvmSetFieldFloat(obj, ifield->byteOffset, value->f);
break;
case 'J':
dvmSetFieldLong(obj, ifield->byteOffset, value->j);
break;
case 'D':
dvmSetFieldDouble(obj, ifield->byteOffset, value->d);
break;
case 'L':
case '[':
dvmSetFieldObject(obj, ifield->byteOffset, (Object *)value->l);
break;
default:
LOGE("Unhandled field signature '%s'", ifield->signature);
dvmAbort();
}
#if ANDROID_SMP != 0
/*
* Special handling for final fields on SMP systems. We need a
* store/store barrier here (JMM requirement).
*/
if (dvmIsFinalField(ifield)) {
ANDROID_MEMBAR_STORE();
}
#endif
} else {
/* need memory barriers and/or 64-bit atomic ops */
switch (ifield->signature[0]) {
case 'Z':
dvmSetFieldBooleanVolatile(obj, ifield->byteOffset, value->z);
break;
case 'B':
dvmSetFieldByteVolatile(obj, ifield->byteOffset, value->b);
break;
case 'S':
dvmSetFieldShortVolatile(obj, ifield->byteOffset, value->s);
break;
case 'C':
dvmSetFieldCharVolatile(obj, ifield->byteOffset, value->c);
break;
case 'I':
dvmSetFieldIntVolatile(obj, ifield->byteOffset, value->i);
break;
case 'F':
dvmSetFieldFloatVolatile(obj, ifield->byteOffset, value->f);
break;
case 'J':
dvmSetFieldLongVolatile(obj, ifield->byteOffset, value->j);
break;
case 'D':
dvmSetFieldDoubleVolatile(obj, ifield->byteOffset, value->d);
break;
case 'L':
case '[':
dvmSetFieldObjectVolatile(obj, ifield->byteOffset, (Object*)value->l);
break;
default:
LOGE("Unhandled field signature '%s'", ifield->signature);
dvmAbort();
}
}
}
/*
* Copy "*value" into the static or instance field.
*/
static void setFieldValue(Field* field, Object* obj, const JValue* value)
{
if (dvmIsStaticField(field)) {
return setStaticFieldValue((StaticField*) field, value);
} else {
return setInstFieldValue((InstField*) field, obj, value);
}
}
/*
* public int getFieldModifiers(Class declaringClass, int slot)
*/
static void Dalvik_java_lang_reflect_Field_getFieldModifiers(const u4* args,
JValue* pResult)
{
/* ignore thisPtr in args[0] */
ClassObject* declaringClass = (ClassObject*) args[1];
int slot = args[2];
Field* field;
field = dvmSlotToField(declaringClass, slot);
RETURN_INT(field->accessFlags & JAVA_FLAGS_MASK);
}
/*
* private Object getField(Object o, Class declaringClass, Class type,
* int slot, boolean noAccessCheck)
*
* Primitive types need to be boxed.
*/
static void Dalvik_java_lang_reflect_Field_getField(const u4* args,
JValue* pResult)
{
/* ignore thisPtr in args[0] */
Object* obj = (Object*) args[1];
ClassObject* declaringClass = (ClassObject*) args[2];
ClassObject* fieldType = (ClassObject*) args[3];
int slot = args[4];
bool noAccessCheck = (args[5] != 0);
Field* field;
JValue value;
DataObject* result;
//dvmDumpClass(obj->clazz, kDumpClassFullDetail);
/* get a pointer to the Field after validating access */
field = validateFieldAccess(obj, declaringClass, slot, false,noAccessCheck);
if (field == NULL)
RETURN_VOID();
getFieldValue(field, obj, &value);
/* if it's primitive, box it up */
result = dvmBoxPrimitive(value, fieldType);
dvmReleaseTrackedAlloc((Object*) result, NULL);
RETURN_PTR(result);
}
/*
* private void setField(Object o, Class declaringClass, Class type,
* int slot, boolean noAccessCheck, Object value)
*
* When assigning into a primitive field we will automatically extract
* the value from box types.
*/
static void Dalvik_java_lang_reflect_Field_setField(const u4* args,
JValue* pResult)
{
/* ignore thisPtr in args[0] */
Object* obj = (Object*) args[1];
ClassObject* declaringClass = (ClassObject*) args[2];
ClassObject* fieldType = (ClassObject*) args[3];
int slot = args[4];
bool noAccessCheck = (args[5] != 0);
Object* valueObj = (Object*) args[6];
Field* field;
JValue value;
/* unbox primitive, or verify object type */
if (!dvmUnboxPrimitive(valueObj, fieldType, &value)) {
dvmThrowIllegalArgumentException("invalid value for field");
RETURN_VOID();
}
/* get a pointer to the Field after validating access */
field = validateFieldAccess(obj, declaringClass, slot, true, noAccessCheck);
if (field != NULL) {
setFieldValue(field, obj, &value);
}
RETURN_VOID();
}
/*
* Primitive field getters, e.g.:
* private double getIField(Object o, Class declaringClass,
* Class type, int slot, boolean noAccessCheck, char descriptor)
*/
static void Dalvik_java_lang_reflect_Field_getPrimitiveField(const u4* args,
JValue* pResult)
{
/* ignore thisPtr in args[0] */
Object* obj = (Object*) args[1];
ClassObject* declaringClass = (ClassObject*) args[2];
ClassObject* fieldType = (ClassObject*) args[3];
int slot = args[4];
bool noAccessCheck = (args[5] != 0);
jchar descriptor = args[6];
PrimitiveType targetType = dexGetPrimitiveTypeFromDescriptorChar(descriptor);
const Field* field;
JValue value;
if (!dvmIsPrimitiveClass(fieldType)) {
dvmThrowIllegalArgumentException("not a primitive field");
RETURN_VOID();
}
/* get a pointer to the Field after validating access */
field = validateFieldAccess(obj, declaringClass, slot, false,noAccessCheck);
if (field == NULL)
RETURN_VOID();
getFieldValue(field, obj, &value);
/* retrieve value, performing a widening conversion if necessary */
if (dvmConvertPrimitiveValue(fieldType->primitiveType, targetType,
&(value.i), &(pResult->i)) < 0)
{
dvmThrowIllegalArgumentException("invalid primitive conversion");
RETURN_VOID();
}
}
/*
* Primitive field setters, e.g.:
* private void setIField(Object o, Class declaringClass,
* Class type, int slot, boolean noAccessCheck, char descriptor, int value)
*/
static void Dalvik_java_lang_reflect_Field_setPrimitiveField(const u4* args,
JValue* pResult)
{
/* ignore thisPtr in args[0] */
Object* obj = (Object*) args[1];
ClassObject* declaringClass = (ClassObject*) args[2];
ClassObject* fieldType = (ClassObject*) args[3];
int slot = args[4];
bool noAccessCheck = (args[5] != 0);
jchar descriptor = args[6];
const s4* valuePtr = (s4*) &args[7]; /* 64-bit vars spill into args[8] */
PrimitiveType srcType = dexGetPrimitiveTypeFromDescriptorChar(descriptor);
Field* field;
JValue value;
if (!dvmIsPrimitiveClass(fieldType)) {
dvmThrowIllegalArgumentException("not a primitive field");
RETURN_VOID();
}
/* convert the 32/64-bit arg to a JValue matching the field type */
if (dvmConvertPrimitiveValue(srcType, fieldType->primitiveType,
valuePtr, &(value.i)) < 0)
{
dvmThrowIllegalArgumentException("invalid primitive conversion");
RETURN_VOID();
}
/* get a pointer to the Field after validating access */
field = validateFieldAccess(obj, declaringClass, slot, true, noAccessCheck);
if (field != NULL) {
setFieldValue(field, obj, &value);
}
RETURN_VOID();
}
/*
* private static Annotation[] getDeclaredAnnotations(
* Class declaringClass, int slot)
*
* Return the annotations declared for this field.
*/
static void Dalvik_java_lang_reflect_Field_getDeclaredAnnotations(
const u4* args, JValue* pResult)
{
ClassObject* declaringClass = (ClassObject*) args[0];
int slot = args[1];
Field* field;
field = dvmSlotToField(declaringClass, slot);
assert(field != NULL);
ArrayObject* annos = dvmGetFieldAnnotations(field);
dvmReleaseTrackedAlloc((Object*) annos, NULL);
RETURN_PTR(annos);
}
/*
* static Annotation getAnnotation(
* Class declaringClass, int slot, Class annotationType);
*/
static void Dalvik_java_lang_reflect_Field_getAnnotation(const u4* args,
JValue* pResult)
{
ClassObject* clazz = (ClassObject*) args[0];
int slot = args[1];
ClassObject* annotationClazz = (ClassObject*) args[2];
Field* field = dvmSlotToField(clazz, slot);
RETURN_PTR(dvmGetFieldAnnotation(clazz, field, annotationClazz));
}
/*
* static boolean isAnnotationPresent(
* Class declaringClass, int slot, Class annotationType);
*/
static void Dalvik_java_lang_reflect_Field_isAnnotationPresent(const u4* args,
JValue* pResult)
{
ClassObject* clazz = (ClassObject*) args[0];
int slot = args[1];
ClassObject* annotationClazz = (ClassObject*) args[2];
Field* field = dvmSlotToField(clazz, slot);
RETURN_BOOLEAN(dvmIsFieldAnnotationPresent(clazz, field, annotationClazz));
}
/*
* private Object[] getSignatureAnnotation()
*
* Returns the signature annotation.
*/
static void Dalvik_java_lang_reflect_Field_getSignatureAnnotation(const u4* args,
JValue* pResult)
{
/* ignore thisPtr in args[0] */
ClassObject* declaringClass = (ClassObject*) args[1];
int slot = args[2];
Field* field;
field = dvmSlotToField(declaringClass, slot);
assert(field != NULL);
ArrayObject* arr = dvmGetFieldSignatureAnnotation(field);
dvmReleaseTrackedAlloc((Object*) arr, NULL);
RETURN_PTR(arr);
}
const DalvikNativeMethod dvm_java_lang_reflect_Field[] = {
{ "getFieldModifiers", "(Ljava/lang/Class;I)I",
Dalvik_java_lang_reflect_Field_getFieldModifiers },
{ "getField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZ)Ljava/lang/Object;",
Dalvik_java_lang_reflect_Field_getField },
{ "getBField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZC)B",
Dalvik_java_lang_reflect_Field_getPrimitiveField },
{ "getCField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZC)C",
Dalvik_java_lang_reflect_Field_getPrimitiveField },
{ "getDField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZC)D",
Dalvik_java_lang_reflect_Field_getPrimitiveField },
{ "getFField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZC)F",
Dalvik_java_lang_reflect_Field_getPrimitiveField },
{ "getIField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZC)I",
Dalvik_java_lang_reflect_Field_getPrimitiveField },
{ "getJField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZC)J",
Dalvik_java_lang_reflect_Field_getPrimitiveField },
{ "getSField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZC)S",
Dalvik_java_lang_reflect_Field_getPrimitiveField },
{ "getZField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZC)Z",
Dalvik_java_lang_reflect_Field_getPrimitiveField },
{ "setField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZLjava/lang/Object;)V",
Dalvik_java_lang_reflect_Field_setField },
{ "setBField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZCB)V",
Dalvik_java_lang_reflect_Field_setPrimitiveField },
{ "setCField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZCC)V",
Dalvik_java_lang_reflect_Field_setPrimitiveField },
{ "setDField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZCD)V",
Dalvik_java_lang_reflect_Field_setPrimitiveField },
{ "setFField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZCF)V",
Dalvik_java_lang_reflect_Field_setPrimitiveField },
{ "setIField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZCI)V",
Dalvik_java_lang_reflect_Field_setPrimitiveField },
{ "setJField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZCJ)V",
Dalvik_java_lang_reflect_Field_setPrimitiveField },
{ "setSField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZCS)V",
Dalvik_java_lang_reflect_Field_setPrimitiveField },
{ "setZField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZCZ)V",
Dalvik_java_lang_reflect_Field_setPrimitiveField },
{ "getDeclaredAnnotations", "(Ljava/lang/Class;I)[Ljava/lang/annotation/Annotation;",
Dalvik_java_lang_reflect_Field_getDeclaredAnnotations },
{ "getAnnotation", "(Ljava/lang/Class;ILjava/lang/Class;)Ljava/lang/annotation/Annotation;",
Dalvik_java_lang_reflect_Field_getAnnotation },
{ "isAnnotationPresent", "(Ljava/lang/Class;ILjava/lang/Class;)Z",
Dalvik_java_lang_reflect_Field_isAnnotationPresent },
{ "getSignatureAnnotation", "(Ljava/lang/Class;I)[Ljava/lang/Object;",
Dalvik_java_lang_reflect_Field_getSignatureAnnotation },
{ NULL, NULL, NULL },
};