Home | History | Annotate | Download | only in native
      1 /*
      2  * Copyright (C) 2008 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 /*
     18  * java.lang.reflect.Field
     19  */
     20 #include "Dalvik.h"
     21 #include "native/InternalNativePriv.h"
     22 
     23 
     24 /*
     25  * Get the address of a field from an object.  This can be used with "get"
     26  * or "set".
     27  *
     28  * "declaringClass" is the class in which the field was declared.  For an
     29  * instance field, "obj" is the object that holds the field data; for a
     30  * static field its value is ignored.
     31  *
     32  * "If the underlying field is static, the class that declared the
     33  * field is initialized if it has not already been initialized."
     34  *
     35  * On failure, throws an exception and returns NULL.
     36  *
     37  * The documentation lists exceptional conditions and the exceptions that
     38  * should be thrown, but doesn't say which exception previals when two or
     39  * more exceptional conditions exist at the same time.  For example,
     40  * attempting to set a protected field from an unrelated class causes an
     41  * IllegalAccessException, while passing in a data type that doesn't match
     42  * the field causes an IllegalArgumentException.  If code does both at the
     43  * same time, we have to choose one or othe other.
     44  *
     45  * The expected order is:
     46  *  (1) Check for illegal access. Throw IllegalAccessException.
     47  *  (2) Make sure the object actually has the field.  Throw
     48  *      IllegalArgumentException.
     49  *  (3) Make sure the field matches the expected type, e.g. if we issued
     50  *      a "getInteger" call make sure the field is an integer or can be
     51  *      converted to an int with a widening conversion.  Throw
     52  *      IllegalArgumentException.
     53  *  (4) Make sure "obj" is not null.  Throw NullPointerException.
     54  *
     55  * TODO: we're currently handling #3 after #4, because we don't check the
     56  * widening conversion until we're actually extracting the value from the
     57  * object (which won't work well if it's a null reference).
     58  */
     59 static JValue* getFieldDataAddr(Object* obj, ClassObject* declaringClass,
     60     int slot, bool isSetOperation, bool noAccessCheck)
     61 {
     62     Field* field;
     63     JValue* result;
     64 
     65     field = dvmSlotToField(declaringClass, slot);
     66     assert(field != NULL);
     67 
     68     /* verify access */
     69     if (!noAccessCheck) {
     70         if (isSetOperation && dvmIsFinalField(field)) {
     71             dvmThrowException("Ljava/lang/IllegalAccessException;",
     72                 "field is marked 'final'");
     73             return NULL;
     74         }
     75 
     76         ClassObject* callerClass =
     77             dvmGetCaller2Class(dvmThreadSelf()->curFrame);
     78 
     79         /*
     80          * We need to check two things:
     81          *  (1) Would an instance of the calling class have access to the field?
     82          *  (2) If the field is "protected", is the object an instance of the
     83          *      calling class, or is the field's declaring class in the same
     84          *      package as the calling class?
     85          *
     86          * #1 is basic access control.  #2 ensures that, just because
     87          * you're a subclass of Foo, you can't mess with protected fields
     88          * in arbitrary Foo objects from other packages.
     89          */
     90         if (!dvmCheckFieldAccess(callerClass, field)) {
     91             dvmThrowException("Ljava/lang/IllegalAccessException;",
     92                 "access to field not allowed");
     93             return NULL;
     94         }
     95         if (dvmIsProtectedField(field)) {
     96             bool isInstance, samePackage;
     97 
     98             if (obj != NULL)
     99                 isInstance = dvmInstanceof(obj->clazz, callerClass);
    100             else
    101                 isInstance = false;
    102             samePackage = dvmInSamePackage(declaringClass, callerClass);
    103 
    104             if (!isInstance && !samePackage) {
    105                 dvmThrowException("Ljava/lang/IllegalAccessException;",
    106                     "access to protected field not allowed");
    107                 return NULL;
    108             }
    109         }
    110     }
    111 
    112     if (dvmIsStaticField(field)) {
    113         /* init class if necessary, then return ptr to storage in "field" */
    114         if (!dvmIsClassInitialized(declaringClass)) {
    115             if (!dvmInitClass(declaringClass)) {
    116                 assert(dvmCheckException(dvmThreadSelf()));
    117                 return NULL;
    118             }
    119         }
    120 
    121         result = dvmStaticFieldPtr((StaticField*) field);
    122     } else {
    123         /*
    124          * Verify object is of correct type (i.e. it actually has the
    125          * expected field in it), then grab a pointer to obj storage.
    126          * The call to dvmVerifyObjectInClass throws an NPE if "obj" is NULL.
    127          */
    128         if (!dvmVerifyObjectInClass(obj, declaringClass)) {
    129             assert(dvmCheckException(dvmThreadSelf()));
    130             if (obj != NULL) {
    131                 LOGD("Wrong type of object for field lookup: %s %s\n",
    132                     obj->clazz->descriptor, declaringClass->descriptor);
    133             }
    134             return NULL;
    135         }
    136         result = dvmFieldPtr(obj, ((InstField*) field)->byteOffset);
    137     }
    138 
    139     return result;
    140 }
    141 
    142 /*
    143  * public int getFieldModifiers(Class declaringClass, int slot)
    144  */
    145 static void Dalvik_java_lang_reflect_Field_getFieldModifiers(
    146     const u4* args, JValue* pResult)
    147 {
    148     // ignore thisPtr in args[0]
    149     ClassObject* declaringClass = (ClassObject*) args[1];
    150     int slot = args[2];
    151     Field* field;
    152 
    153     field = dvmSlotToField(declaringClass, slot);
    154     RETURN_INT(field->accessFlags & JAVA_FLAGS_MASK);
    155 }
    156 
    157 /*
    158  * private Object getField(Object o, Class declaringClass, Class type,
    159  *     int slot, boolean noAccessCheck)
    160  *
    161  * Primitive types need to be boxed.
    162  */
    163 static void Dalvik_java_lang_reflect_Field_getField(const u4* args,
    164     JValue* pResult)
    165 {
    166     // ignore thisPtr in args[0]
    167     Object* obj = (Object*) args[1];
    168     ClassObject* declaringClass = (ClassObject*) args[2];
    169     ClassObject* fieldType = (ClassObject*) args[3];
    170     int slot = args[4];
    171     bool noAccessCheck = (args[5] != 0);
    172     JValue value;
    173     const JValue* fieldPtr;
    174     DataObject* result;
    175 
    176     //dvmDumpClass(obj->clazz, kDumpClassFullDetail);
    177 
    178     /* get a pointer to the field's data; performs access checks */
    179     fieldPtr = getFieldDataAddr(obj, declaringClass, slot, false,noAccessCheck);
    180     if (fieldPtr == NULL)
    181         RETURN_VOID();
    182 
    183     /* copy 4 or 8 bytes out */
    184     if (fieldType->primitiveType == PRIM_LONG ||
    185         fieldType->primitiveType == PRIM_DOUBLE)
    186     {
    187         value.j = fieldPtr->j;
    188     } else {
    189         value.i = fieldPtr->i;
    190     }
    191 
    192     result = dvmWrapPrimitive(value, fieldType);
    193     dvmReleaseTrackedAlloc((Object*) result, NULL);
    194     RETURN_PTR(result);
    195 }
    196 
    197 /*
    198  * private void setField(Object o, Class declaringClass, Class type,
    199  *     int slot, boolean noAccessCheck, Object value)
    200  *
    201  * When assigning into a primitive field we will automatically extract
    202  * the value from box types.
    203  */
    204 static void Dalvik_java_lang_reflect_Field_setField(const u4* args,
    205     JValue* pResult)
    206 {
    207     // ignore thisPtr in args[0]
    208     Object* obj = (Object*) args[1];
    209     ClassObject* declaringClass = (ClassObject*) args[2];
    210     ClassObject* fieldType = (ClassObject*) args[3];
    211     int slot = args[4];
    212     bool noAccessCheck = (args[5] != 0);
    213     Object* valueObj = (Object*) args[6];
    214     JValue* fieldPtr;
    215     JValue value;
    216 
    217     /* unwrap primitive, or verify object type */
    218     if (!dvmUnwrapPrimitive(valueObj, fieldType, &value)) {
    219         dvmThrowException("Ljava/lang/IllegalArgumentException;",
    220             "invalid value for field");
    221         RETURN_VOID();
    222     }
    223 
    224     /* get a pointer to the field's data; performs access checks */
    225     fieldPtr = getFieldDataAddr(obj, declaringClass, slot, true, noAccessCheck);
    226     if (fieldPtr == NULL)
    227         RETURN_VOID();
    228 
    229     /* store 4 or 8 bytes */
    230     if (fieldType->primitiveType == PRIM_LONG ||
    231         fieldType->primitiveType == PRIM_DOUBLE)
    232     {
    233         fieldPtr->j = value.j;
    234     } else if (fieldType->primitiveType == PRIM_NOT) {
    235         if (slot < 0) {
    236             StaticField *sfield;
    237             sfield = (StaticField *)dvmSlotToField(declaringClass, slot);
    238             assert(fieldPtr == &sfield->value);
    239             dvmSetStaticFieldObject(sfield, value.l);
    240         } else {
    241             int offset = declaringClass->ifields[slot].byteOffset;
    242             assert(fieldPtr == (JValue *)BYTE_OFFSET(obj, offset));
    243             dvmSetFieldObject(obj, offset, value.l);
    244         }
    245     } else {
    246         fieldPtr->i = value.i;
    247     }
    248 
    249     RETURN_VOID();
    250 }
    251 
    252 /*
    253  * Convert a reflection primitive type ordinal (inherited from the previous
    254  * VM's reflection classes) to our value.
    255  */
    256 static PrimitiveType convPrimType(int typeNum)
    257 {
    258     static const PrimitiveType conv[PRIM_MAX] = {
    259         PRIM_NOT, PRIM_BOOLEAN, PRIM_BYTE, PRIM_CHAR, PRIM_SHORT,
    260         PRIM_INT, PRIM_FLOAT, PRIM_LONG, PRIM_DOUBLE
    261     };
    262     if (typeNum <= 0 || typeNum > 8)
    263         return PRIM_NOT;
    264     return conv[typeNum];
    265 }
    266 
    267 /*
    268  * Primitive field getters, e.g.:
    269  * private double getIField(Object o, Class declaringClass,
    270  *     Class type, int slot, boolean noAccessCheck, int type_no)
    271  *
    272  * The "type_no" is defined by the java.lang.reflect.Field class.
    273  */
    274 static void Dalvik_java_lang_reflect_Field_getPrimitiveField(const u4* args,
    275     JValue* pResult)
    276 {
    277     // ignore thisPtr in args[0]
    278     Object* obj = (Object*) args[1];
    279     ClassObject* declaringClass = (ClassObject*) args[2];
    280     ClassObject* fieldType = (ClassObject*) args[3];
    281     int slot = args[4];
    282     bool noAccessCheck = (args[5] != 0);
    283     int typeNum = args[6];
    284     PrimitiveType targetType = convPrimType(typeNum);
    285     const JValue* fieldPtr;
    286     JValue value;
    287 
    288     if (!dvmIsPrimitiveClass(fieldType)) {
    289         dvmThrowException("Ljava/lang/IllegalArgumentException;",
    290             "not a primitive field");
    291         RETURN_VOID();
    292     }
    293 
    294     /* get a pointer to the field's data; performs access checks */
    295     fieldPtr = getFieldDataAddr(obj, declaringClass, slot, false,noAccessCheck);
    296     if (fieldPtr == NULL)
    297         RETURN_VOID();
    298 
    299     /* copy 4 or 8 bytes out */
    300     if (fieldType->primitiveType == PRIM_LONG ||
    301         fieldType->primitiveType == PRIM_DOUBLE)
    302     {
    303         value.j = fieldPtr->j;
    304     } else {
    305         value.i = fieldPtr->i;
    306     }
    307 
    308     /* retrieve value, performing a widening conversion if necessary */
    309     if (dvmConvertPrimitiveValue(fieldType->primitiveType, targetType,
    310         &(value.i), &(pResult->i)) < 0)
    311     {
    312         dvmThrowException("Ljava/lang/IllegalArgumentException;",
    313             "invalid primitive conversion");
    314         RETURN_VOID();
    315     }
    316 }
    317 
    318 /*
    319  * Primitive field setters, e.g.:
    320  * private void setIField(Object o, Class declaringClass,
    321  *     Class type, int slot, boolean noAccessCheck, int type_no, int value)
    322  *
    323  * The "type_no" is defined by the java.lang.reflect.Field class.
    324  */
    325 static void Dalvik_java_lang_reflect_Field_setPrimitiveField(const u4* args,
    326     JValue* pResult)
    327 {
    328     // ignore thisPtr in args[0]
    329     Object* obj = (Object*) args[1];
    330     ClassObject* declaringClass = (ClassObject*) args[2];
    331     ClassObject* fieldType = (ClassObject*) args[3];
    332     int slot = args[4];
    333     bool noAccessCheck = (args[5] != 0);
    334     int typeNum = args[6];
    335     const s4* valuePtr = (s4*) &args[7];
    336     PrimitiveType srcType = convPrimType(typeNum);
    337     JValue* fieldPtr;
    338     JValue value;
    339 
    340     if (!dvmIsPrimitiveClass(fieldType)) {
    341         dvmThrowException("Ljava/lang/IllegalArgumentException;",
    342             "not a primitive field");
    343         RETURN_VOID();
    344     }
    345 
    346     /* convert the 32/64-bit arg to a JValue matching the field type */
    347     if (dvmConvertPrimitiveValue(srcType, fieldType->primitiveType,
    348         valuePtr, &(value.i)) < 0)
    349     {
    350         dvmThrowException("Ljava/lang/IllegalArgumentException;",
    351             "invalid primitive conversion");
    352         RETURN_VOID();
    353     }
    354 
    355     /* get a pointer to the field's data; performs access checks */
    356     fieldPtr = getFieldDataAddr(obj, declaringClass, slot, true, noAccessCheck);
    357     if (fieldPtr == NULL)
    358         RETURN_VOID();
    359 
    360     /* store 4 or 8 bytes */
    361     if (fieldType->primitiveType == PRIM_LONG ||
    362         fieldType->primitiveType == PRIM_DOUBLE)
    363     {
    364         fieldPtr->j = value.j;
    365     } else {
    366         fieldPtr->i = value.i;
    367     }
    368 
    369     RETURN_VOID();
    370 }
    371 
    372 /*
    373  * public Annotation[] getDeclaredAnnotations(Class declaringClass, int slot)
    374  *
    375  * Return the annotations declared for this field.
    376  */
    377 static void Dalvik_java_lang_reflect_Field_getDeclaredAnnotations(
    378     const u4* args, JValue* pResult)
    379 {
    380     // ignore thisPtr in args[0]
    381     ClassObject* declaringClass = (ClassObject*) args[1];
    382     int slot = args[2];
    383     Field* field;
    384 
    385     field = dvmSlotToField(declaringClass, slot);
    386     assert(field != NULL);
    387 
    388     ArrayObject* annos = dvmGetFieldAnnotations(field);
    389     dvmReleaseTrackedAlloc((Object*) annos, NULL);
    390     RETURN_PTR(annos);
    391 }
    392 
    393 /*
    394  * private Object[] getSignatureAnnotation()
    395  *
    396  * Returns the signature annotation.
    397  */
    398 static void Dalvik_java_lang_reflect_Field_getSignatureAnnotation(const u4* args,
    399     JValue* pResult)
    400 {
    401     // ignore thisPtr in args[0]
    402     ClassObject* declaringClass = (ClassObject*) args[1];
    403     int slot = args[2];
    404     Field* field;
    405 
    406     field = dvmSlotToField(declaringClass, slot);
    407     assert(field != NULL);
    408 
    409     ArrayObject* arr = dvmGetFieldSignatureAnnotation(field);
    410     dvmReleaseTrackedAlloc((Object*) arr, NULL);
    411     RETURN_PTR(arr);
    412 }
    413 
    414 const DalvikNativeMethod dvm_java_lang_reflect_Field[] = {
    415     { "getFieldModifiers",  "(Ljava/lang/Class;I)I",
    416         Dalvik_java_lang_reflect_Field_getFieldModifiers },
    417     { "getField",           "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZ)Ljava/lang/Object;",
    418         Dalvik_java_lang_reflect_Field_getField },
    419     { "getBField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)B",
    420         Dalvik_java_lang_reflect_Field_getPrimitiveField },
    421     { "getCField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)C",
    422         Dalvik_java_lang_reflect_Field_getPrimitiveField },
    423     { "getDField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)D",
    424         Dalvik_java_lang_reflect_Field_getPrimitiveField },
    425     { "getFField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)F",
    426         Dalvik_java_lang_reflect_Field_getPrimitiveField },
    427     { "getIField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)I",
    428         Dalvik_java_lang_reflect_Field_getPrimitiveField },
    429     { "getJField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)J",
    430         Dalvik_java_lang_reflect_Field_getPrimitiveField },
    431     { "getSField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)S",
    432         Dalvik_java_lang_reflect_Field_getPrimitiveField },
    433     { "getZField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)Z",
    434         Dalvik_java_lang_reflect_Field_getPrimitiveField },
    435     { "setField",           "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZLjava/lang/Object;)V",
    436         Dalvik_java_lang_reflect_Field_setField },
    437     { "setBField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZIB)V",
    438         Dalvik_java_lang_reflect_Field_setPrimitiveField },
    439     { "setCField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZIC)V",
    440         Dalvik_java_lang_reflect_Field_setPrimitiveField },
    441     { "setDField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZID)V",
    442         Dalvik_java_lang_reflect_Field_setPrimitiveField },
    443     { "setFField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZIF)V",
    444         Dalvik_java_lang_reflect_Field_setPrimitiveField },
    445     { "setIField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZII)V",
    446         Dalvik_java_lang_reflect_Field_setPrimitiveField },
    447     { "setJField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZIJ)V",
    448         Dalvik_java_lang_reflect_Field_setPrimitiveField },
    449     { "setSField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZIS)V",
    450         Dalvik_java_lang_reflect_Field_setPrimitiveField },
    451     { "setZField",          "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZIZ)V",
    452         Dalvik_java_lang_reflect_Field_setPrimitiveField },
    453     { "getDeclaredAnnotations", "(Ljava/lang/Class;I)[Ljava/lang/annotation/Annotation;",
    454         Dalvik_java_lang_reflect_Field_getDeclaredAnnotations },
    455     { "getSignatureAnnotation",  "(Ljava/lang/Class;I)[Ljava/lang/Object;",
    456         Dalvik_java_lang_reflect_Field_getSignatureAnnotation },
    457     { NULL, NULL, NULL },
    458 };
    459