summaryrefslogtreecommitdiff
path: root/vm/Exception.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'vm/Exception.cpp')
-rw-r--r--vm/Exception.cpp1443
1 files changed, 1443 insertions, 0 deletions
diff --git a/vm/Exception.cpp b/vm/Exception.cpp
new file mode 100644
index 0000000..5af48ba
--- /dev/null
+++ b/vm/Exception.cpp
@@ -0,0 +1,1443 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+/*
+ * Exception handling.
+ */
+#include "Dalvik.h"
+#include "libdex/DexCatch.h"
+
+#include <stdlib.h>
+
+/*
+Notes on Exception Handling
+
+We have one fairly sticky issue to deal with: creating the exception stack
+trace. The trouble is that we need the current value of the program
+counter for the method now being executed, but that's only held in a local
+variable or hardware register in the main interpreter loop.
+
+The exception mechanism requires that the current stack trace be associated
+with a Throwable at the time the Throwable is constructed. The construction
+may or may not be associated with a throw. We have three situations to
+consider:
+
+ (1) A Throwable is created with a "new Throwable" statement in the
+ application code, for immediate or deferred use with a "throw" statement.
+ (2) The VM throws an exception from within the interpreter core, e.g.
+ after an integer divide-by-zero.
+ (3) The VM throws an exception from somewhere deeper down, e.g. while
+ trying to link a class.
+
+We need to have the current value for the PC, which means that for
+situation (3) the interpreter loop must copy it to an externally-accessible
+location before handling any opcode that could cause the VM to throw
+an exception. We can't store it globally, because the various threads
+would trample each other. We can't store it in the Thread structure,
+because it'll get overwritten as soon as the Throwable constructor starts
+executing. It needs to go on the stack, but our stack frames hold the
+caller's *saved* PC, not the current PC.
+
+Situation #1 doesn't require special handling. Situation #2 could be dealt
+with by passing the PC into the exception creation function. The trick
+is to solve situation #3 in a way that adds minimal overhead to common
+operations. Making it more costly to throw an exception is acceptable.
+
+There are a few ways to deal with this:
+
+ (a) Change "savedPc" to "currentPc" in the stack frame. All of the
+ stack logic gets offset by one frame. The current PC is written
+ to the current stack frame when necessary.
+ (b) Write the current PC into the current stack frame, but without
+ replacing "savedPc". The JNI local refs pointer, which is only
+ used for native code, can be overloaded to save space.
+ (c) In dvmThrowException(), push an extra stack frame on, with the
+ current PC in it. The current PC is written into the Thread struct
+ when necessary, and copied out when the VM throws.
+ (d) Before doing something that might throw an exception, push a
+ temporary frame on with the saved PC in it.
+
+Solution (a) is the simplest, but breaks Dalvik's goal of mingling native
+and interpreted stacks.
+
+Solution (b) retains the simplicity of (a) without rearranging the stack,
+but now in some cases we're storing the PC twice, which feels wrong.
+
+Solution (c) usually works, because we push the saved PC onto the stack
+before the Throwable construction can overwrite the copy in Thread. One
+way solution (c) could break is:
+ - Interpreter saves the PC
+ - Execute some bytecode, which runs successfully (and alters the saved PC)
+ - Throw an exception before re-saving the PC (i.e in the same opcode)
+This is a risk for anything that could cause <clinit> to execute, e.g.
+executing a static method or accessing a static field. Attemping to access
+a field that doesn't exist in a class that does exist might cause this.
+It may be possible to simply bracket the dvmCallMethod*() functions to
+save/restore it.
+
+Solution (d) incurs additional overhead, but may have other benefits (e.g.
+it's easy to find the stack frames that should be removed before storage
+in the Throwable).
+
+Current plan is option (b), because it's simple, fast, and doesn't change
+the way the stack works.
+*/
+
+/* fwd */
+static bool initException(Object* exception, const char* msg, Object* cause,
+ Thread* self);
+
+void dvmThrowExceptionFmtV(ClassObject* exceptionClass,
+ const char* fmt, va_list args)
+{
+ char msgBuf[512];
+
+ vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
+ dvmThrowChainedException(exceptionClass, msgBuf, NULL);
+}
+
+void dvmThrowChainedException(ClassObject* excepClass, const char* msg,
+ Object* cause)
+{
+ Thread* self = dvmThreadSelf();
+ Object* exception;
+
+ if (excepClass == NULL) {
+ /*
+ * The exception class was passed in as NULL. This might happen
+ * early on in VM initialization. There's nothing better to do
+ * than just log the message as an error and abort.
+ */
+ LOGE("Fatal error: %s", msg);
+ dvmAbort();
+ }
+
+ /* make sure the exception is initialized */
+ if (!dvmIsClassInitialized(excepClass) && !dvmInitClass(excepClass)) {
+ LOGE("ERROR: unable to initialize exception class '%s'",
+ excepClass->descriptor);
+ if (strcmp(excepClass->descriptor, "Ljava/lang/InternalError;") == 0)
+ dvmAbort();
+ dvmThrowChainedException(gDvm.exInternalError,
+ "failed to init original exception class", cause);
+ return;
+ }
+
+ exception = dvmAllocObject(excepClass, ALLOC_DEFAULT);
+ if (exception == NULL) {
+ /*
+ * We're in a lot of trouble. We might be in the process of
+ * throwing an out-of-memory exception, in which case the
+ * pre-allocated object will have been thrown when our object alloc
+ * failed. So long as there's an exception raised, return and
+ * allow the system to try to recover. If not, something is broken
+ * and we need to bail out.
+ */
+ if (dvmCheckException(self))
+ goto bail;
+ LOGE("FATAL: unable to allocate exception '%s' '%s'",
+ excepClass->descriptor, msg != NULL ? msg : "(no msg)");
+ dvmAbort();
+ }
+
+ /*
+ * Init the exception.
+ */
+ if (gDvm.optimizing) {
+ /* need the exception object, but can't invoke interpreted code */
+ LOGV("Skipping init of exception %s '%s'",
+ excepClass->descriptor, msg);
+ } else {
+ assert(excepClass == exception->clazz);
+ if (!initException(exception, msg, cause, self)) {
+ /*
+ * Whoops. If we can't initialize the exception, we can't use
+ * it. If there's an exception already set, the constructor
+ * probably threw an OutOfMemoryError.
+ */
+ if (!dvmCheckException(self)) {
+ /*
+ * We're required to throw something, so we just
+ * throw the pre-constructed internal error.
+ */
+ self->exception = gDvm.internalErrorObj;
+ }
+ goto bail;
+ }
+ }
+
+ self->exception = exception;
+
+bail:
+ dvmReleaseTrackedAlloc(exception, self);
+}
+
+void dvmThrowChainedExceptionWithClassMessage(
+ ClassObject* exceptionClass, const char* messageDescriptor,
+ Object* cause)
+{
+ char* message = dvmDescriptorToName(messageDescriptor);
+
+ dvmThrowChainedException(exceptionClass, message, cause);
+ free(message);
+}
+
+/*
+ * 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");
+ }
+}
+
+/*
+ * 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)",
+ msg, excepClass->descriptor);
+ goto bail;
+ }
+ }
+
+ if (cause != NULL) {
+ if (!dvmInstanceof(cause->clazz, gDvm.exThrowable)) {
+ LOGE("Tried to init exception with cause '%s'",
+ 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)",
+ excepClass->descriptor, msg, initKind);
+ assert(strcmp(excepClass->descriptor,
+ "Ljava/lang/RuntimeException;") != 0);
+ dvmThrowChainedException(gDvm.exRuntimeException,
+ "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)", needInitCause);
+ dvmCallMethod(self, initMethod, exception, &unused);
+ break;
+ case kInitMsg:
+ LOGVV("+++ exc msg (ic=%d)", 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)",
+ 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)",
+ self->exception->clazz->descriptor,
+ exception->clazz->descriptor);
+ goto bail;
+ }
+ } else {
+ LOGW("WARNING: couldn't find initCause in '%s'",
+ excepClass->descriptor);
+ }
+ }
+
+
+ result = true;
+
+bail:
+ dvmReleaseTrackedAlloc((Object*) msgStr, self); // NULL is ok
+ return result;
+}
+
+
+/*
+ * Clear the pending exception. This is used by the optimization and
+ * verification code, which mostly happens during runs of dexopt.
+ *
+ * This can also be called when the VM is in a "normal" state, e.g. when
+ * verifying classes that couldn't be verified at optimization time.
+ */
+void dvmClearOptException(Thread* self)
+{
+ self->exception = NULL;
+}
+
+/*
+ * Returns "true" if this is a "checked" exception, i.e. it's a subclass
+ * of Throwable (assumed) but not a subclass of RuntimeException or Error.
+ */
+bool dvmIsCheckedException(const Object* exception)
+{
+ if (dvmInstanceof(exception->clazz, gDvm.exError) ||
+ dvmInstanceof(exception->clazz, gDvm.exRuntimeException))
+ {
+ return false;
+ } else {
+ return true;
+ }
+}
+
+/*
+ * 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);
+}
+
+/*
+ * 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.exThrowable)) {
+ LOGE("Tried to get cause from object of type '%s'",
+ exception->clazz->descriptor);
+ dvmAbort();
+ }
+ Object* cause =
+ dvmGetFieldObject(exception, gDvm.offJavaLangThrowable_cause);
+ if (cause == exception)
+ return NULL;
+ else
+ return cause;
+}
+
+/*
+ * 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()
+{
+ Thread* self = dvmThreadSelf();
+ Object* exception;
+ Method* printMethod;
+
+ exception = self->exception;
+ if (exception == NULL)
+ return;
+
+ dvmAddTrackedAlloc(exception, self);
+ 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",
+ exception->clazz->descriptor);
+ }
+
+ if (self->exception != NULL) {
+ LOGW("NOTE: exception thrown while printing stack trace: %s",
+ self->exception->clazz->descriptor);
+ }
+
+ self->exception = exception;
+ dvmReleaseTrackedAlloc(exception, self);
+}
+
+/*
+ * Search the method's list of exceptions for a match.
+ *
+ * Returns the offset of the catch block on success, or -1 on failure.
+ */
+static int findCatchInMethod(Thread* self, const Method* method, int relPc,
+ ClassObject* excepClass)
+{
+ /*
+ * Need to clear the exception before entry. Otherwise, dvmResolveClass
+ * might think somebody threw an exception while it was loading a class.
+ */
+ assert(!dvmCheckException(self));
+ assert(!dvmIsNativeMethod(method));
+
+ LOGVV("findCatchInMethod %s.%s excep=%s depth=%d",
+ method->clazz->descriptor, method->name, excepClass->descriptor,
+ dvmComputeExactFrameDepth(self->interpSave.curFrame));
+
+ DvmDex* pDvmDex = method->clazz->pDvmDex;
+ const DexCode* pCode = dvmGetMethodCode(method);
+ DexCatchIterator iterator;
+
+ if (dexFindCatchHandler(&iterator, pCode, relPc)) {
+ for (;;) {
+ DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
+
+ if (handler == NULL) {
+ break;
+ }
+
+ if (handler->typeIdx == kDexNoIndex) {
+ /* catch-all */
+ LOGV("Match on catch-all block at 0x%02x in %s.%s for %s",
+ relPc, method->clazz->descriptor,
+ method->name, excepClass->descriptor);
+ return handler->address;
+ }
+
+ ClassObject* throwable =
+ dvmDexGetResolvedClass(pDvmDex, handler->typeIdx);
+ if (throwable == NULL) {
+ /*
+ * TODO: this behaves badly if we run off the stack
+ * while trying to throw an exception. The problem is
+ * that, if we're in a class loaded by a class loader,
+ * the call to dvmResolveClass has to ask the class
+ * loader for help resolving any previously-unresolved
+ * classes. If this particular class loader hasn't
+ * resolved StackOverflowError, it will call into
+ * interpreted code, and blow up.
+ *
+ * We currently replace the previous exception with
+ * the StackOverflowError, which means they won't be
+ * catching it *unless* they explicitly catch
+ * StackOverflowError, in which case we'll be unable
+ * to resolve the class referred to by the "catch"
+ * block.
+ *
+ * We end up getting a huge pile of warnings if we do
+ * a simple synthetic test, because this method gets
+ * called on every stack frame up the tree, and it
+ * fails every time.
+ *
+ * This eventually bails out, effectively becoming an
+ * uncatchable exception, so other than the flurry of
+ * warnings it's not really a problem. Still, we could
+ * probably handle this better.
+ */
+ throwable = dvmResolveClass(method->clazz, handler->typeIdx,
+ true);
+ if (throwable == NULL) {
+ /*
+ * We couldn't find the exception they wanted in
+ * our class files (or, perhaps, the stack blew up
+ * while we were querying a class loader). Cough
+ * up a warning, then move on to the next entry.
+ * Keep the exception status clear.
+ */
+ LOGW("Could not resolve class ref'ed in exception "
+ "catch list (class index %d, exception %s)",
+ handler->typeIdx,
+ (self->exception != NULL) ?
+ self->exception->clazz->descriptor : "(none)");
+ dvmClearException(self);
+ continue;
+ }
+ }
+
+ //LOGD("ADDR MATCH, check %s instanceof %s",
+ // excepClass->descriptor, pEntry->excepClass->descriptor);
+
+ if (dvmInstanceof(excepClass, throwable)) {
+ LOGV("Match on catch block at 0x%02x in %s.%s for %s",
+ relPc, method->clazz->descriptor,
+ method->name, excepClass->descriptor);
+ return handler->address;
+ }
+ }
+ }
+
+ LOGV("No matching catch block at 0x%02x in %s for %s",
+ relPc, method->name, excepClass->descriptor);
+ return -1;
+}
+
+/*
+ * Find a matching "catch" block. "pc" is the relative PC within the
+ * current method, indicating the offset from the start in 16-bit units.
+ *
+ * Returns the offset to the catch block, or -1 if we run up against a
+ * break frame without finding anything.
+ *
+ * The class resolution stuff we have to do while evaluating the "catch"
+ * blocks could cause an exception. The caller should clear the exception
+ * before calling here and restore it after.
+ *
+ * Sets *newFrame to the frame pointer of the frame with the catch block.
+ * If "scanOnly" is false, self->interpSave.curFrame is also set to this value.
+ */
+int dvmFindCatchBlock(Thread* self, int relPc, Object* exception,
+ bool scanOnly, void** newFrame)
+{
+ u4* fp = self->interpSave.curFrame;
+ int catchAddr = -1;
+
+ assert(!dvmCheckException(self));
+
+ while (true) {
+ StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+ catchAddr = findCatchInMethod(self, saveArea->method, relPc,
+ exception->clazz);
+ if (catchAddr >= 0)
+ break;
+
+ /*
+ * Normally we'd check for ACC_SYNCHRONIZED methods and unlock
+ * them as we unroll. Dalvik uses what amount to generated
+ * "finally" blocks to take care of this for us.
+ */
+
+ /* output method profiling info */
+ if (!scanOnly) {
+ TRACE_METHOD_UNROLL(self, saveArea->method);
+ }
+
+ /*
+ * Move up one frame. If the next thing up is a break frame,
+ * break out now so we're left unrolled to the last method frame.
+ * We need to point there so we can roll up the JNI local refs
+ * if this was a native method.
+ */
+ assert(saveArea->prevFrame != NULL);
+ if (dvmIsBreakFrame((u4*)saveArea->prevFrame)) {
+ if (!scanOnly)
+ break; // bail with catchAddr == -1
+
+ /*
+ * We're scanning for the debugger. It needs to know if this
+ * exception is going to be caught or not, and we need to figure
+ * out if it will be caught *ever* not just between the current
+ * position and the next break frame. We can't tell what native
+ * code is going to do, so we assume it never catches exceptions.
+ *
+ * Start by finding an interpreted code frame.
+ */
+ fp = saveArea->prevFrame; // this is the break frame
+ saveArea = SAVEAREA_FROM_FP(fp);
+ fp = saveArea->prevFrame; // this may be a good one
+ while (fp != NULL) {
+ if (!dvmIsBreakFrame((u4*)fp)) {
+ saveArea = SAVEAREA_FROM_FP(fp);
+ if (!dvmIsNativeMethod(saveArea->method))
+ break;
+ }
+
+ fp = SAVEAREA_FROM_FP(fp)->prevFrame;
+ }
+ if (fp == NULL)
+ break; // bail with catchAddr == -1
+
+ /*
+ * Now fp points to the "good" frame. When the interp code
+ * invoked the native code, it saved a copy of its current PC
+ * into xtra.currentPc. Pull it out of there.
+ */
+ relPc =
+ saveArea->xtra.currentPc - SAVEAREA_FROM_FP(fp)->method->insns;
+ } else {
+ fp = saveArea->prevFrame;
+
+ /* savedPc in was-current frame goes with method in now-current */
+ relPc = saveArea->savedPc - SAVEAREA_FROM_FP(fp)->method->insns;
+ }
+ }
+
+ if (!scanOnly)
+ self->interpSave.curFrame = fp;
+
+ /*
+ * The class resolution in findCatchInMethod() could cause an exception.
+ * Clear it to be safe.
+ */
+ self->exception = NULL;
+
+ *newFrame = fp;
+ return catchAddr;
+}
+
+/*
+ * We have to carry the exception's stack trace around, but in many cases
+ * it will never be examined. It makes sense to keep it in a compact,
+ * VM-specific object, rather than an array of Objects with strings.
+ *
+ * Pass in the thread whose stack we're interested in. If "thread" is
+ * not self, the thread must be suspended. This implies that the thread
+ * list lock is held, which means we can't allocate objects or we risk
+ * jamming the GC. So, we allow this function to return different formats.
+ * (This shouldn't be called directly -- see the inline functions in the
+ * header file.)
+ *
+ * If "wantObject" is true, this returns a newly-allocated Object, which is
+ * presently an array of integers, but could become something else in the
+ * future. If "wantObject" is false, return plain malloc data.
+ *
+ * NOTE: if we support class unloading, we will need to scan the class
+ * object references out of these arrays.
+ */
+void* dvmFillInStackTraceInternal(Thread* thread, bool wantObject, size_t* pCount)
+{
+ ArrayObject* stackData = NULL;
+ int* simpleData = NULL;
+ void* fp;
+ void* startFp;
+ size_t stackDepth;
+ int* intPtr;
+
+ if (pCount != NULL)
+ *pCount = 0;
+ fp = thread->interpSave.curFrame;
+
+ assert(thread == dvmThreadSelf() || dvmIsSuspended(thread));
+
+ /*
+ * We're looking at a stack frame for code running below a Throwable
+ * constructor. We want to remove the Throwable methods and the
+ * superclass initializations so the user doesn't see them when they
+ * read the stack dump.
+ *
+ * TODO: this just scrapes off the top layers of Throwable. Might not do
+ * the right thing if we create an exception object or cause a VM
+ * exception while in a Throwable method.
+ */
+ while (fp != NULL) {
+ const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+ const Method* method = saveArea->method;
+
+ if (dvmIsBreakFrame((u4*)fp))
+ break;
+ if (!dvmInstanceof(method->clazz, gDvm.exThrowable))
+ break;
+ //LOGD("EXCEP: ignoring %s.%s",
+ // method->clazz->descriptor, method->name);
+ fp = saveArea->prevFrame;
+ }
+ startFp = fp;
+
+ /*
+ * Compute the stack depth.
+ */
+ stackDepth = 0;
+ while (fp != NULL) {
+ const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+
+ if (!dvmIsBreakFrame((u4*)fp))
+ stackDepth++;
+
+ assert(fp != saveArea->prevFrame);
+ fp = saveArea->prevFrame;
+ }
+ //LOGD("EXCEP: stack depth is %d", stackDepth);
+
+ if (!stackDepth)
+ goto bail;
+
+ /*
+ * We need to store a pointer to the Method and the program counter.
+ * We have 4-byte pointers, so we use '[I'.
+ */
+ if (wantObject) {
+ assert(sizeof(Method*) == 4);
+ stackData = dvmAllocPrimitiveArray('I', stackDepth*2, ALLOC_DEFAULT);
+ if (stackData == NULL) {
+ assert(dvmCheckException(dvmThreadSelf()));
+ goto bail;
+ }
+ intPtr = (int*)(void*)stackData->contents;
+ } else {
+ /* array of ints; first entry is stack depth */
+ assert(sizeof(Method*) == sizeof(int));
+ simpleData = (int*) malloc(sizeof(int) * stackDepth*2);
+ if (simpleData == NULL)
+ goto bail;
+
+ assert(pCount != NULL);
+ intPtr = simpleData;
+ }
+ if (pCount != NULL)
+ *pCount = stackDepth;
+
+ fp = startFp;
+ while (fp != NULL) {
+ const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
+ const Method* method = saveArea->method;
+
+ if (!dvmIsBreakFrame((u4*)fp)) {
+ //LOGD("EXCEP keeping %s.%s", method->clazz->descriptor,
+ // method->name);
+
+ *intPtr++ = (int) method;
+ if (dvmIsNativeMethod(method)) {
+ *intPtr++ = 0; /* no saved PC for native methods */
+ } else {
+ assert(saveArea->xtra.currentPc >= method->insns &&
+ saveArea->xtra.currentPc <
+ method->insns + dvmGetMethodInsnsSize(method));
+ *intPtr++ = (int) (saveArea->xtra.currentPc - method->insns);
+ }
+
+ stackDepth--; // for verification
+ }
+
+ assert(fp != saveArea->prevFrame);
+ fp = saveArea->prevFrame;
+ }
+ assert(stackDepth == 0);
+
+bail:
+ if (wantObject) {
+ dvmReleaseTrackedAlloc((Object*) stackData, dvmThreadSelf());
+ return stackData;
+ } else {
+ return simpleData;
+ }
+}
+
+
+/*
+ * Given an Object previously created by dvmFillInStackTrace(), use the
+ * contents of the saved stack trace to generate an array of
+ * java/lang/StackTraceElement objects.
+ *
+ * The returned array is not added to the "local refs" list.
+ */
+ArrayObject* dvmGetStackTrace(const Object* ostackData)
+{
+ const ArrayObject* stackData = (const ArrayObject*) ostackData;
+ size_t stackSize = stackData->length / 2;
+ const int* intVals = (const int*)(void*)stackData->contents;
+ return dvmGetStackTraceRaw(intVals, stackSize);
+}
+
+/*
+ * 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, size_t stackDepth)
+{
+ /* allocate a StackTraceElement array */
+ ClassObject* klass = gDvm.classJavaLangStackTraceElementArray;
+ ArrayObject* array = dvmAllocArrayByClass(klass, stackDepth, ALLOC_DEFAULT);
+ if (array != NULL){
+ dvmFillStackTraceElements(intVals, stackDepth, array);
+ dvmReleaseTrackedAlloc((Object*) array, NULL);
+ }
+ return array;
+}
+
+/*
+ * Fills the StackTraceElement array elements from the raw integer
+ * data encoded by dvmFillInStackTrace().
+ *
+ * "intVals" points to the first {method,pc} pair.
+ */
+void dvmFillStackTraceElements(const int* intVals, size_t stackDepth, ArrayObject* steArray)
+{
+ unsigned int i;
+
+ /* init this if we haven't yet */
+ if (!dvmIsClassInitialized(gDvm.classJavaLangStackTraceElement))
+ dvmInitClass(gDvm.classJavaLangStackTraceElement);
+
+ /*
+ * 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 = dvmAllocObject(gDvm.classJavaLangStackTraceElement,ALLOC_DEFAULT);
+ if (ste == NULL) {
+ return;
+ }
+
+ Method* meth = (Method*) *intVals++;
+ int pc = *intVals++;
+
+ int lineNumber;
+ if (pc == -1) // broken top frame?
+ lineNumber = 0;
+ else
+ lineNumber = dvmLineNumFromPC(meth, pc);
+
+ std::string dotName(dvmHumanReadableDescriptor(meth->clazz->descriptor));
+ StringObject* className = dvmCreateStringFromCstr(dotName);
+
+ StringObject* methodName = dvmCreateStringFromCstr(meth->name);
+
+ const char* sourceFile = dvmGetMethodSourceFile(meth);
+ StringObject* fileName = (sourceFile != NULL) ? dvmCreateStringFromCstr(sourceFile) : 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())) {
+ return;
+ }
+
+ dvmSetObjectArrayElement(steArray, i, ste);
+ }
+}
+
+/*
+ * Dump the contents of a raw stack trace to the log.
+ */
+void dvmLogRawStackTrace(const int* intVals, int stackDepth) {
+ /*
+ * Run through the array of stack frame data.
+ */
+ for (int i = 0; i < stackDepth; i++) {
+ Method* meth = (Method*) *intVals++;
+ int pc = *intVals++;
+
+ std::string dotName(dvmHumanReadableDescriptor(meth->clazz->descriptor));
+ if (dvmIsNativeMethod(meth)) {
+ LOGI("\tat %s.%s(Native Method)", dotName.c_str(), meth->name);
+ } else {
+ LOGI("\tat %s.%s(%s:%d)",
+ dotName.c_str(), meth->name, dvmGetMethodSourceFile(meth),
+ dvmLineNumFromPC(meth, pc));
+ }
+ }
+}
+
+/*
+ * Get the message string. We'd like to just grab the field out of
+ * Throwable, but the getMessage() function can be overridden by the
+ * sub-class.
+ *
+ * Returns the message string object, or NULL if it wasn't set or
+ * we encountered a failure trying to retrieve it. The string will
+ * be added to the tracked references table.
+ */
+static StringObject* getExceptionMessage(Object* exception)
+{
+ Thread* self = dvmThreadSelf();
+ Method* getMessageMethod;
+ StringObject* messageStr = NULL;
+ Object* pendingException;
+
+ /*
+ * If an exception is pending, clear it while we work and restore
+ * it when we're done.
+ */
+ pendingException = dvmGetException(self);
+ if (pendingException != NULL) {
+ dvmAddTrackedAlloc(pendingException, self);
+ dvmClearException(self);
+ }
+
+ getMessageMethod = dvmFindVirtualMethodHierByDescriptor(exception->clazz,
+ "getMessage", "()Ljava/lang/String;");
+ if (getMessageMethod != NULL) {
+ /* could be in NATIVE mode from CheckJNI, so switch state */
+ ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_RUNNING);
+ JValue result;
+
+ dvmCallMethod(self, getMessageMethod, exception, &result);
+ messageStr = (StringObject*) result.l;
+ if (messageStr != NULL)
+ dvmAddTrackedAlloc((Object*) messageStr, self);
+
+ dvmChangeStatus(self, oldStatus);
+ } else {
+ LOGW("WARNING: could not find getMessage in %s",
+ exception->clazz->descriptor);
+ }
+
+ if (dvmGetException(self) != NULL) {
+ LOGW("NOTE: exception thrown while retrieving exception message: %s",
+ dvmGetException(self)->clazz->descriptor);
+ /* will be overwritten below */
+ }
+
+ dvmSetException(self, pendingException);
+ if (pendingException != NULL) {
+ dvmReleaseTrackedAlloc(pendingException, self);
+ }
+ return messageStr;
+}
+
+/*
+ * Print the direct stack trace of the given exception to the log.
+ */
+static void logStackTraceOf(Object* exception) {
+ std::string className(dvmHumanReadableDescriptor(exception->clazz->descriptor));
+ StringObject* messageStr = getExceptionMessage(exception);
+ if (messageStr != NULL) {
+ char* cp = dvmCreateCstrFromString(messageStr);
+ dvmReleaseTrackedAlloc((Object*) messageStr, dvmThreadSelf());
+ messageStr = NULL;
+
+ LOGI("%s: %s", className.c_str(), cp);
+ free(cp);
+ } else {
+ LOGI("%s:", className.c_str());
+ }
+
+ /*
+ * This relies on the stackState field, which contains the "raw"
+ * form of the stack. The Throwable class may clear this field
+ * after it generates the "cooked" form, in which case we'll have
+ * nothing to show.
+ */
+ const ArrayObject* stackData = (const ArrayObject*) dvmGetFieldObject(exception,
+ gDvm.offJavaLangThrowable_stackState);
+ if (stackData == NULL) {
+ LOGI(" (raw stack trace not found)");
+ return;
+ }
+
+ int stackSize = stackData->length / 2;
+ const int* intVals = (const int*)(void*)stackData->contents;
+
+ dvmLogRawStackTrace(intVals, stackSize);
+}
+
+/*
+ * Print the stack trace of the current thread's exception, as well as
+ * the stack traces of any chained exceptions, to the log. We extract
+ * the stored stack trace and process it internally instead of calling
+ * interpreted code.
+ */
+void dvmLogExceptionStackTrace()
+{
+ Object* exception = dvmThreadSelf()->exception;
+ Object* cause;
+
+ if (exception == NULL) {
+ LOGW("tried to log a null exception?");
+ return;
+ }
+
+ for (;;) {
+ logStackTraceOf(exception);
+ cause = dvmGetExceptionCause(exception);
+ if (cause == NULL) {
+ break;
+ }
+ LOGI("Caused by:");
+ exception = cause;
+ }
+}
+
+/*
+ * Helper for a few of the throw functions defined below. This throws
+ * the indicated exception, with a message based on a format in which
+ * "%s" is used exactly twice, first for a received class and second
+ * for the expected class.
+ */
+static void throwTypeError(ClassObject* exceptionClass, const char* fmt,
+ ClassObject* actual, ClassObject* desired)
+{
+ std::string actualClassName(dvmHumanReadableDescriptor(actual->descriptor));
+ std::string desiredClassName(dvmHumanReadableDescriptor(desired->descriptor));
+ dvmThrowExceptionFmt(exceptionClass, fmt, actualClassName.c_str(), desiredClassName.c_str());
+}
+
+void dvmThrowAbstractMethodError(const char* msg) {
+ dvmThrowException(gDvm.exAbstractMethodError, msg);
+}
+
+void dvmThrowArithmeticException(const char* msg) {
+ dvmThrowException(gDvm.exArithmeticException, msg);
+}
+
+void dvmThrowArrayIndexOutOfBoundsException(int length, int index)
+{
+ dvmThrowExceptionFmt(gDvm.exArrayIndexOutOfBoundsException,
+ "length=%d; index=%d", length, index);
+}
+
+void dvmThrowArrayStoreExceptionIncompatibleElement(ClassObject* objectType,
+ ClassObject* arrayType)
+{
+ throwTypeError(gDvm.exArrayStoreException,
+ "%s cannot be stored in an array of type %s",
+ objectType, arrayType);
+}
+
+void dvmThrowArrayStoreExceptionNotArray(ClassObject* actual, const char* label) {
+ std::string actualClassName(dvmHumanReadableDescriptor(actual->descriptor));
+ dvmThrowExceptionFmt(gDvm.exArrayStoreException, "%s of type %s is not an array",
+ label, actualClassName.c_str());
+}
+
+void dvmThrowArrayStoreExceptionIncompatibleArrays(ClassObject* source, ClassObject* destination)
+{
+ throwTypeError(gDvm.exArrayStoreException,
+ "%s and %s are incompatible array types",
+ source, destination);
+}
+
+void dvmThrowArrayStoreExceptionIncompatibleArrayElement(s4 index, ClassObject* objectType,
+ ClassObject* arrayType)
+{
+ std::string objectClassName(dvmHumanReadableDescriptor(objectType->descriptor));
+ std::string arrayClassName(dvmHumanReadableDescriptor(arrayType->descriptor));
+ dvmThrowExceptionFmt(gDvm.exArrayStoreException,
+ "source[%d] of type %s cannot be stored in destination array of type %s",
+ index, objectClassName.c_str(), arrayClassName.c_str());
+}
+
+void dvmThrowClassCastException(ClassObject* actual, ClassObject* desired)
+{
+ throwTypeError(gDvm.exClassCastException,
+ "%s cannot be cast to %s", actual, desired);
+}
+
+void dvmThrowClassCircularityError(const char* descriptor) {
+ dvmThrowExceptionWithClassMessage(gDvm.exClassCircularityError,
+ descriptor);
+}
+
+void dvmThrowClassFormatError(const char* msg) {
+ dvmThrowException(gDvm.exClassFormatError, msg);
+}
+
+void dvmThrowClassNotFoundException(const char* name) {
+ dvmThrowChainedClassNotFoundException(name, NULL);
+}
+
+void dvmThrowChainedClassNotFoundException(const char* name, Object* cause) {
+ /*
+ * Note: This exception is thrown in response to a request coming
+ * from client code for the name as given, so it is preferable to
+ * make the exception message be that string, per se, instead of
+ * trying to prettify it.
+ */
+ dvmThrowChainedException(gDvm.exClassNotFoundException, name, cause);
+}
+
+void dvmThrowExceptionInInitializerError()
+{
+ /*
+ * TODO: Do we want to wrap it if the original is an Error rather than
+ * an Exception?
+ *
+ * TODO: Should this just use dvmWrapException()?
+ */
+
+ if (gDvm.exExceptionInInitializerError == NULL) {
+ /*
+ * ExceptionInInitializerError isn't itself initialized. This
+ * can happen very early during VM startup if there is a
+ * problem with one of the corest-of-the-core classes, and it
+ * can possibly happen during a dexopt run. Rather than do
+ * anything fancier, we just abort here with a blatant
+ * message.
+ */
+ LOGE("Fatal error during early class initialization:");
+ dvmLogExceptionStackTrace();
+ dvmAbort();
+ }
+
+ Thread* self = dvmThreadSelf();
+ Object* exception = dvmGetException(self);
+
+ dvmAddTrackedAlloc(exception, self);
+ dvmClearException(self);
+
+ dvmThrowChainedException(gDvm.exExceptionInInitializerError,
+ NULL, exception);
+ dvmReleaseTrackedAlloc(exception, self);
+}
+
+void dvmThrowFileNotFoundException(const char* msg) {
+ dvmThrowException(gDvm.exFileNotFoundException, msg);
+}
+
+void dvmThrowIOException(const char* msg) {
+ dvmThrowException(gDvm.exIOException, msg);
+}
+
+void dvmThrowIllegalAccessException(const char* msg) {
+ dvmThrowException(gDvm.exIllegalAccessException, msg);
+}
+
+void dvmThrowIllegalAccessError(const char* msg) {
+ dvmThrowException(gDvm.exIllegalAccessError, msg);
+}
+
+void dvmThrowIllegalArgumentException(const char* msg) {
+ dvmThrowException(gDvm.exIllegalArgumentException, msg);
+}
+
+void dvmThrowIllegalMonitorStateException(const char* msg) {
+ dvmThrowException(gDvm.exIllegalMonitorStateException, msg);
+}
+
+void dvmThrowIllegalStateException(const char* msg) {
+ dvmThrowException(gDvm.exIllegalStateException, msg);
+}
+
+void dvmThrowIllegalThreadStateException(const char* msg) {
+ dvmThrowException(gDvm.exIllegalThreadStateException, msg);
+}
+
+void dvmThrowIncompatibleClassChangeError(const char* msg) {
+ dvmThrowException(gDvm.exIncompatibleClassChangeError, msg);
+}
+
+void dvmThrowIncompatibleClassChangeErrorWithClassMessage(
+ const char* descriptor)
+{
+ dvmThrowExceptionWithClassMessage(
+ gDvm.exIncompatibleClassChangeError, descriptor);
+}
+
+void dvmThrowInstantiationException(ClassObject* clazz, const char* extraDetail) {
+ std::string className(dvmHumanReadableDescriptor(clazz->descriptor));
+ dvmThrowExceptionFmt(gDvm.exInstantiationException,
+ "can't instantiate class %s%s%s", className.c_str(),
+ (extraDetail == NULL) ? "" : "; ",
+ (extraDetail == NULL) ? "" : extraDetail);
+}
+
+void dvmThrowInternalError(const char* msg) {
+ dvmThrowException(gDvm.exInternalError, msg);
+}
+
+void dvmThrowInterruptedException(const char* msg) {
+ dvmThrowException(gDvm.exInterruptedException, msg);
+}
+
+void dvmThrowLinkageError(const char* msg) {
+ dvmThrowException(gDvm.exLinkageError, msg);
+}
+
+void dvmThrowNegativeArraySizeException(s4 size) {
+ dvmThrowExceptionFmt(gDvm.exNegativeArraySizeException, "%d", size);
+}
+
+void dvmThrowNoClassDefFoundError(const char* descriptor) {
+ dvmThrowExceptionWithClassMessage(gDvm.exNoClassDefFoundError,
+ descriptor);
+}
+
+void dvmThrowChainedNoClassDefFoundError(const char* descriptor,
+ Object* cause) {
+ dvmThrowChainedExceptionWithClassMessage(
+ gDvm.exNoClassDefFoundError, descriptor, cause);
+}
+
+void dvmThrowNoSuchFieldError(const char* msg) {
+ dvmThrowException(gDvm.exNoSuchFieldError, msg);
+}
+
+void dvmThrowNoSuchFieldException(const char* msg) {
+ dvmThrowException(gDvm.exNoSuchFieldException, msg);
+}
+
+void dvmThrowNoSuchMethodError(const char* msg) {
+ dvmThrowException(gDvm.exNoSuchMethodError, msg);
+}
+
+void dvmThrowNullPointerException(const char* msg) {
+ dvmThrowException(gDvm.exNullPointerException, msg);
+}
+
+void dvmThrowOutOfMemoryError(const char* msg) {
+ dvmThrowException(gDvm.exOutOfMemoryError, msg);
+}
+
+void dvmThrowRuntimeException(const char* msg) {
+ dvmThrowException(gDvm.exRuntimeException, msg);
+}
+
+void dvmThrowStaleDexCacheError(const char* msg) {
+ dvmThrowException(gDvm.exStaleDexCacheError, msg);
+}
+
+void dvmThrowStringIndexOutOfBoundsExceptionWithIndex(jsize stringLength,
+ jsize requestIndex) {
+ dvmThrowExceptionFmt(gDvm.exStringIndexOutOfBoundsException,
+ "length=%d; index=%d", stringLength, requestIndex);
+}
+
+void dvmThrowStringIndexOutOfBoundsExceptionWithRegion(jsize stringLength,
+ jsize requestStart, jsize requestLength) {
+ dvmThrowExceptionFmt(gDvm.exStringIndexOutOfBoundsException,
+ "length=%d; regionStart=%d; regionLength=%d",
+ stringLength, requestStart, requestLength);
+}
+
+void dvmThrowTypeNotPresentException(const char* descriptor) {
+ dvmThrowExceptionWithClassMessage(gDvm.exTypeNotPresentException,
+ descriptor);
+}
+
+void dvmThrowUnsatisfiedLinkError(const char* msg) {
+ dvmThrowException(gDvm.exUnsatisfiedLinkError, msg);
+}
+
+void dvmThrowUnsupportedOperationException(const char* msg) {
+ dvmThrowException(gDvm.exUnsupportedOperationException, msg);
+}
+
+void dvmThrowVerifyError(const char* descriptor) {
+ dvmThrowExceptionWithClassMessage(gDvm.exVerifyError, descriptor);
+}
+
+void dvmThrowVirtualMachineError(const char* msg) {
+ dvmThrowException(gDvm.exVirtualMachineError, msg);
+}