Exception.cpp revision 60fc806b679a3655c228b4093058c59941a49cfe
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/* 25Notes on Exception Handling 26 27We have one fairly sticky issue to deal with: creating the exception stack 28trace. The trouble is that we need the current value of the program 29counter for the method now being executed, but that's only held in a local 30variable or hardware register in the main interpreter loop. 31 32The exception mechanism requires that the current stack trace be associated 33with a Throwable at the time the Throwable is constructed. The construction 34may or may not be associated with a throw. We have three situations to 35consider: 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 44We need to have the current value for the PC, which means that for 45situation (3) the interpreter loop must copy it to an externally-accessible 46location before handling any opcode that could cause the VM to throw 47an exception. We can't store it globally, because the various threads 48would trample each other. We can't store it in the Thread structure, 49because it'll get overwritten as soon as the Throwable constructor starts 50executing. It needs to go on the stack, but our stack frames hold the 51caller's *saved* PC, not the current PC. 52 53Situation #1 doesn't require special handling. Situation #2 could be dealt 54with by passing the PC into the exception creation function. The trick 55is to solve situation #3 in a way that adds minimal overhead to common 56operations. Making it more costly to throw an exception is acceptable. 57 58There 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 72Solution (a) is the simplest, but breaks Dalvik's goal of mingling native 73and interpreted stacks. 74 75Solution (b) retains the simplicity of (a) without rearranging the stack, 76but now in some cases we're storing the PC twice, which feels wrong. 77 78Solution (c) usually works, because we push the saved PC onto the stack 79before the Throwable construction can overwrite the copy in Thread. One 80way 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) 84This is a risk for anything that could cause <clinit> to execute, e.g. 85executing a static method or accessing a static field. Attemping to access 86a field that doesn't exist in a class that does exist might cause this. 87It may be possible to simply bracket the dvmCallMethod*() functions to 88save/restore it. 89 90Solution (d) incurs additional overhead, but may have other benefits (e.g. 91it's easy to find the stack frames that should be removed before storage 92in the Throwable). 93 94Current plan is option (b), because it's simple, fast, and doesn't change 95the way the stack works. 96*/ 97 98/* fwd */ 99static bool initException(Object* exception, const char* msg, Object* cause, 100 Thread* self); 101 102void 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 111void 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 LOGE("Fatal error: %s", msg); 124 dvmAbort(); 125 } 126 127 /* make sure the exception is initialized */ 128 if (!dvmIsClassInitialized(excepClass) && !dvmInitClass(excepClass)) { 129 LOGE("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 LOGE("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 LOGV("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 183bail: 184 dvmReleaseTrackedAlloc(exception, self); 185} 186 187void 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 */ 201static 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 */ 247static 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 LOGW("Could not allocate message string \"%s\" while " 273 "throwing internal exception (%s)\n", 274 msg, excepClass->descriptor); 275 goto bail; 276 } 277 } 278 279 if (cause != NULL) { 280 if (!dvmInstanceof(cause->clazz, gDvm.exThrowable)) { 281 LOGE("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 LOGW("WARNING: exception class '%s' missing constructor " 370 "(msg='%s' kind=%d)\n", 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 LOGW("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 LOGW("Exception thrown (%s) during initCause() " 433 "of internal exception (%s)\n", 434 self->exception->clazz->descriptor, 435 exception->clazz->descriptor); 436 goto bail; 437 } 438 } else { 439 LOGW("WARNING: couldn't find initCause in '%s'", 440 excepClass->descriptor); 441 } 442 } 443 444 445 result = true; 446 447bail: 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 */ 460void 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 */ 469bool 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 */ 488void 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 */ 539Object* dvmGetExceptionCause(const Object* exception) 540{ 541 if (!dvmInstanceof(exception->clazz, gDvm.exThrowable)) { 542 LOGE("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 */ 564void 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 LOGW("WARNING: could not find printStackTrace in %s", 583 exception->clazz->descriptor); 584 } 585 586 if (self->exception != NULL) { 587 LOGW("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 */ 600static 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 LOGV("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 LOGW("Could not resolve class ref'ed in exception " 675 "catch list (class index %d, exception %s)\n", 676 handler->typeIdx, 677 (self->exception != NULL) ? 678 self->exception->clazz->descriptor : "(none)"); 679 dvmClearException(self); 680 continue; 681 } 682 } 683 684 //LOGD("ADDR MATCH, check %s instanceof %s", 685 // excepClass->descriptor, pEntry->excepClass->descriptor); 686 687 if (dvmInstanceof(excepClass, throwable)) { 688 LOGV("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 LOGV("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 */ 715int 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 */ 823void* 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 //LOGD("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 //LOGD("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 //LOGD("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 932bail: 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 */ 949ArrayObject* 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 */ 965ArrayObject* 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 */ 983void 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; 997 Method* meth; 998 StringObject* className; 999 StringObject* methodName; 1000 StringObject* fileName; 1001 int lineNumber, pc; 1002 const char* sourceFile; 1003 char* dotName; 1004 1005 ste = dvmAllocObject(gDvm.classJavaLangStackTraceElement,ALLOC_DEFAULT); 1006 if (ste == NULL) 1007 return; 1008 1009 meth = (Method*) *intVals++; 1010 pc = *intVals++; 1011 1012 if (pc == -1) // broken top frame? 1013 lineNumber = 0; 1014 else 1015 lineNumber = dvmLineNumFromPC(meth, pc); 1016 1017 dotName = dvmHumanReadableDescriptor(meth->clazz->descriptor); 1018 className = dvmCreateStringFromCstr(dotName); 1019 free(dotName); 1020 1021 methodName = dvmCreateStringFromCstr(meth->name); 1022 sourceFile = dvmGetMethodSourceFile(meth); 1023 if (sourceFile != NULL) 1024 fileName = dvmCreateStringFromCstr(sourceFile); 1025 else 1026 fileName = NULL; 1027 1028 /* 1029 * Invoke: 1030 * public StackTraceElement(String declaringClass, String methodName, 1031 * String fileName, int lineNumber) 1032 * (where lineNumber==-2 means "native") 1033 */ 1034 JValue unused; 1035 dvmCallMethod(dvmThreadSelf(), gDvm.methJavaLangStackTraceElement_init, 1036 ste, &unused, className, methodName, fileName, lineNumber); 1037 1038 dvmReleaseTrackedAlloc(ste, NULL); 1039 dvmReleaseTrackedAlloc((Object*) className, NULL); 1040 dvmReleaseTrackedAlloc((Object*) methodName, NULL); 1041 dvmReleaseTrackedAlloc((Object*) fileName, NULL); 1042 1043 if (dvmCheckException(dvmThreadSelf())) 1044 return; 1045 1046 dvmSetObjectArrayElement(steArray, i, ste); 1047 } 1048} 1049 1050/* 1051 * Dump the contents of a raw stack trace to the log. 1052 */ 1053void dvmLogRawStackTrace(const int* intVals, int stackDepth) 1054{ 1055 int i; 1056 1057 /* 1058 * Run through the array of stack frame data. 1059 */ 1060 for (i = 0; i < stackDepth; i++) { 1061 Method* meth; 1062 int lineNumber, pc; 1063 const char* sourceFile; 1064 char* dotName; 1065 1066 meth = (Method*) *intVals++; 1067 pc = *intVals++; 1068 1069 if (pc == -1) // broken top frame? 1070 lineNumber = 0; 1071 else 1072 lineNumber = dvmLineNumFromPC(meth, pc); 1073 1074 // probably don't need to do this, but it looks nicer 1075 dotName = dvmHumanReadableDescriptor(meth->clazz->descriptor); 1076 1077 if (dvmIsNativeMethod(meth)) { 1078 LOGI("\tat %s.%s(Native Method)", dotName, meth->name); 1079 } else { 1080 LOGI("\tat %s.%s(%s:%d)", 1081 dotName, meth->name, dvmGetMethodSourceFile(meth), 1082 dvmLineNumFromPC(meth, pc)); 1083 } 1084 1085 free(dotName); 1086 1087 sourceFile = dvmGetMethodSourceFile(meth); 1088 } 1089} 1090 1091/* 1092 * Get the message string. We'd like to just grab the field out of 1093 * Throwable, but the getMessage() function can be overridden by the 1094 * sub-class. 1095 * 1096 * Returns the message string object, or NULL if it wasn't set or 1097 * we encountered a failure trying to retrieve it. The string will 1098 * be added to the tracked references table. 1099 */ 1100static StringObject* getExceptionMessage(Object* exception) 1101{ 1102 Thread* self = dvmThreadSelf(); 1103 Method* getMessageMethod; 1104 StringObject* messageStr = NULL; 1105 Object* pendingException; 1106 1107 /* 1108 * If an exception is pending, clear it while we work and restore 1109 * it when we're done. 1110 */ 1111 pendingException = dvmGetException(self); 1112 if (pendingException != NULL) { 1113 dvmAddTrackedAlloc(pendingException, self); 1114 dvmClearException(self); 1115 } 1116 1117 getMessageMethod = dvmFindVirtualMethodHierByDescriptor(exception->clazz, 1118 "getMessage", "()Ljava/lang/String;"); 1119 if (getMessageMethod != NULL) { 1120 /* could be in NATIVE mode from CheckJNI, so switch state */ 1121 ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_RUNNING); 1122 JValue result; 1123 1124 dvmCallMethod(self, getMessageMethod, exception, &result); 1125 messageStr = (StringObject*) result.l; 1126 if (messageStr != NULL) 1127 dvmAddTrackedAlloc((Object*) messageStr, self); 1128 1129 dvmChangeStatus(self, oldStatus); 1130 } else { 1131 LOGW("WARNING: could not find getMessage in %s", 1132 exception->clazz->descriptor); 1133 } 1134 1135 if (dvmGetException(self) != NULL) { 1136 LOGW("NOTE: exception thrown while retrieving exception message: %s", 1137 dvmGetException(self)->clazz->descriptor); 1138 /* will be overwritten below */ 1139 } 1140 1141 dvmSetException(self, pendingException); 1142 if (pendingException != NULL) { 1143 dvmReleaseTrackedAlloc(pendingException, self); 1144 } 1145 return messageStr; 1146} 1147 1148/* 1149 * Print the direct stack trace of the given exception to the log. 1150 */ 1151static void logStackTraceOf(Object* exception) 1152{ 1153 const ArrayObject* stackData; 1154 StringObject* messageStr; 1155 int stackSize; 1156 const int* intVals; 1157 char* className; 1158 1159 className = dvmHumanReadableDescriptor(exception->clazz->descriptor); 1160 messageStr = getExceptionMessage(exception); 1161 if (messageStr != NULL) { 1162 char* cp = dvmCreateCstrFromString(messageStr); 1163 dvmReleaseTrackedAlloc((Object*) messageStr, dvmThreadSelf()); 1164 messageStr = NULL; 1165 1166 LOGI("%s: %s", className, cp); 1167 free(cp); 1168 } else { 1169 LOGI("%s:", className); 1170 } 1171 free(className); 1172 1173 /* 1174 * This relies on the stackState field, which contains the "raw" 1175 * form of the stack. The Throwable class may clear this field 1176 * after it generates the "cooked" form, in which case we'll have 1177 * nothing to show. 1178 */ 1179 stackData = (const ArrayObject*) dvmGetFieldObject(exception, 1180 gDvm.offJavaLangThrowable_stackState); 1181 if (stackData == NULL) { 1182 LOGI(" (raw stack trace not found)"); 1183 return; 1184 } 1185 1186 stackSize = stackData->length / 2; 1187 intVals = (const int*)(void*)stackData->contents; 1188 1189 dvmLogRawStackTrace(intVals, stackSize); 1190} 1191 1192/* 1193 * Print the stack trace of the current thread's exception, as well as 1194 * the stack traces of any chained exceptions, to the log. We extract 1195 * the stored stack trace and process it internally instead of calling 1196 * interpreted code. 1197 */ 1198void dvmLogExceptionStackTrace() 1199{ 1200 Object* exception = dvmThreadSelf()->exception; 1201 Object* cause; 1202 1203 if (exception == NULL) { 1204 LOGW("tried to log a null exception?"); 1205 return; 1206 } 1207 1208 for (;;) { 1209 logStackTraceOf(exception); 1210 cause = dvmGetExceptionCause(exception); 1211 if (cause == NULL) { 1212 break; 1213 } 1214 LOGI("Caused by:"); 1215 exception = cause; 1216 } 1217} 1218 1219/* 1220 * Helper for a few of the throw functions defined below. This throws 1221 * the indicated exception, with a message based on a format in which 1222 * "%s" is used exactly twice, first for a received class and second 1223 * for the expected class. 1224 */ 1225static void throwTypeError(ClassObject* exceptionClass, const char* fmt, 1226 ClassObject* actual, ClassObject* desired) 1227{ 1228 char* actualClassName = dvmHumanReadableDescriptor(actual->descriptor); 1229 char* desiredClassName = dvmHumanReadableDescriptor(desired->descriptor); 1230 dvmThrowExceptionFmt(exceptionClass, fmt, 1231 actualClassName, desiredClassName); 1232 free(desiredClassName); 1233 free(actualClassName); 1234} 1235 1236void dvmThrowAbstractMethodError(const char* msg) { 1237 dvmThrowException(gDvm.exAbstractMethodError, msg); 1238} 1239 1240void dvmThrowArithmeticException(const char* msg) { 1241 dvmThrowException(gDvm.exArithmeticException, msg); 1242} 1243 1244void dvmThrowArrayIndexOutOfBoundsException(int length, int index) 1245{ 1246 dvmThrowExceptionFmt(gDvm.exArrayIndexOutOfBoundsException, 1247 "length=%d; index=%d", length, index); 1248} 1249 1250void dvmThrowArrayStoreExceptionIncompatibleElement(ClassObject* objectType, 1251 ClassObject* arrayType) 1252{ 1253 throwTypeError(gDvm.exArrayStoreException, 1254 "%s cannot be stored in an array of type %s", 1255 objectType, arrayType); 1256} 1257 1258void dvmThrowArrayStoreExceptionNotArray(ClassObject* actual, const char* label) 1259{ 1260 char* actualClassName = dvmHumanReadableDescriptor(actual->descriptor); 1261 dvmThrowExceptionFmt(gDvm.exArrayStoreException, 1262 "%s of type %s is not an array", 1263 label, actualClassName); 1264 free(actualClassName); 1265} 1266 1267void dvmThrowArrayStoreExceptionIncompatibleArrays(ClassObject* source, ClassObject* destination) 1268{ 1269 throwTypeError(gDvm.exArrayStoreException, 1270 "%s and %s are incompatible array types", 1271 source, destination); 1272} 1273 1274void dvmThrowArrayStoreExceptionIncompatibleArrayElement(s4 index, ClassObject* objectType, 1275 ClassObject* arrayType) 1276{ 1277 char* objectClassName = dvmHumanReadableDescriptor(objectType->descriptor); 1278 char* arrayClassName = dvmHumanReadableDescriptor(arrayType->descriptor); 1279 dvmThrowExceptionFmt(gDvm.exArrayStoreException, 1280 "source[%d] of type %s cannot be stored in destination array of type %s", 1281 index, objectClassName, arrayClassName); 1282 free(objectClassName); 1283 free(arrayClassName); 1284} 1285 1286void dvmThrowClassCastException(ClassObject* actual, ClassObject* desired) 1287{ 1288 throwTypeError(gDvm.exClassCastException, 1289 "%s cannot be cast to %s", actual, desired); 1290} 1291 1292void dvmThrowClassCircularityError(const char* descriptor) { 1293 dvmThrowExceptionWithClassMessage(gDvm.exClassCircularityError, 1294 descriptor); 1295} 1296 1297void dvmThrowClassFormatError(const char* msg) { 1298 dvmThrowException(gDvm.exClassFormatError, msg); 1299} 1300 1301void dvmThrowClassNotFoundException(const char* name) { 1302 dvmThrowChainedClassNotFoundException(name, NULL); 1303} 1304 1305void dvmThrowChainedClassNotFoundException(const char* name, Object* cause) { 1306 /* 1307 * Note: This exception is thrown in response to a request coming 1308 * from client code for the name as given, so it is preferable to 1309 * make the exception message be that string, per se, instead of 1310 * trying to prettify it. 1311 */ 1312 dvmThrowChainedException(gDvm.exClassNotFoundException, name, cause); 1313} 1314 1315void dvmThrowExceptionInInitializerError() 1316{ 1317 /* 1318 * TODO: Do we want to wrap it if the original is an Error rather than 1319 * an Exception? 1320 * 1321 * TODO: Should this just use dvmWrapException()? 1322 */ 1323 1324 if (gDvm.exExceptionInInitializerError == NULL) { 1325 /* 1326 * ExceptionInInitializerError isn't itself initialized. This 1327 * can happen very early during VM startup if there is a 1328 * problem with one of the corest-of-the-core classes, and it 1329 * can possibly happen during a dexopt run. Rather than do 1330 * anything fancier, we just abort here with a blatant 1331 * message. 1332 */ 1333 LOGE("Fatal error during early class initialization:"); 1334 dvmLogExceptionStackTrace(); 1335 dvmAbort(); 1336 } 1337 1338 Thread* self = dvmThreadSelf(); 1339 Object* exception = dvmGetException(self); 1340 1341 dvmAddTrackedAlloc(exception, self); 1342 dvmClearException(self); 1343 1344 dvmThrowChainedException(gDvm.exExceptionInInitializerError, 1345 NULL, exception); 1346 dvmReleaseTrackedAlloc(exception, self); 1347} 1348 1349void dvmThrowFileNotFoundException(const char* msg) { 1350 dvmThrowException(gDvm.exFileNotFoundException, msg); 1351} 1352 1353void dvmThrowIOException(const char* msg) { 1354 dvmThrowException(gDvm.exIOException, msg); 1355} 1356 1357void dvmThrowIllegalAccessException(const char* msg) { 1358 dvmThrowException(gDvm.exIllegalAccessException, msg); 1359} 1360 1361void dvmThrowIllegalAccessError(const char* msg) { 1362 dvmThrowException(gDvm.exIllegalAccessError, msg); 1363} 1364 1365void dvmThrowIllegalArgumentException(const char* msg) { 1366 dvmThrowException(gDvm.exIllegalArgumentException, msg); 1367} 1368 1369void dvmThrowIllegalMonitorStateException(const char* msg) { 1370 dvmThrowException(gDvm.exIllegalMonitorStateException, msg); 1371} 1372 1373void dvmThrowIllegalStateException(const char* msg) { 1374 dvmThrowException(gDvm.exIllegalStateException, msg); 1375} 1376 1377void dvmThrowIllegalThreadStateException(const char* msg) { 1378 dvmThrowException(gDvm.exIllegalThreadStateException, msg); 1379} 1380 1381void dvmThrowIncompatibleClassChangeError(const char* msg) { 1382 dvmThrowException(gDvm.exIncompatibleClassChangeError, msg); 1383} 1384 1385void dvmThrowIncompatibleClassChangeErrorWithClassMessage( 1386 const char* descriptor) 1387{ 1388 dvmThrowExceptionWithClassMessage( 1389 gDvm.exIncompatibleClassChangeError, descriptor); 1390} 1391 1392void dvmThrowInstantiationException(ClassObject* clazz, 1393 const char* extraDetail) { 1394 char* className = dvmHumanReadableDescriptor(clazz->descriptor); 1395 dvmThrowExceptionFmt(gDvm.exInstantiationException, 1396 "can't instantiate class %s%s%s", className, 1397 (extraDetail == NULL) ? "" : "; ", 1398 (extraDetail == NULL) ? "" : extraDetail); 1399 free(className); 1400} 1401 1402void dvmThrowInternalError(const char* msg) { 1403 dvmThrowException(gDvm.exInternalError, msg); 1404} 1405 1406void dvmThrowInterruptedException(const char* msg) { 1407 dvmThrowException(gDvm.exInterruptedException, msg); 1408} 1409 1410void dvmThrowLinkageError(const char* msg) { 1411 dvmThrowException(gDvm.exLinkageError, msg); 1412} 1413 1414void dvmThrowNegativeArraySizeException(s4 size) { 1415 dvmThrowExceptionFmt(gDvm.exNegativeArraySizeException, "%d", size); 1416} 1417 1418void dvmThrowNoClassDefFoundError(const char* descriptor) { 1419 dvmThrowExceptionWithClassMessage(gDvm.exNoClassDefFoundError, 1420 descriptor); 1421} 1422 1423void dvmThrowChainedNoClassDefFoundError(const char* descriptor, 1424 Object* cause) { 1425 dvmThrowChainedExceptionWithClassMessage( 1426 gDvm.exNoClassDefFoundError, descriptor, cause); 1427} 1428 1429void dvmThrowNoSuchFieldError(const char* msg) { 1430 dvmThrowException(gDvm.exNoSuchFieldError, msg); 1431} 1432 1433void dvmThrowNoSuchFieldException(const char* msg) { 1434 dvmThrowException(gDvm.exNoSuchFieldException, msg); 1435} 1436 1437void dvmThrowNoSuchMethodError(const char* msg) { 1438 dvmThrowException(gDvm.exNoSuchMethodError, msg); 1439} 1440 1441void dvmThrowNullPointerException(const char* msg) { 1442 dvmThrowException(gDvm.exNullPointerException, msg); 1443} 1444 1445void dvmThrowOutOfMemoryError(const char* msg) { 1446 dvmThrowException(gDvm.exOutOfMemoryError, msg); 1447} 1448 1449void dvmThrowRuntimeException(const char* msg) { 1450 dvmThrowException(gDvm.exRuntimeException, msg); 1451} 1452 1453void dvmThrowStaleDexCacheError(const char* msg) { 1454 dvmThrowException(gDvm.exStaleDexCacheError, msg); 1455} 1456 1457void dvmThrowStringIndexOutOfBoundsExceptionWithIndex(jsize stringLength, 1458 jsize requestIndex) { 1459 dvmThrowExceptionFmt(gDvm.exStringIndexOutOfBoundsException, 1460 "length=%d; index=%d", stringLength, requestIndex); 1461} 1462 1463void dvmThrowStringIndexOutOfBoundsExceptionWithRegion(jsize stringLength, 1464 jsize requestStart, jsize requestLength) { 1465 dvmThrowExceptionFmt(gDvm.exStringIndexOutOfBoundsException, 1466 "length=%d; regionStart=%d; regionLength=%d", 1467 stringLength, requestStart, requestLength); 1468} 1469 1470void dvmThrowTypeNotPresentException(const char* descriptor) { 1471 dvmThrowExceptionWithClassMessage(gDvm.exTypeNotPresentException, 1472 descriptor); 1473} 1474 1475void dvmThrowUnsatisfiedLinkError(const char* msg) { 1476 dvmThrowException(gDvm.exUnsatisfiedLinkError, msg); 1477} 1478 1479void dvmThrowUnsupportedOperationException(const char* msg) { 1480 dvmThrowException(gDvm.exUnsupportedOperationException, msg); 1481} 1482 1483void dvmThrowVerifyError(const char* descriptor) { 1484 dvmThrowExceptionWithClassMessage(gDvm.exVerifyError, descriptor); 1485} 1486 1487void dvmThrowVirtualMachineError(const char* msg) { 1488 dvmThrowException(gDvm.exVirtualMachineError, msg); 1489} 1490