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 void dvmThrowExceptionFmtV(ClassObject* exceptionClass, 103 const char* fmt, va_list args) 104 { 105 char msgBuf[512]; 106 107 vsnprintf(msgBuf, sizeof(msgBuf), fmt, args); 108 dvmThrowChainedException(exceptionClass, msgBuf, NULL); 109 } 110 111 void dvmThrowChainedException(ClassObject* excepClass, const char* msg, 112 Object* cause) 113 { 114 Thread* self = dvmThreadSelf(); 115 Object* exception; 116 117 if (excepClass == NULL) { 118 /* 119 * The exception class was passed in as NULL. This might happen 120 * early on in VM initialization. There's nothing better to do 121 * than just log the message as an error and abort. 122 */ 123 ALOGE("Fatal error: %s", msg); 124 dvmAbort(); 125 } 126 127 /* make sure the exception is initialized */ 128 if (!dvmIsClassInitialized(excepClass) && !dvmInitClass(excepClass)) { 129 ALOGE("ERROR: unable to initialize exception class '%s'", 130 excepClass->descriptor); 131 if (strcmp(excepClass->descriptor, "Ljava/lang/InternalError;") == 0) 132 dvmAbort(); 133 dvmThrowChainedException(gDvm.exInternalError, 134 "failed to init original exception class", cause); 135 return; 136 } 137 138 exception = dvmAllocObject(excepClass, ALLOC_DEFAULT); 139 if (exception == NULL) { 140 /* 141 * We're in a lot of trouble. We might be in the process of 142 * throwing an out-of-memory exception, in which case the 143 * pre-allocated object will have been thrown when our object alloc 144 * failed. So long as there's an exception raised, return and 145 * allow the system to try to recover. If not, something is broken 146 * and we need to bail out. 147 */ 148 if (dvmCheckException(self)) 149 goto bail; 150 ALOGE("FATAL: unable to allocate exception '%s' '%s'", 151 excepClass->descriptor, msg != NULL ? msg : "(no msg)"); 152 dvmAbort(); 153 } 154 155 /* 156 * Init the exception. 157 */ 158 if (gDvm.optimizing) { 159 /* need the exception object, but can't invoke interpreted code */ 160 ALOGV("Skipping init of exception %s '%s'", 161 excepClass->descriptor, msg); 162 } else { 163 assert(excepClass == exception->clazz); 164 if (!initException(exception, msg, cause, self)) { 165 /* 166 * Whoops. If we can't initialize the exception, we can't use 167 * it. If there's an exception already set, the constructor 168 * probably threw an OutOfMemoryError. 169 */ 170 if (!dvmCheckException(self)) { 171 /* 172 * We're required to throw something, so we just 173 * throw the pre-constructed internal error. 174 */ 175 self->exception = gDvm.internalErrorObj; 176 } 177 goto bail; 178 } 179 } 180 181 self->exception = exception; 182 183 bail: 184 dvmReleaseTrackedAlloc(exception, self); 185 } 186 187 void dvmThrowChainedExceptionWithClassMessage( 188 ClassObject* exceptionClass, const char* messageDescriptor, 189 Object* cause) 190 { 191 char* message = dvmDescriptorToName(messageDescriptor); 192 193 dvmThrowChainedException(exceptionClass, message, cause); 194 free(message); 195 } 196 197 /* 198 * Find and return an exception constructor method that can take the 199 * indicated parameters, or return NULL if no such constructor exists. 200 */ 201 static Method* findExceptionInitMethod(ClassObject* excepClass, 202 bool hasMessage, bool hasCause) 203 { 204 if (hasMessage) { 205 Method* result; 206 207 if (hasCause) { 208 result = dvmFindDirectMethodByDescriptor( 209 excepClass, "<init>", 210 "(Ljava/lang/String;Ljava/lang/Throwable;)V"); 211 } else { 212 result = dvmFindDirectMethodByDescriptor( 213 excepClass, "<init>", "(Ljava/lang/String;)V"); 214 } 215 216 if (result != NULL) { 217 return result; 218 } 219 220 if (hasCause) { 221 return dvmFindDirectMethodByDescriptor( 222 excepClass, "<init>", 223 "(Ljava/lang/Object;Ljava/lang/Throwable;)V"); 224 } else { 225 return dvmFindDirectMethodByDescriptor( 226 excepClass, "<init>", "(Ljava/lang/Object;)V"); 227 } 228 } else if (hasCause) { 229 return dvmFindDirectMethodByDescriptor( 230 excepClass, "<init>", "(Ljava/lang/Throwable;)V"); 231 } else { 232 return dvmFindDirectMethodByDescriptor(excepClass, "<init>", "()V"); 233 } 234 } 235 236 /* 237 * Initialize an exception with an appropriate constructor. 238 * 239 * "exception" is the exception object to initialize. 240 * Either or both of "msg" and "cause" may be null. 241 * "self" is dvmThreadSelf(), passed in so we don't have to look it up again. 242 * 243 * If the process of initializing the exception causes another 244 * exception (e.g., OutOfMemoryError) to be thrown, return an error 245 * and leave self->exception intact. 246 */ 247 static bool initException(Object* exception, const char* msg, Object* cause, 248 Thread* self) 249 { 250 enum { 251 kInitUnknown, 252 kInitNoarg, 253 kInitMsg, 254 kInitMsgThrow, 255 kInitThrow 256 } initKind = kInitUnknown; 257 Method* initMethod = NULL; 258 ClassObject* excepClass = exception->clazz; 259 StringObject* msgStr = NULL; 260 bool result = false; 261 bool needInitCause = false; 262 263 assert(self != NULL); 264 assert(self->exception == NULL); 265 266 /* if we have a message, create a String */ 267 if (msg == NULL) 268 msgStr = NULL; 269 else { 270 msgStr = dvmCreateStringFromCstr(msg); 271 if (msgStr == NULL) { 272 ALOGW("Could not allocate message string \"%s\" while " 273 "throwing internal exception (%s)", 274 msg, excepClass->descriptor); 275 goto bail; 276 } 277 } 278 279 if (cause != NULL) { 280 if (!dvmInstanceof(cause->clazz, gDvm.exThrowable)) { 281 ALOGE("Tried to init exception with cause '%s'", 282 cause->clazz->descriptor); 283 dvmAbort(); 284 } 285 } 286 287 /* 288 * The Throwable class has four public constructors: 289 * (1) Throwable() 290 * (2) Throwable(String message) 291 * (3) Throwable(String message, Throwable cause) (added in 1.4) 292 * (4) Throwable(Throwable cause) (added in 1.4) 293 * 294 * The first two are part of the original design, and most exception 295 * classes should support them. The third prototype was used by 296 * individual exceptions. e.g. ClassNotFoundException added it in 1.2. 297 * The general "cause" mechanism was added in 1.4. Some classes, 298 * such as IllegalArgumentException, initially supported the first 299 * two, but added the second two in a later release. 300 * 301 * Exceptions may be picky about how their "cause" field is initialized. 302 * If you call ClassNotFoundException(String), it may choose to 303 * initialize its "cause" field to null. Doing so prevents future 304 * calls to Throwable.initCause(). 305 * 306 * So, if "cause" is not NULL, we need to look for a constructor that 307 * takes a throwable. If we can't find one, we fall back on calling 308 * #1/#2 and making a separate call to initCause(). Passing a null ref 309 * for "message" into Throwable(String, Throwable) is allowed, but we 310 * prefer to use the Throwable-only version because it has different 311 * behavior. 312 * 313 * java.lang.TypeNotPresentException is a strange case -- it has #3 but 314 * not #2. (Some might argue that the constructor is actually not #3, 315 * because it doesn't take the message string as an argument, but it 316 * has the same effect and we can work with it here.) 317 * 318 * java.lang.AssertionError is also a strange case -- it has a 319 * constructor that takes an Object, but not one that takes a String. 320 * There may be other cases like this, as well, so we generally look 321 * for an Object-taking constructor if we can't find one that takes 322 * a String. 323 */ 324 if (cause == NULL) { 325 if (msgStr == NULL) { 326 initMethod = findExceptionInitMethod(excepClass, false, false); 327 initKind = kInitNoarg; 328 } else { 329 initMethod = findExceptionInitMethod(excepClass, true, false); 330 if (initMethod != NULL) { 331 initKind = kInitMsg; 332 } else { 333 /* no #2, try #3 */ 334 initMethod = findExceptionInitMethod(excepClass, true, true); 335 if (initMethod != NULL) { 336 initKind = kInitMsgThrow; 337 } 338 } 339 } 340 } else { 341 if (msgStr == NULL) { 342 initMethod = findExceptionInitMethod(excepClass, false, true); 343 if (initMethod != NULL) { 344 initKind = kInitThrow; 345 } else { 346 initMethod = findExceptionInitMethod(excepClass, false, false); 347 initKind = kInitNoarg; 348 needInitCause = true; 349 } 350 } else { 351 initMethod = findExceptionInitMethod(excepClass, true, true); 352 if (initMethod != NULL) { 353 initKind = kInitMsgThrow; 354 } else { 355 initMethod = findExceptionInitMethod(excepClass, true, false); 356 initKind = kInitMsg; 357 needInitCause = true; 358 } 359 } 360 } 361 362 if (initMethod == NULL) { 363 /* 364 * We can't find the desired constructor. This can happen if a 365 * subclass of java/lang/Throwable doesn't define an expected 366 * constructor, e.g. it doesn't provide one that takes a string 367 * when a message has been provided. 368 */ 369 ALOGW("WARNING: exception class '%s' missing constructor " 370 "(msg='%s' kind=%d)", 371 excepClass->descriptor, msg, initKind); 372 assert(strcmp(excepClass->descriptor, 373 "Ljava/lang/RuntimeException;") != 0); 374 dvmThrowChainedException(gDvm.exRuntimeException, 375 "re-throw on exception class missing constructor", NULL); 376 goto bail; 377 } 378 379 /* 380 * Call the constructor with the appropriate arguments. 381 */ 382 JValue unused; 383 switch (initKind) { 384 case kInitNoarg: 385 LOGVV("+++ exc noarg (ic=%d)", needInitCause); 386 dvmCallMethod(self, initMethod, exception, &unused); 387 break; 388 case kInitMsg: 389 LOGVV("+++ exc msg (ic=%d)", needInitCause); 390 dvmCallMethod(self, initMethod, exception, &unused, msgStr); 391 break; 392 case kInitThrow: 393 LOGVV("+++ exc throw"); 394 assert(!needInitCause); 395 dvmCallMethod(self, initMethod, exception, &unused, cause); 396 break; 397 case kInitMsgThrow: 398 LOGVV("+++ exc msg+throw"); 399 assert(!needInitCause); 400 dvmCallMethod(self, initMethod, exception, &unused, msgStr, cause); 401 break; 402 default: 403 assert(false); 404 goto bail; 405 } 406 407 /* 408 * It's possible the constructor has thrown an exception. If so, we 409 * return an error and let our caller deal with it. 410 */ 411 if (self->exception != NULL) { 412 ALOGW("Exception thrown (%s) while throwing internal exception (%s)", 413 self->exception->clazz->descriptor, exception->clazz->descriptor); 414 goto bail; 415 } 416 417 /* 418 * If this exception was caused by another exception, and we weren't 419 * able to find a cause-setting constructor, set the "cause" field 420 * with an explicit call. 421 */ 422 if (needInitCause) { 423 Method* initCause; 424 initCause = dvmFindVirtualMethodHierByDescriptor(excepClass, "initCause", 425 "(Ljava/lang/Throwable;)Ljava/lang/Throwable;"); 426 if (initCause != NULL) { 427 dvmCallMethod(self, initCause, exception, &unused, cause); 428 if (self->exception != NULL) { 429 /* initCause() threw an exception; return an error and 430 * let the caller deal with it. 431 */ 432 ALOGW("Exception thrown (%s) during initCause() " 433 "of internal exception (%s)", 434 self->exception->clazz->descriptor, 435 exception->clazz->descriptor); 436 goto bail; 437 } 438 } else { 439 ALOGW("WARNING: couldn't find initCause in '%s'", 440 excepClass->descriptor); 441 } 442 } 443 444 445 result = true; 446 447 bail: 448 dvmReleaseTrackedAlloc((Object*) msgStr, self); // NULL is ok 449 return result; 450 } 451 452 453 /* 454 * Clear the pending exception. This is used by the optimization and 455 * verification code, which mostly happens during runs of dexopt. 456 * 457 * This can also be called when the VM is in a "normal" state, e.g. when 458 * verifying classes that couldn't be verified at optimization time. 459 */ 460 void dvmClearOptException(Thread* self) 461 { 462 self->exception = NULL; 463 } 464 465 /* 466 * Returns "true" if this is a "checked" exception, i.e. it's a subclass 467 * of Throwable (assumed) but not a subclass of RuntimeException or Error. 468 */ 469 bool dvmIsCheckedException(const Object* exception) 470 { 471 if (dvmInstanceof(exception->clazz, gDvm.exError) || 472 dvmInstanceof(exception->clazz, gDvm.exRuntimeException)) 473 { 474 return false; 475 } else { 476 return true; 477 } 478 } 479 480 /* 481 * Wrap the now-pending exception in a different exception. This is useful 482 * for reflection stuff that wants to hand a checked exception back from a 483 * method that doesn't declare it. 484 * 485 * If something fails, an (unchecked) exception related to that failure 486 * will be pending instead. 487 */ 488 void dvmWrapException(const char* newExcepStr) 489 { 490 Thread* self = dvmThreadSelf(); 491 Object* origExcep; 492 ClassObject* iteClass; 493 494 origExcep = dvmGetException(self); 495 dvmAddTrackedAlloc(origExcep, self); // don't let the GC free it 496 497 dvmClearException(self); // clear before class lookup 498 iteClass = dvmFindSystemClass(newExcepStr); 499 if (iteClass != NULL) { 500 Object* iteExcep; 501 Method* initMethod; 502 503 iteExcep = dvmAllocObject(iteClass, ALLOC_DEFAULT); 504 if (iteExcep != NULL) { 505 initMethod = dvmFindDirectMethodByDescriptor(iteClass, "<init>", 506 "(Ljava/lang/Throwable;)V"); 507 if (initMethod != NULL) { 508 JValue unused; 509 dvmCallMethod(self, initMethod, iteExcep, &unused, 510 origExcep); 511 512 /* if <init> succeeded, replace the old exception */ 513 if (!dvmCheckException(self)) 514 dvmSetException(self, iteExcep); 515 } 516 dvmReleaseTrackedAlloc(iteExcep, NULL); 517 518 /* if initMethod doesn't exist, or failed... */ 519 if (!dvmCheckException(self)) 520 dvmSetException(self, origExcep); 521 } else { 522 /* leave OutOfMemoryError pending */ 523 } 524 } else { 525 /* leave ClassNotFoundException pending */ 526 } 527 528 assert(dvmCheckException(self)); 529 dvmReleaseTrackedAlloc(origExcep, self); 530 } 531 532 /* 533 * Get the "cause" field from an exception. 534 * 535 * The Throwable class initializes the "cause" field to "this" to 536 * differentiate between being initialized to null and never being 537 * initialized. We check for that here and convert it to NULL. 538 */ 539 Object* dvmGetExceptionCause(const Object* exception) 540 { 541 if (!dvmInstanceof(exception->clazz, gDvm.exThrowable)) { 542 ALOGE("Tried to get cause from object of type '%s'", 543 exception->clazz->descriptor); 544 dvmAbort(); 545 } 546 Object* cause = 547 dvmGetFieldObject(exception, gDvm.offJavaLangThrowable_cause); 548 if (cause == exception) 549 return NULL; 550 else 551 return cause; 552 } 553 554 /* 555 * Print the stack trace of the current exception on stderr. This is called 556 * from the JNI ExceptionDescribe call. 557 * 558 * For consistency we just invoke the Throwable printStackTrace method, 559 * which might be overridden in the exception object. 560 * 561 * Exceptions thrown during the course of printing the stack trace are 562 * ignored. 563 */ 564 void dvmPrintExceptionStackTrace() 565 { 566 Thread* self = dvmThreadSelf(); 567 Object* exception; 568 Method* printMethod; 569 570 exception = self->exception; 571 if (exception == NULL) 572 return; 573 574 dvmAddTrackedAlloc(exception, self); 575 self->exception = NULL; 576 printMethod = dvmFindVirtualMethodHierByDescriptor(exception->clazz, 577 "printStackTrace", "()V"); 578 if (printMethod != NULL) { 579 JValue unused; 580 dvmCallMethod(self, printMethod, exception, &unused); 581 } else { 582 ALOGW("WARNING: could not find printStackTrace in %s", 583 exception->clazz->descriptor); 584 } 585 586 if (self->exception != NULL) { 587 ALOGW("NOTE: exception thrown while printing stack trace: %s", 588 self->exception->clazz->descriptor); 589 } 590 591 self->exception = exception; 592 dvmReleaseTrackedAlloc(exception, self); 593 } 594 595 /* 596 * Search the method's list of exceptions for a match. 597 * 598 * Returns the offset of the catch block on success, or -1 on failure. 599 */ 600 static int findCatchInMethod(Thread* self, const Method* method, int relPc, 601 ClassObject* excepClass) 602 { 603 /* 604 * Need to clear the exception before entry. Otherwise, dvmResolveClass 605 * might think somebody threw an exception while it was loading a class. 606 */ 607 assert(!dvmCheckException(self)); 608 assert(!dvmIsNativeMethod(method)); 609 610 LOGVV("findCatchInMethod %s.%s excep=%s depth=%d", 611 method->clazz->descriptor, method->name, excepClass->descriptor, 612 dvmComputeExactFrameDepth(self->interpSave.curFrame)); 613 614 DvmDex* pDvmDex = method->clazz->pDvmDex; 615 const DexCode* pCode = dvmGetMethodCode(method); 616 DexCatchIterator iterator; 617 618 if (dexFindCatchHandler(&iterator, pCode, relPc)) { 619 for (;;) { 620 DexCatchHandler* handler = dexCatchIteratorNext(&iterator); 621 622 if (handler == NULL) { 623 break; 624 } 625 626 if (handler->typeIdx == kDexNoIndex) { 627 /* catch-all */ 628 ALOGV("Match on catch-all block at 0x%02x in %s.%s for %s", 629 relPc, method->clazz->descriptor, 630 method->name, excepClass->descriptor); 631 return handler->address; 632 } 633 634 ClassObject* throwable = 635 dvmDexGetResolvedClass(pDvmDex, handler->typeIdx); 636 if (throwable == NULL) { 637 /* 638 * TODO: this behaves badly if we run off the stack 639 * while trying to throw an exception. The problem is 640 * that, if we're in a class loaded by a class loader, 641 * the call to dvmResolveClass has to ask the class 642 * loader for help resolving any previously-unresolved 643 * classes. If this particular class loader hasn't 644 * resolved StackOverflowError, it will call into 645 * interpreted code, and blow up. 646 * 647 * We currently replace the previous exception with 648 * the StackOverflowError, which means they won't be 649 * catching it *unless* they explicitly catch 650 * StackOverflowError, in which case we'll be unable 651 * to resolve the class referred to by the "catch" 652 * block. 653 * 654 * We end up getting a huge pile of warnings if we do 655 * a simple synthetic test, because this method gets 656 * called on every stack frame up the tree, and it 657 * fails every time. 658 * 659 * This eventually bails out, effectively becoming an 660 * uncatchable exception, so other than the flurry of 661 * warnings it's not really a problem. Still, we could 662 * probably handle this better. 663 */ 664 throwable = dvmResolveClass(method->clazz, handler->typeIdx, 665 true); 666 if (throwable == NULL) { 667 /* 668 * We couldn't find the exception they wanted in 669 * our class files (or, perhaps, the stack blew up 670 * while we were querying a class loader). Cough 671 * up a warning, then move on to the next entry. 672 * Keep the exception status clear. 673 */ 674 ALOGW("Could not resolve class ref'ed in exception " 675 "catch list (class index %d, exception %s)", 676 handler->typeIdx, 677 (self->exception != NULL) ? 678 self->exception->clazz->descriptor : "(none)"); 679 dvmClearException(self); 680 continue; 681 } 682 } 683 684 //ALOGD("ADDR MATCH, check %s instanceof %s", 685 // excepClass->descriptor, pEntry->excepClass->descriptor); 686 687 if (dvmInstanceof(excepClass, throwable)) { 688 ALOGV("Match on catch block at 0x%02x in %s.%s for %s", 689 relPc, method->clazz->descriptor, 690 method->name, excepClass->descriptor); 691 return handler->address; 692 } 693 } 694 } 695 696 ALOGV("No matching catch block at 0x%02x in %s for %s", 697 relPc, method->name, excepClass->descriptor); 698 return -1; 699 } 700 701 /* 702 * Find a matching "catch" block. "pc" is the relative PC within the 703 * current method, indicating the offset from the start in 16-bit units. 704 * 705 * Returns the offset to the catch block, or -1 if we run up against a 706 * break frame without finding anything. 707 * 708 * The class resolution stuff we have to do while evaluating the "catch" 709 * blocks could cause an exception. The caller should clear the exception 710 * before calling here and restore it after. 711 * 712 * Sets *newFrame to the frame pointer of the frame with the catch block. 713 * If "scanOnly" is false, self->interpSave.curFrame is also set to this value. 714 */ 715 int dvmFindCatchBlock(Thread* self, int relPc, Object* exception, 716 bool scanOnly, void** newFrame) 717 { 718 u4* fp = self->interpSave.curFrame; 719 int catchAddr = -1; 720 721 assert(!dvmCheckException(self)); 722 723 while (true) { 724 StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp); 725 catchAddr = findCatchInMethod(self, saveArea->method, relPc, 726 exception->clazz); 727 if (catchAddr >= 0) 728 break; 729 730 /* 731 * Normally we'd check for ACC_SYNCHRONIZED methods and unlock 732 * them as we unroll. Dalvik uses what amount to generated 733 * "finally" blocks to take care of this for us. 734 */ 735 736 /* output method profiling info */ 737 if (!scanOnly) { 738 TRACE_METHOD_UNROLL(self, saveArea->method); 739 } 740 741 /* 742 * Move up one frame. If the next thing up is a break frame, 743 * break out now so we're left unrolled to the last method frame. 744 * We need to point there so we can roll up the JNI local refs 745 * if this was a native method. 746 */ 747 assert(saveArea->prevFrame != NULL); 748 if (dvmIsBreakFrame((u4*)saveArea->prevFrame)) { 749 if (!scanOnly) 750 break; // bail with catchAddr == -1 751 752 /* 753 * We're scanning for the debugger. It needs to know if this 754 * exception is going to be caught or not, and we need to figure 755 * out if it will be caught *ever* not just between the current 756 * position and the next break frame. We can't tell what native 757 * code is going to do, so we assume it never catches exceptions. 758 * 759 * Start by finding an interpreted code frame. 760 */ 761 fp = saveArea->prevFrame; // this is the break frame 762 saveArea = SAVEAREA_FROM_FP(fp); 763 fp = saveArea->prevFrame; // this may be a good one 764 while (fp != NULL) { 765 if (!dvmIsBreakFrame((u4*)fp)) { 766 saveArea = SAVEAREA_FROM_FP(fp); 767 if (!dvmIsNativeMethod(saveArea->method)) 768 break; 769 } 770 771 fp = SAVEAREA_FROM_FP(fp)->prevFrame; 772 } 773 if (fp == NULL) 774 break; // bail with catchAddr == -1 775 776 /* 777 * Now fp points to the "good" frame. When the interp code 778 * invoked the native code, it saved a copy of its current PC 779 * into xtra.currentPc. Pull it out of there. 780 */ 781 relPc = 782 saveArea->xtra.currentPc - SAVEAREA_FROM_FP(fp)->method->insns; 783 } else { 784 fp = saveArea->prevFrame; 785 786 /* savedPc in was-current frame goes with method in now-current */ 787 relPc = saveArea->savedPc - SAVEAREA_FROM_FP(fp)->method->insns; 788 } 789 } 790 791 if (!scanOnly) 792 self->interpSave.curFrame = fp; 793 794 /* 795 * The class resolution in findCatchInMethod() could cause an exception. 796 * Clear it to be safe. 797 */ 798 self->exception = NULL; 799 800 *newFrame = fp; 801 return catchAddr; 802 } 803 804 /* 805 * We have to carry the exception's stack trace around, but in many cases 806 * it will never be examined. It makes sense to keep it in a compact, 807 * VM-specific object, rather than an array of Objects with strings. 808 * 809 * Pass in the thread whose stack we're interested in. If "thread" is 810 * not self, the thread must be suspended. This implies that the thread 811 * list lock is held, which means we can't allocate objects or we risk 812 * jamming the GC. So, we allow this function to return different formats. 813 * (This shouldn't be called directly -- see the inline functions in the 814 * header file.) 815 * 816 * If "wantObject" is true, this returns a newly-allocated Object, which is 817 * presently an array of integers, but could become something else in the 818 * future. If "wantObject" is false, return plain malloc data. 819 * 820 * NOTE: if we support class unloading, we will need to scan the class 821 * object references out of these arrays. 822 */ 823 void* dvmFillInStackTraceInternal(Thread* thread, bool wantObject, size_t* pCount) 824 { 825 ArrayObject* stackData = NULL; 826 int* simpleData = NULL; 827 void* fp; 828 void* startFp; 829 size_t stackDepth; 830 int* intPtr; 831 832 if (pCount != NULL) 833 *pCount = 0; 834 fp = thread->interpSave.curFrame; 835 836 assert(thread == dvmThreadSelf() || dvmIsSuspended(thread)); 837 838 /* 839 * We're looking at a stack frame for code running below a Throwable 840 * constructor. We want to remove the Throwable methods and the 841 * superclass initializations so the user doesn't see them when they 842 * read the stack dump. 843 * 844 * TODO: this just scrapes off the top layers of Throwable. Might not do 845 * the right thing if we create an exception object or cause a VM 846 * exception while in a Throwable method. 847 */ 848 while (fp != NULL) { 849 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp); 850 const Method* method = saveArea->method; 851 852 if (dvmIsBreakFrame((u4*)fp)) 853 break; 854 if (!dvmInstanceof(method->clazz, gDvm.exThrowable)) 855 break; 856 //ALOGD("EXCEP: ignoring %s.%s", 857 // method->clazz->descriptor, method->name); 858 fp = saveArea->prevFrame; 859 } 860 startFp = fp; 861 862 /* 863 * Compute the stack depth. 864 */ 865 stackDepth = 0; 866 while (fp != NULL) { 867 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp); 868 869 if (!dvmIsBreakFrame((u4*)fp)) 870 stackDepth++; 871 872 assert(fp != saveArea->prevFrame); 873 fp = saveArea->prevFrame; 874 } 875 //ALOGD("EXCEP: stack depth is %d", stackDepth); 876 877 if (!stackDepth) 878 goto bail; 879 880 /* 881 * We need to store a pointer to the Method and the program counter. 882 * We have 4-byte pointers, so we use '[I'. 883 */ 884 if (wantObject) { 885 assert(sizeof(Method*) == 4); 886 stackData = dvmAllocPrimitiveArray('I', stackDepth*2, ALLOC_DEFAULT); 887 if (stackData == NULL) { 888 assert(dvmCheckException(dvmThreadSelf())); 889 goto bail; 890 } 891 intPtr = (int*)(void*)stackData->contents; 892 } else { 893 /* array of ints; first entry is stack depth */ 894 assert(sizeof(Method*) == sizeof(int)); 895 simpleData = (int*) malloc(sizeof(int) * stackDepth*2); 896 if (simpleData == NULL) 897 goto bail; 898 899 assert(pCount != NULL); 900 intPtr = simpleData; 901 } 902 if (pCount != NULL) 903 *pCount = stackDepth; 904 905 fp = startFp; 906 while (fp != NULL) { 907 const StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp); 908 const Method* method = saveArea->method; 909 910 if (!dvmIsBreakFrame((u4*)fp)) { 911 //ALOGD("EXCEP keeping %s.%s", method->clazz->descriptor, 912 // method->name); 913 914 *intPtr++ = (int) method; 915 if (dvmIsNativeMethod(method)) { 916 *intPtr++ = 0; /* no saved PC for native methods */ 917 } else { 918 assert(saveArea->xtra.currentPc >= method->insns && 919 saveArea->xtra.currentPc < 920 method->insns + dvmGetMethodInsnsSize(method)); 921 *intPtr++ = (int) (saveArea->xtra.currentPc - method->insns); 922 } 923 924 stackDepth--; // for verification 925 } 926 927 assert(fp != saveArea->prevFrame); 928 fp = saveArea->prevFrame; 929 } 930 assert(stackDepth == 0); 931 932 bail: 933 if (wantObject) { 934 dvmReleaseTrackedAlloc((Object*) stackData, dvmThreadSelf()); 935 return stackData; 936 } else { 937 return simpleData; 938 } 939 } 940 941 942 /* 943 * Given an Object previously created by dvmFillInStackTrace(), use the 944 * contents of the saved stack trace to generate an array of 945 * java/lang/StackTraceElement objects. 946 * 947 * The returned array is not added to the "local refs" list. 948 */ 949 ArrayObject* dvmGetStackTrace(const Object* ostackData) 950 { 951 const ArrayObject* stackData = (const ArrayObject*) ostackData; 952 size_t stackSize = stackData->length / 2; 953 const int* intVals = (const int*)(void*)stackData->contents; 954 return dvmGetStackTraceRaw(intVals, stackSize); 955 } 956 957 /* 958 * Generate an array of StackTraceElement objects from the raw integer 959 * data encoded by dvmFillInStackTrace(). 960 * 961 * "intVals" points to the first {method,pc} pair. 962 * 963 * The returned array is not added to the "local refs" list. 964 */ 965 ArrayObject* dvmGetStackTraceRaw(const int* intVals, size_t stackDepth) 966 { 967 /* allocate a StackTraceElement array */ 968 ClassObject* klass = gDvm.classJavaLangStackTraceElementArray; 969 ArrayObject* array = dvmAllocArrayByClass(klass, stackDepth, ALLOC_DEFAULT); 970 if (array != NULL){ 971 dvmFillStackTraceElements(intVals, stackDepth, array); 972 dvmReleaseTrackedAlloc((Object*) array, NULL); 973 } 974 return array; 975 } 976 977 /* 978 * Fills the StackTraceElement array elements from the raw integer 979 * data encoded by dvmFillInStackTrace(). 980 * 981 * "intVals" points to the first {method,pc} pair. 982 */ 983 void dvmFillStackTraceElements(const int* intVals, size_t stackDepth, ArrayObject* steArray) 984 { 985 unsigned int i; 986 987 /* init this if we haven't yet */ 988 if (!dvmIsClassInitialized(gDvm.classJavaLangStackTraceElement)) 989 dvmInitClass(gDvm.classJavaLangStackTraceElement); 990 991 /* 992 * Allocate and initialize a StackTraceElement for each stack frame. 993 * We use the standard constructor to configure the object. 994 */ 995 for (i = 0; i < stackDepth; i++) { 996 Object* ste = dvmAllocObject(gDvm.classJavaLangStackTraceElement,ALLOC_DEFAULT); 997 if (ste == NULL) { 998 return; 999 } 1000 1001 Method* meth = (Method*) *intVals++; 1002 int pc = *intVals++; 1003 1004 int lineNumber; 1005 if (pc == -1) // broken top frame? 1006 lineNumber = 0; 1007 else 1008 lineNumber = dvmLineNumFromPC(meth, pc); 1009 1010 std::string dotName(dvmHumanReadableDescriptor(meth->clazz->descriptor)); 1011 StringObject* className = dvmCreateStringFromCstr(dotName); 1012 1013 StringObject* methodName = dvmCreateStringFromCstr(meth->name); 1014 1015 const char* sourceFile = dvmGetMethodSourceFile(meth); 1016 StringObject* fileName = (sourceFile != NULL) ? dvmCreateStringFromCstr(sourceFile) : NULL; 1017 1018 /* 1019 * Invoke: 1020 * public StackTraceElement(String declaringClass, String methodName, 1021 * String fileName, int lineNumber) 1022 * (where lineNumber==-2 means "native") 1023 */ 1024 JValue unused; 1025 dvmCallMethod(dvmThreadSelf(), gDvm.methJavaLangStackTraceElement_init, 1026 ste, &unused, className, methodName, fileName, lineNumber); 1027 1028 dvmReleaseTrackedAlloc(ste, NULL); 1029 dvmReleaseTrackedAlloc((Object*) className, NULL); 1030 dvmReleaseTrackedAlloc((Object*) methodName, NULL); 1031 dvmReleaseTrackedAlloc((Object*) fileName, NULL); 1032 1033 if (dvmCheckException(dvmThreadSelf())) { 1034 return; 1035 } 1036 1037 dvmSetObjectArrayElement(steArray, i, ste); 1038 } 1039 } 1040 1041 /* 1042 * Dump the contents of a raw stack trace to the log. 1043 */ 1044 void dvmLogRawStackTrace(const int* intVals, int stackDepth) { 1045 /* 1046 * Run through the array of stack frame data. 1047 */ 1048 for (int i = 0; i < stackDepth; i++) { 1049 Method* meth = (Method*) *intVals++; 1050 int pc = *intVals++; 1051 1052 std::string dotName(dvmHumanReadableDescriptor(meth->clazz->descriptor)); 1053 if (dvmIsNativeMethod(meth)) { 1054 ALOGI("\tat %s.%s(Native Method)", dotName.c_str(), meth->name); 1055 } else { 1056 ALOGI("\tat %s.%s(%s:%d)", 1057 dotName.c_str(), meth->name, dvmGetMethodSourceFile(meth), 1058 dvmLineNumFromPC(meth, pc)); 1059 } 1060 } 1061 } 1062 1063 /* 1064 * Get the message string. We'd like to just grab the field out of 1065 * Throwable, but the getMessage() function can be overridden by the 1066 * sub-class. 1067 * 1068 * Returns the message string object, or NULL if it wasn't set or 1069 * we encountered a failure trying to retrieve it. The string will 1070 * be added to the tracked references table. 1071 */ 1072 static StringObject* getExceptionMessage(Object* exception) 1073 { 1074 Thread* self = dvmThreadSelf(); 1075 Method* getMessageMethod; 1076 StringObject* messageStr = NULL; 1077 Object* pendingException; 1078 1079 /* 1080 * If an exception is pending, clear it while we work and restore 1081 * it when we're done. 1082 */ 1083 pendingException = dvmGetException(self); 1084 if (pendingException != NULL) { 1085 dvmAddTrackedAlloc(pendingException, self); 1086 dvmClearException(self); 1087 } 1088 1089 getMessageMethod = dvmFindVirtualMethodHierByDescriptor(exception->clazz, 1090 "getMessage", "()Ljava/lang/String;"); 1091 if (getMessageMethod != NULL) { 1092 /* could be in NATIVE mode from CheckJNI, so switch state */ 1093 ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_RUNNING); 1094 JValue result; 1095 1096 dvmCallMethod(self, getMessageMethod, exception, &result); 1097 messageStr = (StringObject*) result.l; 1098 if (messageStr != NULL) 1099 dvmAddTrackedAlloc((Object*) messageStr, self); 1100 1101 dvmChangeStatus(self, oldStatus); 1102 } else { 1103 ALOGW("WARNING: could not find getMessage in %s", 1104 exception->clazz->descriptor); 1105 } 1106 1107 if (dvmGetException(self) != NULL) { 1108 ALOGW("NOTE: exception thrown while retrieving exception message: %s", 1109 dvmGetException(self)->clazz->descriptor); 1110 /* will be overwritten below */ 1111 } 1112 1113 dvmSetException(self, pendingException); 1114 if (pendingException != NULL) { 1115 dvmReleaseTrackedAlloc(pendingException, self); 1116 } 1117 return messageStr; 1118 } 1119 1120 /* 1121 * Print the direct stack trace of the given exception to the log. 1122 */ 1123 static void logStackTraceOf(Object* exception) { 1124 std::string className(dvmHumanReadableDescriptor(exception->clazz->descriptor)); 1125 StringObject* messageStr = getExceptionMessage(exception); 1126 if (messageStr != NULL) { 1127 char* cp = dvmCreateCstrFromString(messageStr); 1128 dvmReleaseTrackedAlloc((Object*) messageStr, dvmThreadSelf()); 1129 messageStr = NULL; 1130 1131 ALOGI("%s: %s", className.c_str(), cp); 1132 free(cp); 1133 } else { 1134 ALOGI("%s:", className.c_str()); 1135 } 1136 1137 /* 1138 * This relies on the stackState field, which contains the "raw" 1139 * form of the stack. The Throwable class may clear this field 1140 * after it generates the "cooked" form, in which case we'll have 1141 * nothing to show. 1142 */ 1143 const ArrayObject* stackData = (const ArrayObject*) dvmGetFieldObject(exception, 1144 gDvm.offJavaLangThrowable_stackState); 1145 if (stackData == NULL) { 1146 ALOGI(" (raw stack trace not found)"); 1147 return; 1148 } 1149 1150 int stackSize = stackData->length / 2; 1151 const int* intVals = (const int*)(void*)stackData->contents; 1152 1153 dvmLogRawStackTrace(intVals, stackSize); 1154 } 1155 1156 /* 1157 * Print the stack trace of the current thread's exception, as well as 1158 * the stack traces of any chained exceptions, to the log. We extract 1159 * the stored stack trace and process it internally instead of calling 1160 * interpreted code. 1161 */ 1162 void dvmLogExceptionStackTrace() 1163 { 1164 Object* exception = dvmThreadSelf()->exception; 1165 Object* cause; 1166 1167 if (exception == NULL) { 1168 ALOGW("tried to log a null exception?"); 1169 return; 1170 } 1171 1172 for (;;) { 1173 logStackTraceOf(exception); 1174 cause = dvmGetExceptionCause(exception); 1175 if (cause == NULL) { 1176 break; 1177 } 1178 ALOGI("Caused by:"); 1179 exception = cause; 1180 } 1181 } 1182 1183 /* 1184 * Helper for a few of the throw functions defined below. This throws 1185 * the indicated exception, with a message based on a format in which 1186 * "%s" is used exactly twice, first for a received class and second 1187 * for the expected class. 1188 */ 1189 static void throwTypeError(ClassObject* exceptionClass, const char* fmt, 1190 ClassObject* actual, ClassObject* desired) 1191 { 1192 std::string actualClassName(dvmHumanReadableDescriptor(actual->descriptor)); 1193 std::string desiredClassName(dvmHumanReadableDescriptor(desired->descriptor)); 1194 dvmThrowExceptionFmt(exceptionClass, fmt, actualClassName.c_str(), desiredClassName.c_str()); 1195 } 1196 1197 void dvmThrowAbstractMethodError(const char* msg) { 1198 dvmThrowException(gDvm.exAbstractMethodError, msg); 1199 } 1200 1201 void dvmThrowArithmeticException(const char* msg) { 1202 dvmThrowException(gDvm.exArithmeticException, msg); 1203 } 1204 1205 void dvmThrowArrayIndexOutOfBoundsException(int length, int index) 1206 { 1207 dvmThrowExceptionFmt(gDvm.exArrayIndexOutOfBoundsException, 1208 "length=%d; index=%d", length, index); 1209 } 1210 1211 void dvmThrowArrayStoreExceptionIncompatibleElement(ClassObject* objectType, 1212 ClassObject* arrayType) 1213 { 1214 throwTypeError(gDvm.exArrayStoreException, 1215 "%s cannot be stored in an array of type %s", 1216 objectType, arrayType); 1217 } 1218 1219 void dvmThrowArrayStoreExceptionNotArray(ClassObject* actual, const char* label) { 1220 std::string actualClassName(dvmHumanReadableDescriptor(actual->descriptor)); 1221 dvmThrowExceptionFmt(gDvm.exArrayStoreException, "%s of type %s is not an array", 1222 label, actualClassName.c_str()); 1223 } 1224 1225 void dvmThrowArrayStoreExceptionIncompatibleArrays(ClassObject* source, ClassObject* destination) 1226 { 1227 throwTypeError(gDvm.exArrayStoreException, 1228 "%s and %s are incompatible array types", 1229 source, destination); 1230 } 1231 1232 void dvmThrowArrayStoreExceptionIncompatibleArrayElement(s4 index, ClassObject* objectType, 1233 ClassObject* arrayType) 1234 { 1235 std::string objectClassName(dvmHumanReadableDescriptor(objectType->descriptor)); 1236 std::string arrayClassName(dvmHumanReadableDescriptor(arrayType->descriptor)); 1237 dvmThrowExceptionFmt(gDvm.exArrayStoreException, 1238 "source[%d] of type %s cannot be stored in destination array of type %s", 1239 index, objectClassName.c_str(), arrayClassName.c_str()); 1240 } 1241 1242 void dvmThrowClassCastException(ClassObject* actual, ClassObject* desired) 1243 { 1244 throwTypeError(gDvm.exClassCastException, 1245 "%s cannot be cast to %s", actual, desired); 1246 } 1247 1248 void dvmThrowClassCircularityError(const char* descriptor) { 1249 dvmThrowExceptionWithClassMessage(gDvm.exClassCircularityError, 1250 descriptor); 1251 } 1252 1253 void dvmThrowClassFormatError(const char* msg) { 1254 dvmThrowException(gDvm.exClassFormatError, msg); 1255 } 1256 1257 void dvmThrowClassNotFoundException(const char* name) { 1258 dvmThrowChainedClassNotFoundException(name, NULL); 1259 } 1260 1261 void dvmThrowChainedClassNotFoundException(const char* name, Object* cause) { 1262 /* 1263 * Note: This exception is thrown in response to a request coming 1264 * from client code for the name as given, so it is preferable to 1265 * make the exception message be that string, per se, instead of 1266 * trying to prettify it. 1267 */ 1268 dvmThrowChainedException(gDvm.exClassNotFoundException, name, cause); 1269 } 1270 1271 void dvmThrowExceptionInInitializerError() 1272 { 1273 /* 1274 * TODO: Should this just use dvmWrapException()? 1275 */ 1276 1277 if (gDvm.exExceptionInInitializerError == NULL || gDvm.exError == NULL) { 1278 /* 1279 * ExceptionInInitializerError isn't itself initialized. This 1280 * can happen very early during VM startup if there is a 1281 * problem with one of the corest-of-the-core classes, and it 1282 * can possibly happen during a dexopt run. Rather than do 1283 * anything fancier, we just abort here with a blatant 1284 * message. 1285 */ 1286 ALOGE("Fatal error during early class initialization:"); 1287 dvmLogExceptionStackTrace(); 1288 dvmAbort(); 1289 } 1290 1291 Thread* self = dvmThreadSelf(); 1292 Object* exception = dvmGetException(self); 1293 1294 // We only wrap non-Error exceptions; an Error can just be used as-is. 1295 if (dvmInstanceof(exception->clazz, gDvm.exError)) { 1296 return; 1297 } 1298 1299 dvmAddTrackedAlloc(exception, self); 1300 dvmClearException(self); 1301 1302 dvmThrowChainedException(gDvm.exExceptionInInitializerError, 1303 NULL, exception); 1304 dvmReleaseTrackedAlloc(exception, self); 1305 } 1306 1307 void dvmThrowFileNotFoundException(const char* msg) { 1308 dvmThrowException(gDvm.exFileNotFoundException, msg); 1309 } 1310 1311 void dvmThrowIOException(const char* msg) { 1312 dvmThrowException(gDvm.exIOException, msg); 1313 } 1314 1315 void dvmThrowIllegalAccessException(const char* msg) { 1316 dvmThrowException(gDvm.exIllegalAccessException, msg); 1317 } 1318 1319 void dvmThrowIllegalAccessError(const char* msg) { 1320 dvmThrowException(gDvm.exIllegalAccessError, msg); 1321 } 1322 1323 void dvmThrowIllegalArgumentException(const char* msg) { 1324 dvmThrowException(gDvm.exIllegalArgumentException, msg); 1325 } 1326 1327 void dvmThrowIllegalMonitorStateException(const char* msg) { 1328 dvmThrowException(gDvm.exIllegalMonitorStateException, msg); 1329 } 1330 1331 void dvmThrowIllegalStateException(const char* msg) { 1332 dvmThrowException(gDvm.exIllegalStateException, msg); 1333 } 1334 1335 void dvmThrowIllegalThreadStateException(const char* msg) { 1336 dvmThrowException(gDvm.exIllegalThreadStateException, msg); 1337 } 1338 1339 void dvmThrowIncompatibleClassChangeError(const char* msg) { 1340 dvmThrowException(gDvm.exIncompatibleClassChangeError, msg); 1341 } 1342 1343 void dvmThrowIncompatibleClassChangeErrorWithClassMessage( 1344 const char* descriptor) 1345 { 1346 dvmThrowExceptionWithClassMessage( 1347 gDvm.exIncompatibleClassChangeError, descriptor); 1348 } 1349 1350 void dvmThrowInstantiationException(ClassObject* clazz, const char* extraDetail) { 1351 std::string className(dvmHumanReadableDescriptor(clazz->descriptor)); 1352 dvmThrowExceptionFmt(gDvm.exInstantiationException, 1353 "can't instantiate class %s%s%s", className.c_str(), 1354 (extraDetail == NULL) ? "" : "; ", 1355 (extraDetail == NULL) ? "" : extraDetail); 1356 } 1357 1358 void dvmThrowInternalError(const char* msg) { 1359 dvmThrowException(gDvm.exInternalError, msg); 1360 } 1361 1362 void dvmThrowInterruptedException(const char* msg) { 1363 dvmThrowException(gDvm.exInterruptedException, msg); 1364 } 1365 1366 void dvmThrowLinkageError(const char* msg) { 1367 dvmThrowException(gDvm.exLinkageError, msg); 1368 } 1369 1370 void dvmThrowNegativeArraySizeException(s4 size) { 1371 dvmThrowExceptionFmt(gDvm.exNegativeArraySizeException, "%d", size); 1372 } 1373 1374 void dvmThrowNoClassDefFoundError(const char* descriptor) { 1375 dvmThrowExceptionWithClassMessage(gDvm.exNoClassDefFoundError, 1376 descriptor); 1377 } 1378 1379 void dvmThrowChainedNoClassDefFoundError(const char* descriptor, 1380 Object* cause) { 1381 dvmThrowChainedExceptionWithClassMessage( 1382 gDvm.exNoClassDefFoundError, descriptor, cause); 1383 } 1384 1385 void dvmThrowNoSuchFieldError(const char* msg) { 1386 dvmThrowException(gDvm.exNoSuchFieldError, msg); 1387 } 1388 1389 void dvmThrowNoSuchFieldException(const char* msg) { 1390 dvmThrowException(gDvm.exNoSuchFieldException, msg); 1391 } 1392 1393 void dvmThrowNoSuchMethodError(const char* msg) { 1394 dvmThrowException(gDvm.exNoSuchMethodError, msg); 1395 } 1396 1397 void dvmThrowNullPointerException(const char* msg) { 1398 dvmThrowException(gDvm.exNullPointerException, msg); 1399 } 1400 1401 void dvmThrowOutOfMemoryError(const char* msg) { 1402 dvmThrowException(gDvm.exOutOfMemoryError, msg); 1403 } 1404 1405 void dvmThrowRuntimeException(const char* msg) { 1406 dvmThrowException(gDvm.exRuntimeException, msg); 1407 } 1408 1409 void dvmThrowStaleDexCacheError(const char* msg) { 1410 dvmThrowException(gDvm.exStaleDexCacheError, msg); 1411 } 1412 1413 void dvmThrowStringIndexOutOfBoundsExceptionWithIndex(jsize stringLength, 1414 jsize requestIndex) { 1415 dvmThrowExceptionFmt(gDvm.exStringIndexOutOfBoundsException, 1416 "length=%d; index=%d", stringLength, requestIndex); 1417 } 1418 1419 void dvmThrowStringIndexOutOfBoundsExceptionWithRegion(jsize stringLength, 1420 jsize requestStart, jsize requestLength) { 1421 dvmThrowExceptionFmt(gDvm.exStringIndexOutOfBoundsException, 1422 "length=%d; regionStart=%d; regionLength=%d", 1423 stringLength, requestStart, requestLength); 1424 } 1425 1426 void dvmThrowTypeNotPresentException(const char* descriptor) { 1427 dvmThrowExceptionWithClassMessage(gDvm.exTypeNotPresentException, 1428 descriptor); 1429 } 1430 1431 void dvmThrowUnsatisfiedLinkError(const char* msg) { 1432 dvmThrowException(gDvm.exUnsatisfiedLinkError, msg); 1433 } 1434 1435 void dvmThrowUnsatisfiedLinkError(const char* msg, const Method* method) { 1436 char* desc = dexProtoCopyMethodDescriptor(&method->prototype); 1437 char* className = dvmDescriptorToDot(method->clazz->descriptor); 1438 dvmThrowExceptionFmt(gDvm.exUnsatisfiedLinkError, "%s: %s.%s:%s", 1439 msg, className, method->name, desc); 1440 free(className); 1441 free(desc); 1442 } 1443 1444 void dvmThrowUnsupportedOperationException(const char* msg) { 1445 dvmThrowException(gDvm.exUnsupportedOperationException, msg); 1446 } 1447 1448 void dvmThrowVerifyError(const char* descriptor) { 1449 dvmThrowExceptionWithClassMessage(gDvm.exVerifyError, descriptor); 1450 } 1451 1452 void dvmThrowVirtualMachineError(const char* msg) { 1453 dvmThrowException(gDvm.exVirtualMachineError, msg); 1454 } 1455