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 */
59static 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 */
145static 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 */
163static 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 */
204static 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 */
256static 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 */
274static 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 */
325static 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 */
377static 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 */
398static 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
414const 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