Пример #1
0
/** @return true if we need to schedule a call to enqueue().
 */
static bool enqueueReference(Object *reference)
{
#if FANCY_REFERENCE_SUBCLASS
    /* See if this reference class has overridden enqueue();
     * if not, we can take a shortcut.
     */
    if (reference->clazz->vtable[gDvm.voffJavaLangRefReference_enqueue]->clazz
            == gDvm.classJavaLangRefReference)
#endif
    {
        Object *queue = dvmGetFieldObject(reference,
                gDvm.offJavaLangRefReference_queue);
        Object *queueNext = dvmGetFieldObject(reference,
                gDvm.offJavaLangRefReference_queueNext);
        if (queue == NULL || queueNext != NULL) {
            /* There is no queue, or the reference has already
             * been enqueued.  The Reference.enqueue() method
             * will do nothing even if we call it.
             */
            return false;
        }
    }

    /* We need to call enqueue(), but if we called it from
     * here we'd probably deadlock.  Schedule a call.
     */
    return true;
}
Пример #2
0
/*
 * Print the direct stack trace of the given exception to the log.
 */
static void logStackTraceOf(Object* exception)
{
    const ArrayObject* stackData;
    StringObject* messageStr;
    int stackSize;
    const int* intVals;

    messageStr = (StringObject*) dvmGetFieldObject(exception,
                    gDvm.offJavaLangThrowable_message);
    if (messageStr != NULL) {
        char* cp = dvmCreateCstrFromString(messageStr);
        LOGI("%s: %s\n", exception->clazz->descriptor, cp);
        free(cp);
    } else {
        LOGI("%s:\n", exception->clazz->descriptor);
    }

    stackData = (const ArrayObject*) dvmGetFieldObject(exception,
                    gDvm.offJavaLangThrowable_stackState);
    if (stackData == NULL) {
        LOGI("  (no stack trace data found)\n");
        return;
    }

    stackSize = stackData->length / 2;
    intVals = (const int*) stackData->contents;

    dvmLogRawStackTrace(intVals, stackSize);
}
Пример #3
0
	void ObjTracer::extract_activInfo( const Object* const obj )
	{
		InstField* pF;
		ClassObject * clazz  = obj->clazz;
		Object* o;
	
		fprintf( traceFile_, "instUid %u #%d:  %p\n", 
			opcodeTracer.get_instUid(), CMPNM_OBJ, obj );

		//taskAffinity in ActivityInfo
		pF = &clazz->ifields[0];
		o = dvmGetFieldObject( obj, pF->byteOffset );
		this -> extract_str( o );

		clazz = clazz->super;
		//processName in ComponentInfo
		pF = &clazz->ifields[1];
		o = dvmGetFieldObject( obj, pF->byteOffset );
		this -> extract_str( o );

		clazz = clazz->super;
		//packageName in PackageItemInfo
		pF = &clazz->ifields[0];
		o = dvmGetFieldObject( obj, pF->byteOffset );
		this -> extract_str( o );

		//name in PackageItemInfo
		pF = &clazz->ifields[0];
		o = dvmGetFieldObject( obj, pF->byteOffset );
		this -> extract_str( o );

		//fprintf(f, "\n");
		fflush(traceFile_);

	}
Пример #4
0
/* Mark all objects referred to by a DataObject's instance fields.
 */
static void scanInstanceFields(const DataObject *obj, ClassObject *clazz,
        GcMarkContext *ctx)
{
//TODO: Optimize this by avoiding walking the superclass chain
    while (clazz != NULL) {
        InstField *f;
        int i;

        /* All of the fields that contain object references
         * are guaranteed to be at the beginning of the ifields list.
         */
        f = clazz->ifields;
        for (i = 0; i < clazz->ifieldRefCount; i++) {
            /* Mark the array or object reference.
             * May be NULL.
             *
             * Note that, per the comment on struct InstField,
             * f->byteOffset is the offset from the beginning of
             * obj, not the offset into obj->instanceData.
             */
            markObject(dvmGetFieldObject((Object*)obj, f->byteOffset), ctx);
            f++;
        }

        /* This will be NULL when we hit java.lang.Object
         */
        clazz = clazz->super;
    }
}
Пример #5
0
/* Returns the taint on an object.
 * - Currently only arrays and java.lang.String is supported
 */
u4 getObjectTaint(Object* obj, const char* descriptor)
{
    ArrayObject *arrObj = NULL;

    if (obj == NULL) {
	return TAINT_CLEAR;
    }

    if (descriptor[0] == '[') {
	/* Get the taint from the array */
	arrObj = (ArrayObject*) obj;
	if (arrObj != NULL) {
	    return arrObj->taint.tag;
	}
    }

    if (strcmp(descriptor, "Ljava/lang/String;") == 0) {
	arrObj = (ArrayObject*) dvmGetFieldObject(obj,
		gDvm.offJavaLangString_value);
	if (arrObj != NULL) {
	    return arrObj->taint.tag;
	} /* else, empty string? don't worry about it */
    }

    /* TODO: What about classes derived from String? */

    /* Don't worry about other object types */
    return TAINT_CLEAR;
}
Пример #6
0
/*
 * public char charAt(int index)
 */
bool javaLangString_charAt(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
    JValue* pResult)
{
    int count, offset;
    ArrayObject* chars;

    /* null reference check on "this" */
    if ((Object*) arg0 == NULL) {
        dvmThrowNullPointerException(NULL);
        return false;
    }

    //ALOGI("String.charAt this=0x%08x index=%d", arg0, arg1);
    count = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_COUNT);
    if ((s4) arg1 < 0 || (s4) arg1 >= count) {
        dvmThrowStringIndexOutOfBoundsExceptionWithIndex(count, arg1);
        return false;
    } else {
        offset = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_OFFSET);
        chars = (ArrayObject*)
            dvmGetFieldObject((Object*) arg0, STRING_FIELDOFF_VALUE);

        pResult->i = ((const u2*)(void*)chars->contents)[arg1 + offset];
        return true;
    }
}
Пример #7
0
/* Process all enqueued heap work, including finalizers and reference
 * enqueueing. Clearing has already been done by the VM.
 *
 * Caller must hold gDvm.heapWorkerLock.
 */
static void doHeapWork(Thread *self)
{
    Object *obj;
    HeapWorkerOperation op;
    int numFinalizersCalled, numReferencesEnqueued;

    assert(gDvm.voffJavaLangObject_finalize >= 0);
    assert(gDvm.methJavaLangRefReference_enqueueInternal != NULL);

    numFinalizersCalled = 0;
    numReferencesEnqueued = 0;
    while ((obj = dvmGetNextHeapWorkerObject(&op)) != NULL) {
        Method *method = NULL;

        /* Make sure the object hasn't been collected since
         * being scheduled.
         */
        assert(dvmIsValidObject(obj));

        /* Call the appropriate method(s).
         */
        if (op == WORKER_FINALIZE) {
            numFinalizersCalled++;
            method = obj->clazz->vtable[gDvm.voffJavaLangObject_finalize];
            assert(dvmCompareNameDescriptorAndMethod("finalize", "()V",
                            method) == 0);
            assert(method->clazz != gDvm.classJavaLangObject);
            callMethod(self, obj, method);
        } else {
            assert(op == WORKER_ENQUEUE);
            assert(dvmGetFieldObject(
                       obj, gDvm.offJavaLangRefReference_queue) != NULL);
            assert(dvmGetFieldObject(
                       obj, gDvm.offJavaLangRefReference_queueNext) == NULL);
            numReferencesEnqueued++;
            callMethod(self, obj,
                       gDvm.methJavaLangRefReference_enqueueInternal);
        }

        /* Let the GC collect the object.
         */
        dvmReleaseTrackedAlloc(obj, self);
    }
    LOGV("Called %d finalizers\n", numFinalizersCalled);
    LOGV("Enqueued %d references\n", numReferencesEnqueued);
}
Пример #8
0
/*
 * Send a notification when a thread starts or stops.
 *
 * Because we broadcast the full set of threads when the notifications are
 * first enabled, it's possible for "thread" to be actively executing.
 */
void dvmDdmSendThreadNotification(Thread* thread, bool started)
{
    if (!gDvm.ddmThreadNotification)
        return;

    StringObject* nameObj = NULL;
    Object* threadObj = thread->threadObj;

    if (threadObj != NULL) {
        nameObj = (StringObject*)
            dvmGetFieldObject(threadObj, gDvm.offJavaLangThread_name);
    }

    int type, len;
    u1 buf[256];

    if (started) {
        const u2* chars;
        u2* outChars;
        size_t stringLen;

        type = CHUNK_TYPE("THCR");

        if (nameObj != NULL) {
            stringLen = dvmStringLen(nameObj);
            chars = dvmStringChars(nameObj);
        } else {
            stringLen = 0;
            chars = NULL;
        }

        /* leave room for the two integer fields */
        if (stringLen > (sizeof(buf) - sizeof(u4)*2) / 2)
            stringLen = (sizeof(buf) - sizeof(u4)*2) / 2;
        len = stringLen*2 + sizeof(u4)*2;

        set4BE(&buf[0x00], thread->threadId);
        set4BE(&buf[0x04], stringLen);

        /* copy the UTF-16 string, transforming to big-endian */
        outChars = (u2*) &buf[0x08];
        while (stringLen--)
            set2BE((u1*) (outChars++), *chars++);
    } else {
        type = CHUNK_TYPE("THDE");

        len = 4;

        set4BE(&buf[0x00], thread->threadId);
    }

    dvmDbgDdmSendChunk(type, len, buf);
}
Пример #9
0
/* Mark all objects referred to by a DataObject's instance fields.
 */
static void scanInstanceFields(const DataObject *obj, ClassObject *clazz,
        GcMarkContext *ctx)
{
    if (clazz->refOffsets != CLASS_WALK_SUPER) {
        unsigned int refOffsets = clazz->refOffsets;
        while (refOffsets != 0) {
            const int rshift = CLZ(refOffsets);
            refOffsets &= ~(CLASS_HIGH_BIT >> rshift);
            markObject(dvmGetFieldObject((Object*)obj,
                                         CLASS_OFFSET_FROM_CLZ(rshift)), ctx);
        }
    } else {
Пример #10
0
/*
 * Get the "cause" field from an exception.
 *
 * The Throwable class initializes the "cause" field to "this" to
 * differentiate between being initialized to null and never being
 * initialized.  We check for that here and convert it to NULL.
 */
Object* dvmGetExceptionCause(const Object* exception)
{
    if (!dvmInstanceof(exception->clazz, gDvm.classJavaLangThrowable)) {
        LOGE("Tried to get cause from object of type '%s'\n",
            exception->clazz->descriptor);
        dvmAbort();
    }
    Object* cause =
        dvmGetFieldObject(exception, gDvm.offJavaLangThrowable_cause);
    if (cause == exception)
        return NULL;
    else
        return cause;
}
/* Returns the object pointer for a field
 * - obj only used if the field is not static
 * - Note: will not return an array object
 */
Object* getObjectFromField(Field* field, Object* obj)
{
    if (field->signature[0] != 'L') {
	return NULL;
    }

    if (dvmIsStaticField(field)) {
	StaticField* sfield = (StaticField*) field;
	return dvmGetStaticFieldObject(sfield);
    } else {
	InstField* ifield = (InstField*) field;
	return dvmGetFieldObject(obj, ifield->byteOffset);
    }
}
Пример #12
0
/*
 * Utility function when we're evaluating alternative implementations.
 */
static void badMatch(StringObject* thisStrObj, StringObject* compStrObj,
    int expectResult, int newResult, const char* compareType)
{
    ArrayObject* thisArray;
    ArrayObject* compArray;
    const char* thisStr;
    const char* compStr;
    int thisOffset, compOffset, thisCount, compCount;

    thisCount =
        dvmGetFieldInt((Object*) thisStrObj, STRING_FIELDOFF_COUNT);
    compCount =
        dvmGetFieldInt((Object*) compStrObj, STRING_FIELDOFF_COUNT);
    thisOffset =
        dvmGetFieldInt((Object*) thisStrObj, STRING_FIELDOFF_OFFSET);
    compOffset =
        dvmGetFieldInt((Object*) compStrObj, STRING_FIELDOFF_OFFSET);
    thisArray = (ArrayObject*)
        dvmGetFieldObject((Object*) thisStrObj, STRING_FIELDOFF_VALUE);
    compArray = (ArrayObject*)
        dvmGetFieldObject((Object*) compStrObj, STRING_FIELDOFF_VALUE);

    thisStr = dvmCreateCstrFromString(thisStrObj);
    compStr = dvmCreateCstrFromString(compStrObj);

    ALOGE("%s expected %d got %d", compareType, expectResult, newResult);
    ALOGE(" this (o=%d l=%d) '%s'", thisOffset, thisCount, thisStr);
    ALOGE(" comp (o=%d l=%d) '%s'", compOffset, compCount, compStr);
    dvmPrintHexDumpEx(ANDROID_LOG_INFO, LOG_TAG,
        ((const u2*) thisArray->contents) + thisOffset, thisCount*2,
        kHexDumpLocal);
    dvmPrintHexDumpEx(ANDROID_LOG_INFO, LOG_TAG,
        ((const u2*) compArray->contents) + compOffset, compCount*2,
        kHexDumpLocal);
    dvmAbort();
}
Пример #13
0
/*
 * JNI reflection support: convert reflection object to Method ptr.
 */
Method* dvmGetMethodFromReflectObj(Object* obj)
{
    ClassObject* clazz;
    int slot;

    if (obj->clazz == gDvm.classJavaLangReflectConstructor) {
        clazz = (ClassObject*)dvmGetFieldObject(obj,
                                gDvm.offJavaLangReflectConstructor_declClass);
        slot = dvmGetFieldInt(obj, gDvm.offJavaLangReflectConstructor_slot);
    } else if (obj->clazz == gDvm.classJavaLangReflectMethod) {
        clazz = (ClassObject*)dvmGetFieldObject(obj,
                                gDvm.offJavaLangReflectMethod_declClass);
        slot = dvmGetFieldInt(obj, gDvm.offJavaLangReflectMethod_slot);
    } else {
        assert(false);
        return NULL;
    }

    /* must initialize the class before returning a method ID */
    if (!dvmInitClass(clazz))
        return NULL;

    return dvmSlotToMethod(clazz, slot);
}
Пример #14
0
/*
 * Scans instance fields.
 */
static void scanInstanceFields(const Object *obj, GcMarkContext *ctx)
{
    assert(obj != NULL);
    assert(obj->clazz != NULL);
    assert(ctx != NULL);

    if (obj->clazz->refOffsets != CLASS_WALK_SUPER) {
        unsigned int refOffsets = obj->clazz->refOffsets;
        while (refOffsets != 0) {
            const int rshift = CLZ(refOffsets);
            refOffsets &= ~(CLASS_HIGH_BIT >> rshift);
            markObject(dvmGetFieldObject((Object*)obj,
                                          CLASS_OFFSET_FROM_CLZ(rshift)), ctx);
        }
    } else {
Пример #15
0
/* Sets the taint on the return value
 * - rtaint points to an address in the args array
 * - descriptor is the return type
 * - for return objects, only arrays and java.lang.String supported
 *   (will taint object reference returned otherwise)
 */
void setReturnTaint(u4 tag, u4* rtaint, JValue* pResult,
	const char* descriptor)
{
    Object* obj = NULL;
    ArrayObject* arrObj = NULL;

    switch (descriptor[0]) {
	case 'V':
	    /* void, do nothing */
	    break;
	case 'Z':
	case 'B':
	case 'C':
	case 'S':
	case 'I':
	case 'J':
	case 'F':
	case 'D':
	    /* Easy case */
	    *rtaint |= tag;
	    break;
	case '[':
	    /* Best we can do is taint the array, however
	     * this is not right for "[[" or "[L" */
	    arrObj = (ArrayObject*) pResult->l;
	    if (arrObj != NULL) {
		arrObj->taint.tag |= tag;
	    } /* else, method returning null pointer */
	    break;
	case 'L':
	    obj = (Object*) pResult->l;

	    if (obj != NULL) {
		if (strcmp(descriptor, "Ljava/lang/String;") == 0) {
		    arrObj = (ArrayObject*)dvmGetFieldObject(obj,
			    gDvm.offJavaLangString_value);
		    if (arrObj != NULL) {
			arrObj->taint.tag |= tag;
		    } /* else, empty string?, don't worry about it */
		} else {
		    /* TODO: What about classes derived from String? */
		    /* Best we can do is to taint the object ref */
		    *rtaint |= tag;
		}
	    }
	    break;
    }
}
Пример #16
0
/*
 * JNI reflection support: convert reflection object to Field ptr.
 */
Field* dvmGetFieldFromReflectObj(Object* obj)
{
    ClassObject* clazz;
    int slot;

    assert(obj->clazz == gDvm.classJavaLangReflectField);
    clazz = (ClassObject*)dvmGetFieldObject(obj,
                                gDvm.offJavaLangReflectField_declClass);
    slot = dvmGetFieldInt(obj, gDvm.offJavaLangReflectField_slot);

    /* must initialize the class before returning a field ID */
    if (!dvmInitClass(clazz))
        return NULL;

    return dvmSlotToField(clazz, slot);
}
Пример #17
0
	void ObjTracer::extract_location(const Object * const obj)
    	{
		fprintf( traceFile_, "instUid %u #%d:  %p\n", 
			opcodeTracer.get_instUid(), LOCAT_OBJ, obj);

		//longitude
		InstField *pF = &obj->clazz->ifields[6];
		union myunion m;
		m.d = dvmGetFieldDouble( obj, pF->byteOffset );
		this->record_normal('D', m.u);

		//latitude
		pF = &obj->clazz->ifields[9];
		m.d = dvmGetFieldDouble( obj, pF->byteOffset );
		this->record_normal('D', m.u);

		//lng2 in cache
		pF = &obj->clazz->ifields[7];
		m.d = dvmGetFieldDouble( obj, pF->byteOffset );
		this->record_normal('D', m.u);

		//lat2 in cache
		pF = &obj->clazz->ifields[11];
		m.d = dvmGetFieldDouble( obj, pF->byteOffset );
		this->record_normal('D', m.u);

		//lng1 in cache
		pF = &obj->clazz->ifields[8];
		m.d = dvmGetFieldDouble( obj, pF->byteOffset );
		this->record_normal('D', m.u);

		//lat1 in cache
		pF = &obj->clazz->ifields[10];
		m.d = dvmGetFieldDouble( obj, pF->byteOffset );
		this->record_normal('D', m.u);

		//provider: GPS or Network
		pF = &obj->clazz->ifields[1];
		Object *o = dvmGetFieldObject ( obj, pF->byteOffset );
		this->extract_str( o );

		//fprintf(f, "\n");
		fflush(traceFile_);
    }
Пример #18
0
	void ObjTracer::extract_compName( const Object * const obj )
	{
		InstField* pF;
		Object* o;

		fprintf( traceFile_, "instUid %u #%d:  %p\n", 
			opcodeTracer.get_instUid(), CMPNM_OBJ, obj );

		for ( int i = 0; i < 2; i++ )
		{
			pF = &obj->clazz->ifields[i];
			o = dvmGetFieldObject( obj, pF->byteOffset );
			this->extract_str(o);
		}

		//fprintf(f, "\n");
		fflush(traceFile_);

	}
Пример #19
0
/*
 * Determine the index of the first character matching "ch".  The string
 * to search is described by "chars", "offset", and "count".
 *
 * The character must be <= 0xffff. Supplementary characters are handled in
 * Java.
 *
 * The "start" parameter must be clamped to [0..count].
 *
 * Returns -1 if no match is found.
 */
static inline int indexOfCommon(Object* strObj, int ch, int start)
{
    //if ((ch & 0xffff) != ch)        /* 32-bit code point */
    //    return -1;

    /* pull out the basic elements */
    ArrayObject* charArray =
        (ArrayObject*) dvmGetFieldObject(strObj, STRING_FIELDOFF_VALUE);
    const u2* chars = (const u2*)(void*)charArray->contents;
    int offset = dvmGetFieldInt(strObj, STRING_FIELDOFF_OFFSET);
    int count = dvmGetFieldInt(strObj, STRING_FIELDOFF_COUNT);
    //ALOGI("String.indexOf(0x%08x, 0x%04x, %d) off=%d count=%d",
    //    (u4) strObj, ch, start, offset, count);

    /* factor out the offset */
    chars += offset;

    if (start < 0)
        start = 0;
    else if (start > count)
        start = count;

#if 0
    /* 16-bit loop, simple */
    while (start < count) {
        if (chars[start] == ch)
            return start;
        start++;
    }
#else
    /* 16-bit loop, slightly better on ARM */
    const u2* ptr = chars + start;
    const u2* endPtr = chars + count;
    while (ptr < endPtr) {
        if (*ptr++ == ch)
            return (ptr-1) - chars;
    }
#endif

    return -1;
}
Пример #20
0
	void ObjTracer::extract_procRecord( const Object* const obj )
	{
		fprintf( traceFile_, "instUid %u #%d:  %p\n", 
			opcodeTracer.get_instUid(), PROREC_OBJ, obj );

//processName
		InstField* pF = &obj->clazz->ifields[20];
		Object* o = dvmGetFieldObject( obj, pF->byteOffset );
		this->extract_str(o);
//pid
		pF = &obj->clazz->ifields[58];
		u4 i = dvmGetFieldInt( obj, pF->byteOffset );
		this->record_normal('I', &i );
//uid
		pF = &obj->clazz->ifields[77];
		i = dvmGetFieldInt( obj, pF->byteOffset );
		this->record_normal('I', &i );

		//fprintf(f, "\n");
		fflush(traceFile_);
	}
Пример #21
0
	void ObjTracer::extract_intent( Object * obj )
	{	
		InstField *pF1, *pF2;
		Object *o1, *o2;
		
		fprintf( traceFile_, "instUid %u #%d:  %p\n", 
			opcodeTracer.get_instUid(), INTENT_OBJ, obj );

			
		// mAction in Intent
		pF1 = &obj->clazz->ifields[0];
		o1 = dvmGetFieldObject ( obj, pF1->byteOffset );
		this->extract_str(o1);
		
		//mComponent in Intent
		pF1 = &obj->clazz->ifields[3];
		o1 = dvmGetFieldObject ( obj, pF1->byteOffset );
		{
			if (check_obj(o1))
			{
				//mClass in mComponent
				pF2 = &o1->clazz->ifields[0];
				o2 = dvmGetFieldObject ( o1, pF2->byteOffset );
				this->extract_str(o2);
				//mPackage in mComponent
				pF2 =  &o1->clazz->ifields[1];
				o2 = dvmGetFieldObject ( o1, pF2->byteOffset );
				this->extract_str(o2);
			}
			else
				fprintf(traceFile_, "\n\n");
			
		}

		//mType in Intent
		pF1 = &obj->clazz->ifields[6];
		o1 = dvmGetFieldObject ( obj, pF1->byteOffset );
		this->extract_str(o1);
		
		//mPackage in Intent
		pF1 = &obj->clazz->ifields[7];
		o1 = dvmGetFieldObject( obj, pF1->byteOffset );
		this->extract_str(o1);

		//fprintf(f, "\n");
		fflush(traceFile_);				
	}
Пример #22
0
	void ObjTracer::extract_activRecord( const Object * const obj )
	{
		InstField *pF, *pF1;
		Object *o, *o1;
		
		fprintf( traceFile_, "instUid %u #%d:  %p\n", 
			opcodeTracer.get_instUid(), ACTREC_OBJ, obj );

		//baseDir
		pF = &obj->clazz->ifields[2];
		o = dvmGetFieldObject ( obj, pF->byteOffset );
		this->extract_str( o );

		//taskAffinity
		pF = &obj->clazz->ifields[6];
		o = dvmGetFieldObject ( obj, pF->byteOffset );
		this->extract_str( o );
		
		//dataDir
		pF = &obj->clazz->ifields[10];
		o = dvmGetFieldObject ( obj, pF->byteOffset );
		this->extract_str( o );
		
		//stringName
		pF = &obj->clazz->ifields[11];
		o = dvmGetFieldObject ( obj, pF->byteOffset );
		this->extract_str( o );
		
		//shortComponentName
		pF = &obj->clazz->ifields[14];
		o = dvmGetFieldObject ( obj, pF->byteOffset );
		this->extract_str( o );
		
		//resDir
		pF = &obj->clazz->ifields[21];
		o = dvmGetFieldObject ( obj, pF->byteOffset );
		this->extract_str( o );
		
		//realActivity
		pF = &obj->clazz->ifields[22];
		o = dvmGetFieldObject ( obj, pF->byteOffset );
		if ( check_obj(o) )
		{
			//mClass in mComponent
			pF1 = &o->clazz->ifields[0];
			o1 = dvmGetFieldObject ( o, pF1->byteOffset );
			this->extract_str(o1);
			//mPackage in mComponent
			pF1 =  &o->clazz->ifields[1];
			o1 = dvmGetFieldObject ( o, pF1->byteOffset );
			this->extract_str(o1);
		}
		else
			fprintf(traceFile_, "\n\n");
		
		//processName
		pF = &obj->clazz->ifields[23];
		o = dvmGetFieldObject ( obj, pF->byteOffset );
		this->extract_str( o );
		
		//intent
		pF = &obj->clazz->ifields[25];
		o = dvmGetFieldObject ( obj, pF->byteOffset );
		if (check_obj(o))
		{
			pF1 = &o->clazz->ifields[0];
			o1 = dvmGetFieldObject( o, pF1->byteOffset );
			this->extract_str(o1);
		}
		else
			fprintf(traceFile_, "\n");
		
		//packageName	
		pF = &obj->clazz->ifields[28];
		o = dvmGetFieldObject ( obj, pF->byteOffset );
		this->extract_str( o );	
		
		//launchedFromUid
		pF = &obj->clazz->ifields[61];
		u4 i = dvmGetFieldInt( obj, pF->byteOffset );
		this->record_normal('I', &i );

		//fprintf(f, "\n");
		fflush(traceFile_);
	}
Пример #23
0
/*
 * "buf" contains a full JDWP packet, possibly with multiple chunks.  We
 * need to process each, accumulate the replies, and ship the whole thing
 * back.
 *
 * Returns "true" if we have a reply.  The reply buffer is newly allocated,
 * and includes the chunk type/length, followed by the data.
 *
 * TODO: we currently assume that the request and reply include a single
 * chunk.  If this becomes inconvenient we will need to adapt.
 */
bool dvmDdmHandlePacket(const u1* buf, int dataLen, u1** pReplyBuf,
    int* pReplyLen)
{
    Thread* self = dvmThreadSelf();
    const int kChunkHdrLen = 8;
    ArrayObject* dataArray = NULL;
    bool result = false;

    assert(dataLen >= 0);

    /*
     * Prep DdmServer.  We could throw this in gDvm.
     */
    ClassObject* ddmServerClass;
    Method* dispatch;

    ddmServerClass =
        dvmFindClass("Lorg/apache/harmony/dalvik/ddmc/DdmServer;", NULL);
    if (ddmServerClass == NULL) {
        LOGW("Unable to find org.apache.harmony.dalvik.ddmc.DdmServer\n");
        goto bail;
    }
    dispatch = dvmFindDirectMethodByDescriptor(ddmServerClass, "dispatch",
                    "(I[BII)Lorg/apache/harmony/dalvik/ddmc/Chunk;");
    if (dispatch == NULL) {
        LOGW("Unable to find DdmServer.dispatch\n");
        goto bail;
    }

    /*
     * Prep Chunk.
     */
    int chunkTypeOff, chunkDataOff, chunkOffsetOff, chunkLengthOff;
    ClassObject* chunkClass;
    chunkClass = dvmFindClass("Lorg/apache/harmony/dalvik/ddmc/Chunk;", NULL);
    if (chunkClass == NULL) {
        LOGW("Unable to find org.apache.harmony.dalvik.ddmc.Chunk\n");
        goto bail;
    }
    chunkTypeOff = dvmFindFieldOffset(chunkClass, "type", "I");
    chunkDataOff = dvmFindFieldOffset(chunkClass, "data", "[B");
    chunkOffsetOff = dvmFindFieldOffset(chunkClass, "offset", "I");
    chunkLengthOff = dvmFindFieldOffset(chunkClass, "length", "I");
    if (chunkTypeOff < 0 || chunkDataOff < 0 ||
        chunkOffsetOff < 0 || chunkLengthOff < 0)
    {
        LOGW("Unable to find all chunk fields\n");
        goto bail;
    }

    /*
     * The chunk handlers are written in the Java programming language, so
     * we need to convert the buffer to a byte array.
     */
    dataArray = dvmAllocPrimitiveArray('B', dataLen, ALLOC_DEFAULT);
    if (dataArray == NULL) {
        LOGW("array alloc failed (%d)\n", dataLen);
        dvmClearException(self);
        goto bail;
    }
    memcpy(dataArray->contents, buf, dataLen);

    /*
     * Run through and find all chunks.  [Currently just find the first.]
     */
    unsigned int offset, length, type;
    type = get4BE((u1*)dataArray->contents + 0);
    length = get4BE((u1*)dataArray->contents + 4);
    offset = kChunkHdrLen;
    if (offset+length > (unsigned int) dataLen) {
        LOGW("WARNING: bad chunk found (len=%u pktLen=%d)\n", length, dataLen);
        goto bail;
    }

    /*
     * Call the handler.
     */
    JValue callRes;
    dvmCallMethod(self, dispatch, NULL, &callRes, type, dataArray, offset,
        length);
    if (dvmCheckException(self)) {
        LOGI("Exception thrown by dispatcher for 0x%08x\n", type);
        dvmLogExceptionStackTrace();
        dvmClearException(self);
        goto bail;
    }

    Object* chunk;
    ArrayObject* replyData;
    chunk = (Object*) callRes.l;
    if (chunk == NULL)
        goto bail;

    /*
     * Pull the pieces out of the chunk.  We copy the results into a
     * newly-allocated buffer that the caller can free.  We don't want to
     * continue using the Chunk object because nothing has a reference to it.
     * (If we do an alloc in here, we need to dvmAddTrackedAlloc it.)
     *
     * We could avoid this by returning type/data/offset/length and having
     * the caller be aware of the object lifetime issues, but that
     * integrates the JDWP code more tightly into the VM, and doesn't work
     * if we have responses for multiple chunks.
     *
     * So we're pretty much stuck with copying data around multiple times.
     */
    type = dvmGetFieldInt(chunk, chunkTypeOff);
    replyData = (ArrayObject*) dvmGetFieldObject(chunk, chunkDataOff);
    offset = dvmGetFieldInt(chunk, chunkOffsetOff);
    length = dvmGetFieldInt(chunk, chunkLengthOff);

    LOGV("DDM reply: type=0x%08x data=%p offset=%d length=%d\n",
        type, replyData, offset, length);

    if (length == 0 || replyData == NULL)
        goto bail;
    if (offset + length > replyData->length) {
        LOGW("WARNING: chunk off=%d len=%d exceeds reply array len %d\n",
            offset, length, replyData->length);
        goto bail;
    }

    u1* reply;
    reply = (u1*) malloc(length + kChunkHdrLen);
    if (reply == NULL) {
        LOGW("malloc %d failed\n", length+kChunkHdrLen);
        goto bail;
    }
    set4BE(reply + 0, type);
    set4BE(reply + 4, length);
    memcpy(reply+kChunkHdrLen, (const u1*)replyData->contents + offset, length);

    *pReplyBuf = reply;
    *pReplyLen = length + kChunkHdrLen;
    result = true;

    LOGV("dvmHandleDdm returning type=%.4s buf=%p len=%d\n",
        (char*) reply, reply, length);

bail:
    dvmReleaseTrackedAlloc((Object*) dataArray, NULL);
    return result;
}
Пример #24
0
/*
 * This is the common message body for proxy methods.
 *
 * The method we're calling looks like:
 *   public Object invoke(Object proxy, Method method, Object[] args)
 *
 * This means we have to create a Method object, box our arguments into
 * a new Object[] array, make the call, and unbox the return value if
 * necessary.
 */
static void proxyInvoker(const u4* args, JValue* pResult,
    const Method* method, Thread* self)
{
    Object* thisObj = (Object*) args[0];
    Object* methodObj = NULL;
    ArrayObject* argArray = NULL;
    Object* handler;
    Method* invoke;
    ClassObject* returnType;
    int hOffset;
    JValue invokeResult;

    /*
     * Retrieve handler object for this proxy instance.
     */
    hOffset = dvmFindFieldOffset(thisObj->clazz, "h",
                    "Ljava/lang/reflect/InvocationHandler;");
    if (hOffset < 0) {
        LOGE("Unable to find 'h' in Proxy object\n");
        dvmAbort();
    }
    handler = dvmGetFieldObject(thisObj, hOffset);

    /*
     * Find the invoke() method, looking in "this"s class.  (Because we
     * start here we don't have to convert it to a vtable index and then
     * index into this' vtable.)
     */
    invoke = dvmFindVirtualMethodHierByDescriptor(handler->clazz, "invoke",
            "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;");
    if (invoke == NULL) {
        LOGE("Unable to find invoke()\n");
        dvmAbort();
    }

    LOGV("invoke: %s.%s, this=%p, handler=%s\n",
        method->clazz->descriptor, method->name,
        thisObj, handler->clazz->descriptor);

    /*
     * Create a java.lang.reflect.Method object for this method.
     *
     * We don't want to use "method", because that's the concrete
     * implementation in the proxy class.  We want the abstract Method
     * from the declaring interface.  We have a pointer to it tucked
     * away in the "insns" field.
     *
     * TODO: this could be cached for performance.
     */
    methodObj = dvmCreateReflectMethodObject((Method*) method->insns);
    if (methodObj == NULL) {
        assert(dvmCheckException(self));
        goto bail;
    }

    /*
     * Determine the return type from the signature.
     *
     * TODO: this could be cached for performance.
     */
    returnType = dvmGetBoxedReturnType(method);
    if (returnType == NULL) {
        char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
        LOGE("Could not determine return type for '%s'\n", desc);
        free(desc);
        assert(dvmCheckException(self));
        goto bail;
    }
    LOGV("  return type will be %s\n", returnType->descriptor);

    /*
     * Convert "args" array into Object[] array, using the method
     * signature to determine types.  If the method takes no arguments,
     * we must pass null.
     */
    argArray = boxMethodArgs(method, args+1);
    if (dvmCheckException(self))
        goto bail;

    /*
     * Call h.invoke(proxy, method, args).
     *
     * We don't need to repackage exceptions, so if one has been thrown
     * just jump to the end.
     */
    dvmCallMethod(self, invoke, handler, &invokeResult,
        thisObj, methodObj, argArray);
    if (dvmCheckException(self))
        goto bail;

    /*
     * Unbox the return value.  If it's the wrong type, throw a
     * ClassCastException.  If it's a null pointer and we need a
     * primitive type, throw a NullPointerException.
     */
    if (returnType->primitiveType == PRIM_VOID) {
        LOGVV("+++ ignoring return to void\n");
    } else if (invokeResult.l == NULL) {
        if (dvmIsPrimitiveClass(returnType)) {
            dvmThrowException("Ljava/lang/NullPointerException;",
                "null result when primitive expected");
            goto bail;
        }
        pResult->l = NULL;
    } else {
        if (!dvmUnwrapPrimitive(invokeResult.l, returnType, pResult)) {
            dvmThrowExceptionWithClassMessage("Ljava/lang/ClassCastException;",
                ((Object*)invokeResult.l)->clazz->descriptor);
            goto bail;
        }
    }

bail:
    dvmReleaseTrackedAlloc(methodObj, self);
    dvmReleaseTrackedAlloc((Object*)argArray, self);
}
Пример #25
0
/*
 * public int compareTo(String s)
 */
bool javaLangString_compareTo(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
    JValue* pResult)
{
    /*
     * Null reference check on "this".  Normally this is performed during
     * the setup of the virtual method call.  We need to do it before
     * anything else.  While we're at it, check out the other string,
     * which must also be non-null.
     */
    if ((Object*) arg0 == NULL || (Object*) arg1 == NULL) {
        dvmThrowNullPointerException(NULL);
        return false;
    }

    /* quick test for comparison with itself */
    if (arg0 == arg1) {
        pResult->i = 0;
        return true;
    }

    /*
     * This would be simpler and faster if we promoted StringObject to
     * a full representation, lining up the C structure fields with the
     * actual object fields.
     */
    int thisCount, thisOffset, compCount, compOffset;
    ArrayObject* thisArray;
    ArrayObject* compArray;
    const u2* thisChars;
    const u2* compChars;
    int minCount, countDiff;

    thisCount = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_COUNT);
    compCount = dvmGetFieldInt((Object*) arg1, STRING_FIELDOFF_COUNT);
    countDiff = thisCount - compCount;
    minCount = (countDiff < 0) ? thisCount : compCount;
    thisOffset = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_OFFSET);
    compOffset = dvmGetFieldInt((Object*) arg1, STRING_FIELDOFF_OFFSET);
    thisArray = (ArrayObject*)
        dvmGetFieldObject((Object*) arg0, STRING_FIELDOFF_VALUE);
    compArray = (ArrayObject*)
        dvmGetFieldObject((Object*) arg1, STRING_FIELDOFF_VALUE);
    thisChars = ((const u2*)(void*)thisArray->contents) + thisOffset;
    compChars = ((const u2*)(void*)compArray->contents) + compOffset;

#ifdef HAVE__MEMCMP16
    /*
     * Use assembly version, which returns the difference between the
     * characters.  The annoying part here is that 0x00e9 - 0xffff != 0x00ea,
     * because the interpreter converts the characters to 32-bit integers
     * *without* sign extension before it subtracts them (which makes some
     * sense since "char" is unsigned).  So what we get is the result of
     * 0x000000e9 - 0x0000ffff, which is 0xffff00ea.
     */
    int otherRes = __memcmp16(thisChars, compChars, minCount);
# ifdef CHECK_MEMCMP16
    int i;
    for (i = 0; i < minCount; i++) {
        if (thisChars[i] != compChars[i]) {
            pResult->i = (s4) thisChars[i] - (s4) compChars[i];
            if (pResult->i != otherRes) {
                badMatch((StringObject*) arg0, (StringObject*) arg1,
                    pResult->i, otherRes, "compareTo");
            }
            return true;
        }
    }
# endif
    if (otherRes != 0) {
        pResult->i = otherRes;
        return true;
    }

#else
    /*
     * Straightforward implementation, examining 16 bits at a time.  Compare
     * the characters that overlap, and if they're all the same then return
     * the difference in lengths.
     */
    int i;
    for (i = 0; i < minCount; i++) {
        if (thisChars[i] != compChars[i]) {
            pResult->i = (s4) thisChars[i] - (s4) compChars[i];
            return true;
        }
    }
#endif

    pResult->i = countDiff;
    return true;
}
Пример #26
0
/* All objects for stronger reference levels have been
 * marked before this is called.
 */
void dvmHeapHandleReferences(Object *refListHead, enum RefType refType)
{
    Object *reference;
    GcMarkContext *markContext = &gDvm.gcHeap->markContext;
    const int offVmData = gDvm.offJavaLangRefReference_vmData;
    const int offReferent = gDvm.offJavaLangRefReference_referent;
    bool workRequired = false;

size_t numCleared = 0;
size_t numEnqueued = 0;
    reference = refListHead;
    while (reference != NULL) {
        Object *next;
        Object *referent;

        /* Pull the interesting fields out of the Reference object.
         */
        next = dvmGetFieldObject(reference, offVmData);
        referent = dvmGetFieldObject(reference, offReferent);

        //TODO: when handling REF_PHANTOM, unlink any references
        //      that fail this initial if().  We need to re-walk
        //      the list, and it would be nice to avoid the extra
        //      work.
        if (referent != NULL && !isMarked(ptr2chunk(referent), markContext)) {
            bool schedClear, schedEnqueue;

            /* This is the strongest reference that refers to referent.
             * Do the right thing.
             */
            switch (refType) {
            case REF_SOFT:
            case REF_WEAK:
                schedClear = clearReference(reference);
                schedEnqueue = enqueueReference(reference);
                break;
            case REF_PHANTOM:
                /* PhantomReferences are not cleared automatically.
                 * Until someone clears it (or the reference itself
                 * is collected), the referent must remain alive.
                 *
                 * It's necessary to fully mark the referent because
                 * it will still be present during the next GC, and
                 * all objects that it points to must be valid.
                 * (The referent will be marked outside of this loop,
                 * after handing all references of this strength, in
                 * case multiple references point to the same object.)
                 */
                schedClear = false;

                /* A PhantomReference is only useful with a
                 * queue, but since it's possible to create one
                 * without a queue, we need to check.
                 */
                schedEnqueue = enqueueReference(reference);
                break;
            default:
                assert(!"Bad reference type");
                schedClear = false;
                schedEnqueue = false;
                break;
            }
numCleared += schedClear ? 1 : 0;
numEnqueued += schedEnqueue ? 1 : 0;

            if (schedClear || schedEnqueue) {
                uintptr_t workBits;

                /* Stuff the clear/enqueue bits in the bottom of
                 * the pointer.  Assumes that objects are 8-byte
                 * aligned.
                 *
                 * Note that we are adding the *Reference* (which
                 * is by definition already marked at this point) to
                 * this list; we're not adding the referent (which
                 * has already been cleared).
                 */
                assert(((intptr_t)reference & 3) == 0);
                assert(((WORKER_CLEAR | WORKER_ENQUEUE) & ~3) == 0);
                workBits = (schedClear ? WORKER_CLEAR : 0) |
                           (schedEnqueue ? WORKER_ENQUEUE : 0);
                if (!dvmHeapAddRefToLargeTable(
                        &gDvm.gcHeap->referenceOperations,
                        (Object *)((uintptr_t)reference | workBits)))
                {
                    LOGE_HEAP("dvmMalloc(): no room for any more "
                            "reference operations\n");
                    dvmAbort();
                }
                workRequired = true;
            }

            if (refType != REF_PHANTOM) {
                /* Let later GCs know not to reschedule this reference.
                 */
                dvmSetFieldObject(reference, offVmData,
                        SCHEDULED_REFERENCE_MAGIC);
            } // else this is handled later for REF_PHANTOM

        } // else there was a stronger reference to the referent.

        reference = next;
    }
#define refType2str(r) \
    ((r) == REF_SOFT ? "soft" : ( \
     (r) == REF_WEAK ? "weak" : ( \
     (r) == REF_PHANTOM ? "phantom" : "UNKNOWN" )))
LOGD_HEAP("dvmHeapHandleReferences(): cleared %zd, enqueued %zd %s references\n", numCleared, numEnqueued, refType2str(refType));

    /* Walk though the reference list again, and mark any non-clear/marked
     * referents.  Only PhantomReferences can have non-clear referents
     * at this point.
     */
    if (refType == REF_PHANTOM) {
        bool scanRequired = false;

        HPROF_SET_GC_SCAN_STATE(HPROF_ROOT_REFERENCE_CLEANUP, 0);
        reference = refListHead;
        while (reference != NULL) {
            Object *next;
            Object *referent;

            /* Pull the interesting fields out of the Reference object.
             */
            next = dvmGetFieldObject(reference, offVmData);
            referent = dvmGetFieldObject(reference, offReferent);

            if (referent != NULL && !isMarked(ptr2chunk(referent), markContext)) {
                markObjectNonNull(referent, markContext);
                scanRequired = true;

                /* Let later GCs know not to reschedule this reference.
                 */
                dvmSetFieldObject(reference, offVmData,
                        SCHEDULED_REFERENCE_MAGIC);
            }

            reference = next;
        }
        HPROF_CLEAR_GC_SCAN_STATE();

        if (scanRequired) {
            processMarkStack(markContext);
        }
    }

    if (workRequired) {
        dvmSignalHeapWorker(false);
    }
}
Пример #27
0
/*
 * public boolean equals(Object anObject)
 */
bool javaLangString_equals(u4 arg0, u4 arg1, u4 arg2, u4 arg3,
    JValue* pResult)
{
    /*
     * Null reference check on "this".
     */
    if ((Object*) arg0 == NULL) {
        dvmThrowNullPointerException(NULL);
        return false;
    }

    /* quick test for comparison with itself */
    if (arg0 == arg1) {
        pResult->i = true;
        return true;
    }

    /*
     * See if the other object is also a String.
     *
     * str.equals(null) is expected to return false, presumably based on
     * the results of the instanceof test.
     */
    if (arg1 == 0 || ((Object*) arg0)->clazz != ((Object*) arg1)->clazz) {
        pResult->i = false;
        return true;
    }

    /*
     * This would be simpler and faster if we promoted StringObject to
     * a full representation, lining up the C structure fields with the
     * actual object fields.
     */
    int thisCount, thisOffset, compCount, compOffset;
    ArrayObject* thisArray;
    ArrayObject* compArray;
    const u2* thisChars;
    const u2* compChars;

    /* quick length check */
    thisCount = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_COUNT);
    compCount = dvmGetFieldInt((Object*) arg1, STRING_FIELDOFF_COUNT);
    if (thisCount != compCount) {
        pResult->i = false;
        return true;
    }

    /*
     * You may, at this point, be tempted to pull out the hashCode fields
     * and compare them.  If both fields have been initialized, and they
     * are not equal, we can return false immediately.
     *
     * However, the hashCode field is often not set.  If it is set,
     * there's an excellent chance that the String is being used as a key
     * in a hashed data structure (e.g. HashMap).  That data structure has
     * already made the comparison and determined that the hashes are equal,
     * making a check here redundant.
     *
     * It's not clear that checking the hashes will be a win in "typical"
     * use cases.  We err on the side of simplicity and ignore them.
     */

    thisOffset = dvmGetFieldInt((Object*) arg0, STRING_FIELDOFF_OFFSET);
    compOffset = dvmGetFieldInt((Object*) arg1, STRING_FIELDOFF_OFFSET);
    thisArray = (ArrayObject*)
        dvmGetFieldObject((Object*) arg0, STRING_FIELDOFF_VALUE);
    compArray = (ArrayObject*)
        dvmGetFieldObject((Object*) arg1, STRING_FIELDOFF_VALUE);
    thisChars = ((const u2*)(void*)thisArray->contents) + thisOffset;
    compChars = ((const u2*)(void*)compArray->contents) + compOffset;

#ifdef HAVE__MEMCMP16
    pResult->i = (__memcmp16(thisChars, compChars, thisCount) == 0);
# ifdef CHECK_MEMCMP16
    int otherRes = (memcmp(thisChars, compChars, thisCount * 2) == 0);
    if (pResult->i != otherRes) {
        badMatch((StringObject*) arg0, (StringObject*) arg1,
            otherRes, pResult->i, "equals-1");
    }
# endif
#else
    /*
     * Straightforward implementation, examining 16 bits at a time.  The
     * direction of the loop doesn't matter, and starting at the end may
     * give us an advantage when comparing certain types of strings (e.g.
     * class names).
     *
     * We want to go forward for benchmarks against __memcmp16 so we get a
     * meaningful comparison when the strings don't match (could also test
     * with palindromes).
     */
    int i;
    //for (i = 0; i < thisCount; i++)
    for (i = thisCount-1; i >= 0; --i)
    {
        if (thisChars[i] != compChars[i]) {
            pResult->i = false;
            return true;
        }
    }
    pResult->i = true;
#endif

    return true;
}
Пример #28
0
/* Mark all objects that obj refers to.
 *
 * Called on every object in markList.
 */
static void scanObject(const Object *obj, GcMarkContext *ctx)
{
    ClassObject *clazz;

    assert(dvmIsValidObject(obj));
    LOGV_SCAN("0x%08x %s\n", (uint)obj, obj->clazz->name);

#if WITH_HPROF
    if (gDvm.gcHeap->hprofContext != NULL) {
        hprofDumpHeapObject(gDvm.gcHeap->hprofContext, obj);
    }
#endif

    /* Get and mark the class object for this particular instance.
     */
    clazz = obj->clazz;
    if (clazz == NULL) {
        /* This can happen if we catch an object between
         * dvmMalloc() and DVM_OBJECT_INIT().  The object
         * won't contain any references yet, so we can
         * just skip it.
         */
        return;
    } else if (clazz == gDvm.unlinkedJavaLangClass) {
        /* This class hasn't been linked yet.  We're guaranteed
         * that the object doesn't contain any references that
         * aren't already tracked, so we can skip scanning it.
         *
         * NOTE: unlinkedJavaLangClass is not on the heap, so
         * it's very important that we don't try marking it.
         */
        return;
    }
#if WITH_OBJECT_HEADERS
    gMarkParent = obj;
    if (ptr2chunk(obj)->scanGeneration == gGeneration) {
        LOGE("object 0x%08x was already scanned this generation\n",
                (uintptr_t)obj);
        dvmAbort();
    }
    ptr2chunk(obj)->oldScanGeneration = ptr2chunk(obj)->scanGeneration;
    ptr2chunk(obj)->scanGeneration = gGeneration;
    ptr2chunk(obj)->scanCount++;
#endif

    assert(dvmIsValidObject((Object *)clazz));
    markObjectNonNull((Object *)clazz, ctx);

    /* Mark any references in this object.
     */
    if (IS_CLASS_FLAG_SET(clazz, CLASS_ISARRAY)) {
        /* It's an array object.
         */
        if (IS_CLASS_FLAG_SET(clazz, CLASS_ISOBJECTARRAY)) {
            /* It's an array of object references.
             */
            scanObjectArray((ArrayObject *)obj, ctx);
        }
        // else there's nothing else to scan
    } else {
        /* It's a DataObject-compatible object.
         */
        scanInstanceFields((DataObject *)obj, clazz, ctx);

        if (IS_CLASS_FLAG_SET(clazz, CLASS_ISREFERENCE)) {
            GcHeap *gcHeap = gDvm.gcHeap;
            Object *referent;

            /* It's a subclass of java/lang/ref/Reference.
             * The fields in this class have been arranged
             * such that scanInstanceFields() did not actually
             * mark the "referent" field;  we need to handle
             * it specially.
             *
             * If the referent already has a strong mark (isMarked(referent)),
             * we don't care about its reference status.
             */
            referent = dvmGetFieldObject(obj,
                    gDvm.offJavaLangRefReference_referent);
            if (referent != NULL &&
                    !isMarked(ptr2chunk(referent), &gcHeap->markContext))
            {
                u4 refFlags;

                if (gcHeap->markAllReferents) {
                    LOG_REF("Hard-marking a reference\n");

                    /* Don't bother with normal reference-following
                     * behavior, just mark the referent.  This should
                     * only be used when following objects that just
                     * became scheduled for finalization.
                     */
                    markObjectNonNull(referent, ctx);
                    goto skip_reference;
                }

                /* See if this reference was handled by a previous GC.
                 */
                if (dvmGetFieldObject(obj,
                            gDvm.offJavaLangRefReference_vmData) ==
                        SCHEDULED_REFERENCE_MAGIC)
                {
                    LOG_REF("Skipping scheduled reference\n");

                    /* Don't reschedule it, but make sure that its
                     * referent doesn't get collected (in case it's
                     * a PhantomReference and wasn't cleared automatically).
                     */
                    //TODO: Mark these after handling all new refs of
                    //      this strength, in case the new refs refer
                    //      to the same referent.  Not a very common
                    //      case, though.
                    markObjectNonNull(referent, ctx);
                    goto skip_reference;
                }

                /* Find out what kind of reference is pointing
                 * to referent.
                 */
                refFlags = GET_CLASS_FLAG_GROUP(clazz,
                    CLASS_ISREFERENCE |
                    CLASS_ISWEAKREFERENCE |
                    CLASS_ISPHANTOMREFERENCE);

            /* We use the vmData field of Reference objects
             * as a next pointer in a singly-linked list.
             * That way, we don't need to allocate any memory
             * while we're doing a GC.
             */
#define ADD_REF_TO_LIST(list, ref) \
            do { \
                Object *ARTL_ref_ = (/*de-const*/Object *)(ref); \
                dvmSetFieldObject(ARTL_ref_, \
                        gDvm.offJavaLangRefReference_vmData, list); \
                list = ARTL_ref_; \
            } while (false)

                /* At this stage, we just keep track of all of
                 * the live references that we've seen.  Later,
                 * we'll walk through each of these lists and
                 * deal with the referents.
                 */
                if (refFlags == CLASS_ISREFERENCE) {
                    /* It's a soft reference.  Depending on the state,
                     * we'll attempt to collect all of them, some of
                     * them, or none of them.
                     */
                    if (gcHeap->softReferenceCollectionState ==
                            SR_COLLECT_NONE)
                    {
                sr_collect_none:
                        markObjectNonNull(referent, ctx);
                    } else if (gcHeap->softReferenceCollectionState ==
                            SR_COLLECT_ALL)
                    {
                sr_collect_all:
                        ADD_REF_TO_LIST(gcHeap->softReferences, obj);
                    } else {
                        /* We'll only try to collect half of the
                         * referents.
                         */
                        if (gcHeap->softReferenceColor++ & 1) {
                            goto sr_collect_none;
                        }
                        goto sr_collect_all;
                    }
                } else {
                    /* It's a weak or phantom reference.
                     * Clearing CLASS_ISREFERENCE will reveal which.
                     */
                    refFlags &= ~CLASS_ISREFERENCE;
                    if (refFlags == CLASS_ISWEAKREFERENCE) {
                        ADD_REF_TO_LIST(gcHeap->weakReferences, obj);
                    } else if (refFlags == CLASS_ISPHANTOMREFERENCE) {
                        ADD_REF_TO_LIST(gcHeap->phantomReferences, obj);
                    } else {
                        assert(!"Unknown reference type");
                    }
                }
#undef ADD_REF_TO_LIST
            }
        }

    skip_reference:
        /* If this is a class object, mark various other things that
         * its internals point to.
         *
         * All class objects are instances of java.lang.Class,
         * including the java.lang.Class class object.
         */
        if (clazz == gDvm.classJavaLangClass) {
            scanClassObject((ClassObject *)obj, ctx);
        }
    }

#if WITH_OBJECT_HEADERS
    gMarkParent = NULL;
#endif
}