/* * Resolve a native method and invoke it. * * This is executed as if it were a native bridge or function. If the * resolution succeeds, method->insns is replaced, and we don't go through * here again. * * Initializes method's class if necessary. * * An exception is thrown on resolution failure. */ void dvmResolveNativeMethod(const u4* args, JValue* pResult, const Method* method, Thread* self) { ClassObject* clazz = method->clazz; void* func; /* * If this is a static method, it could be called before the class * has been initialized. */ if (dvmIsStaticMethod(method)) { if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) { assert(dvmCheckException(dvmThreadSelf())); return; } } else { assert(dvmIsClassInitialized(clazz) || dvmIsClassInitializing(clazz)); } /* start with our internal-native methods */ func = dvmLookupInternalNativeMethod(method); if (func != NULL) { /* resolution always gets the same answer, so no race here */ IF_LOGVV() { char* desc = dexProtoCopyMethodDescriptor(&method->prototype); LOGVV("+++ resolved native %s.%s %s, invoking\n", clazz->descriptor, method->name, desc); free(desc); }
/* * Get an array with all constructors declared by a class. */ ArrayObject* dvmGetDeclaredConstructors(ClassObject* clazz, bool publicOnly) { if (!dvmIsClassInitialized(gDvm.classJavaLangReflectConstructor)) dvmInitClass(gDvm.classJavaLangReflectConstructor); /* * Ordinarily we init the class the first time we resolve a method. * We're bypassing the normal resolution mechanism, so we init it here. */ if (!dvmIsClassInitialized(clazz)) dvmInitClass(clazz); /* * Count up the #of relevant methods. */ size_t count = 0; for (int i = 0; i < clazz->directMethodCount; ++i) { Method* meth = &clazz->directMethods[i]; if ((!publicOnly || dvmIsPublicMethod(meth)) && dvmIsConstructorMethod(meth) && !dvmIsStaticMethod(meth)) { count++; } } /* * Create an array of Constructor objects. */ ClassObject* arrayClass = gDvm.classJavaLangReflectConstructorArray; ArrayObject* ctorArray = dvmAllocArrayByClass(arrayClass, count, ALLOC_DEFAULT); if (ctorArray == NULL) return NULL; /* * Fill out the array. */ size_t ctorObjCount = 0; for (int i = 0; i < clazz->directMethodCount; ++i) { Method* meth = &clazz->directMethods[i]; if ((!publicOnly || dvmIsPublicMethod(meth)) && dvmIsConstructorMethod(meth) && !dvmIsStaticMethod(meth)) { Object* ctorObj = createConstructorObject(meth); if (ctorObj == NULL) { dvmReleaseTrackedAlloc((Object*) ctorArray, NULL); return NULL; } dvmSetObjectArrayElement(ctorArray, ctorObjCount, ctorObj); ++ctorObjCount; dvmReleaseTrackedAlloc(ctorObj, NULL); } } assert(ctorObjCount == ctorArray->length); /* caller must call dvmReleaseTrackedAlloc */ return ctorArray; }
/* * Resolve a static field reference. The DexFile format doesn't distinguish * between static and instance field references, so the "resolved" pointer * in the Dex struct will have the wrong type. We trivially cast it here. * * Causes the field's class to be initialized. */ StaticField* dvmResolveStaticField(const ClassObject* referrer, u4 sfieldIdx) { DvmDex* pDvmDex = referrer->pDvmDex; ClassObject* resClass; const DexFieldId* pFieldId; StaticField* resField; pFieldId = dexGetFieldId(pDvmDex->pDexFile, sfieldIdx); /* * Find the field's class. */ resClass = dvmResolveClass(referrer, pFieldId->classIdx, false); if (resClass == NULL) { assert(dvmCheckException(dvmThreadSelf())); return NULL; } resField = dvmFindStaticFieldHier(resClass, dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx), dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx)); if (resField == NULL) { dvmThrowNoSuchFieldError( dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx)); return NULL; } /* * If we're the first to resolve the field in which this class resides, * we need to do it now. Note that, if the field was inherited from * a superclass, it is not necessarily the same as "resClass". */ if (!dvmIsClassInitialized(resField->clazz) && !dvmInitClass(resField->clazz)) { assert(dvmCheckException(dvmThreadSelf())); return NULL; } /* * If the class has been initialized, add a pointer to our data structure * so we don't have to jump through the hoops again. If it's still * initializing (i.e. this thread is executing <clinit>), don't do * the store, otherwise other threads could use the field without waiting * for class init to finish. */ if (dvmIsClassInitialized(resField->clazz)) { dvmDexSetResolvedField(pDvmDex, sfieldIdx, (Field*) resField); } else { LOGVV("--- not caching resolved field %s.%s (class init=%d/%d)", resField->clazz->descriptor, resField->name, dvmIsClassInitializing(resField->clazz), dvmIsClassInitialized(resField->clazz)); } return resField; }
/* * Get the named field. */ Object* dvmGetDeclaredField(ClassObject* clazz, StringObject* nameObj) { int i; Object* fieldObj = NULL; char* name = dvmCreateCstrFromString(nameObj); if (!dvmIsClassInitialized(gDvm.classJavaLangReflectField)) dvmInitClass(gDvm.classJavaLangReflectField); for (i = 0; i < clazz->sfieldCount; i++) { Field* field = &clazz->sfields[i]; if (strcmp(name, field->name) == 0) { fieldObj = createFieldObject(field, clazz); break; } } if (fieldObj == NULL) { for (i = 0; i < clazz->ifieldCount; i++) { Field* field = &clazz->ifields[i]; if (strcmp(name, field->name) == 0) { fieldObj = createFieldObject(field, clazz); break; } } } free(name); return fieldObj; }
/* * Create an instance of the specified class. * * Returns NULL and throws an exception on failure. */ Object* dvmAllocObject(ClassObject* clazz, int flags) { Object* newObj; assert(clazz != NULL); assert(dvmIsClassInitialized(clazz) || dvmIsClassInitializing(clazz)); /* allocate on GC heap; memory is zeroed out */ newObj = (Object*)dvmMalloc(clazz->objectSize, flags); if (newObj != NULL) { DVM_OBJECT_INIT(newObj, clazz); dvmTrackAllocation(clazz, clazz->objectSize); /* notify DDMS */ } //SVM CODE SUPPORT //init the tag of an object to 0 newObj->tag = 0; pthread_mutex_lock(&gDvm.s_mtx); if(gDvm.newObjHook){ gDvm.newObjHook(newObj); } pthread_mutex_unlock(&gDvm.s_mtx); //SVM CODE SUPPORT END return newObj; }
/* * Create an instance of the specified class. * * Returns NULL and throws an exception on failure. */ Object* dvmAllocObject(ClassObject* clazz, int flags) { Object* newObj; assert(dvmIsClassInitialized(clazz) || dvmIsClassInitializing(clazz)); if (IS_CLASS_FLAG_SET(clazz, CLASS_ISFINALIZABLE)) { flags |= ALLOC_FINALIZABLE; } /* allocate on GC heap; memory is zeroed out */ newObj = dvmMalloc(clazz->objectSize, flags); if (newObj != NULL) { DVM_OBJECT_INIT(newObj, clazz); Monitor* mon = NULL;//dvmCreateMonitor(newObj); newObj->lock = (u4)mon | LW_SHAPE_FAT; LOGVV("AllocObject: %s (%d)\n", clazz->descriptor, (int) clazz->objectSize); #if WITH_HPROF && WITH_HPROF_STACK hprofFillInStackTrace(newObj); #endif dvmTrackAllocation(clazz, clazz->objectSize); } return newObj; }
// Based on dvmResolveInstField/dvmResolveStaticField. static void preloadDexCachesResolveField(DvmDex* pDvmDex, uint32_t fieldIdx, bool instance) { Field* field = dvmDexGetResolvedField(pDvmDex, fieldIdx); if (field != NULL) { return; } const DexFile* pDexFile = pDvmDex->pDexFile; const DexFieldId* pFieldId = dexGetFieldId(pDexFile, fieldIdx); ClassObject* clazz = dvmDexGetResolvedClass(pDvmDex, pFieldId->classIdx); if (clazz == NULL) { return; } // Skip static fields for uninitialized classes because a filled // cache entry implies the class is initialized. if (!instance && !dvmIsClassInitialized(clazz)) { return; } const char* fieldName = dexStringById(pDexFile, pFieldId->nameIdx); const char* signature = dexStringByTypeIdx(pDexFile, pFieldId->typeIdx); if (instance) { field = dvmFindInstanceFieldHier(clazz, fieldName, signature); } else { field = dvmFindStaticFieldHier(clazz, fieldName, signature); } if (field == NULL) { return; } // ALOGI("VMRuntime.preloadDexCaches found field %s %s.%s", // signature, clazz->descriptor, fieldName); dvmDexSetResolvedField(pDvmDex, fieldIdx, field); }
/* * JNI reflection support: convert Method to reflection object. * * The returned object will be either a java.lang.reflect.Method or * .Constructor, depending on whether "method" is a constructor. * * This is also used for certain "system" annotations. * * Caller must call dvmReleaseTrackedAlloc(). */ Object* dvmCreateReflectObjForMethod(const ClassObject* clazz, Method* method) { UNUSED_PARAMETER(clazz); if (strcmp(method->name, "<init>") == 0) { if (!dvmIsClassInitialized(gDvm.classJavaLangReflectConstructor)) dvmInitClass(gDvm.classJavaLangReflectConstructor); return createConstructorObject(method); } else { if (!dvmIsClassInitialized(gDvm.classJavaLangReflectMethod)) dvmInitClass(gDvm.classJavaLangReflectMethod); return dvmCreateReflectMethodObject(method); } }
/* * JNI reflection support: convert Field to reflection object. * * The return value is a java.lang.reflect.Field. * * Caller must call dvmReleaseTrackedAlloc(). */ Object* dvmCreateReflectObjForField(const ClassObject* clazz, Field* field) { if (!dvmIsClassInitialized(gDvm.classJavaLangReflectField)) dvmInitClass(gDvm.classJavaLangReflectField); /* caller must dvmReleaseTrackedAlloc(result) */ return createFieldObject(field, clazz); }
/* * Resolve an instance field reference. * * Returns NULL and throws an exception on error (no such field, illegal * access). */ InstField* dvmResolveInstField(const ClassObject* referrer, u4 ifieldIdx) { DvmDex* pDvmDex = referrer->pDvmDex; ClassObject* resClass; const DexFieldId* pFieldId; InstField* resField; LOGVV("--- resolving field %u (referrer=%s cl=%p)", ifieldIdx, referrer->descriptor, referrer->classLoader); pFieldId = dexGetFieldId(pDvmDex->pDexFile, ifieldIdx); /* * Find the field's class. */ resClass = dvmResolveClass(referrer, pFieldId->classIdx, false); if (resClass == NULL) { assert(dvmCheckException(dvmThreadSelf())); return NULL; } resField = dvmFindInstanceFieldHier(resClass, dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx), dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx)); if (resField == NULL) { dvmThrowNoSuchFieldError( dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx)); return NULL; } /* * Class must be initialized by now (unless verifier is buggy). We * could still be in the process of initializing it if the field * access is from a static initializer. */ assert(dvmIsClassInitialized(resField->clazz) || dvmIsClassInitializing(resField->clazz)); /* * The class is initialized (or initializing), the field has been * found. Add a pointer to our data structure so we don't have to * jump through the hoops again. * * Anything that uses the resolved table entry must have an instance * of the class, so any class init activity has already happened (or * been deliberately bypassed when <clinit> created an instance). * So it's always okay to update the table. */ dvmDexSetResolvedField(pDvmDex, ifieldIdx, (Field*)resField); LOGVV(" field %u is %s.%s", ifieldIdx, resField->clazz->descriptor, resField->name); return resField; }
/* * Resolve an instance field reference. * * Returns NULL and throws an exception on error (no such field, illegal * access). */ InstField* dvmResolveInstField(const ClassObject* referrer, u4 ifieldIdx) { DvmDex* pDvmDex = referrer->pDvmDex; ClassObject* resClass; const DexFieldId* pFieldId; InstField* resField; LOGVV("--- resolving field %u (referrer=%s cl=%p)\n", ifieldIdx, referrer->descriptor, referrer->classLoader); pFieldId = dexGetFieldId(pDvmDex->pDexFile, ifieldIdx); /* * Find the field's class. */ resClass = dvmResolveClass(referrer, pFieldId->classIdx, false); if (resClass == NULL) { assert(dvmCheckException(dvmThreadSelf())); return NULL; } resField = dvmFindInstanceFieldHier(resClass, dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx), dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx)); if (resField == NULL) { dvmThrowException("Ljava/lang/NoSuchFieldError;", dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx)); return NULL; } /* * Class must be initialized by now (unless verifier is buggy). We * could still be in the process of initializing it if the field * access is from a static initializer. */ assert(dvmIsClassInitialized(resField->field.clazz) || dvmIsClassInitializing(resField->field.clazz)); /* * The class is initialized, the method has been found. Add a pointer * to our data structure so we don't have to jump through the hoops again. */ dvmDexSetResolvedField(pDvmDex, ifieldIdx, (Field*)resField); LOGVV(" field %u is %s.%s\n", ifieldIdx, resField->field.clazz->descriptor, resField->field.name); return resField; }
/* * Create an instance of the specified class. * * Returns NULL and throws an exception on failure. */ Object* dvmAllocObject(ClassObject* clazz, int flags) { Object* newObj; assert(clazz != NULL); assert(dvmIsClassInitialized(clazz) || dvmIsClassInitializing(clazz)); /* allocate on GC heap; memory is zeroed out */ newObj = (Object*)dvmMalloc(clazz->objectSize, flags); if (newObj != NULL) { DVM_OBJECT_INIT(newObj, clazz); dvmTrackAllocation(clazz, clazz->objectSize); /* notify DDMS */ } return newObj; }
/* * Resolve a static field reference. The DexFile format doesn't distinguish * between static and instance field references, so the "resolved" pointer * in the Dex struct will have the wrong type. We trivially cast it here. * * Causes the field's class to be initialized. */ StaticField* dvmResolveStaticField(const ClassObject* referrer, u4 sfieldIdx) { DvmDex* pDvmDex = referrer->pDvmDex; ClassObject* resClass; const DexFieldId* pFieldId; StaticField* resField; pFieldId = dexGetFieldId(pDvmDex->pDexFile, sfieldIdx); /* * Find the field's class. */ resClass = dvmResolveClass(referrer, pFieldId->classIdx, false); if (resClass == NULL) { assert(dvmCheckException(dvmThreadSelf())); return NULL; } resField = dvmFindStaticFieldHier(resClass, dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx), dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->typeIdx)); if (resField == NULL) { dvmThrowException("Ljava/lang/NoSuchFieldError;", dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx)); return NULL; } /* * If we're the first to resolve the field in which this class resides, * we need to do it now. Note that, if the field was inherited from * a superclass, it is not necessarily the same as "resClass". */ if (!dvmIsClassInitialized(resField->field.clazz) && !dvmInitClass(resField->field.clazz)) { assert(dvmCheckException(dvmThreadSelf())); return NULL; } /* * The class is initialized, the method has been found. Add a pointer * to our data structure so we don't have to jump through the hoops again. */ dvmDexSetResolvedField(pDvmDex, sfieldIdx, (Field*) resField); return resField; }
/* * Create a new java.lang.reflect.Field object from "field". * * The Field spec doesn't specify the constructor. We're going to use the * one from our existing class libs: * * private Field(Class declaringClass, Class type, String name, int slot) */ static Object* createFieldObject(Field* field, const ClassObject* clazz) { Object* result = NULL; Object* fieldObj = NULL; StringObject* nameObj = NULL; ClassObject* type; char* mangle; char* cp; int slot, field_idx; assert(dvmIsClassInitialized(gDvm.classJavaLangReflectField)); fieldObj = dvmAllocObject(gDvm.classJavaLangReflectField, ALLOC_DEFAULT); if (fieldObj == NULL) goto bail; cp = mangle = strdup(field->signature); type = convertSignaturePartToClass(&cp, clazz); free(mangle); if (type == NULL) goto bail; nameObj = dvmCreateStringFromCstr(field->name); if (nameObj == NULL) goto bail; slot = fieldToSlot(field, clazz); field_idx = dvmGetFieldIdx(field); JValue unused; dvmCallMethod(dvmThreadSelf(), gDvm.methJavaLangReflectField_init, fieldObj, &unused, clazz, type, nameObj, slot, field_idx); if (dvmCheckException(dvmThreadSelf())) { ALOGD("Field class init threw exception"); goto bail; } result = fieldObj; bail: dvmReleaseTrackedAlloc((Object*) nameObj, NULL); if (result == NULL) dvmReleaseTrackedAlloc((Object*) fieldObj, NULL); /* caller must dvmReleaseTrackedAlloc(result) */ return result; }
/* * Create an instance of the specified class. * * Returns NULL and throws an exception on failure. */ Object* dvmAllocObject(ClassObject* clazz, int flags) { Object* newObj; assert(dvmIsClassInitialized(clazz) || dvmIsClassInitializing(clazz)); if (IS_CLASS_FLAG_SET(clazz, CLASS_ISFINALIZABLE)) { flags |= ALLOC_FINALIZABLE; } /* allocate on GC heap; memory is zeroed out */ newObj = dvmMalloc(clazz->objectSize, flags); if (newObj != NULL) { DVM_OBJECT_INIT(newObj, clazz); #if WITH_HPROF && WITH_HPROF_STACK hprofFillInStackTrace(newObj); #endif dvmTrackAllocation(clazz, clazz->objectSize); } return newObj; }
/* * public int constructNative(Object[] args, Class declaringClass, * Class[] parameterTypes, int slot, boolean noAccessCheck) * * We get here through Constructor.newInstance(). The Constructor object * would not be available if the constructor weren't public (per the * definition of Class.getConstructor), so we can skip the method access * check. We can also safely assume the constructor isn't associated * with an interface, array, or primitive class. */ static void Dalvik_java_lang_reflect_Constructor_constructNative( const u4* args, JValue* pResult) { // ignore thisPtr in args[0] ArrayObject* argList = (ArrayObject*) args[1]; ClassObject* declaringClass = (ClassObject*) args[2]; ArrayObject* params = (ArrayObject*) args[3]; int slot = args[4]; bool noAccessCheck = (args[5] != 0); Object* newObj; Method* meth; if (dvmIsAbstractClass(declaringClass)) { dvmThrowInstantiationException(declaringClass, NULL); RETURN_VOID(); } /* initialize the class if it hasn't been already */ if (!dvmIsClassInitialized(declaringClass)) { if (!dvmInitClass(declaringClass)) { ALOGW("Class init failed in Constructor.constructNative (%s)", declaringClass->descriptor); assert(dvmCheckException(dvmThreadSelf())); RETURN_VOID(); } } newObj = dvmAllocObject(declaringClass, ALLOC_DEFAULT); if (newObj == NULL) RETURN_PTR(NULL); meth = dvmSlotToMethod(declaringClass, slot); assert(meth != NULL); (void) dvmInvokeMethod(newObj, meth, argList, params, NULL, noAccessCheck); dvmReleaseTrackedAlloc(newObj, NULL); RETURN_PTR(newObj); }
/* * Allocate a new instance of the class String, performing first-use * initialization of the class if necessary. Upon success, the * returned value will have all its fields except hashCode already * filled in, including a reference to a newly-allocated char[] for * the contents, sized as given. Additionally, a reference to the * chars array is stored to the pChars pointer. Callers must * subsequently call dvmReleaseTrackedAlloc() on the result pointer. * This function returns NULL on failure. */ static StringObject* makeStringObject(u4 charsLength, ArrayObject** pChars) { /* * The String class should have already gotten found (but not * necessarily initialized) before making it here. We assert it * explicitly, since historically speaking, we have had bugs with * regard to when the class String gets set up. The assert helps * make any regressions easier to diagnose. */ assert(gDvm.classJavaLangString != NULL); if (!dvmIsClassInitialized(gDvm.classJavaLangString)) { /* Perform first-time use initialization of the class. */ if (!dvmInitClass(gDvm.classJavaLangString)) { LOGE("FATAL: Could not initialize class String"); dvmAbort(); } } Object* result = dvmAllocObject(gDvm.classJavaLangString, ALLOC_DEFAULT); if (result == NULL) { return NULL; } ArrayObject* chars = dvmAllocPrimitiveArray('C', charsLength, ALLOC_DEFAULT); if (chars == NULL) { dvmReleaseTrackedAlloc(result, NULL); return NULL; } dvmSetFieldInt(result, STRING_FIELDOFF_COUNT, charsLength); dvmSetFieldObject(result, STRING_FIELDOFF_VALUE, (Object*) chars); dvmReleaseTrackedAlloc((Object*) chars, NULL); /* Leave offset and hashCode set to zero. */ *pChars = chars; return (StringObject*) result; }
/* * Get all interfaces a class implements. If this is unable to allocate * the result array, this raises an OutOfMemoryError and returns NULL. */ ArrayObject* dvmGetInterfaces(ClassObject* clazz) { if (!dvmIsClassInitialized(gDvm.classJavaLangReflectMethod)) dvmInitClass(gDvm.classJavaLangReflectMethod); /* * Create an array of Class objects. */ size_t count = clazz->interfaceCount; ArrayObject* interfaceArray = dvmAllocArrayByClass(gDvm.classJavaLangClassArray, count, ALLOC_DEFAULT); if (interfaceArray == NULL) return NULL; /* * Fill out the array. */ memcpy(interfaceArray->contents, clazz->interfaces, count * sizeof(Object *)); dvmWriteBarrierArray(interfaceArray, 0, count); /* caller must call dvmReleaseTrackedAlloc */ return interfaceArray; }
// Based on dvmResolveMethod. static void preloadDexCachesResolveMethod(DvmDex* pDvmDex, uint32_t methodIdx, MethodType methodType) { Method* method = dvmDexGetResolvedMethod(pDvmDex, methodIdx); if (method != NULL) { return; } const DexFile* pDexFile = pDvmDex->pDexFile; const DexMethodId* pMethodId = dexGetMethodId(pDexFile, methodIdx); ClassObject* clazz = dvmDexGetResolvedClass(pDvmDex, pMethodId->classIdx); if (clazz == NULL) { return; } // Skip static methods for uninitialized classes because a filled // cache entry implies the class is initialized. if ((methodType == METHOD_STATIC) && !dvmIsClassInitialized(clazz)) { return; } const char* methodName = dexStringById(pDexFile, pMethodId->nameIdx); DexProto proto; dexProtoSetFromMethodId(&proto, pDexFile, pMethodId); if (methodType == METHOD_DIRECT) { method = dvmFindDirectMethod(clazz, methodName, &proto); } else if (methodType == METHOD_STATIC) { method = dvmFindDirectMethodHier(clazz, methodName, &proto); } else { method = dvmFindVirtualMethodHier(clazz, methodName, &proto); } if (method == NULL) { return; } // ALOGI("VMRuntime.preloadDexCaches found method %s.%s", // clazz->descriptor, methodName); dvmDexSetResolvedMethod(pDvmDex, methodIdx, method); }
// Based on dvmResolveClass. static void preloadDexCachesResolveType(DvmDex* pDvmDex, uint32_t typeIdx) { ClassObject* clazz = dvmDexGetResolvedClass(pDvmDex, typeIdx); if (clazz != NULL) { return; } const DexFile* pDexFile = pDvmDex->pDexFile; const char* className = dexStringByTypeIdx(pDexFile, typeIdx); if (className[0] != '\0' && className[1] == '\0') { /* primitive type */ clazz = dvmFindPrimitiveClass(className[0]); } else { clazz = dvmLookupClass(className, NULL, true); } if (clazz == NULL) { return; } // Skip uninitialized classes because filled cache entry implies it is initialized. if (!dvmIsClassInitialized(clazz)) { // ALOGI("VMRuntime.preloadDexCaches uninitialized clazz=%s", className); return; } // ALOGI("VMRuntime.preloadDexCaches found clazz=%s", className); dvmDexSetResolvedClass(pDvmDex, typeIdx, clazz); }
/* * Start/continue throwing process now that we have a class reference. */ void dvmThrowChainedExceptionByClass(ClassObject* excepClass, const char* msg, Object* cause) { Thread* self = dvmThreadSelf(); Object* exception; /* make sure the exception is initialized */ if (!dvmIsClassInitialized(excepClass) && !dvmInitClass(excepClass)) { LOGE("ERROR: unable to initialize exception class '%s'\n", excepClass->descriptor); if (strcmp(excepClass->descriptor, "Ljava/lang/InternalError;") == 0) dvmAbort(); dvmThrowChainedException("Ljava/lang/InternalError;", "failed to init original exception class", cause); return; } exception = dvmAllocObject(excepClass, ALLOC_DEFAULT); if (exception == NULL) { /* * We're in a lot of trouble. We might be in the process of * throwing an out-of-memory exception, in which case the * pre-allocated object will have been thrown when our object alloc * failed. So long as there's an exception raised, return and * allow the system to try to recover. If not, something is broken * and we need to bail out. */ if (dvmCheckException(self)) goto bail; LOGE("FATAL: unable to allocate exception '%s' '%s'\n", excepClass->descriptor, msg != NULL ? msg : "(no msg)"); dvmAbort(); } /* * Init the exception. */ if (gDvm.optimizing) { /* need the exception object, but can't invoke interpreted code */ LOGV("Skipping init of exception %s '%s'\n", excepClass->descriptor, msg); } else { assert(excepClass == exception->clazz); if (!initException(exception, msg, cause, self)) { /* * Whoops. If we can't initialize the exception, we can't use * it. If there's an exception already set, the constructor * probably threw an OutOfMemoryError. */ if (!dvmCheckException(self)) { /* * We're required to throw something, so we just * throw the pre-constructed internal error. */ self->exception = gDvm.internalErrorObj; } goto bail; } } self->exception = exception; bail: dvmReleaseTrackedAlloc(exception, self); }
GOTO_TARGET(filledNewArray, bool methodCallRange) { ClassObject* arrayClass; ArrayObject* newArray; u4* contents; char typeCh; int i; u4 arg5; EXPORT_PC(); ref = FETCH(1); /* class ref */ vdst = FETCH(2); /* first 4 regs -or- range base */ if (methodCallRange) { vsrc1 = INST_AA(inst); /* #of elements */ arg5 = -1; /* silence compiler warning */ ILOGV("|filled-new-array-range args=%d @0x%04x {regs=v%d-v%d}", vsrc1, ref, vdst, vdst+vsrc1-1); } else { arg5 = INST_A(inst); vsrc1 = INST_B(inst); /* #of elements */ ILOGV("|filled-new-array args=%d @0x%04x {regs=0x%04x %x}", vsrc1, ref, vdst, arg5); } /* * Resolve the array class. */ arrayClass = dvmDexGetResolvedClass(methodClassDex, ref); if (arrayClass == NULL) { arrayClass = dvmResolveClass(curMethod->clazz, ref, false); if (arrayClass == NULL) GOTO_exceptionThrown(); } /* if (!dvmIsArrayClass(arrayClass)) { dvmThrowException("Ljava/lang/RuntimeError;", "filled-new-array needs array class"); GOTO_exceptionThrown(); } */ /* verifier guarantees this is an array class */ assert(dvmIsArrayClass(arrayClass)); assert(dvmIsClassInitialized(arrayClass)); /* * Create an array of the specified type. */ LOGVV("+++ filled-new-array type is '%s'\n", arrayClass->descriptor); typeCh = arrayClass->descriptor[1]; if (typeCh == 'D' || typeCh == 'J') { /* category 2 primitives not allowed */ dvmThrowException("Ljava/lang/RuntimeError;", "bad filled array req"); GOTO_exceptionThrown(); } else if (typeCh != 'L' && typeCh != '[' && typeCh != 'I') { /* TODO: requires multiple "fill in" loops with different widths */ LOGE("non-int primitives not implemented\n"); dvmThrowException("Ljava/lang/InternalError;", "filled-new-array not implemented for anything but 'int'"); GOTO_exceptionThrown(); } newArray = dvmAllocArrayByClass(arrayClass, vsrc1, ALLOC_DONT_TRACK); if (newArray == NULL) GOTO_exceptionThrown(); /* * Fill in the elements. It's legal for vsrc1 to be zero. */ contents = (u4*) newArray->contents; if (methodCallRange) { for (i = 0; i < vsrc1; i++) contents[i] = GET_REGISTER(vdst+i); } else { assert(vsrc1 <= 5); if (vsrc1 == 5) { contents[4] = GET_REGISTER(arg5); vsrc1--; } for (i = 0; i < vsrc1; i++) { contents[i] = GET_REGISTER(vdst & 0x0f); vdst >>= 4; } } retval.l = newArray; }
/* * Find the method corresponding to "methodRef". * * We use "referrer" to find the DexFile with the constant pool that * "methodRef" is an index into. We also use its class loader. The method * being resolved may very well be in a different DEX file. * * If this is a static method, we ensure that the method's class is * initialized. */ Method* dvmResolveMethod(const ClassObject* referrer, u4 methodIdx, MethodType methodType) { DvmDex* pDvmDex = referrer->pDvmDex; ClassObject* resClass; const DexMethodId* pMethodId; Method* resMethod; assert(methodType != METHOD_INTERFACE); LOGVV("--- resolving method %u (referrer=%s)\n", methodIdx, referrer->descriptor); pMethodId = dexGetMethodId(pDvmDex->pDexFile, methodIdx); resClass = dvmResolveClass(referrer, pMethodId->classIdx, false); if (resClass == NULL) { /* can't find the class that the method is a part of */ assert(dvmCheckException(dvmThreadSelf())); return NULL; } if (dvmIsInterfaceClass(resClass)) { /* method is part of an interface */ dvmThrowExceptionWithClassMessage( "Ljava/lang/IncompatibleClassChangeError;", resClass->descriptor); return NULL; } const char* name = dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx); DexProto proto; dexProtoSetFromMethodId(&proto, pDvmDex->pDexFile, pMethodId); /* * We need to chase up the class hierarchy to find methods defined * in super-classes. (We only want to check the current class * if we're looking for a constructor; since DIRECT calls are only * for constructors and private methods, we don't want to walk up.) */ if (methodType == METHOD_DIRECT) { resMethod = dvmFindDirectMethod(resClass, name, &proto); } else if (methodType == METHOD_STATIC) { resMethod = dvmFindDirectMethodHier(resClass, name, &proto); } else { resMethod = dvmFindVirtualMethodHier(resClass, name, &proto); } if (resMethod == NULL) { dvmThrowException("Ljava/lang/NoSuchMethodError;", name); return NULL; } LOGVV("--- found method %d (%s.%s)\n", methodIdx, resClass->descriptor, resMethod->name); /* see if this is a pure-abstract method */ if (dvmIsAbstractMethod(resMethod) && !dvmIsAbstractClass(resClass)) { dvmThrowException("Ljava/lang/AbstractMethodError;", name); return NULL; } /* * If we're the first to resolve this class, we need to initialize * it now. Only necessary for METHOD_STATIC. */ if (methodType == METHOD_STATIC) { if (!dvmIsClassInitialized(resMethod->clazz) && !dvmInitClass(resMethod->clazz)) { assert(dvmCheckException(dvmThreadSelf())); return NULL; } else { assert(!dvmCheckException(dvmThreadSelf())); } } else { /* * Edge case: if the <clinit> for a class creates an instance * of itself, we will call <init> on a class that is still being * initialized by us. */ assert(dvmIsClassInitialized(resMethod->clazz) || dvmIsClassInitializing(resMethod->clazz)); } /* * The class is initialized, the method has been found. Add a pointer * to our data structure so we don't have to jump through the hoops again. */ dvmDexSetResolvedMethod(pDvmDex, methodIdx, resMethod); return resMethod; }
/* * Get an array with all methods declared by a class. * * This includes both static and virtual methods, and can include private * members if "publicOnly" is false. It does not include Miranda methods, * since those weren't declared in the class, or constructors. */ ArrayObject* dvmGetDeclaredMethods(ClassObject* clazz, bool publicOnly) { if (!dvmIsClassInitialized(gDvm.classJavaLangReflectMethod)) dvmInitClass(gDvm.classJavaLangReflectMethod); /* * Count up the #of relevant methods. * * Ignore virtual Miranda methods and direct class/object constructors. */ size_t count = 0; Method* meth = clazz->virtualMethods; for (int i = 0; i < clazz->virtualMethodCount; i++, meth++) { if ((!publicOnly || dvmIsPublicMethod(meth)) && !dvmIsMirandaMethod(meth)) { count++; } } meth = clazz->directMethods; for (int i = 0; i < clazz->directMethodCount; i++, meth++) { if ((!publicOnly || dvmIsPublicMethod(meth)) && meth->name[0] != '<') { count++; } } /* * Create an array of Method objects. */ ArrayObject* methodArray = dvmAllocArrayByClass(gDvm.classJavaLangReflectMethodArray, count, ALLOC_DEFAULT); if (methodArray == NULL) return NULL; /* * Fill out the array. */ meth = clazz->virtualMethods; size_t methObjCount = 0; for (int i = 0; i < clazz->virtualMethodCount; i++, meth++) { if ((!publicOnly || dvmIsPublicMethod(meth)) && !dvmIsMirandaMethod(meth)) { Object* methObj = dvmCreateReflectMethodObject(meth); if (methObj == NULL) goto fail; dvmSetObjectArrayElement(methodArray, methObjCount, methObj); ++methObjCount; dvmReleaseTrackedAlloc(methObj, NULL); } } meth = clazz->directMethods; for (int i = 0; i < clazz->directMethodCount; i++, meth++) { if ((!publicOnly || dvmIsPublicMethod(meth)) && meth->name[0] != '<') { Object* methObj = dvmCreateReflectMethodObject(meth); if (methObj == NULL) goto fail; dvmSetObjectArrayElement(methodArray, methObjCount, methObj); ++methObjCount; dvmReleaseTrackedAlloc(methObj, NULL); } } assert(methObjCount == methodArray->length); /* caller must call dvmReleaseTrackedAlloc */ return methodArray; fail: dvmReleaseTrackedAlloc((Object*) methodArray, NULL); return NULL; }
/* * Get the address of a field from an object. This can be used with "get" * or "set". * * "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 previals 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 othe 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 JValue* getFieldDataAddr(Object* obj, ClassObject* declaringClass, int slot, bool isSetOperation, bool noAccessCheck) { Field* field; JValue* result; field = dvmSlotToField(declaringClass, slot); assert(field != NULL); /* verify access */ if (!noAccessCheck) { if (isSetOperation && dvmIsFinalField(field)) { dvmThrowException("Ljava/lang/IllegalAccessException;", "field is marked 'final'"); return NULL; } ClassObject* callerClass = dvmGetCaller2Class(dvmThreadSelf()->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)) { dvmThrowException("Ljava/lang/IllegalAccessException;", "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) { dvmThrowException("Ljava/lang/IllegalAccessException;", "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; } } result = dvmStaticFieldPtr((StaticField*) field); } 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())); if (obj != NULL) { LOGD("Wrong type of object for field lookup: %s %s\n", obj->clazz->descriptor, declaringClass->descriptor); } return NULL; } result = dvmFieldPtr(obj, ((InstField*) field)->byteOffset); } return result; }
/* * Create a new java/lang/reflect/Method object, using the contents of * "meth" to construct it. * * The spec doesn't specify the constructor. We're going to use the * one from our existing class libs: * * private Method(Class declaring, Class[] paramTypes, Class[] exceptTypes, * Class returnType, String name, int slot) * * The caller must call dvmReleaseTrackedAlloc() on the result. */ Object* dvmCreateReflectMethodObject(const Method* meth) { Object* result = NULL; ArrayObject* params = NULL; ArrayObject* exceptions = NULL; StringObject* nameObj = NULL; Object* methObj; ClassObject* returnType; DexStringCache mangle; char* cp; int slot, method_idx; if (dvmCheckException(dvmThreadSelf())) { ALOGW("WARNING: dvmCreateReflectMethodObject called with " "exception pending"); return NULL; } dexStringCacheInit(&mangle); /* parent should guarantee init so we don't have to check on every call */ assert(dvmIsClassInitialized(gDvm.classJavaLangReflectMethod)); methObj = dvmAllocObject(gDvm.classJavaLangReflectMethod, ALLOC_DEFAULT); if (methObj == NULL) goto bail; /* * Convert the signature string into an array of classes representing * the arguments, and a class for the return type. */ cp = dvmCopyDescriptorStringFromMethod(meth, &mangle); params = convertSignatureToClassArray(&cp, meth->clazz); if (params == NULL) goto bail; assert(*cp == ')'); cp++; returnType = convertSignaturePartToClass(&cp, meth->clazz); if (returnType == NULL) goto bail; /* * Create an array with one entry for every exception that the class * is declared to throw. */ exceptions = dvmGetMethodThrows(meth); if (dvmCheckException(dvmThreadSelf())) goto bail; /* method name */ nameObj = dvmCreateStringFromCstr(meth->name); if (nameObj == NULL) goto bail; slot = methodToSlot(meth); method_idx = dvmGetMethodIdx(meth); JValue unused; dvmCallMethod(dvmThreadSelf(), gDvm.methJavaLangReflectMethod_init, methObj, &unused, meth->clazz, params, exceptions, returnType, nameObj, slot, method_idx); if (dvmCheckException(dvmThreadSelf())) { ALOGD("Method class init threw exception"); goto bail; } result = methObj; bail: dexStringCacheRelease(&mangle); if (result == NULL) { assert(dvmCheckException(dvmThreadSelf())); } dvmReleaseTrackedAlloc((Object*) nameObj, NULL); dvmReleaseTrackedAlloc((Object*) params, NULL); dvmReleaseTrackedAlloc((Object*) exceptions, NULL); if (result == NULL) dvmReleaseTrackedAlloc(methObj, NULL); return result; }
/* * private Object invokeNative(Object obj, Object[] args, Class declaringClass, * Class[] parameterTypes, Class returnType, int slot, boolean noAccessCheck) * * Invoke a static or virtual method via reflection. */ static void Dalvik_java_lang_reflect_Method_invokeNative(const u4* args, JValue* pResult) { // ignore thisPtr in args[0] Object* methObj = (Object*) args[1]; // null for static methods ArrayObject* argList = (ArrayObject*) args[2]; ClassObject* declaringClass = (ClassObject*) args[3]; ArrayObject* params = (ArrayObject*) args[4]; ClassObject* returnType = (ClassObject*) args[5]; int slot = args[6]; bool noAccessCheck = (args[7] != 0); const Method* meth; Object* result; /* * "If the underlying method is static, the class that declared the * method is initialized if it has not already been initialized." */ meth = dvmSlotToMethod(declaringClass, slot); assert(meth != NULL); if (dvmIsStaticMethod(meth)) { if (!dvmIsClassInitialized(declaringClass)) { if (!dvmInitClass(declaringClass)) goto init_failed; } } else { /* looks like interfaces need this too? */ if (dvmIsInterfaceClass(declaringClass) && !dvmIsClassInitialized(declaringClass)) { if (!dvmInitClass(declaringClass)) goto init_failed; } /* make sure the object is an instance of the expected class */ if (!dvmVerifyObjectInClass(methObj, declaringClass)) { assert(dvmCheckException(dvmThreadSelf())); RETURN_VOID(); } /* do the virtual table lookup for the method */ meth = dvmGetVirtualizedMethod(methObj->clazz, meth); if (meth == NULL) { assert(dvmCheckException(dvmThreadSelf())); RETURN_VOID(); } } /* * If the method has a return value, "result" will be an object or * a boxed primitive. */ result = dvmInvokeMethod(methObj, meth, argList, params, returnType, noAccessCheck); RETURN_PTR(result); init_failed: /* * If initialization failed, an exception will be raised. */ ALOGD("Method.invoke() on bad class %s failed", declaringClass->descriptor); assert(dvmCheckException(dvmThreadSelf())); RETURN_VOID(); }
/* * Create a new java/lang/reflect/Constructor object, using the contents of * "meth" to construct it. * * The spec doesn't specify the constructor. We're going to use the * one from our existing class libs: * * private Constructor (Class declaringClass, Class[] ptypes, Class[] extypes, * int slot) */ static Object* createConstructorObject(Method* meth) { Object* result = NULL; ArrayObject* params = NULL; ArrayObject* exceptions = NULL; Object* consObj; DexStringCache mangle; char* cp; int slot, method_idx; dexStringCacheInit(&mangle); /* parent should guarantee init so we don't have to check on every call */ assert(dvmIsClassInitialized(gDvm.classJavaLangReflectConstructor)); consObj = dvmAllocObject(gDvm.classJavaLangReflectConstructor, ALLOC_DEFAULT); if (consObj == NULL) goto bail; /* * Convert the signature string into an array of classes representing * the arguments. */ cp = dvmCopyDescriptorStringFromMethod(meth, &mangle); params = convertSignatureToClassArray(&cp, meth->clazz); if (params == NULL) goto bail; assert(*cp == ')'); assert(*(cp+1) == 'V'); /* * Create an array with one entry for every exception that the class * is declared to throw. */ exceptions = dvmGetMethodThrows(meth); if (dvmCheckException(dvmThreadSelf())) goto bail; slot = methodToSlot(meth); method_idx = dvmGetMethodIdx(meth); JValue unused; dvmCallMethod(dvmThreadSelf(), gDvm.methJavaLangReflectConstructor_init, consObj, &unused, meth->clazz, params, exceptions, slot, method_idx); if (dvmCheckException(dvmThreadSelf())) { ALOGD("Constructor class init threw exception"); goto bail; } result = consObj; bail: dexStringCacheRelease(&mangle); dvmReleaseTrackedAlloc((Object*) params, NULL); dvmReleaseTrackedAlloc((Object*) exceptions, NULL); if (result == NULL) { assert(dvmCheckException(dvmThreadSelf())); dvmReleaseTrackedAlloc(consObj, NULL); } /* caller must dvmReleaseTrackedAlloc(result) */ return result; }
/* * * Get an array with all fields declared by a class. * * This includes both static and instance fields. */ ArrayObject* dvmGetDeclaredFields(ClassObject* clazz, bool publicOnly) { if (!dvmIsClassInitialized(gDvm.classJavaLangReflectField)) dvmInitClass(gDvm.classJavaLangReflectField); /* count #of fields */ size_t count; if (!publicOnly) count = clazz->sfieldCount + clazz->ifieldCount; else { count = 0; for (int i = 0; i < clazz->sfieldCount; i++) { if ((clazz->sfields[i].accessFlags & ACC_PUBLIC) != 0) count++; } for (int i = 0; i < clazz->ifieldCount; i++) { if ((clazz->ifields[i].accessFlags & ACC_PUBLIC) != 0) count++; } } /* create the Field[] array */ ArrayObject* fieldArray = dvmAllocArrayByClass(gDvm.classJavaLangReflectFieldArray, count, ALLOC_DEFAULT); if (fieldArray == NULL) return NULL; /* populate */ size_t fieldCount = 0; for (int i = 0; i < clazz->sfieldCount; i++) { if (!publicOnly || (clazz->sfields[i].accessFlags & ACC_PUBLIC) != 0) { Object* field = createFieldObject(&clazz->sfields[i], clazz); if (field == NULL) { goto fail; } dvmSetObjectArrayElement(fieldArray, fieldCount, field); dvmReleaseTrackedAlloc(field, NULL); ++fieldCount; } } for (int i = 0; i < clazz->ifieldCount; i++) { if (!publicOnly || (clazz->ifields[i].accessFlags & ACC_PUBLIC) != 0) { Object* field = createFieldObject(&clazz->ifields[i], clazz); if (field == NULL) { goto fail; } dvmSetObjectArrayElement(fieldArray, fieldCount, field); dvmReleaseTrackedAlloc(field, NULL); ++fieldCount; } } assert(fieldCount == fieldArray->length); /* caller must call dvmReleaseTrackedAlloc */ return fieldArray; fail: dvmReleaseTrackedAlloc((Object*) fieldArray, NULL); return NULL; }
/* * Generate an array of StackTraceElement objects from the raw integer * data encoded by dvmFillInStackTrace(). * * "intVals" points to the first {method,pc} pair. * * The returned array is not added to the "local refs" list. */ ArrayObject* dvmGetStackTraceRaw(const int* intVals, int stackDepth) { ArrayObject* steArray = NULL; int i; /* init this if we haven't yet */ if (!dvmIsClassInitialized(gDvm.classJavaLangStackTraceElement)) dvmInitClass(gDvm.classJavaLangStackTraceElement); /* allocate a StackTraceElement array */ steArray = dvmAllocArray(gDvm.classJavaLangStackTraceElementArray, stackDepth, kObjectArrayRefWidth, ALLOC_DEFAULT); if (steArray == NULL) goto bail; /* * Allocate and initialize a StackTraceElement for each stack frame. * We use the standard constructor to configure the object. */ for (i = 0; i < stackDepth; i++) { Object* ste; Method* meth; StringObject* className; StringObject* methodName; StringObject* fileName; int lineNumber, pc; const char* sourceFile; char* dotName; ste = dvmAllocObject(gDvm.classJavaLangStackTraceElement,ALLOC_DEFAULT); if (ste == NULL) goto bail; meth = (Method*) *intVals++; pc = *intVals++; if (pc == -1) // broken top frame? lineNumber = 0; else lineNumber = dvmLineNumFromPC(meth, pc); dotName = dvmDescriptorToDot(meth->clazz->descriptor); className = dvmCreateStringFromCstr(dotName); free(dotName); methodName = dvmCreateStringFromCstr(meth->name); sourceFile = dvmGetMethodSourceFile(meth); if (sourceFile != NULL) fileName = dvmCreateStringFromCstr(sourceFile); else fileName = NULL; /* * Invoke: * public StackTraceElement(String declaringClass, String methodName, * String fileName, int lineNumber) * (where lineNumber==-2 means "native") */ JValue unused; dvmCallMethod(dvmThreadSelf(), gDvm.methJavaLangStackTraceElement_init, ste, &unused, className, methodName, fileName, lineNumber); dvmReleaseTrackedAlloc(ste, NULL); dvmReleaseTrackedAlloc((Object*) className, NULL); dvmReleaseTrackedAlloc((Object*) methodName, NULL); dvmReleaseTrackedAlloc((Object*) fileName, NULL); if (dvmCheckException(dvmThreadSelf())) goto bail; dvmSetObjectArrayElement(steArray, i, ste); } bail: dvmReleaseTrackedAlloc((Object*) steArray, NULL); return steArray; }