OSDN Git Service

misc DalvikRunner changes
[android-x86/dalvik.git] / vm / Exception.c
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 /*
17  * Exception handling.
18  */
19 #include "Dalvik.h"
20 #include "libdex/DexCatch.h"
21
22 #include <stdlib.h>
23
24 /*
25 Notes on Exception Handling
26
27 We have one fairly sticky issue to deal with: creating the exception stack
28 trace.  The trouble is that we need the current value of the program
29 counter for the method now being executed, but that's only held in a local
30 variable or hardware register in the main interpreter loop.
31
32 The exception mechanism requires that the current stack trace be associated
33 with a Throwable at the time the Throwable is constructed.  The construction
34 may or may not be associated with a throw.  We have three situations to
35 consider:
36
37  (1) A Throwable is created with a "new Throwable" statement in the
38      application code, for immediate or deferred use with a "throw" statement.
39  (2) The VM throws an exception from within the interpreter core, e.g.
40      after an integer divide-by-zero.
41  (3) The VM throws an exception from somewhere deeper down, e.g. while
42      trying to link a class.
43
44 We need to have the current value for the PC, which means that for
45 situation (3) the interpreter loop must copy it to an externally-accessible
46 location before handling any opcode that could cause the VM to throw
47 an exception.  We can't store it globally, because the various threads
48 would trample each other.  We can't store it in the Thread structure,
49 because it'll get overwritten as soon as the Throwable constructor starts
50 executing.  It needs to go on the stack, but our stack frames hold the
51 caller's *saved* PC, not the current PC.
52
53 Situation #1 doesn't require special handling.  Situation #2 could be dealt
54 with by passing the PC into the exception creation function.  The trick
55 is to solve situation #3 in a way that adds minimal overhead to common
56 operations.  Making it more costly to throw an exception is acceptable.
57
58 There are a few ways to deal with this:
59
60  (a) Change "savedPc" to "currentPc" in the stack frame.  All of the
61      stack logic gets offset by one frame.  The current PC is written
62      to the current stack frame when necessary.
63  (b) Write the current PC into the current stack frame, but without
64      replacing "savedPc".  The JNI local refs pointer, which is only
65      used for native code, can be overloaded to save space.
66  (c) In dvmThrowException(), push an extra stack frame on, with the
67      current PC in it.  The current PC is written into the Thread struct
68      when necessary, and copied out when the VM throws.
69  (d) Before doing something that might throw an exception, push a
70      temporary frame on with the saved PC in it.
71
72 Solution (a) is the simplest, but breaks Dalvik's goal of mingling native
73 and interpreted stacks.
74
75 Solution (b) retains the simplicity of (a) without rearranging the stack,
76 but now in some cases we're storing the PC twice, which feels wrong.
77
78 Solution (c) usually works, because we push the saved PC onto the stack
79 before the Throwable construction can overwrite the copy in Thread.  One
80 way solution (c) could break is:
81  - Interpreter saves the PC
82  - Execute some bytecode, which runs successfully (and alters the saved PC)
83  - Throw an exception before re-saving the PC (i.e in the same opcode)
84 This is a risk for anything that could cause <clinit> to execute, e.g.
85 executing a static method or accessing a static field.  Attemping to access
86 a field that doesn't exist in a class that does exist might cause this.
87 It may be possible to simply bracket the dvmCallMethod*() functions to
88 save/restore it.
89
90 Solution (d) incurs additional overhead, but may have other benefits (e.g.
91 it's easy to find the stack frames that should be removed before storage
92 in the Throwable).
93
94 Current plan is option (b), because it's simple, fast, and doesn't change
95 the way the stack works.
96 */
97
98 /* fwd */
99 static bool initException(Object* exception, const char* msg, Object* cause,
100     Thread* self);
101
102
103 /*
104  * Cache pointers to some of the exception classes we use locally.
105  *
106  * Note this is NOT called during dexopt optimization.  Some of the fields
107  * are initialized by the verifier (dvmVerifyCodeFlow).
108  */
109 bool dvmExceptionStartup(void)
110 {
111     gDvm.classJavaLangThrowable =
112         dvmFindSystemClassNoInit("Ljava/lang/Throwable;");
113     gDvm.classJavaLangRuntimeException =
114         dvmFindSystemClassNoInit("Ljava/lang/RuntimeException;");
115     gDvm.classJavaLangStackOverflowError =
116         dvmFindSystemClassNoInit("Ljava/lang/StackOverflowError;");
117     gDvm.classJavaLangError =
118         dvmFindSystemClassNoInit("Ljava/lang/Error;");
119     gDvm.classJavaLangStackTraceElement =
120         dvmFindSystemClassNoInit("Ljava/lang/StackTraceElement;");
121     gDvm.classJavaLangStackTraceElementArray =
122         dvmFindArrayClass("[Ljava/lang/StackTraceElement;", NULL);
123     if (gDvm.classJavaLangThrowable == NULL ||
124         gDvm.classJavaLangStackTraceElement == NULL ||
125         gDvm.classJavaLangStackTraceElementArray == NULL)
126     {
127         LOGE("Could not find one or more essential exception classes\n");
128         return false;
129     }
130
131     /*
132      * Find the constructor.  Note that, unlike other saved method lookups,
133      * we're using a Method* instead of a vtable offset.  This is because
134      * constructors don't have vtable offsets.  (Also, since we're creating
135      * the object in question, it's impossible for anyone to sub-class it.)
136      */
137     Method* meth;
138     meth = dvmFindDirectMethodByDescriptor(gDvm.classJavaLangStackTraceElement,
139         "<init>", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;I)V");
140     if (meth == NULL) {
141         LOGE("Unable to find constructor for StackTraceElement\n");
142         return false;
143     }
144     gDvm.methJavaLangStackTraceElement_init = meth;
145
146     /* grab an offset for the stackData field */
147     gDvm.offJavaLangThrowable_stackState =
148         dvmFindFieldOffset(gDvm.classJavaLangThrowable,
149             "stackState", "Ljava/lang/Object;");
150     if (gDvm.offJavaLangThrowable_stackState < 0) {
151         LOGE("Unable to find Throwable.stackState\n");
152         return false;
153     }
154
155     /* and one for the message field, in case we want to show it */
156     gDvm.offJavaLangThrowable_message =
157         dvmFindFieldOffset(gDvm.classJavaLangThrowable,
158             "detailMessage", "Ljava/lang/String;");
159     if (gDvm.offJavaLangThrowable_message < 0) {
160         LOGE("Unable to find Throwable.detailMessage\n");
161         return false;
162     }
163
164     /* and one for the cause field, just 'cause */
165     gDvm.offJavaLangThrowable_cause =
166         dvmFindFieldOffset(gDvm.classJavaLangThrowable,
167             "cause", "Ljava/lang/Throwable;");
168     if (gDvm.offJavaLangThrowable_cause < 0) {
169         LOGE("Unable to find Throwable.cause\n");
170         return false;
171     }
172
173     return true;
174 }
175
176 /*
177  * Clean up.
178  */
179 void dvmExceptionShutdown(void)
180 {
181     // nothing to do
182 }
183
184
185 /*
186  * Format the message into a small buffer and pass it along.
187  */
188 void dvmThrowExceptionFmtV(const char* exceptionDescriptor, const char* fmt,
189     va_list args)
190 {
191     char msgBuf[512];
192
193     vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
194     dvmThrowChainedException(exceptionDescriptor, msgBuf, NULL);
195 }
196
197 /*
198  * Create a Throwable and throw an exception in the current thread (where
199  * "throwing" just means "set the thread's exception pointer").
200  *
201  * "msg" and/or "cause" may be NULL.
202  *
203  * If we have a bad exception hierarchy -- something in Throwable.<init>
204  * is missing -- then every attempt to throw an exception will result
205  * in another exception.  Exceptions are generally allowed to "chain"
206  * to other exceptions, so it's hard to auto-detect this problem.  It can
207  * only happen if the system classes are broken, so it's probably not
208  * worth spending cycles to detect it.
209  *
210  * We do have one case to worry about: if the classpath is completely
211  * wrong, we'll go into a death spin during startup because we can't find
212  * the initial class and then we can't find NoClassDefFoundError.  We have
213  * to handle this case.
214  *
215  * [Do we want to cache pointers to common exception classes?]
216  */
217 void dvmThrowChainedException(const char* exceptionDescriptor, const char* msg,
218     Object* cause)
219 {
220     ClassObject* excepClass;
221
222     LOGV("THROW '%s' msg='%s' cause=%s\n",
223         exceptionDescriptor, msg,
224         (cause != NULL) ? cause->clazz->descriptor : "(none)");
225
226     if (gDvm.initializing) {
227         if (++gDvm.initExceptionCount >= 2) {
228             LOGE("Too many exceptions during init (failed on '%s' '%s')\n",
229                 exceptionDescriptor, msg);
230             dvmAbort();
231         }
232     }
233
234     excepClass = dvmFindSystemClass(exceptionDescriptor);
235     if (excepClass == NULL) {
236         /*
237          * We couldn't find the exception class.  The attempt to find a
238          * nonexistent class should have raised an exception.  If no
239          * exception is currently raised, then we're pretty clearly unable
240          * to throw ANY sort of exception, and we need to pack it in.
241          *
242          * If we were able to throw the "class load failed" exception,
243          * stick with that.  Ideally we'd stuff the original exception
244          * into the "cause" field, but since we can't find it we can't
245          * do that.  The exception class name should be in the "message"
246          * field.
247          */
248         if (!dvmCheckException(dvmThreadSelf())) {
249             LOGE("FATAL: unable to throw exception (failed on '%s' '%s')\n",
250                 exceptionDescriptor, msg);
251             dvmAbort();
252         }
253         return;
254     }
255
256     dvmThrowChainedExceptionByClass(excepClass, msg, cause);
257 }
258
259 /*
260  * Start/continue throwing process now that we have a class reference.
261  */
262 void dvmThrowChainedExceptionByClass(ClassObject* excepClass, const char* msg,
263     Object* cause)
264 {
265     Thread* self = dvmThreadSelf();
266     Object* exception;
267
268     /* make sure the exception is initialized */
269     if (!dvmIsClassInitialized(excepClass) && !dvmInitClass(excepClass)) {
270         LOGE("ERROR: unable to initialize exception class '%s'\n",
271             excepClass->descriptor);
272         if (strcmp(excepClass->descriptor, "Ljava/lang/InternalError;") == 0)
273             dvmAbort();
274         dvmThrowChainedException("Ljava/lang/InternalError;",
275             "failed to init original exception class", cause);
276         return;
277     }
278
279     exception = dvmAllocObject(excepClass, ALLOC_DEFAULT);
280     if (exception == NULL) {
281         /*
282          * We're in a lot of trouble.  We might be in the process of
283          * throwing an out-of-memory exception, in which case the
284          * pre-allocated object will have been thrown when our object alloc
285          * failed.  So long as there's an exception raised, return and
286          * allow the system to try to recover.  If not, something is broken
287          * and we need to bail out.
288          */
289         if (dvmCheckException(self))
290             goto bail;
291         LOGE("FATAL: unable to allocate exception '%s' '%s'\n",
292             excepClass->descriptor, msg != NULL ? msg : "(no msg)");
293         dvmAbort();
294     }
295
296     /*
297      * Init the exception.
298      */
299     if (gDvm.optimizing) {
300         /* need the exception object, but can't invoke interpreted code */
301         LOGV("Skipping init of exception %s '%s'\n",
302             excepClass->descriptor, msg);
303     } else {
304         assert(excepClass == exception->clazz);
305         if (!initException(exception, msg, cause, self)) {
306             /*
307              * Whoops.  If we can't initialize the exception, we can't use
308              * it.  If there's an exception already set, the constructor
309              * probably threw an OutOfMemoryError.
310              */
311             if (!dvmCheckException(self)) {
312                 /*
313                  * We're required to throw something, so we just
314                  * throw the pre-constructed internal error.
315                  */
316                 self->exception = gDvm.internalErrorObj;
317             }
318             goto bail;
319         }
320     }
321
322     self->exception = exception;
323
324 bail:
325     dvmReleaseTrackedAlloc(exception, self);
326 }
327
328 /*
329  * Throw the named exception using the dotted form of the class
330  * descriptor as the exception message, and with the specified cause.
331  */
332 void dvmThrowChainedExceptionWithClassMessage(const char* exceptionDescriptor,
333     const char* messageDescriptor, Object* cause)
334 {
335     char* message = dvmDescriptorToDot(messageDescriptor);
336
337     dvmThrowChainedException(exceptionDescriptor, message, cause);
338     free(message);
339 }
340
341 /*
342  * Like dvmThrowExceptionWithMessageFromDescriptor, but take a
343  * class object instead of a name.
344  */
345 void dvmThrowExceptionByClassWithClassMessage(ClassObject* exceptionClass,
346     const char* messageDescriptor)
347 {
348     char* message = dvmDescriptorToName(messageDescriptor);
349
350     dvmThrowExceptionByClass(exceptionClass, message);
351     free(message);
352 }
353
354 /*
355  * Find and return an exception constructor method that can take the
356  * indicated parameters, or return NULL if no such constructor exists.
357  */
358 static Method* findExceptionInitMethod(ClassObject* excepClass,
359     bool hasMessage, bool hasCause)
360 {
361     if (hasMessage) {
362         Method* result;
363
364         if (hasCause) {
365             result = dvmFindDirectMethodByDescriptor(
366                     excepClass, "<init>",
367                     "(Ljava/lang/String;Ljava/lang/Throwable;)V");
368         } else {
369             result = dvmFindDirectMethodByDescriptor(
370                     excepClass, "<init>", "(Ljava/lang/String;)V");
371         }
372
373         if (result != NULL) {
374             return result;
375         }
376
377         if (hasCause) {
378             return dvmFindDirectMethodByDescriptor(
379                     excepClass, "<init>",
380                     "(Ljava/lang/Object;Ljava/lang/Throwable;)V");
381         } else {
382             return dvmFindDirectMethodByDescriptor(
383                     excepClass, "<init>", "(Ljava/lang/Object;)V");
384         }
385     } else if (hasCause) {
386         return dvmFindDirectMethodByDescriptor(
387                 excepClass, "<init>", "(Ljava/lang/Throwable;)V");
388     } else {
389         return dvmFindDirectMethodByDescriptor(excepClass, "<init>", "()V");
390     }
391 }
392
393 /*
394  * Initialize an exception with an appropriate constructor.
395  *
396  * "exception" is the exception object to initialize.
397  * Either or both of "msg" and "cause" may be null.
398  * "self" is dvmThreadSelf(), passed in so we don't have to look it up again.
399  *
400  * If the process of initializing the exception causes another
401  * exception (e.g., OutOfMemoryError) to be thrown, return an error
402  * and leave self->exception intact.
403  */
404 static bool initException(Object* exception, const char* msg, Object* cause,
405     Thread* self)
406 {
407     enum {
408         kInitUnknown,
409         kInitNoarg,
410         kInitMsg,
411         kInitMsgThrow,
412         kInitThrow
413     } initKind = kInitUnknown;
414     Method* initMethod = NULL;
415     ClassObject* excepClass = exception->clazz;
416     StringObject* msgStr = NULL;
417     bool result = false;
418     bool needInitCause = false;
419
420     assert(self != NULL);
421     assert(self->exception == NULL);
422
423     /* if we have a message, create a String */
424     if (msg == NULL)
425         msgStr = NULL;
426     else {
427         msgStr = dvmCreateStringFromCstr(msg, ALLOC_DEFAULT);
428         if (msgStr == NULL) {
429             LOGW("Could not allocate message string \"%s\" while "
430                     "throwing internal exception (%s)\n",
431                     msg, excepClass->descriptor);
432             goto bail;
433         }
434     }
435
436     if (cause != NULL) {
437         if (!dvmInstanceof(cause->clazz, gDvm.classJavaLangThrowable)) {
438             LOGE("Tried to init exception with cause '%s'\n",
439                 cause->clazz->descriptor);
440             dvmAbort();
441         }
442     }
443
444     /*
445      * The Throwable class has four public constructors:
446      *  (1) Throwable()
447      *  (2) Throwable(String message)
448      *  (3) Throwable(String message, Throwable cause)  (added in 1.4)
449      *  (4) Throwable(Throwable cause)                  (added in 1.4)
450      *
451      * The first two are part of the original design, and most exception
452      * classes should support them.  The third prototype was used by
453      * individual exceptions. e.g. ClassNotFoundException added it in 1.2.
454      * The general "cause" mechanism was added in 1.4.  Some classes,
455      * such as IllegalArgumentException, initially supported the first
456      * two, but added the second two in a later release.
457      *
458      * Exceptions may be picky about how their "cause" field is initialized.
459      * If you call ClassNotFoundException(String), it may choose to
460      * initialize its "cause" field to null.  Doing so prevents future
461      * calls to Throwable.initCause().
462      *
463      * So, if "cause" is not NULL, we need to look for a constructor that
464      * takes a throwable.  If we can't find one, we fall back on calling
465      * #1/#2 and making a separate call to initCause().  Passing a null ref
466      * for "message" into Throwable(String, Throwable) is allowed, but we
467      * prefer to use the Throwable-only version because it has different
468      * behavior.
469      *
470      * java.lang.TypeNotPresentException is a strange case -- it has #3 but
471      * not #2.  (Some might argue that the constructor is actually not #3,
472      * because it doesn't take the message string as an argument, but it
473      * has the same effect and we can work with it here.)
474      *
475      * java.lang.AssertionError is also a strange case -- it has a
476      * constructor that takes an Object, but not one that takes a String.
477      * There may be other cases like this, as well, so we generally look
478      * for an Object-taking constructor if we can't find one that takes
479      * a String.
480      */
481     if (cause == NULL) {
482         if (msgStr == NULL) {
483             initMethod = findExceptionInitMethod(excepClass, false, false);
484             initKind = kInitNoarg;
485         } else {
486             initMethod = findExceptionInitMethod(excepClass, true, false);
487             if (initMethod != NULL) {
488                 initKind = kInitMsg;
489             } else {
490                 /* no #2, try #3 */
491                 initMethod = findExceptionInitMethod(excepClass, true, true);
492                 if (initMethod != NULL) {
493                     initKind = kInitMsgThrow;
494                 }
495             }
496         }
497     } else {
498         if (msgStr == NULL) {
499             initMethod = findExceptionInitMethod(excepClass, false, true);
500             if (initMethod != NULL) {
501                 initKind = kInitThrow;
502             } else {
503                 initMethod = findExceptionInitMethod(excepClass, false, false);
504                 initKind = kInitNoarg;
505                 needInitCause = true;
506             }
507         } else {
508             initMethod = findExceptionInitMethod(excepClass, true, true);
509             if (initMethod != NULL) {
510                 initKind = kInitMsgThrow;
511             } else {
512                 initMethod = findExceptionInitMethod(excepClass, true, false);
513                 initKind = kInitMsg;
514                 needInitCause = true;
515             }
516         }
517     }
518
519     if (initMethod == NULL) {
520         /*
521          * We can't find the desired constructor.  This can happen if a
522          * subclass of java/lang/Throwable doesn't define an expected
523          * constructor, e.g. it doesn't provide one that takes a string
524          * when a message has been provided.
525          */
526         LOGW("WARNING: exception class '%s' missing constructor "
527             "(msg='%s' kind=%d)\n",
528             excepClass->descriptor, msg, initKind);
529         assert(strcmp(excepClass->descriptor,
530                       "Ljava/lang/RuntimeException;") != 0);
531         dvmThrowChainedException("Ljava/lang/RuntimeException;", 
532             "re-throw on exception class missing constructor", NULL);
533         goto bail;
534     }
535
536     /*
537      * Call the constructor with the appropriate arguments.
538      */
539     JValue unused;
540     switch (initKind) {
541     case kInitNoarg:
542         LOGVV("+++ exc noarg (ic=%d)\n", needInitCause);
543         dvmCallMethod(self, initMethod, exception, &unused);
544         break;
545     case kInitMsg:
546         LOGVV("+++ exc msg (ic=%d)\n", needInitCause);
547         dvmCallMethod(self, initMethod, exception, &unused, msgStr);
548         break;
549     case kInitThrow:
550         LOGVV("+++ exc throw");
551         assert(!needInitCause);
552         dvmCallMethod(self, initMethod, exception, &unused, cause);
553         break;
554     case kInitMsgThrow:
555         LOGVV("+++ exc msg+throw");
556         assert(!needInitCause);
557         dvmCallMethod(self, initMethod, exception, &unused, msgStr, cause);
558         break;
559     default:
560         assert(false);
561         goto bail;
562     }
563
564     /*
565      * It's possible the constructor has thrown an exception.  If so, we
566      * return an error and let our caller deal with it.
567      */
568     if (self->exception != NULL) {
569         LOGW("Exception thrown (%s) while throwing internal exception (%s)\n",
570             self->exception->clazz->descriptor, exception->clazz->descriptor);
571         goto bail;
572     }
573
574     /*
575      * If this exception was caused by another exception, and we weren't
576      * able to find a cause-setting constructor, set the "cause" field
577      * with an explicit call.
578      */
579     if (needInitCause) {
580         Method* initCause;
581         initCause = dvmFindVirtualMethodHierByDescriptor(excepClass, "initCause",
582             "(Ljava/lang/Throwable;)Ljava/lang/Throwable;");
583         if (initCause != NULL) {
584             dvmCallMethod(self, initCause, exception, &unused, cause);
585             if (self->exception != NULL) {
586                 /* initCause() threw an exception; return an error and
587                  * let the caller deal with it.
588                  */
589                 LOGW("Exception thrown (%s) during initCause() "
590                         "of internal exception (%s)\n",
591                         self->exception->clazz->descriptor,
592                         exception->clazz->descriptor);
593                 goto bail;
594             }
595         } else {
596             LOGW("WARNING: couldn't find initCause in '%s'\n",
597                 excepClass->descriptor);
598         }
599     }
600
601
602     result = true;
603
604 bail:
605     dvmReleaseTrackedAlloc((Object*) msgStr, self);     // NULL is ok
606     return result;
607 }
608
609
610 /*
611  * Clear the pending exception and the "initExceptionCount" counter.  This
612  * is used by the optimization and verification code, which has to run with
613  * "initializing" set to avoid going into a death-spin if the "class not
614  * found" exception can't be found.
615  *
616  * This can also be called when the VM is in a "normal" state, e.g. when
617  * verifying classes that couldn't be verified at optimization time.  The
618  * reset of initExceptionCount should be harmless in that case.
619  */
620 void dvmClearOptException(Thread* self)
621 {
622     self->exception = NULL;
623     gDvm.initExceptionCount = 0;
624 }
625
626 /*
627  * Returns "true" if this is a "checked" exception, i.e. it's a subclass
628  * of Throwable (assumed) but not a subclass of RuntimeException or Error.
629  */
630 bool dvmIsCheckedException(const Object* exception)
631 {
632     if (dvmInstanceof(exception->clazz, gDvm.classJavaLangError) ||
633         dvmInstanceof(exception->clazz, gDvm.classJavaLangRuntimeException))
634     {
635         return false;
636     } else {
637         return true;
638     }
639 }
640
641 /*
642  * Wrap the now-pending exception in a different exception.  This is useful
643  * for reflection stuff that wants to hand a checked exception back from a
644  * method that doesn't declare it.
645  *
646  * If something fails, an (unchecked) exception related to that failure
647  * will be pending instead.
648  */
649 void dvmWrapException(const char* newExcepStr)
650 {
651     Thread* self = dvmThreadSelf();
652     Object* origExcep;
653     ClassObject* iteClass;
654
655     origExcep = dvmGetException(self);
656     dvmAddTrackedAlloc(origExcep, self);    // don't let the GC free it
657
658     dvmClearException(self);                // clear before class lookup
659     iteClass = dvmFindSystemClass(newExcepStr);
660     if (iteClass != NULL) {
661         Object* iteExcep;
662         Method* initMethod;
663
664         iteExcep = dvmAllocObject(iteClass, ALLOC_DEFAULT);
665         if (iteExcep != NULL) {
666             initMethod = dvmFindDirectMethodByDescriptor(iteClass, "<init>",
667                             "(Ljava/lang/Throwable;)V");
668             if (initMethod != NULL) {
669                 JValue unused;
670                 dvmCallMethod(self, initMethod, iteExcep, &unused,
671                     origExcep);
672
673                 /* if <init> succeeded, replace the old exception */
674                 if (!dvmCheckException(self))
675                     dvmSetException(self, iteExcep);
676             }
677             dvmReleaseTrackedAlloc(iteExcep, NULL);
678
679             /* if initMethod doesn't exist, or failed... */
680             if (!dvmCheckException(self))
681                 dvmSetException(self, origExcep);
682         } else {
683             /* leave OutOfMemoryError pending */
684         }
685     } else {
686         /* leave ClassNotFoundException pending */
687     }
688
689     assert(dvmCheckException(self));
690     dvmReleaseTrackedAlloc(origExcep, self);
691 }
692
693 /*
694  * Get the "cause" field from an exception.
695  *
696  * The Throwable class initializes the "cause" field to "this" to
697  * differentiate between being initialized to null and never being
698  * initialized.  We check for that here and convert it to NULL.
699  */
700 Object* dvmGetExceptionCause(const Object* exception)
701 {
702     if (!dvmInstanceof(exception->clazz, gDvm.classJavaLangThrowable)) {
703         LOGE("Tried to get cause from object of type '%s'\n",
704             exception->clazz->descriptor);
705         dvmAbort();
706     }
707     Object* cause =
708         dvmGetFieldObject(exception, gDvm.offJavaLangThrowable_cause);
709     if (cause == exception)
710         return NULL;
711     else
712         return cause;
713 }
714
715 /*
716  * Print the stack trace of the current exception on stderr.  This is called
717  * from the JNI ExceptionDescribe call.
718  *
719  * For consistency we just invoke the Throwable printStackTrace method,
720  * which might be overridden in the exception object.
721  *
722  * Exceptions thrown during the course of printing the stack trace are
723  * ignored.
724  */
725 void dvmPrintExceptionStackTrace(void)
726 {
727     Thread* self = dvmThreadSelf();
728     Object* exception;
729     Method* printMethod;
730
731     exception = self->exception;
732     if (exception == NULL)
733         return;
734
735     self->exception = NULL;
736     printMethod = dvmFindVirtualMethodHierByDescriptor(exception->clazz,
737                     "printStackTrace", "()V");
738     if (printMethod != NULL) {
739         JValue unused;
740         dvmCallMethod(self, printMethod, exception, &unused);
741     } else {
742         LOGW("WARNING: could not find printStackTrace in %s\n",
743             exception->clazz->descriptor);
744     }
745
746     if (self->exception != NULL) {
747         LOGI("NOTE: exception thrown while printing stack trace: %s\n",
748             self->exception->clazz->descriptor);
749     }
750
751     self->exception = exception;
752 }
753
754 /*
755  * Search the method's list of exceptions for a match.
756  *
757  * Returns the offset of the catch block on success, or -1 on failure.
758  */
759 static int findCatchInMethod(Thread* self, const Method* method, int relPc,
760     ClassObject* excepClass)
761 {
762     /*
763      * Need to clear the exception before entry.  Otherwise, dvmResolveClass
764      * might think somebody threw an exception while it was loading a class.
765      */
766     assert(!dvmCheckException(self));
767     assert(!dvmIsNativeMethod(method));
768
769     LOGVV("findCatchInMethod %s.%s excep=%s depth=%d\n",
770         method->clazz->descriptor, method->name, excepClass->descriptor,
771         dvmComputeExactFrameDepth(self->curFrame));
772
773     DvmDex* pDvmDex = method->clazz->pDvmDex;
774     const DexCode* pCode = dvmGetMethodCode(method);
775     DexCatchIterator iterator;
776
777     if (dexFindCatchHandler(&iterator, pCode, relPc)) {
778         for (;;) {
779             DexCatchHandler* handler = dexCatchIteratorNext(&iterator);
780
781             if (handler == NULL) {
782                 break;
783             }
784                 
785             if (handler->typeIdx == kDexNoIndex) {
786                 /* catch-all */
787                 LOGV("Match on catch-all block at 0x%02x in %s.%s for %s\n",
788                         relPc, method->clazz->descriptor,
789                         method->name, excepClass->descriptor);
790                 return handler->address;
791             }
792
793             ClassObject* throwable =
794                 dvmDexGetResolvedClass(pDvmDex, handler->typeIdx);
795             if (throwable == NULL) {
796                 /*
797                  * TODO: this behaves badly if we run off the stack
798                  * while trying to throw an exception.  The problem is
799                  * that, if we're in a class loaded by a class loader,
800                  * the call to dvmResolveClass has to ask the class
801                  * loader for help resolving any previously-unresolved
802                  * classes.  If this particular class loader hasn't
803                  * resolved StackOverflowError, it will call into
804                  * interpreted code, and blow up.
805                  *
806                  * We currently replace the previous exception with
807                  * the StackOverflowError, which means they won't be
808                  * catching it *unless* they explicitly catch
809                  * StackOverflowError, in which case we'll be unable
810                  * to resolve the class referred to by the "catch"
811                  * block.
812                  *
813                  * We end up getting a huge pile of warnings if we do
814                  * a simple synthetic test, because this method gets
815                  * called on every stack frame up the tree, and it
816                  * fails every time.
817                  *
818                  * This eventually bails out, effectively becoming an
819                  * uncatchable exception, so other than the flurry of
820                  * warnings it's not really a problem.  Still, we could
821                  * probably handle this better.
822                  */
823                 throwable = dvmResolveClass(method->clazz, handler->typeIdx,
824                     true);
825                 if (throwable == NULL) {
826                     /*
827                      * We couldn't find the exception they wanted in
828                      * our class files (or, perhaps, the stack blew up
829                      * while we were querying a class loader). Cough
830                      * up a warning, then move on to the next entry.
831                      * Keep the exception status clear.
832                      */
833                     LOGW("Could not resolve class ref'ed in exception "
834                             "catch list (class index %d, exception %s)\n",
835                             handler->typeIdx,
836                             (self->exception != NULL) ?
837                             self->exception->clazz->descriptor : "(none)");
838                     dvmClearException(self);
839                     continue;
840                 }
841             }
842
843             //LOGD("ADDR MATCH, check %s instanceof %s\n",
844             //    excepClass->descriptor, pEntry->excepClass->descriptor);
845
846             if (dvmInstanceof(excepClass, throwable)) {
847                 LOGV("Match on catch block at 0x%02x in %s.%s for %s\n",
848                         relPc, method->clazz->descriptor,
849                         method->name, excepClass->descriptor);
850                 return handler->address;
851             }
852         }
853     }
854
855     LOGV("No matching catch block at 0x%02x in %s for %s\n",
856         relPc, method->name, excepClass->descriptor);
857     return -1;
858 }
859
860 /*
861  * Find a matching "catch" block.  "pc" is the relative PC within the
862  * current method, indicating the offset from the start in 16-bit units.
863  *
864  * Returns the offset to the catch block, or -1 if we run up against a
865  * break frame without finding anything.
866  *
867  * The class resolution stuff we have to do while evaluating the "catch"
868  * blocks could cause an exception.  The caller should clear the exception
869  * before calling here and restore it after.
870  *
871  * Sets *newFrame to the frame pointer of the frame with the catch block.
872  * If "scanOnly" is false, self->curFrame is also set to this value.
873  */
874 int dvmFindCatchBlock(Thread* self, int relPc, Object* exception,
875     bool scanOnly, void** newFrame)
876 {
877     void* fp = self->curFrame;
878     int catchAddr = -1;
879
880     assert(!dvmCheckException(self));
881
882     while (true) {
883         StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
884         catchAddr = findCatchInMethod(self, saveArea->method, relPc,
885                         exception->clazz);
886         if (catchAddr >= 0)
887             break;
888
889         /*
890          * Normally we'd check for ACC_SYNCHRONIZED methods and unlock
891          * them as we unroll.  Dalvik uses what amount to generated
892          * "finally" blocks to take care of this for us.
893          */
894
895         /* output method profiling info */
896         if (!scanOnly) {
897             TRACE_METHOD_UNROLL(self, saveArea->method);
898         }
899
900         /*
901          * Move up one frame.  If the next thing up is a break frame,
902          * break out now so we're left unrolled to the last method frame.
903          * We need to point there so we can roll up the JNI local refs
904          * if this was a native method.
905          */
906         assert(saveArea->prevFrame != NULL);
907         if (dvmIsBreakFrame(saveArea->prevFrame)) {
908             if (!scanOnly)
909                 break;      // bail with catchAddr == -1
910
911             /*
912              * We're scanning for the debugger.  It needs to know if this
913              * exception is going to be caught or not, and we need to figure
914              * out if it will be caught *ever* not just between the current
915              * position and the next break frame.  We can't tell what native
916              * code is going to do, so we assume it never catches exceptions.
917              *
918              * Start by finding an interpreted code frame.
919              */
920             fp = saveArea->prevFrame;           // this is the break frame
921             saveArea = SAVEAREA_FROM_FP(fp);
922             fp = saveArea->prevFrame;           // this may be a good one
923             while (fp != NULL) {
924                 if (!dvmIsBreakFrame(fp)) {
925                     saveArea = SAVEAREA_FROM_FP(fp);
926                     if (!dvmIsNativeMethod(saveArea->method))
927                         break;
928                 }
929
930                 fp = SAVEAREA_FROM_FP(fp)->prevFrame;
931             }
932             if (fp == NULL)
933                 break;      // bail with catchAddr == -1
934
935             /*
936              * Now fp points to the "good" frame.  When the interp code
937              * invoked the native code, it saved a copy of its current PC
938              * into xtra.currentPc.  Pull it out of there.
939              */
940             relPc =
941                 saveArea->xtra.currentPc - SAVEAREA_FROM_FP(fp)->method->insns;
942         } else {
943             fp = saveArea->prevFrame;
944
945             /* savedPc in was-current frame goes with method in now-current */
946             relPc = saveArea->savedPc - SAVEAREA_FROM_FP(fp)->method->insns;
947         }
948     }
949
950     if (!scanOnly)
951         self->curFrame = fp;
952
953     /*
954      * The class resolution in findCatchInMethod() could cause an exception.
955      * Clear it to be safe.
956      */
957     self->exception = NULL;
958
959     *newFrame = fp;
960     return catchAddr;
961 }
962
963 /*
964  * We have to carry the exception's stack trace around, but in many cases
965  * it will never be examined.  It makes sense to keep it in a compact,
966  * VM-specific object, rather than an array of Objects with strings.
967  *
968  * Pass in the thread whose stack we're interested in.  If "thread" is
969  * not self, the thread must be suspended.  This implies that the thread
970  * list lock is held, which means we can't allocate objects or we risk
971  * jamming the GC.  So, we allow this function to return different formats.
972  * (This shouldn't be called directly -- see the inline functions in the
973  * header file.)
974  *
975  * If "wantObject" is true, this returns a newly-allocated Object, which is
976  * presently an array of integers, but could become something else in the
977  * future.  If "wantObject" is false, return plain malloc data.
978  *
979  * NOTE: if we support class unloading, we will need to scan the class
980  * object references out of these arrays.
981  */
982 void* dvmFillInStackTraceInternal(Thread* thread, bool wantObject, int* pCount)
983 {
984     ArrayObject* stackData = NULL;
985     int* simpleData = NULL;
986     void* fp;
987     void* startFp;
988     int stackDepth;
989     int* intPtr;
990
991     if (pCount != NULL)
992         *pCount = 0;
993     fp = thread->curFrame;
994
995     assert(thread == dvmThreadSelf() || dvmIsSuspended(thread));
996
997     /*
998      * We're looking at a stack frame for code running below a Throwable
999      * constructor.  We want to remove the Throwable methods and the
1000      * superclass initializations so the user doesn't see them when they
1001      * read the stack dump.
1002      *
1003      * TODO: this just scrapes off the top layers of Throwable.  Might not do
1004      * the right thing if we create an exception object or cause a VM
1005      * exception while in a Throwable method.
1006      */
1007     while (fp != NULL) {
1008         const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
1009         const Method* method = saveArea->method;
1010
1011         if (dvmIsBreakFrame(fp))
1012             break;
1013         if (!dvmInstanceof(method->clazz, gDvm.classJavaLangThrowable))
1014             break;
1015         //LOGD("EXCEP: ignoring %s.%s\n",
1016         //         method->clazz->descriptor, method->name);
1017         fp = saveArea->prevFrame;
1018     }
1019     startFp = fp;
1020
1021     /*
1022      * Compute the stack depth.
1023      */
1024     stackDepth = 0;
1025     while (fp != NULL) {
1026         const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
1027
1028         if (!dvmIsBreakFrame(fp))
1029             stackDepth++;
1030
1031         assert(fp != saveArea->prevFrame);
1032         fp = saveArea->prevFrame;
1033     }
1034     //LOGD("EXCEP: stack depth is %d\n", stackDepth);
1035
1036     if (!stackDepth)
1037         goto bail;
1038
1039     /*
1040      * We need to store a pointer to the Method and the program counter.
1041      * We have 4-byte pointers, so we use '[I'.
1042      */
1043     if (wantObject) {
1044         assert(sizeof(Method*) == 4);
1045         stackData = dvmAllocPrimitiveArray('I', stackDepth*2, ALLOC_DEFAULT);
1046         if (stackData == NULL) {
1047             assert(dvmCheckException(dvmThreadSelf()));
1048             goto bail;
1049         }
1050         intPtr = (int*) stackData->contents;
1051     } else {
1052         /* array of ints; first entry is stack depth */
1053         assert(sizeof(Method*) == sizeof(int));
1054         simpleData = (int*) malloc(sizeof(int) * stackDepth*2);
1055         if (simpleData == NULL)
1056             goto bail;
1057
1058         assert(pCount != NULL);
1059         intPtr = simpleData;
1060     }
1061     if (pCount != NULL)
1062         *pCount = stackDepth;
1063
1064     fp = startFp;
1065     while (fp != NULL) {
1066         const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
1067         const Method* method = saveArea->method;
1068
1069         if (!dvmIsBreakFrame(fp)) {
1070             //LOGD("EXCEP keeping %s.%s\n", method->clazz->descriptor,
1071             //         method->name);
1072
1073             *intPtr++ = (int) method;
1074             if (dvmIsNativeMethod(method)) {
1075                 *intPtr++ = 0;      /* no saved PC for native methods */
1076             } else {
1077                 assert(saveArea->xtra.currentPc >= method->insns &&
1078                         saveArea->xtra.currentPc < 
1079                         method->insns + dvmGetMethodInsnsSize(method));
1080                 *intPtr++ = (int) (saveArea->xtra.currentPc - method->insns);
1081             }
1082
1083             stackDepth--;       // for verification
1084         }
1085
1086         assert(fp != saveArea->prevFrame);
1087         fp = saveArea->prevFrame;
1088     }
1089     assert(stackDepth == 0);
1090
1091 bail:
1092     if (wantObject) {
1093         dvmReleaseTrackedAlloc((Object*) stackData, dvmThreadSelf());
1094         return stackData;
1095     } else {
1096         return simpleData;
1097     }
1098 }
1099
1100
1101 /*
1102  * Given an Object previously created by dvmFillInStackTrace(), use the
1103  * contents of the saved stack trace to generate an array of
1104  * java/lang/StackTraceElement objects.
1105  *
1106  * The returned array is not added to the "local refs" list.
1107  */
1108 ArrayObject* dvmGetStackTrace(const Object* ostackData)
1109 {
1110     const ArrayObject* stackData = (const ArrayObject*) ostackData;
1111     const int* intVals;
1112     int i, stackSize;
1113
1114     stackSize = stackData->length / 2;
1115     intVals = (const int*) stackData->contents;
1116     return dvmGetStackTraceRaw(intVals, stackSize);
1117 }
1118
1119 /*
1120  * Generate an array of StackTraceElement objects from the raw integer
1121  * data encoded by dvmFillInStackTrace().
1122  *
1123  * "intVals" points to the first {method,pc} pair.
1124  *
1125  * The returned array is not added to the "local refs" list.
1126  */
1127 ArrayObject* dvmGetStackTraceRaw(const int* intVals, int stackDepth)
1128 {
1129     ArrayObject* steArray = NULL;
1130     Object** stePtr;
1131     int i;
1132
1133     /* init this if we haven't yet */
1134     if (!dvmIsClassInitialized(gDvm.classJavaLangStackTraceElement))
1135         dvmInitClass(gDvm.classJavaLangStackTraceElement);
1136
1137     /* allocate a StackTraceElement array */
1138     steArray = dvmAllocArray(gDvm.classJavaLangStackTraceElementArray,
1139                     stackDepth, kObjectArrayRefWidth, ALLOC_DEFAULT);
1140     if (steArray == NULL)
1141         goto bail;
1142     stePtr = (Object**) steArray->contents;
1143
1144     /*
1145      * Allocate and initialize a StackTraceElement for each stack frame.
1146      * We use the standard constructor to configure the object.
1147      */
1148     for (i = 0; i < stackDepth; i++) {
1149         Object* ste;
1150         Method* meth;
1151         StringObject* className;
1152         StringObject* methodName;
1153         StringObject* fileName;
1154         int lineNumber, pc;
1155         const char* sourceFile;
1156         char* dotName;
1157
1158         ste = dvmAllocObject(gDvm.classJavaLangStackTraceElement,ALLOC_DEFAULT);
1159         if (ste == NULL)
1160             goto bail;
1161
1162         meth = (Method*) *intVals++;
1163         pc = *intVals++;
1164
1165         if (pc == -1)      // broken top frame?
1166             lineNumber = 0;
1167         else
1168             lineNumber = dvmLineNumFromPC(meth, pc);
1169
1170         dotName = dvmDescriptorToDot(meth->clazz->descriptor);
1171         className = dvmCreateStringFromCstr(dotName, ALLOC_DEFAULT);
1172         free(dotName);
1173
1174         methodName = dvmCreateStringFromCstr(meth->name, ALLOC_DEFAULT);
1175         sourceFile = dvmGetMethodSourceFile(meth);
1176         if (sourceFile != NULL)
1177             fileName = dvmCreateStringFromCstr(sourceFile, ALLOC_DEFAULT);
1178         else
1179             fileName = NULL;
1180
1181         /*
1182          * Invoke:
1183          *  public StackTraceElement(String declaringClass, String methodName,
1184          *      String fileName, int lineNumber)
1185          * (where lineNumber==-2 means "native")
1186          */
1187         JValue unused;
1188         dvmCallMethod(dvmThreadSelf(), gDvm.methJavaLangStackTraceElement_init,
1189             ste, &unused, className, methodName, fileName, lineNumber);
1190
1191         dvmReleaseTrackedAlloc(ste, NULL);
1192         dvmReleaseTrackedAlloc((Object*) className, NULL);
1193         dvmReleaseTrackedAlloc((Object*) methodName, NULL);
1194         dvmReleaseTrackedAlloc((Object*) fileName, NULL);
1195
1196         if (dvmCheckException(dvmThreadSelf()))
1197             goto bail;
1198
1199         *stePtr++ = ste;
1200     }
1201
1202 bail:
1203     dvmReleaseTrackedAlloc((Object*) steArray, NULL);
1204     return steArray;
1205 }
1206
1207 /*
1208  * Dump the contents of a raw stack trace to the log.
1209  */
1210 void dvmLogRawStackTrace(const int* intVals, int stackDepth)
1211 {
1212     int i;
1213
1214     /*
1215      * Run through the array of stack frame data.
1216      */
1217     for (i = 0; i < stackDepth; i++) {
1218         Method* meth;
1219         int lineNumber, pc;
1220         const char* sourceFile;
1221         char* dotName;
1222
1223         meth = (Method*) *intVals++;
1224         pc = *intVals++;
1225
1226         if (pc == -1)      // broken top frame?
1227             lineNumber = 0;
1228         else
1229             lineNumber = dvmLineNumFromPC(meth, pc);
1230
1231         // probably don't need to do this, but it looks nicer
1232         dotName = dvmDescriptorToDot(meth->clazz->descriptor);
1233
1234         if (dvmIsNativeMethod(meth)) {
1235             LOGI("\tat %s.%s(Native Method)\n", dotName, meth->name);
1236         } else {
1237             LOGI("\tat %s.%s(%s:%d)\n",
1238                 dotName, meth->name, dvmGetMethodSourceFile(meth),
1239                 dvmLineNumFromPC(meth, pc));
1240         }
1241
1242         free(dotName);
1243
1244         sourceFile = dvmGetMethodSourceFile(meth);
1245     }
1246 }
1247
1248 /*
1249  * Print the direct stack trace of the given exception to the log.
1250  */
1251 static void logStackTraceOf(Object* exception)
1252 {
1253     const ArrayObject* stackData;
1254     StringObject* messageStr;
1255     int stackSize;
1256     const int* intVals;
1257
1258     messageStr = (StringObject*) dvmGetFieldObject(exception,
1259                     gDvm.offJavaLangThrowable_message);
1260     if (messageStr != NULL) {
1261         char* cp = dvmCreateCstrFromString(messageStr);
1262         LOGI("%s: %s\n", exception->clazz->descriptor, cp);
1263         free(cp);
1264     } else {
1265         LOGI("%s:\n", exception->clazz->descriptor);
1266     }
1267
1268     stackData = (const ArrayObject*) dvmGetFieldObject(exception,
1269                     gDvm.offJavaLangThrowable_stackState);
1270     if (stackData == NULL) {
1271         LOGI("  (no stack trace data found)\n");
1272         return;
1273     }
1274
1275     stackSize = stackData->length / 2;
1276     intVals = (const int*) stackData->contents;
1277
1278     dvmLogRawStackTrace(intVals, stackSize);
1279 }
1280
1281 /*
1282  * Print the stack trace of the current thread's exception, as well as
1283  * the stack traces of any chained exceptions, to the log. We extract
1284  * the stored stack trace and process it internally instead of calling
1285  * interpreted code.
1286  */
1287 void dvmLogExceptionStackTrace(void)
1288 {
1289     Object* exception = dvmThreadSelf()->exception;
1290     Object* cause;
1291
1292     if (exception == NULL) {
1293         LOGW("tried to log a null exception?\n");
1294         return;
1295     }
1296
1297     for (;;) {
1298         logStackTraceOf(exception);
1299         cause = dvmGetExceptionCause(exception);
1300         if (cause == NULL) {
1301             break;
1302         }
1303         LOGI("Caused by:\n");
1304         exception = cause;
1305     }
1306 }
1307