static bool performanceTest() { static const int kTableMax = 100; IndirectRefTable irt; IndirectRef manyRefs[kTableMax]; ClassObject* clazz = dvmFindClass("Ljava/lang/Object;", NULL); Object* obj0 = dvmAllocObject(clazz, ALLOC_DONT_TRACK); const u4 cookie = IRT_FIRST_SEGMENT; const int kLoops = 100000; Stopwatch stopwatch; DBUG_MSG("+++ START performance\n"); if (!irt.init(kTableMax, kTableMax, kIndirectKindGlobal)) { return false; } stopwatch.reset(); for (int loop = 0; loop < kLoops; loop++) { for (int i = 0; i < kTableMax; i++) { manyRefs[i] = irt.add(cookie, obj0); } for (int i = 0; i < kTableMax; i++) { irt.remove(cookie, manyRefs[i]); } } DBUG_MSG("Add/remove %d objects FIFO order, %d iterations, %0.3fms / iteration", kTableMax, kLoops, stopwatch.elapsedSeconds() * 1000 / kLoops); stopwatch.reset(); for (int loop = 0; loop < kLoops; loop++) { for (int i = 0; i < kTableMax; i++) { manyRefs[i] = irt.add(cookie, obj0); } for (int i = kTableMax; i-- > 0; ) { irt.remove(cookie, manyRefs[i]); } } DBUG_MSG("Add/remove %d objects LIFO order, %d iterations, %0.3fms / iteration", kTableMax, kLoops, stopwatch.elapsedSeconds() * 1000 / kLoops); for (int i = 0; i < kTableMax; i++) { manyRefs[i] = irt.add(cookie, obj0); } stopwatch.reset(); for (int loop = 0; loop < kLoops; loop++) { for (int i = 0; i < kTableMax; i++) { irt.get(manyRefs[i]); } } DBUG_MSG("Get %d objects, %d iterations, %0.3fms / iteration", kTableMax, kLoops, stopwatch.elapsedSeconds() * 1000 / kLoops); for (int i = kTableMax; i-- > 0; ) { irt.remove(cookie, manyRefs[i]); } irt.destroy(); return true; }
/* * Create a "stock instance" of an exception class. */ static Object* createStockException(const char* descriptor, const char* msg) { Thread* self = dvmThreadSelf(); StringObject* msgStr = NULL; ClassObject* clazz; Method* init; Object* obj; /* find class, initialize if necessary */ clazz = dvmFindSystemClass(descriptor); if (clazz == NULL) { LOGE("Unable to find %s", descriptor); return NULL; } init = dvmFindDirectMethodByDescriptor(clazz, "<init>", "(Ljava/lang/String;)V"); if (init == NULL) { LOGE("Unable to find String-arg constructor for %s", descriptor); return NULL; } obj = dvmAllocObject(clazz, ALLOC_DEFAULT); if (obj == NULL) return NULL; if (msg == NULL) { msgStr = NULL; } else { msgStr = dvmCreateStringFromCstr(msg); if (msgStr == NULL) { LOGW("Could not allocate message string \"%s\"", msg); dvmReleaseTrackedAlloc(obj, self); return NULL; } } JValue unused; dvmCallMethod(self, init, obj, &unused, msgStr); if (dvmCheckException(self)) { dvmReleaseTrackedAlloc((Object*) msgStr, self); dvmReleaseTrackedAlloc(obj, self); return NULL; } dvmReleaseTrackedAlloc((Object*) msgStr, self); // okay if msgStr NULL return obj; }
/* * 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; }
/* * Wrap the now-pending exception in a different exception. This is useful * for reflection stuff that wants to hand a checked exception back from a * method that doesn't declare it. * * If something fails, an (unchecked) exception related to that failure * will be pending instead. */ void dvmWrapException(const char* newExcepStr) { Thread* self = dvmThreadSelf(); Object* origExcep; ClassObject* iteClass; origExcep = dvmGetException(self); dvmAddTrackedAlloc(origExcep, self); // don't let the GC free it dvmClearException(self); // clear before class lookup iteClass = dvmFindSystemClass(newExcepStr); if (iteClass != NULL) { Object* iteExcep; Method* initMethod; iteExcep = dvmAllocObject(iteClass, ALLOC_DEFAULT); if (iteExcep != NULL) { initMethod = dvmFindDirectMethodByDescriptor(iteClass, "<init>", "(Ljava/lang/Throwable;)V"); if (initMethod != NULL) { JValue unused; dvmCallMethod(self, initMethod, iteExcep, &unused, origExcep); /* if <init> succeeded, replace the old exception */ if (!dvmCheckException(self)) dvmSetException(self, iteExcep); } dvmReleaseTrackedAlloc(iteExcep, NULL); /* if initMethod doesn't exist, or failed... */ if (!dvmCheckException(self)) dvmSetException(self, origExcep); } else { /* leave OutOfMemoryError pending */ } } else { /* leave ClassNotFoundException pending */ } assert(dvmCheckException(self)); dvmReleaseTrackedAlloc(origExcep, self); }
/* * Create a wrapper object for a primitive data type. If "returnType" is * not primitive, this just casts "value" to an object and returns it. * * We could invoke the "toValue" method on the box types to take * advantage of pre-created values, but running that through the * interpreter is probably less efficient than just allocating storage here. * * The caller must call dvmReleaseTrackedAlloc on the result. */ DataObject* dvmBoxPrimitive(JValue value, ClassObject* returnType) { ClassObject* wrapperClass; DataObject* wrapperObj; s4* dataPtr; PrimitiveType typeIndex = returnType->primitiveType; const char* classDescriptor; if (typeIndex == PRIM_NOT) { /* add to tracking table so return value is always in table */ if (value.l != NULL) dvmAddTrackedAlloc((Object*)value.l, NULL); return (DataObject*) value.l; } classDescriptor = dexGetBoxedTypeDescriptor(typeIndex); if (classDescriptor == NULL) { return NULL; } wrapperClass = dvmFindSystemClass(classDescriptor); if (wrapperClass == NULL) { ALOGW("Unable to find '%s'", classDescriptor); assert(dvmCheckException(dvmThreadSelf())); return NULL; } wrapperObj = (DataObject*) dvmAllocObject(wrapperClass, ALLOC_DEFAULT); if (wrapperObj == NULL) return NULL; dataPtr = (s4*) wrapperObj->instanceData; /* assumes value is stored in first instance field */ /* (see dvmValidateBoxClasses) */ if (typeIndex == PRIM_LONG || typeIndex == PRIM_DOUBLE) *(s8*)dataPtr = value.j; else *dataPtr = value.i; return wrapperObj; }
/* * 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; }
/* * public int constructNative(Object[] args, Class declaringClass, * Class[] parameterTypes, int slot, boolean noAccessCheck) */ 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; 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); }
/* * Basic add/get/delete tests in an unsegmented table. */ static bool basicTest() { static const int kTableMax = 20; IndirectRefTable irt; IndirectRef iref0, iref1, iref2, iref3; IndirectRef manyRefs[kTableMax]; ClassObject* clazz = dvmFindClass("Ljava/lang/Object;", NULL); Object* obj0 = dvmAllocObject(clazz, ALLOC_DONT_TRACK); Object* obj1 = dvmAllocObject(clazz, ALLOC_DONT_TRACK); Object* obj2 = dvmAllocObject(clazz, ALLOC_DONT_TRACK); Object* obj3 = dvmAllocObject(clazz, ALLOC_DONT_TRACK); const u4 cookie = IRT_FIRST_SEGMENT; bool result = false; if (!irt.init(kTableMax/2, kTableMax, kIndirectKindGlobal)) { return false; } iref0 = (IndirectRef) 0x11110; if (irt.remove(cookie, iref0)) { ALOGE("unexpectedly successful removal"); goto bail; } /* * Add three, check, remove in the order in which they were added. */ DBUG_MSG("+++ START fifo\n"); iref0 = irt.add(cookie, obj0); iref1 = irt.add(cookie, obj1); iref2 = irt.add(cookie, obj2); if (iref0 == NULL || iref1 == NULL || iref2 == NULL) { ALOGE("trivial add1 failed"); goto bail; } if (irt.get(iref0) != obj0 || irt.get(iref1) != obj1 || irt.get(iref2) != obj2) { ALOGE("objects don't match expected values %p %p %p vs. %p %p %p", irt.get(iref0), irt.get(iref1), irt.get(iref2), obj0, obj1, obj2); goto bail; } else { DBUG_MSG("+++ obj1=%p --> iref1=%p\n", obj1, iref1); } if (!irt.remove(cookie, iref0) || !irt.remove(cookie, iref1) || !irt.remove(cookie, iref2)) { ALOGE("fifo deletion failed"); goto bail; } /* table should be empty now */ if (irt.capacity() != 0) { ALOGE("fifo del not empty"); goto bail; } /* get invalid entry (off the end of the list) */ if (irt.get(iref0) != kInvalidIndirectRefObject) { ALOGE("stale entry get succeeded unexpectedly"); goto bail; } /* * Add three, remove in the opposite order. */ DBUG_MSG("+++ START lifo\n"); iref0 = irt.add(cookie, obj0); iref1 = irt.add(cookie, obj1); iref2 = irt.add(cookie, obj2); if (iref0 == NULL || iref1 == NULL || iref2 == NULL) { ALOGE("trivial add2 failed"); goto bail; } if (!irt.remove(cookie, iref2) || !irt.remove(cookie, iref1) || !irt.remove(cookie, iref0)) { ALOGE("lifo deletion failed"); goto bail; } /* table should be empty now */ if (irt.capacity() != 0) { ALOGE("lifo del not empty"); goto bail; } /* * Add three, remove middle / middle / bottom / top. (Second attempt * to remove middle should fail.) */ DBUG_MSG("+++ START unorder\n"); iref0 = irt.add(cookie, obj0); iref1 = irt.add(cookie, obj1); iref2 = irt.add(cookie, obj2); if (iref0 == NULL || iref1 == NULL || iref2 == NULL) { ALOGE("trivial add3 failed"); goto bail; } if (irt.capacity() != 3) { ALOGE("expected 3 entries, found %d", irt.capacity()); goto bail; } if (!irt.remove(cookie, iref1) || irt.remove(cookie, iref1)) { ALOGE("unorder deletion1 failed"); goto bail; } /* get invalid entry (from hole) */ if (irt.get(iref1) != kInvalidIndirectRefObject) { ALOGE("hole get succeeded unexpectedly"); goto bail; } if (!irt.remove(cookie, iref2) || !irt.remove(cookie, iref0)) { ALOGE("unorder deletion2 failed"); goto bail; } /* table should be empty now */ if (irt.capacity() != 0) { ALOGE("unorder del not empty"); goto bail; } /* * Add four entries. Remove #1, add new entry, verify that table size * is still 4 (i.e. holes are getting filled). Remove #1 and #3, verify * that we delete one and don't hole-compact the other. */ DBUG_MSG("+++ START hole fill\n"); iref0 = irt.add(cookie, obj0); iref1 = irt.add(cookie, obj1); iref2 = irt.add(cookie, obj2); iref3 = irt.add(cookie, obj3); if (iref0 == NULL || iref1 == NULL || iref2 == NULL || iref3 == NULL) { ALOGE("trivial add4 failed"); goto bail; } if (!irt.remove(cookie, iref1)) { ALOGE("remove 1 of 4 failed"); goto bail; } iref1 = irt.add(cookie, obj1); if (irt.capacity() != 4) { ALOGE("hole not filled"); goto bail; } if (!irt.remove(cookie, iref1) || !irt.remove(cookie, iref3)) { ALOGE("remove 1/3 failed"); goto bail; } if (irt.capacity() != 3) { ALOGE("should be 3 after two deletions"); goto bail; } if (!irt.remove(cookie, iref2) || !irt.remove(cookie, iref0)) { ALOGE("remove 2/0 failed"); goto bail; } if (irt.capacity() != 0) { ALOGE("not empty after split remove"); goto bail; } /* * Add an entry, remove it, add a new entry, and try to use the original * iref. They have the same slot number but are for different objects. * With the extended checks in place, this should fail. */ DBUG_MSG("+++ START switched\n"); iref0 = irt.add(cookie, obj0); irt.remove(cookie, iref0); iref1 = irt.add(cookie, obj1); if (irt.remove(cookie, iref0)) { ALOGE("mismatched del succeeded (%p vs %p)", iref0, iref1); goto bail; } if (!irt.remove(cookie, iref1)) { ALOGE("switched del failed"); goto bail; } if (irt.capacity() != 0) { ALOGE("switching del not empty"); goto bail; } /* * Same as above, but with the same object. A more rigorous checker * (e.g. with slot serialization) will catch this. */ DBUG_MSG("+++ START switched same object\n"); iref0 = irt.add(cookie, obj0); irt.remove(cookie, iref0); iref1 = irt.add(cookie, obj0); if (iref0 != iref1) { /* try 0, should not work */ if (irt.remove(cookie, iref0)) { ALOGE("temporal del succeeded (%p vs %p)", iref0, iref1); goto bail; } } if (!irt.remove(cookie, iref1)) { ALOGE("temporal cleanup failed"); goto bail; } if (irt.capacity() != 0) { ALOGE("temporal del not empty"); goto bail; } DBUG_MSG("+++ START null lookup\n"); if (irt.get(NULL) != kInvalidIndirectRefObject) { ALOGE("null lookup succeeded"); goto bail; } DBUG_MSG("+++ START stale lookup\n"); iref0 = irt.add(cookie, obj0); irt.remove(cookie, iref0); if (irt.get(iref0) != kInvalidIndirectRefObject) { ALOGE("stale lookup succeeded"); goto bail; } /* * Test table overflow. */ DBUG_MSG("+++ START overflow\n"); int i; for (i = 0; i < kTableMax; i++) { manyRefs[i] = irt.add(cookie, obj0); if (manyRefs[i] == NULL) { ALOGE("Failed adding %d of %d", i, kTableMax); goto bail; } } if (irt.add(cookie, obj0) != NULL) { ALOGE("Table overflow succeeded"); goto bail; } if (irt.capacity() != (size_t)kTableMax) { ALOGE("Expected %d entries, found %d", kTableMax, irt.capacity()); goto bail; } irt.dump("table with 20 entries, all filled"); for (i = 0; i < kTableMax-1; i++) { if (!irt.remove(cookie, manyRefs[i])) { ALOGE("multi-remove failed at %d", i); goto bail; } } irt.dump("table with 20 entries, 19 of them holes"); /* because of removal order, should have 20 entries, 19 of them holes */ if (irt.capacity() != (size_t)kTableMax) { ALOGE("Expected %d entries (with holes), found %d", kTableMax, irt.capacity()); goto bail; } if (!irt.remove(cookie, manyRefs[kTableMax-1])) { ALOGE("multi-remove final failed"); goto bail; } if (irt.capacity() != 0) { ALOGE("multi-del not empty"); goto bail; } /* Done */ DBUG_MSG("+++ basic test complete\n"); result = true; bail: irt.destroy(); 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; }
/* * 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; }
/* * Test operations on a segmented table. */ static bool segmentTest(void) { static const int kTableMax = 20; IndirectRefTable irt; IndirectRef iref0, iref1, iref2, iref3; ClassObject* clazz = dvmFindClass("Ljava/lang/Object;", NULL); Object* obj0 = dvmAllocObject(clazz, ALLOC_DONT_TRACK); Object* obj1 = dvmAllocObject(clazz, ALLOC_DONT_TRACK); Object* obj2 = dvmAllocObject(clazz, ALLOC_DONT_TRACK); Object* obj3 = dvmAllocObject(clazz, ALLOC_DONT_TRACK); u4 cookie; u4 segmentState[4]; bool result = false; if (!dvmInitIndirectRefTable(&irt, kTableMax, kTableMax, kIndirectKindLocal)) { return false; } cookie = segmentState[0] = IRT_FIRST_SEGMENT; DBUG_MSG("+++ objs %p %p %p %p\n", obj0, obj1, obj2, obj3); /* * Push two, create new segment, push two more, try to get all four, * try to delete all 4. All four should be accessible, but only the * last two should be deletable. */ DBUG_MSG("+++ START basic segment\n"); iref0 = dvmAddToIndirectRefTable(&irt, cookie, obj0); iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1); cookie = segmentState[1] = dvmPushIndirectRefTableSegment(&irt); DBUG_MSG("+++ pushed, cookie is 0x%08x\n", cookie); iref2 = dvmAddToIndirectRefTable(&irt, cookie, obj2); iref3 = dvmAddToIndirectRefTable(&irt, cookie, obj3); if (dvmRemoveFromIndirectRefTable(&irt, cookie, iref0) || dvmRemoveFromIndirectRefTable(&irt, cookie, iref1)) { LOGE("removed values from earlier segment\n"); goto bail; } if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref2) || !dvmRemoveFromIndirectRefTable(&irt, cookie, iref3)) { LOGE("unable to remove values from current segment\n"); goto bail; } if (dvmIndirectRefTableEntries(&irt) != 2) { LOGE("wrong total entries\n"); goto bail; } dvmPopIndirectRefTableSegment(&irt, segmentState[1]); cookie = segmentState[0]; if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref0) || !dvmRemoveFromIndirectRefTable(&irt, cookie, iref1)) { LOGE("unable to remove values from first segment\n"); goto bail; } if (dvmIndirectRefTableEntries(&irt) != 0) { LOGE("basic push/pop not empty\n"); goto bail; } /* * Push two, delete first, segment, push two more, pop segment, verify * the last two are no longer present and hole count is right. The * adds after the segment pop should not be filling in the hole. */ DBUG_MSG("+++ START segment pop\n"); iref0 = dvmAddToIndirectRefTable(&irt, cookie, obj0); iref1 = dvmAddToIndirectRefTable(&irt, cookie, obj1); dvmRemoveFromIndirectRefTable(&irt, cookie, iref0); cookie = segmentState[1] = dvmPushIndirectRefTableSegment(&irt); iref2 = dvmAddToIndirectRefTable(&irt, cookie, obj2); iref3 = dvmAddToIndirectRefTable(&irt, cookie, obj3); dvmPopIndirectRefTableSegment(&irt, segmentState[1]); cookie = segmentState[0]; if (dvmIndirectRefTableEntries(&irt) != 2) { LOGE("wrong total entries after pop\n"); goto bail; } dvmRemoveFromIndirectRefTable(&irt, cookie, iref1); if (dvmIndirectRefTableEntries(&irt) != 0) { LOGE("not back to zero after pop + del\n"); goto bail; } /* * Multiple segments, some empty. */ DBUG_MSG("+++ START multiseg\n"); iref0 = dvmAppendToIndirectRefTable(&irt, cookie, obj0); iref1 = dvmAppendToIndirectRefTable(&irt, cookie, obj1); cookie = segmentState[1] = dvmPushIndirectRefTableSegment(&irt); cookie = segmentState[2] = dvmPushIndirectRefTableSegment(&irt); iref3 = dvmAppendToIndirectRefTable(&irt, cookie, obj3); iref2 = dvmAppendToIndirectRefTable(&irt, cookie, obj2); dvmRemoveFromIndirectRefTable(&irt, cookie, iref3); cookie = segmentState[3] = dvmPushIndirectRefTableSegment(&irt); iref3 = dvmAppendToIndirectRefTable(&irt, cookie, obj3); if (dvmGetFromIndirectRefTable(&irt, iref0) != obj0 || dvmGetFromIndirectRefTable(&irt, iref1) != obj1 || dvmGetFromIndirectRefTable(&irt, iref2) != obj2 || dvmGetFromIndirectRefTable(&irt, iref3) != obj3) { LOGE("Unable to retrieve all multiseg objects\n"); goto bail; } dvmDumpIndirectRefTable(&irt, "test"); //int i; //for (i = 0; i < sizeof(segmentState) / sizeof(segmentState[0]); i++) { // DBUG_MSG("+++ segment %d = 0x%08x\n", i, segmentState[i]); //} dvmRemoveFromIndirectRefTable(&irt, cookie, iref3); if (dvmRemoveFromIndirectRefTable(&irt, cookie, iref2)) { LOGE("multiseg del2 worked\n"); goto bail; } dvmPopIndirectRefTableSegment(&irt, segmentState[3]); cookie = segmentState[2]; if (!dvmRemoveFromIndirectRefTable(&irt, cookie, iref2)) { LOGE("multiseg del2b failed (cookie=0x%08x ref=%p)\n", cookie, iref2); goto bail; } iref2 = dvmAddToIndirectRefTable(&irt, cookie, obj2); /* pop two off at once */ dvmPopIndirectRefTableSegment(&irt, segmentState[1]); cookie = segmentState[0]; if (dvmIndirectRefTableEntries(&irt) != 2) { LOGE("Unexpected entry count in multiseg\n"); goto bail; } dvmRemoveFromIndirectRefTable(&irt, cookie, iref0); dvmRemoveFromIndirectRefTable(&irt, cookie, iref1); if (dvmIndirectRefTableEntries(&irt) != 0) { LOGE("Unexpected entry count at multiseg end\n"); goto bail; } DBUG_MSG("+++ segment test complete\n"); result = true; bail: dvmClearIndirectRefTable(&irt); return result; }
/* * 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); }
/* * 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; }