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