/* * 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; }
/* * static boolean cacheRegisterMap(String classAndMethodDescr) * * If the specified class is loaded, and the named method exists, ensure * that the method's register map is ready for use. If the class/method * cannot be found, nothing happens. * * This can improve the zygote's sharing of compressed register maps. Do * this after class preloading. * * Returns true if the register map is cached and ready, either as a result * of this call or earlier activity. Returns false if the class isn't loaded, * if the method couldn't be found, or if the method has no register map. * * (Uncomment logs in dvmGetExpandedRegisterMap0() to gather stats.) */ static void Dalvik_dalvik_system_VMDebug_cacheRegisterMap(const u4* args, JValue* pResult) { StringObject* classAndMethodDescStr = (StringObject*) args[0]; ClassObject* clazz; bool result = false; if (classAndMethodDescStr == NULL) { dvmThrowNullPointerException("classAndMethodDesc == null"); RETURN_VOID(); } char* classAndMethodDesc = NULL; /* * Pick the string apart. We have a local copy, so just modify it * in place. */ classAndMethodDesc = dvmCreateCstrFromString(classAndMethodDescStr); char* methodName = strchr(classAndMethodDesc, '.'); if (methodName == NULL) { dvmThrowRuntimeException("method name not found in string"); RETURN_VOID(); } *methodName++ = '\0'; char* methodDescr = strchr(methodName, ':'); if (methodDescr == NULL) { dvmThrowRuntimeException("method descriptor not found in string"); RETURN_VOID(); } *methodDescr++ = '\0'; //ALOGD("GOT: %s %s %s", classAndMethodDesc, methodName, methodDescr); /* * Find the class, but only if it's already loaded. */ clazz = dvmLookupClass(classAndMethodDesc, NULL, false); if (clazz == NULL) { ALOGD("Class %s not found in bootstrap loader", classAndMethodDesc); goto bail; } Method* method; /* * Find the method, which could be virtual or direct, defined directly * or inherited. */ if (methodName[0] == '<') { /* * Constructor or class initializer. Only need to examine the * "direct" list, and don't need to search up the class hierarchy. */ method = dvmFindDirectMethodByDescriptor(clazz, methodName, methodDescr); } else { /* * Try both lists, and scan up the tree. */ method = dvmFindVirtualMethodHierByDescriptor(clazz, methodName, methodDescr); if (method == NULL) { method = dvmFindDirectMethodHierByDescriptor(clazz, methodName, methodDescr); } } if (method != NULL) { /* * Got it. See if there's a register map here. */ const RegisterMap* pMap; pMap = dvmGetExpandedRegisterMap(method); if (pMap == NULL) { ALOGV("No map for %s.%s %s", classAndMethodDesc, methodName, methodDescr); } else { ALOGV("Found map %s.%s %s", classAndMethodDesc, methodName, methodDescr); result = true; } } else { ALOGV("Unable to find %s.%s %s", classAndMethodDesc, methodName, methodDescr); } bail: free(classAndMethodDesc); RETURN_BOOLEAN(result); }
/* * 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); }
/* * 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; }