/* * Find and return an exception constructor method that can take the * indicated parameters, or return NULL if no such constructor exists. */ static Method* findExceptionInitMethod(ClassObject* excepClass, bool hasMessage, bool hasCause) { if (hasMessage) { Method* result; if (hasCause) { result = dvmFindDirectMethodByDescriptor( excepClass, "<init>", "(Ljava/lang/String;Ljava/lang/Throwable;)V"); } else { result = dvmFindDirectMethodByDescriptor( excepClass, "<init>", "(Ljava/lang/String;)V"); } if (result != NULL) { return result; } if (hasCause) { return dvmFindDirectMethodByDescriptor( excepClass, "<init>", "(Ljava/lang/Object;Ljava/lang/Throwable;)V"); } else { return dvmFindDirectMethodByDescriptor( excepClass, "<init>", "(Ljava/lang/Object;)V"); } } else if (hasCause) { return dvmFindDirectMethodByDescriptor( excepClass, "<init>", "(Ljava/lang/Throwable;)V"); } else { return dvmFindDirectMethodByDescriptor(excepClass, "<init>", "()V"); } }
/* * Determine if "method" is a "privileged" invocation, i.e. is it one * of the variations of AccessController.doPrivileged(). * * Because the security stuff pulls in a pile of stuff that we may not * want or need, we don't do the class/method lookups at init time, but * instead on first use. */ bool dvmIsPrivilegedMethod(const Method* method) { int i; assert(method != NULL); if (!gDvm.javaSecurityAccessControllerReady) { /* * Populate on first use. No concurrency risk since we're just * finding pointers to fixed structures. */ static const char* kSignatures[NUM_DOPRIV_FUNCS] = { "(Ljava/security/PrivilegedAction;)Ljava/lang/Object;", "(Ljava/security/PrivilegedExceptionAction;)Ljava/lang/Object;", "(Ljava/security/PrivilegedAction;Ljava/security/AccessControlContext;)Ljava/lang/Object;", "(Ljava/security/PrivilegedExceptionAction;Ljava/security/AccessControlContext;)Ljava/lang/Object;", }; ClassObject* clazz; clazz = dvmFindClassNoInit("Ljava/security/AccessController;", NULL); if (clazz == NULL) { LOGW("Couldn't find java/security/AccessController\n"); return false; } assert(NELEM(gDvm.methJavaSecurityAccessController_doPrivileged) == NELEM(kSignatures)); /* verify init */ for (i = 0; i < NUM_DOPRIV_FUNCS; i++) { gDvm.methJavaSecurityAccessController_doPrivileged[i] = dvmFindDirectMethodByDescriptor(clazz, "doPrivileged", kSignatures[i]); if (gDvm.methJavaSecurityAccessController_doPrivileged[i] == NULL) { LOGW("Warning: couldn't find java/security/AccessController" ".doPrivileged %s\n", kSignatures[i]); return false; } } /* all good, raise volatile readiness flag */ android_atomic_release_store(true, &gDvm.javaSecurityAccessControllerReady); } for (i = 0; i < NUM_DOPRIV_FUNCS; i++) { if (gDvm.methJavaSecurityAccessController_doPrivileged[i] == method) { //LOGI("+++ doPriv match\n"); return true; } } return false; }
static bool initDirectMethodReferenceByClass(Method** pMethod, ClassObject* clazz, const char* name, const char* descriptor) { Method* method = dvmFindDirectMethodByDescriptor(clazz, name, descriptor); if (method == NULL) { ALOGE("Could not find essential direct method %s.%s with descriptor %s", clazz->descriptor, name, descriptor); return false; } *pMethod = method; return true; }
/* * Make an inline call for the "debug" interpreter, used when the debugger * or profiler is active. */ bool dvmPerformInlineOp4Dbg(u4 arg0, u4 arg1, u4 arg2, u4 arg3, JValue* pResult, int opIndex) { Thread* self = dvmThreadSelf(); bool result; assert(opIndex >= 0 && opIndex < NELEM(gDvmInlineOpsTable)); #ifdef WITH_PROFILER /* * Populate the methods table on first use. It's possible the class * hasn't been resolved yet, so we need to do the full "calling the * method for the first time" routine. (It's probably okay to skip * the access checks.) * * Currently assuming that we're only inlining stuff loaded by the * bootstrap class loader. This is a safe assumption for many reasons. */ Method* method = gDvm.inlinedMethods[opIndex]; if (method == NULL) { ClassObject* clazz; clazz = dvmFindClassNoInit( gDvmInlineOpsTable[opIndex].classDescriptor, NULL); if (clazz == NULL) { LOGW("Warning: can't find class '%s'\n", clazz->descriptor); goto skip_prof; } method = dvmFindDirectMethodByDescriptor(clazz, gDvmInlineOpsTable[opIndex].methodName, gDvmInlineOpsTable[opIndex].methodSignature); if (method == NULL) method = dvmFindVirtualMethodByDescriptor(clazz, gDvmInlineOpsTable[opIndex].methodName, gDvmInlineOpsTable[opIndex].methodSignature); if (method == NULL) { LOGW("Warning: can't find method %s.%s %s\n", clazz->descriptor, gDvmInlineOpsTable[opIndex].methodName, gDvmInlineOpsTable[opIndex].methodSignature); goto skip_prof; } gDvm.inlinedMethods[opIndex] = method; IF_LOGV() { char* desc = dexProtoCopyMethodDescriptor(&method->prototype); LOGV("Registered for profile: %s.%s %s\n", method->clazz->descriptor, method->name, desc); free(desc); } }
/* * 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; }
/* * Perform Proxy setup. */ bool dvmReflectProxyStartup() { /* * Standard methods we must provide in our proxy. */ Method* methE; Method* methH; Method* methT; Method* methF; methE = dvmFindVirtualMethodByDescriptor(gDvm.classJavaLangObject, "equals", "(Ljava/lang/Object;)Z"); methH = dvmFindVirtualMethodByDescriptor(gDvm.classJavaLangObject, "hashCode", "()I"); methT = dvmFindVirtualMethodByDescriptor(gDvm.classJavaLangObject, "toString", "()Ljava/lang/String;"); methF = dvmFindVirtualMethodByDescriptor(gDvm.classJavaLangObject, "finalize", "()V"); if (methE == NULL || methH == NULL || methT == NULL || methF == NULL) { LOGE("Could not find equals/hashCode/toString/finalize in Object\n"); return false; } gDvm.voffJavaLangObject_equals = methE->methodIndex; gDvm.voffJavaLangObject_hashCode = methH->methodIndex; gDvm.voffJavaLangObject_toString = methT->methodIndex; gDvm.voffJavaLangObject_finalize = methF->methodIndex; /* * The prototype signature needs to be cloned from a method in a * "real" DEX file. We declared this otherwise unused method just * for this purpose. */ ClassObject* proxyClass; Method* meth; proxyClass = dvmFindSystemClassNoInit("Ljava/lang/reflect/Proxy;"); if (proxyClass == NULL) { LOGE("No java.lang.reflect.Proxy\n"); return false; } meth = dvmFindDirectMethodByDescriptor(proxyClass, "constructorPrototype", "(Ljava/lang/reflect/InvocationHandler;)V"); if (meth == NULL) { LOGE("Could not find java.lang.Proxy.constructorPrototype()\n"); return false; } gDvm.methJavaLangReflectProxy_constructorPrototype = meth; return true; }
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; }
Method* dvmFindInlinableMethod(const char* classDescriptor, const char* methodName, const char* methodSignature) { /* * Find the class. */ ClassObject* clazz = dvmFindClassNoInit(classDescriptor, NULL); if (clazz == NULL) { ALOGE("dvmFindInlinableMethod: can't find class '%s'", classDescriptor); dvmClearException(dvmThreadSelf()); return NULL; } /* * Method could be virtual or direct. Try both. Don't use * the "hier" versions. */ Method* method = dvmFindDirectMethodByDescriptor(clazz, methodName, methodSignature); if (method == NULL) { method = dvmFindVirtualMethodByDescriptor(clazz, methodName, methodSignature); } if (method == NULL) { ALOGE("dvmFindInlinableMethod: can't find method %s.%s %s", clazz->descriptor, methodName, methodSignature); return NULL; } /* * Check that the method is appropriate for inlining. */ if (!dvmIsFinalClass(clazz) && !dvmIsFinalMethod(method)) { ALOGE("dvmFindInlinableMethod: can't inline non-final method %s.%s", clazz->descriptor, method->name); return NULL; } if (dvmIsSynchronizedMethod(method) || dvmIsDeclaredSynchronizedMethod(method)) { ALOGE("dvmFindInlinableMethod: can't inline synchronized method %s.%s", clazz->descriptor, method->name); return NULL; } return method; }
/* * 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); }
/* * 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: ; }
/* * 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); }
/* * "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; }
/* * Cache pointers to some of the exception classes we use locally. * * Note this is NOT called during dexopt optimization. Some of the fields * are initialized by the verifier (dvmVerifyCodeFlow). */ bool dvmExceptionStartup(void) { gDvm.classJavaLangThrowable = dvmFindSystemClassNoInit("Ljava/lang/Throwable;"); gDvm.classJavaLangRuntimeException = dvmFindSystemClassNoInit("Ljava/lang/RuntimeException;"); gDvm.classJavaLangStackOverflowError = dvmFindSystemClassNoInit("Ljava/lang/StackOverflowError;"); gDvm.classJavaLangError = dvmFindSystemClassNoInit("Ljava/lang/Error;"); gDvm.classJavaLangStackTraceElement = dvmFindSystemClassNoInit("Ljava/lang/StackTraceElement;"); gDvm.classJavaLangStackTraceElementArray = dvmFindArrayClass("[Ljava/lang/StackTraceElement;", NULL); if (gDvm.classJavaLangThrowable == NULL || gDvm.classJavaLangStackTraceElement == NULL || gDvm.classJavaLangStackTraceElementArray == NULL) { LOGE("Could not find one or more essential exception classes\n"); return false; } /* * Find the constructor. Note that, unlike other saved method lookups, * we're using a Method* instead of a vtable offset. This is because * constructors don't have vtable offsets. (Also, since we're creating * the object in question, it's impossible for anyone to sub-class it.) */ Method* meth; meth = dvmFindDirectMethodByDescriptor(gDvm.classJavaLangStackTraceElement, "<init>", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V"); if (meth == NULL) { LOGE("Unable to find constructor for StackTraceElement\n"); return false; } gDvm.methJavaLangStackTraceElement_init = meth; /* grab an offset for the stackData field */ gDvm.offJavaLangThrowable_stackState = dvmFindFieldOffset(gDvm.classJavaLangThrowable, "stackState", "Ljava/lang/Object;"); if (gDvm.offJavaLangThrowable_stackState < 0) { LOGE("Unable to find Throwable.stackState\n"); return false; } /* and one for the message field, in case we want to show it */ gDvm.offJavaLangThrowable_message = dvmFindFieldOffset(gDvm.classJavaLangThrowable, "detailMessage", "Ljava/lang/String;"); if (gDvm.offJavaLangThrowable_message < 0) { LOGE("Unable to find Throwable.detailMessage\n"); return false; } /* and one for the cause field, just 'cause */ gDvm.offJavaLangThrowable_cause = dvmFindFieldOffset(gDvm.classJavaLangThrowable, "cause", "Ljava/lang/Throwable;"); if (gDvm.offJavaLangThrowable_cause < 0) { LOGE("Unable to find Throwable.cause\n"); return false; } return true; }
/* * 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, ALLOC_DEFAULT); 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.) */ if (cause == NULL) { if (msgStr == NULL) { initMethod = dvmFindDirectMethodByDescriptor(excepClass, "<init>", "()V"); initKind = kInitNoarg; } else { initMethod = dvmFindDirectMethodByDescriptor(excepClass, "<init>", "(Ljava/lang/String;)V"); if (initMethod != NULL) { initKind = kInitMsg; } else { /* no #2, try #3 */ initMethod = dvmFindDirectMethodByDescriptor(excepClass, "<init>", "(Ljava/lang/String;Ljava/lang/Throwable;)V"); if (initMethod != NULL) initKind = kInitMsgThrow; } } } else { if (msgStr == NULL) { initMethod = dvmFindDirectMethodByDescriptor(excepClass, "<init>", "(Ljava/lang/Throwable;)V"); if (initMethod != NULL) { initKind = kInitThrow; } else { initMethod = dvmFindDirectMethodByDescriptor(excepClass, "<init>", "()V"); initKind = kInitNoarg; needInitCause = true; } } else { initMethod = dvmFindDirectMethodByDescriptor(excepClass, "<init>", "(Ljava/lang/String;Ljava/lang/Throwable;)V"); if (initMethod != NULL) { initKind = kInitMsgThrow; } else { initMethod = dvmFindDirectMethodByDescriptor(excepClass, "<init>", "(Ljava/lang/String;)V"); 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; }