static void callMethod(Thread *self, Object *obj, Method *method)
{
    JValue unused;

    /* Keep track of the method we're about to call and
     * the current time so that other threads can detect
     * when this thread wedges and provide useful information.
     */
    gDvm.gcHeap->heapWorkerInterpStartTime = dvmGetRelativeTimeUsec();
    gDvm.gcHeap->heapWorkerInterpCpuStartTime = dvmGetThreadCpuTimeUsec();
    gDvm.gcHeap->heapWorkerCurrentMethod = method;
    gDvm.gcHeap->heapWorkerCurrentObject = obj;

    /* Call the method.
     *
     * Don't hold the lock when executing interpreted
     * code.  It may suspend, and the GC needs to grab
     * heapWorkerLock.
     */
    dvmUnlockMutex(&gDvm.heapWorkerLock);
    if (false) {
        /* Log entry/exit; this will likely flood the log enough to
         * cause "logcat" to drop entries.
         */
        char tmpTag[16];
        sprintf(tmpTag, "HW%d", self->systemTid);
        LOG(LOG_DEBUG, tmpTag, "Call %s\n", method->clazz->descriptor);
        dvmCallMethod(self, method, obj, &unused);
        LOG(LOG_DEBUG, tmpTag, " done\n");
    } else {
        dvmCallMethod(self, method, obj, &unused);
    }
    /*
     * Reacquire the heap worker lock in a suspend-friendly way.
     */
    lockMutex(&gDvm.heapWorkerLock);

    gDvm.gcHeap->heapWorkerCurrentObject = NULL;
    gDvm.gcHeap->heapWorkerCurrentMethod = NULL;
    gDvm.gcHeap->heapWorkerInterpStartTime = 0LL;

    /* Exceptions thrown during these calls interrupt
     * the method, but are otherwise ignored.
     */
    if (dvmCheckException(self)) {
#if DVM_SHOW_EXCEPTION >= 1
        LOGI("Uncaught exception thrown by finalizer (will be discarded):\n");
        dvmLogExceptionStackTrace();
#endif
        dvmClearException(self);
    }
}
/*
 * Print the stack trace of the current exception on stderr.  This is called
 * from the JNI ExceptionDescribe call.
 *
 * For consistency we just invoke the Throwable printStackTrace method,
 * which might be overridden in the exception object.
 *
 * Exceptions thrown during the course of printing the stack trace are
 * ignored.
 */
void dvmPrintExceptionStackTrace(void)
{
    Thread* self = dvmThreadSelf();
    Object* exception;
    Method* printMethod;

    exception = self->exception;
    if (exception == NULL)
        return;

    self->exception = NULL;
    printMethod = dvmFindVirtualMethodHierByDescriptor(exception->clazz,
                    "printStackTrace", "()V");
    if (printMethod != NULL) {
        JValue unused;
        dvmCallMethod(self, printMethod, exception, &unused);
    } else {
        LOGW("WARNING: could not find printStackTrace in %s\n",
            exception->clazz->descriptor);
    }

    if (self->exception != NULL) {
        LOGW("NOTE: exception thrown while printing stack trace: %s\n",
            self->exception->clazz->descriptor);
    }

    self->exception = exception;
}
Exemple #3
0
/*
 * 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;
}
Exemple #4
0
bool dvmGcStartupClasses()
{
    ClassObject *klass = dvmFindSystemClass("Ljava/lang/Daemons;");
    if (klass == NULL) {
        return false;
    }
    Method *method = dvmFindDirectMethodByDescriptor(klass, "start", "()V");
    if (method == NULL) {
        return false;
    }
    Thread *self = dvmThreadSelf();
    assert(self != NULL);
    JValue unusedResult;
    dvmCallMethod(self, method, NULL, &unusedResult);
    return true;
}
/*
 * 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);
}
Exemple #7
0
/*
 * Broadcast an event to all handlers.
 */
static void broadcast(int event)
{
    ClassObject* ddmServerClass;
    Method* bcast;

    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;
    }
    bcast = dvmFindDirectMethodByDescriptor(ddmServerClass, "broadcast", "(I)V");
    if (bcast == NULL) {
        LOGW("Unable to find DdmServer.broadcast\n");
        goto bail;
    }

    Thread* self = dvmThreadSelf();

    if (self->status != THREAD_RUNNING) {
        LOGE("ERROR: DDM broadcast with thread status=%d\n", self->status);
        /* try anyway? */
    }

    JValue unused;
    dvmCallMethod(self, bcast, NULL, &unused, event);
    if (dvmCheckException(self)) {
        LOGI("Exception thrown by broadcast(%d)\n", event);
        dvmLogExceptionStackTrace();
        dvmClearException(self);
        goto bail;
    }

bail:
    ;
}
/*
 * 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;
}
Exemple #10
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);
}
Exemple #11
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;
}
Exemple #12
0
static void xposedCallHandler(const u4* args, JValue* pResult,
		const Method* method, ::Thread* self) {
	if (!xposedIsHooked(method)) {
		dvmThrowNoSuchMethodError(
				"could not find Xposed original method - how did you even get here?");
		return;
	}

	XposedHookInfo* hookInfo = (XposedHookInfo*) method->insns;
	Method* original = (Method*) hookInfo;
	Object* originalReflected = hookInfo->reflectedMethod;
	Object* additionalInfo = hookInfo->additionalInfo;

	// convert/box arguments
	const char* desc = &method->shorty[1]; // [0] is the return type.
	Object* thisObject = NULL;
	size_t srcIndex = 0;
	size_t dstIndex = 0;

	// for non-static methods determine the "this" pointer
	if (!dvmIsStaticMethod(original)) {
		thisObject = (Object*) args[0];
		srcIndex++;
	}

	ArrayObject* argsArray = dvmAllocArrayByClass(objectArrayClass,
			strlen(method->shorty) - 1, ALLOC_DEFAULT);
	if (argsArray == NULL) {
		return;
	}

	while (*desc != '\0') {
		char descChar = *(desc++);
		JValue value;
		Object* obj;

		switch (descChar) {
		case 'Z':
		case 'C':
		case 'F':
		case 'B':
		case 'S':
		case 'I':
			value.i = args[srcIndex++];
			obj = (Object*) dvmBoxPrimitive(value,
					dvmFindPrimitiveClass(descChar));
			dvmReleaseTrackedAlloc(obj, self);
			break;
		case 'D':
		case 'J':
			value.j = dvmGetArgLong(args, srcIndex);
			srcIndex += 2;
			obj = (Object*) dvmBoxPrimitive(value,
					dvmFindPrimitiveClass(descChar));
			dvmReleaseTrackedAlloc(obj, self);
			break;
		case '[':
		case 'L':
			obj = (Object*) args[srcIndex++];
			break;
		default:
			ALOGE("Unknown method signature description character: %c\n",
					descChar);
			obj = NULL;
			srcIndex++;
		}
		xposedSetObjectArrayElement(argsArray, dstIndex++, obj);
	}

	// call the Java handler function
	JValue result;
	dvmCallMethod(self, xposedHandleHookedMethod, NULL, &result,
			originalReflected, (int) original, additionalInfo, thisObject,
			argsArray);

	dvmReleaseTrackedAlloc(argsArray, self);

	// exceptions are thrown to the caller
	if (dvmCheckException(self)) {
		return;
	}

	// return result with proper type
	ClassObject* returnType = dvmGetBoxedReturnType(method);
	if (returnType->primitiveType == PRIM_VOID) {
		// ignored
	} else if (result.l == NULL) {
		if (dvmIsPrimitiveClass(returnType)) {
			dvmThrowNullPointerException("null result when primitive expected");
		}
		pResult->l = NULL;
	} else {
		if (!dvmUnboxPrimitive(result.l, returnType, pResult)) {
			dvmThrowClassCastException(result.l->clazz, returnType);
		}
	}
}
/*
 * Initialize an exception with an appropriate constructor.
 *
 * "exception" is the exception object to initialize.
 * Either or both of "msg" and "cause" may be null.
 * "self" is dvmThreadSelf(), passed in so we don't have to look it up again.
 *
 * If the process of initializing the exception causes another
 * exception (e.g., OutOfMemoryError) to be thrown, return an error
 * and leave self->exception intact.
 */
static bool initException(Object* exception, const char* msg, Object* cause,
    Thread* self)
{
    enum {
        kInitUnknown,
        kInitNoarg,
        kInitMsg,
        kInitMsgThrow,
        kInitThrow
    } initKind = kInitUnknown;
    Method* initMethod = NULL;
    ClassObject* excepClass = exception->clazz;
    StringObject* msgStr = NULL;
    bool result = false;
    bool needInitCause = false;

    assert(self != NULL);
    assert(self->exception == NULL);

    /* if we have a message, create a String */
    if (msg == NULL)
        msgStr = NULL;
    else {
        msgStr = dvmCreateStringFromCstr(msg);
        if (msgStr == NULL) {
            LOGW("Could not allocate message string \"%s\" while "
                    "throwing internal exception (%s)\n",
                    msg, excepClass->descriptor);
            goto bail;
        }
    }

    if (cause != NULL) {
        if (!dvmInstanceof(cause->clazz, gDvm.classJavaLangThrowable)) {
            LOGE("Tried to init exception with cause '%s'\n",
                cause->clazz->descriptor);
            dvmAbort();
        }
    }

    /*
     * The Throwable class has four public constructors:
     *  (1) Throwable()
     *  (2) Throwable(String message)
     *  (3) Throwable(String message, Throwable cause)  (added in 1.4)
     *  (4) Throwable(Throwable cause)                  (added in 1.4)
     *
     * The first two are part of the original design, and most exception
     * classes should support them.  The third prototype was used by
     * individual exceptions. e.g. ClassNotFoundException added it in 1.2.
     * The general "cause" mechanism was added in 1.4.  Some classes,
     * such as IllegalArgumentException, initially supported the first
     * two, but added the second two in a later release.
     *
     * Exceptions may be picky about how their "cause" field is initialized.
     * If you call ClassNotFoundException(String), it may choose to
     * initialize its "cause" field to null.  Doing so prevents future
     * calls to Throwable.initCause().
     *
     * So, if "cause" is not NULL, we need to look for a constructor that
     * takes a throwable.  If we can't find one, we fall back on calling
     * #1/#2 and making a separate call to initCause().  Passing a null ref
     * for "message" into Throwable(String, Throwable) is allowed, but we
     * prefer to use the Throwable-only version because it has different
     * behavior.
     *
     * java.lang.TypeNotPresentException is a strange case -- it has #3 but
     * not #2.  (Some might argue that the constructor is actually not #3,
     * because it doesn't take the message string as an argument, but it
     * has the same effect and we can work with it here.)
     *
     * java.lang.AssertionError is also a strange case -- it has a
     * constructor that takes an Object, but not one that takes a String.
     * There may be other cases like this, as well, so we generally look
     * for an Object-taking constructor if we can't find one that takes
     * a String.
     */
    if (cause == NULL) {
        if (msgStr == NULL) {
            initMethod = findExceptionInitMethod(excepClass, false, false);
            initKind = kInitNoarg;
        } else {
            initMethod = findExceptionInitMethod(excepClass, true, false);
            if (initMethod != NULL) {
                initKind = kInitMsg;
            } else {
                /* no #2, try #3 */
                initMethod = findExceptionInitMethod(excepClass, true, true);
                if (initMethod != NULL) {
                    initKind = kInitMsgThrow;
                }
            }
        }
    } else {
        if (msgStr == NULL) {
            initMethod = findExceptionInitMethod(excepClass, false, true);
            if (initMethod != NULL) {
                initKind = kInitThrow;
            } else {
                initMethod = findExceptionInitMethod(excepClass, false, false);
                initKind = kInitNoarg;
                needInitCause = true;
            }
        } else {
            initMethod = findExceptionInitMethod(excepClass, true, true);
            if (initMethod != NULL) {
                initKind = kInitMsgThrow;
            } else {
                initMethod = findExceptionInitMethod(excepClass, true, false);
                initKind = kInitMsg;
                needInitCause = true;
            }
        }
    }

    if (initMethod == NULL) {
        /*
         * We can't find the desired constructor.  This can happen if a
         * subclass of java/lang/Throwable doesn't define an expected
         * constructor, e.g. it doesn't provide one that takes a string
         * when a message has been provided.
         */
        LOGW("WARNING: exception class '%s' missing constructor "
            "(msg='%s' kind=%d)\n",
            excepClass->descriptor, msg, initKind);
        assert(strcmp(excepClass->descriptor,
                      "Ljava/lang/RuntimeException;") != 0);
        dvmThrowChainedException("Ljava/lang/RuntimeException;",
            "re-throw on exception class missing constructor", NULL);
        goto bail;
    }

    /*
     * Call the constructor with the appropriate arguments.
     */
    JValue unused;
    switch (initKind) {
    case kInitNoarg:
        LOGVV("+++ exc noarg (ic=%d)\n", needInitCause);
        dvmCallMethod(self, initMethod, exception, &unused);
        break;
    case kInitMsg:
        LOGVV("+++ exc msg (ic=%d)\n", needInitCause);
        dvmCallMethod(self, initMethod, exception, &unused, msgStr);
        break;
    case kInitThrow:
        LOGVV("+++ exc throw");
        assert(!needInitCause);
        dvmCallMethod(self, initMethod, exception, &unused, cause);
        break;
    case kInitMsgThrow:
        LOGVV("+++ exc msg+throw");
        assert(!needInitCause);
        dvmCallMethod(self, initMethod, exception, &unused, msgStr, cause);
        break;
    default:
        assert(false);
        goto bail;
    }

    /*
     * It's possible the constructor has thrown an exception.  If so, we
     * return an error and let our caller deal with it.
     */
    if (self->exception != NULL) {
        LOGW("Exception thrown (%s) while throwing internal exception (%s)\n",
            self->exception->clazz->descriptor, exception->clazz->descriptor);
        goto bail;
    }

    /*
     * If this exception was caused by another exception, and we weren't
     * able to find a cause-setting constructor, set the "cause" field
     * with an explicit call.
     */
    if (needInitCause) {
        Method* initCause;
        initCause = dvmFindVirtualMethodHierByDescriptor(excepClass, "initCause",
            "(Ljava/lang/Throwable;)Ljava/lang/Throwable;");
        if (initCause != NULL) {
            dvmCallMethod(self, initCause, exception, &unused, cause);
            if (self->exception != NULL) {
                /* initCause() threw an exception; return an error and
                 * let the caller deal with it.
                 */
                LOGW("Exception thrown (%s) during initCause() "
                        "of internal exception (%s)\n",
                        self->exception->clazz->descriptor,
                        exception->clazz->descriptor);
                goto bail;
            }
        } else {
            LOGW("WARNING: couldn't find initCause in '%s'\n",
                excepClass->descriptor);
        }
    }


    result = true;

bail:
    dvmReleaseTrackedAlloc((Object*) msgStr, self);     // NULL is ok
    return result;
}
/*
 * 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;
}