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 */
getFieldDataAddr(Object * obj,ClassObject * declaringClass,int slot,bool isSetOperation,bool noAccessCheck)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 */
Dalvik_java_lang_reflect_Field_getFieldModifiers(const u4 * args,JValue * pResult)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 */
Dalvik_java_lang_reflect_Field_getField(const u4 * args,JValue * pResult)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 */
Dalvik_java_lang_reflect_Field_setField(const u4 * args,JValue * pResult)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 {
235 fieldPtr->i = value.i;
236 }
237
238 RETURN_VOID();
239 }
240
241 /*
242 * Convert a reflection primitive type ordinal (inherited from the previous
243 * VM's reflection classes) to our value.
244 */
convPrimType(int typeNum)245 static PrimitiveType convPrimType(int typeNum)
246 {
247 static const PrimitiveType conv[PRIM_MAX] = {
248 PRIM_NOT, PRIM_BOOLEAN, PRIM_BYTE, PRIM_CHAR, PRIM_SHORT,
249 PRIM_INT, PRIM_FLOAT, PRIM_LONG, PRIM_DOUBLE
250 };
251 if (typeNum <= 0 || typeNum > 8)
252 return PRIM_NOT;
253 return conv[typeNum];
254 }
255
256 /*
257 * Primitive field getters, e.g.:
258 * private double getIField(Object o, Class declaringClass,
259 * Class type, int slot, boolean noAccessCheck, int type_no)
260 *
261 * The "type_no" is defined by the java.lang.reflect.Field class.
262 */
Dalvik_java_lang_reflect_Field_getPrimitiveField(const u4 * args,JValue * pResult)263 static void Dalvik_java_lang_reflect_Field_getPrimitiveField(const u4* args,
264 JValue* pResult)
265 {
266 // ignore thisPtr in args[0]
267 Object* obj = (Object*) args[1];
268 ClassObject* declaringClass = (ClassObject*) args[2];
269 ClassObject* fieldType = (ClassObject*) args[3];
270 int slot = args[4];
271 bool noAccessCheck = (args[5] != 0);
272 int typeNum = args[6];
273 PrimitiveType targetType = convPrimType(typeNum);
274 const JValue* fieldPtr;
275 JValue value;
276
277 if (!dvmIsPrimitiveClass(fieldType)) {
278 dvmThrowException("Ljava/lang/IllegalArgumentException;",
279 "not a primitive field");
280 RETURN_VOID();
281 }
282
283 /* get a pointer to the field's data; performs access checks */
284 fieldPtr = getFieldDataAddr(obj, declaringClass, slot, false,noAccessCheck);
285 if (fieldPtr == NULL)
286 RETURN_VOID();
287
288 /* copy 4 or 8 bytes out */
289 if (fieldType->primitiveType == PRIM_LONG ||
290 fieldType->primitiveType == PRIM_DOUBLE)
291 {
292 value.j = fieldPtr->j;
293 } else {
294 value.i = fieldPtr->i;
295 }
296
297 /* retrieve value, performing a widening conversion if necessary */
298 if (dvmConvertPrimitiveValue(fieldType->primitiveType, targetType,
299 &(value.i), &(pResult->i)) < 0)
300 {
301 dvmThrowException("Ljava/lang/IllegalArgumentException;",
302 "invalid primitive conversion");
303 RETURN_VOID();
304 }
305 }
306
307 /*
308 * Primitive field setters, e.g.:
309 * private void setIField(Object o, Class declaringClass,
310 * Class type, int slot, boolean noAccessCheck, int type_no, int value)
311 *
312 * The "type_no" is defined by the java.lang.reflect.Field class.
313 */
Dalvik_java_lang_reflect_Field_setPrimitiveField(const u4 * args,JValue * pResult)314 static void Dalvik_java_lang_reflect_Field_setPrimitiveField(const u4* args,
315 JValue* pResult)
316 {
317 // ignore thisPtr in args[0]
318 Object* obj = (Object*) args[1];
319 ClassObject* declaringClass = (ClassObject*) args[2];
320 ClassObject* fieldType = (ClassObject*) args[3];
321 int slot = args[4];
322 bool noAccessCheck = (args[5] != 0);
323 int typeNum = args[6];
324 const s4* valuePtr = (s4*) &args[7];
325 PrimitiveType srcType = convPrimType(typeNum);
326 JValue* fieldPtr;
327 JValue value;
328
329 if (!dvmIsPrimitiveClass(fieldType)) {
330 dvmThrowException("Ljava/lang/IllegalArgumentException;",
331 "not a primitive field");
332 RETURN_VOID();
333 }
334
335 /* convert the 32/64-bit arg to a JValue matching the field type */
336 if (dvmConvertPrimitiveValue(srcType, fieldType->primitiveType,
337 valuePtr, &(value.i)) < 0)
338 {
339 dvmThrowException("Ljava/lang/IllegalArgumentException;",
340 "invalid primitive conversion");
341 RETURN_VOID();
342 }
343
344 /* get a pointer to the field's data; performs access checks */
345 fieldPtr = getFieldDataAddr(obj, declaringClass, slot, true, noAccessCheck);
346 if (fieldPtr == NULL)
347 RETURN_VOID();
348
349 /* store 4 or 8 bytes */
350 if (fieldType->primitiveType == PRIM_LONG ||
351 fieldType->primitiveType == PRIM_DOUBLE)
352 {
353 fieldPtr->j = value.j;
354 } else {
355 fieldPtr->i = value.i;
356 }
357
358 RETURN_VOID();
359 }
360
361 /*
362 * public Annotation[] getDeclaredAnnotations(Class declaringClass, int slot)
363 *
364 * Return the annotations declared for this field.
365 */
Dalvik_java_lang_reflect_Field_getDeclaredAnnotations(const u4 * args,JValue * pResult)366 static void Dalvik_java_lang_reflect_Field_getDeclaredAnnotations(
367 const u4* args, JValue* pResult)
368 {
369 // ignore thisPtr in args[0]
370 ClassObject* declaringClass = (ClassObject*) args[1];
371 int slot = args[2];
372 Field* field;
373
374 field = dvmSlotToField(declaringClass, slot);
375 assert(field != NULL);
376
377 ArrayObject* annos = dvmGetFieldAnnotations(field);
378 dvmReleaseTrackedAlloc((Object*) annos, NULL);
379 RETURN_PTR(annos);
380 }
381
382 /*
383 * private Object[] getSignatureAnnotation()
384 *
385 * Returns the signature annotation.
386 */
Dalvik_java_lang_reflect_Field_getSignatureAnnotation(const u4 * args,JValue * pResult)387 static void Dalvik_java_lang_reflect_Field_getSignatureAnnotation(const u4* args,
388 JValue* pResult)
389 {
390 // ignore thisPtr in args[0]
391 ClassObject* declaringClass = (ClassObject*) args[1];
392 int slot = args[2];
393 Field* field;
394
395 field = dvmSlotToField(declaringClass, slot);
396 assert(field != NULL);
397
398 ArrayObject* arr = dvmGetFieldSignatureAnnotation(field);
399 dvmReleaseTrackedAlloc((Object*) arr, NULL);
400 RETURN_PTR(arr);
401 }
402
403 const DalvikNativeMethod dvm_java_lang_reflect_Field[] = {
404 { "getFieldModifiers", "(Ljava/lang/Class;I)I",
405 Dalvik_java_lang_reflect_Field_getFieldModifiers },
406 { "getField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZ)Ljava/lang/Object;",
407 Dalvik_java_lang_reflect_Field_getField },
408 { "getBField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)B",
409 Dalvik_java_lang_reflect_Field_getPrimitiveField },
410 { "getCField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)C",
411 Dalvik_java_lang_reflect_Field_getPrimitiveField },
412 { "getDField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)D",
413 Dalvik_java_lang_reflect_Field_getPrimitiveField },
414 { "getFField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)F",
415 Dalvik_java_lang_reflect_Field_getPrimitiveField },
416 { "getIField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)I",
417 Dalvik_java_lang_reflect_Field_getPrimitiveField },
418 { "getJField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)J",
419 Dalvik_java_lang_reflect_Field_getPrimitiveField },
420 { "getSField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)S",
421 Dalvik_java_lang_reflect_Field_getPrimitiveField },
422 { "getZField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZI)Z",
423 Dalvik_java_lang_reflect_Field_getPrimitiveField },
424 { "setField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZLjava/lang/Object;)V",
425 Dalvik_java_lang_reflect_Field_setField },
426 { "setBField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZIB)V",
427 Dalvik_java_lang_reflect_Field_setPrimitiveField },
428 { "setCField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZIC)V",
429 Dalvik_java_lang_reflect_Field_setPrimitiveField },
430 { "setDField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZID)V",
431 Dalvik_java_lang_reflect_Field_setPrimitiveField },
432 { "setFField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZIF)V",
433 Dalvik_java_lang_reflect_Field_setPrimitiveField },
434 { "setIField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZII)V",
435 Dalvik_java_lang_reflect_Field_setPrimitiveField },
436 { "setJField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZIJ)V",
437 Dalvik_java_lang_reflect_Field_setPrimitiveField },
438 { "setSField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZIS)V",
439 Dalvik_java_lang_reflect_Field_setPrimitiveField },
440 { "setZField", "(Ljava/lang/Object;Ljava/lang/Class;Ljava/lang/Class;IZIZ)V",
441 Dalvik_java_lang_reflect_Field_setPrimitiveField },
442 { "getDeclaredAnnotations", "(Ljava/lang/Class;I)[Ljava/lang/annotation/Annotation;",
443 Dalvik_java_lang_reflect_Field_getDeclaredAnnotations },
444 { "getSignatureAnnotation", "(Ljava/lang/Class;I)[Ljava/lang/Object;",
445 Dalvik_java_lang_reflect_Field_getSignatureAnnotation },
446 { NULL, NULL, NULL },
447 };
448
449