方法注册好后要经过哪些路
Android一个异常捕获项目 https://github.com/xroche/coffeecatch
coffeecatch
CoffeeCatch, a tiny native POSIX signal catcher (especially useful for JNI code on Android/Dalvik, but it can be used in non-Java projects)
It allows to "gracefully" recover from a signal (SIGSEGV
, SIGBUS
...) as if it was an exception. It will not gracefully recover from allocator/mutexes corruption etc., however, but at least "most" gentle crashes (null pointer dereferencing, integer division, stack overflow etc.) should be handled without too much troubles.
/** Enter protected section. **/
COFFEE_TRY() {
/** Try to call 'call_some_native_function'. **/
call_some_protected_function();
} COFFEE_CATCH() {
/** Caught a signal: throw Java exception. **/
/** In pure C projects, you may print an error message (coffeecatch_get_message()). **/
coffeecatch_throw_exception(env);
} COFFEE_END();
You may read the corresponding discussion about this project.
The handler is thread-safe, but client must have exclusive control on the signal handlers (ie. the library is installing its own signal handlers on top of the existing ones).
Libraries
If you want to get useful stack traces, you should build all your libraries with -funwind-tables
(this adds unwinding information). On ARM, you may also use the --no-merge-exidx-entries
linker switch, to solve certain issues with unwinding (the switch is possibly not needed anymore). On Android, this can be achieved by using this line in the Android.mk
file in each library block:
LOCAL_CFLAGS := -funwind-tables -Wl,--no-merge-exidx-entries
Example
- Inside JNI (typically, Android)
First, build the library, or just add the two files in the list of local files to be built:
LOCAL_SRC_FILES += coffeecatch.c coffeejni.c
then, use the COFFEE_TRY_JNI() macro to protect your call(s):
/** The potentially dangerous function. **/
jint call_dangerous_function(JNIEnv* env, jobject object) {
// ... do dangerous things!
return 42;
}
/** Protected function stub. **/
void foo_protected(JNIEnv* env, jobject object, jint *retcode) {
/* Try to call 'call_dangerous_function', and raise proper Java Error upon
* fatal error (SEGV, etc.). **/
COFFEE_TRY_JNI(env, *retcode = call_dangerous_function(env, object));
}
/** Regular JNI entry point. **/
jint Java_com_example_android_MyNative_foo(JNIEnv* env, jobject object) {
jint retcode = 0;
foo_protected(env, object, &retcode);
return retcode;
}
and, in case of crash, get something like this (note: the last Exception with native backtrace is produced on Android >= 4.1.1):
FATAL EXCEPTION: AsyncTask #5
java.lang.RuntimeException: An error occured while executing doInBackground()
at android.os.AsyncTask$3.done(AsyncTask.java:299)
at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:352)
at java.util.concurrent.FutureTask.setException(FutureTask.java:219)
at java.util.concurrent.FutureTask.run(FutureTask.java:239)
at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:230)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1080)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:573)
at java.lang.Thread.run(Thread.java:841)
Caused by: java.lang.Error: signal 11 (Address not mapped to object) at address 0x42 [at libexample.so:0xa024]
at com.example.jni.ExampleLib.main(Native Method)
at com.example.ExampleActivity$Runner.runInternal(ExampleActivity.java:998)
at com.example.ExampleActivity$Runner.doInBackground(ExampleActivity.java:919)
at com.example.ExampleActivity$Runner.doInBackground(ExampleActivity.java:1)
at android.os.AsyncTask$2.call(AsyncTask.java:287)
at java.util.concurrent.FutureTask.run(FutureTask.java:234)
... 4 more
Caused by: java.lang.Error: signal 11 (Address not mapped to object) at address 0x42 [at libexample.so:0xa024]
at data.app_lib.com_example.libexample_so.0xa024(Native Method)
at data.app_lib.com_example.libexample_so.0x705fc(hts_main2:0x8f74:0)
at data.app_lib.com_example.libexamplejni_so.0x4cc8(ExampleLib_main:0xf8:0)
at data.app_lib.com_example.libexamplejni_so.0x52d8(Java_com_example_jni_ExampleLib_main:0x64:0)
at system.lib.libdvm_so.0x1dc4c(dvmPlatformInvoke:0x70:0)
at system.lib.libdvm_so.0x4dcab(dvmCallJNIMethod(unsigned int const*, JValue*, Method const*, Thread*):0x18a:0)
at system.lib.libdvm_so.0x385e1(dvmCheckCallJNIMethod(unsigned int const*, JValue*, Method const*, Thread*):0x8:0)
at system.lib.libdvm_so.0x4f699(dvmResolveNativeMethod(unsigned int const*, JValue*, Method const*, Thread*):0xb8:0)
at system.lib.libdvm_so.0x27060(Native Method)
at system.lib.libdvm_so.0x2b580(dvmInterpret(Thread*, Method const*, JValue*):0xb8:0)
at system.lib.libdvm_so.0x5fcbd(dvmCallMethodV(Thread*, Method const*, Object*, bool, JValue*, std::__va_list):0x124:0)
at system.lib.libdvm_so.0x5fce7(dvmCallMethod(Thread*, Method const*, Object*, JValue*, ...):0x14:0)
at system.lib.libdvm_so.0x54a6f(Native Method)
at system.lib.libc_so.0xca58(__thread_entry:0x48:0)
at system.lib.libc_so.0xcbd4(pthread_create:0xd0:0)
- Outside JNI code
The COFFEE_TRY()/COFFEE_CATCH()/COFFEE_END() syntax can be used:
void my_function() {
COFFEE_TRY() {
/** Try to call 'call_some_native_function'. **/
call_some_native_function();
} COFFEE_CATCH() {
/** Caught a signal. **/
const char*const message = coffeecatch_get_message();
fprintf(stderr, "**FATAL ERROR: %s
", message);
} COFFEE_END();
}
- Hints
If you wish to catch signals and continue running your program rather than ending it (this may be dangerous, especially if a crash was spotted within a C library function, such as malloc()
), use thecoffeecatch_cancel_pending_alarm()
function to cancel the default pending alarm triggered to avoid deadlocks.
JNI方法调用改变
dvmCallJNIMethod_general不知从哪个版本就没了,但从http://androidxref.com/这里看,Gingerbread - 2.3.7还有,ICS - 4.0.3就没了。
这里模拟器里导出的libdvm.so,符号,只有dvmCallJNIMethod,如下(4.4.2_API19):
File: /cygdrive/d/Developer/sdk/platforms/android-19/lib/libdvm.so
Symbol table '.dynsym' contains 1713 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 00000000 0 NOTYPE LOCAL DEFAULT UND
1: 00000000 0 FUNC GLOBAL DEFAULT UND __cxa_finalize
394: 0004dd75 664 FUNC GLOBAL DEFAULT 8 _Z16dvmCallJNIMethodPKjP6JValuePK6MethodP6Thread
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 //xref: 4.4.2_r2 /dalvik/vm/interp/Stack.cpp 2 //http://androidxref.com/4.4.2_r2/xref/dalvik/vm/interp/Stack.cpp 3 4 /* 5 * Copyright (C) 2008 The Android Open Source Project 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 */ 19 20 /* 21 * Stacks and their uses (e.g. native --> interpreted method calls). 22 * 23 * See the majestic ASCII art in Stack.h. 24 */ 25 #include "Dalvik.h" 26 #include "jni.h" 27 28 #include <stdlib.h> 29 #include <stdarg.h> 30 31 #ifdef HAVE_ANDROID_OS 32 #include <corkscrew/backtrace.h> 33 #endif 34 35 /* 36 * Initialize the interpreter stack in a new thread. 37 * 38 * Currently this doesn't do much, since we don't need to zero out the 39 * stack (and we really don't want to if it was created with mmap). 40 */ 41 bool dvmInitInterpStack(Thread* thread, int stackSize) 42 { 43 assert(thread->interpStackStart != NULL); 44 45 assert(thread->interpSave.curFrame == NULL); 46 47 return true; 48 } 49 50 /* 51 * We're calling an interpreted method from an internal VM function or 52 * via reflection. 53 * 54 * Push a frame for an interpreted method onto the stack. This is only 55 * used when calling into interpreted code from native code. (The 56 * interpreter does its own stack frame manipulation for interp-->interp 57 * calls.) 58 * 59 * The size we need to reserve is the sum of parameters, local variables, 60 * saved goodies, and outbound parameters. 61 * 62 * We start by inserting a "break" frame, which ensures that the interpreter 63 * hands control back to us after the function we call returns or an 64 * uncaught exception is thrown. 65 */ 66 static bool dvmPushInterpFrame(Thread* self, const Method* method) 67 { 68 StackSaveArea* saveBlock; 69 StackSaveArea* breakSaveBlock; 70 int stackReq; 71 u1* stackPtr; 72 73 assert(!dvmIsNativeMethod(method)); 74 assert(!dvmIsAbstractMethod(method)); 75 76 stackReq = method->registersSize * 4 // params + locals 77 + sizeof(StackSaveArea) * 2 // break frame + regular frame 78 + method->outsSize * 4; // args to other methods 79 80 if (self->interpSave.curFrame != NULL) 81 stackPtr = (u1*) SAVEAREA_FROM_FP(self->interpSave.curFrame); 82 else 83 stackPtr = self->interpStackStart; 84 85 if (stackPtr - stackReq < self->interpStackEnd) { 86 /* not enough space */ 87 ALOGW("Stack overflow on call to interp " 88 "(req=%d top=%p cur=%p size=%d %s.%s)", 89 stackReq, self->interpStackStart, self->interpSave.curFrame, 90 self->interpStackSize, method->clazz->descriptor, method->name); 91 dvmHandleStackOverflow(self, method); 92 assert(dvmCheckException(self)); 93 return false; 94 } 95 96 /* 97 * Shift the stack pointer down, leaving space for the function's 98 * args/registers and save area. 99 */ 100 stackPtr -= sizeof(StackSaveArea); 101 breakSaveBlock = (StackSaveArea*)stackPtr; 102 stackPtr -= method->registersSize * 4 + sizeof(StackSaveArea); 103 saveBlock = (StackSaveArea*) stackPtr; 104 105 #if !defined(NDEBUG) && !defined(PAD_SAVE_AREA) 106 /* debug -- memset the new stack, unless we want valgrind's help */ 107 memset(stackPtr - (method->outsSize*4), 0xaf, stackReq); 108 #endif 109 #ifdef EASY_GDB 110 breakSaveBlock->prevSave = 111 (StackSaveArea*)FP_FROM_SAVEAREA(self->interpSave.curFrame); 112 saveBlock->prevSave = breakSaveBlock; 113 #endif 114 115 breakSaveBlock->prevFrame = self->interpSave.curFrame; 116 breakSaveBlock->savedPc = NULL; // not required 117 breakSaveBlock->xtra.localRefCookie = 0; // not required 118 breakSaveBlock->method = NULL; 119 saveBlock->prevFrame = FP_FROM_SAVEAREA(breakSaveBlock); 120 saveBlock->savedPc = NULL; // not required 121 saveBlock->xtra.currentPc = NULL; // not required? 122 saveBlock->method = method; 123 124 LOGVV("PUSH frame: old=%p new=%p (size=%d)", 125 self->interpSave.curFrame, FP_FROM_SAVEAREA(saveBlock), 126 (u1*)self->interpSave.curFrame - (u1*)FP_FROM_SAVEAREA(saveBlock)); 127 128 self->interpSave.curFrame = FP_FROM_SAVEAREA(saveBlock); 129 130 return true; 131 } 132 133 /* 134 * We're calling a JNI native method from an internal VM fuction or 135 * via reflection. This is also used to create the "fake" native-method 136 * frames at the top of the interpreted stack. 137 * 138 * This actually pushes two frames; the first is a "break" frame. 139 * 140 * The top frame has additional space for JNI local reference tracking. 141 */ 142 bool dvmPushJNIFrame(Thread* self, const Method* method) 143 { 144 StackSaveArea* saveBlock; 145 StackSaveArea* breakSaveBlock; 146 int stackReq; 147 u1* stackPtr; 148 149 assert(dvmIsNativeMethod(method)); 150 151 stackReq = method->registersSize * 4 // params only 152 + sizeof(StackSaveArea) * 2; // break frame + regular frame 153 154 if (self->interpSave.curFrame != NULL) 155 stackPtr = (u1*) SAVEAREA_FROM_FP(self->interpSave.curFrame); 156 else 157 stackPtr = self->interpStackStart; 158 159 if (stackPtr - stackReq < self->interpStackEnd) { 160 /* not enough space */ 161 ALOGW("Stack overflow on call to native " 162 "(req=%d top=%p cur=%p size=%d '%s')", 163 stackReq, self->interpStackStart, self->interpSave.curFrame, 164 self->interpStackSize, method->name); 165 dvmHandleStackOverflow(self, method); 166 assert(dvmCheckException(self)); 167 return false; 168 } 169 170 /* 171 * Shift the stack pointer down, leaving space for just the stack save 172 * area for the break frame, then shift down farther for the full frame. 173 * We leave space for the method args, which are copied in later. 174 */ 175 stackPtr -= sizeof(StackSaveArea); 176 breakSaveBlock = (StackSaveArea*)stackPtr; 177 stackPtr -= method->registersSize * 4 + sizeof(StackSaveArea); 178 saveBlock = (StackSaveArea*) stackPtr; 179 180 #if !defined(NDEBUG) && !defined(PAD_SAVE_AREA) 181 /* debug -- memset the new stack */ 182 memset(stackPtr, 0xaf, stackReq); 183 #endif 184 #ifdef EASY_GDB 185 if (self->interpSave.curFrame == NULL) 186 breakSaveBlock->prevSave = NULL; 187 else { 188 void* fp = FP_FROM_SAVEAREA(self->interpSave.curFrame); 189 breakSaveBlock->prevSave = (StackSaveArea*)fp; 190 } 191 saveBlock->prevSave = breakSaveBlock; 192 #endif 193 194 breakSaveBlock->prevFrame = self->interpSave.curFrame; 195 breakSaveBlock->savedPc = NULL; // not required 196 breakSaveBlock->xtra.localRefCookie = 0; // not required 197 breakSaveBlock->method = NULL; 198 saveBlock->prevFrame = FP_FROM_SAVEAREA(breakSaveBlock); 199 saveBlock->savedPc = NULL; // not required 200 saveBlock->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all; 201 saveBlock->method = method; 202 203 LOGVV("PUSH JNI frame: old=%p new=%p (size=%d)", 204 self->interpSave.curFrame, FP_FROM_SAVEAREA(saveBlock), 205 (u1*)self->interpSave.curFrame - (u1*)FP_FROM_SAVEAREA(saveBlock)); 206 207 self->interpSave.curFrame = FP_FROM_SAVEAREA(saveBlock); 208 209 return true; 210 } 211 212 /* 213 * This is used by the JNI PushLocalFrame call. We push a new frame onto 214 * the stack that has no ins, outs, or locals, and no break frame above it. 215 * It's strictly used for tracking JNI local refs, and will be popped off 216 * by dvmPopFrame if it's not removed explicitly. 217 */ 218 bool dvmPushLocalFrame(Thread* self, const Method* method) 219 { 220 StackSaveArea* saveBlock; 221 int stackReq; 222 u1* stackPtr; 223 224 assert(dvmIsNativeMethod(method)); 225 226 stackReq = sizeof(StackSaveArea); // regular frame 227 228 assert(self->interpSave.curFrame != NULL); 229 stackPtr = (u1*) SAVEAREA_FROM_FP(self->interpSave.curFrame); 230 231 if (stackPtr - stackReq < self->interpStackEnd) { 232 /* not enough space; let JNI throw the exception */ 233 ALOGW("Stack overflow on PushLocal " 234 "(req=%d top=%p cur=%p size=%d '%s')", 235 stackReq, self->interpStackStart, self->interpSave.curFrame, 236 self->interpStackSize, method->name); 237 dvmHandleStackOverflow(self, method); 238 assert(dvmCheckException(self)); 239 return false; 240 } 241 242 /* 243 * Shift the stack pointer down, leaving space for just the stack save 244 * area for the break frame, then shift down farther for the full frame. 245 */ 246 stackPtr -= sizeof(StackSaveArea); 247 saveBlock = (StackSaveArea*) stackPtr; 248 249 #if !defined(NDEBUG) && !defined(PAD_SAVE_AREA) 250 /* debug -- memset the new stack */ 251 memset(stackPtr, 0xaf, stackReq); 252 #endif 253 #ifdef EASY_GDB 254 saveBlock->prevSave = 255 (StackSaveArea*)FP_FROM_SAVEAREA(self->interpSave.curFrame); 256 #endif 257 258 saveBlock->prevFrame = self->interpSave.curFrame; 259 saveBlock->savedPc = NULL; // not required 260 saveBlock->xtra.localRefCookie = self->jniLocalRefTable.segmentState.all; 261 saveBlock->method = method; 262 263 LOGVV("PUSH JNI local frame: old=%p new=%p (size=%d)", 264 self->interpSave.curFrame, FP_FROM_SAVEAREA(saveBlock), 265 (u1*)self->interpSave.curFrame - (u1*)FP_FROM_SAVEAREA(saveBlock)); 266 267 self->interpSave.curFrame = FP_FROM_SAVEAREA(saveBlock); 268 269 return true; 270 } 271 272 /* 273 * Pop one frame pushed on by JNI PushLocalFrame. 274 * 275 * If we've gone too far, the previous frame is either a break frame or 276 * an interpreted frame. Either way, the method pointer won't match. 277 */ 278 bool dvmPopLocalFrame(Thread* self) 279 { 280 StackSaveArea* saveBlock = SAVEAREA_FROM_FP(self->interpSave.curFrame); 281 282 assert(!dvmIsBreakFrame((u4*)self->interpSave.curFrame)); 283 if (saveBlock->method != SAVEAREA_FROM_FP(saveBlock->prevFrame)->method) { 284 /* 285 * The previous frame doesn't have the same method pointer -- we've 286 * been asked to pop too much. 287 */ 288 assert(dvmIsBreakFrame((u4*)saveBlock->prevFrame) || 289 !dvmIsNativeMethod( 290 SAVEAREA_FROM_FP(saveBlock->prevFrame)->method)); 291 return false; 292 } 293 294 LOGVV("POP JNI local frame: removing %s, now %s", 295 saveBlock->method->name, 296 SAVEAREA_FROM_FP(saveBlock->prevFrame)->method->name); 297 dvmPopJniLocals(self, saveBlock); 298 self->interpSave.curFrame = saveBlock->prevFrame; 299 300 return true; 301 } 302 303 /* 304 * Pop a frame we added. There should be one method frame and one break 305 * frame. 306 * 307 * If JNI Push/PopLocalFrame calls were mismatched, we might end up 308 * popping multiple method frames before we find the break. 309 * 310 * Returns "false" if there was no frame to pop. 311 */ 312 static bool dvmPopFrame(Thread* self) 313 { 314 StackSaveArea* saveBlock; 315 316 if (self->interpSave.curFrame == NULL) 317 return false; 318 319 saveBlock = SAVEAREA_FROM_FP(self->interpSave.curFrame); 320 assert(!dvmIsBreakFrame((u4*)self->interpSave.curFrame)); 321 322 /* 323 * Remove everything up to the break frame. If this was a call into 324 * native code, pop the JNI local references table. 325 */ 326 while (saveBlock->prevFrame != NULL && saveBlock->method != NULL) { 327 /* probably a native->native JNI call */ 328 329 if (dvmIsNativeMethod(saveBlock->method)) { 330 LOGVV("Popping JNI stack frame for %s.%s%s", 331 saveBlock->method->clazz->descriptor, 332 saveBlock->method->name, 333 (SAVEAREA_FROM_FP(saveBlock->prevFrame)->method == NULL) ? 334 "" : " (JNI local)"); 335 dvmPopJniLocals(self, saveBlock); 336 } 337 338 saveBlock = SAVEAREA_FROM_FP(saveBlock->prevFrame); 339 } 340 if (saveBlock->method != NULL) { 341 ALOGE("PopFrame missed the break"); 342 assert(false); 343 dvmAbort(); // stack trashed -- nowhere to go in this thread 344 } 345 346 LOGVV("POP frame: cur=%p new=%p", 347 self->interpSave.curFrame, saveBlock->prevFrame); 348 349 self->interpSave.curFrame = saveBlock->prevFrame; 350 return true; 351 } 352 353 /* 354 * Common code for dvmCallMethodV/A and dvmInvokeMethod. 355 * 356 * Pushes a call frame on, advancing self->interpSave.curFrame. 357 */ 358 static ClassObject* callPrep(Thread* self, const Method* method, Object* obj, 359 bool checkAccess) 360 { 361 ClassObject* clazz; 362 363 #ifndef NDEBUG 364 if (self->status != THREAD_RUNNING) { 365 ALOGW("threadid=%d: status=%d on call to %s.%s -", 366 self->threadId, self->status, 367 method->clazz->descriptor, method->name); 368 } 369 #endif 370 371 assert(self != NULL); 372 assert(method != NULL); 373 374 if (obj != NULL) 375 clazz = obj->clazz; 376 else 377 clazz = method->clazz; 378 379 IF_LOGVV() { 380 char* desc = dexProtoCopyMethodDescriptor(&method->prototype); 381 LOGVV("thread=%d native code calling %s.%s %s", self->threadId, 382 clazz->descriptor, method->name, desc); 383 free(desc); 384 } 385 386 if (checkAccess) { 387 /* needed for java.lang.reflect.Method.invoke */ 388 if (!dvmCheckMethodAccess(dvmGetCaller2Class(self->interpSave.curFrame), 389 method)) 390 { 391 /* note this throws IAException, not IAError */ 392 dvmThrowIllegalAccessException("access to method denied"); 393 return NULL; 394 } 395 } 396 397 /* 398 * Push a call frame on. If there isn't enough room for ins, locals, 399 * outs, and the saved state, it will throw an exception. 400 * 401 * This updates self->interpSave.curFrame. 402 */ 403 if (dvmIsNativeMethod(method)) { 404 /* native code calling native code the hard way */ 405 if (!dvmPushJNIFrame(self, method)) { 406 assert(dvmCheckException(self)); 407 return NULL; 408 } 409 } else { 410 /* native code calling interpreted code */ 411 if (!dvmPushInterpFrame(self, method)) { 412 assert(dvmCheckException(self)); 413 return NULL; 414 } 415 } 416 417 return clazz; 418 } 419 420 /* 421 * Issue a method call. 422 * 423 * Pass in NULL for "obj" on calls to static methods. 424 * 425 * (Note this can't be inlined because it takes a variable number of args.) 426 */ 427 void dvmCallMethod(Thread* self, const Method* method, Object* obj, 428 JValue* pResult, ...) 429 { 430 va_list args; 431 va_start(args, pResult); 432 dvmCallMethodV(self, method, obj, false, pResult, args); 433 va_end(args); 434 } 435 436 /* 437 * Issue a method call with a variable number of arguments. We process 438 * the contents of "args" by scanning the method signature. 439 * 440 * Pass in NULL for "obj" on calls to static methods. 441 * 442 * We don't need to take the class as an argument because, in Dalvik, 443 * we don't need to worry about static synchronized methods. 444 */ 445 void dvmCallMethodV(Thread* self, const Method* method, Object* obj, 446 bool fromJni, JValue* pResult, va_list args) 447 { 448 const char* desc = &(method->shorty[1]); // [0] is the return type. 449 int verifyCount = 0; 450 ClassObject* clazz; 451 u4* ins; 452 453 clazz = callPrep(self, method, obj, false); 454 if (clazz == NULL) 455 return; 456 457 /* "ins" for new frame start at frame pointer plus locals */ 458 ins = ((u4*)self->interpSave.curFrame) + 459 (method->registersSize - method->insSize); 460 461 //ALOGD(" FP is %p, INs live at >= %p", self->interpSave.curFrame, ins); 462 463 /* put "this" pointer into in0 if appropriate */ 464 if (!dvmIsStaticMethod(method)) { 465 #ifdef WITH_EXTRA_OBJECT_VALIDATION 466 assert(obj != NULL && dvmIsHeapAddress(obj)); 467 #endif 468 *ins++ = (u4) obj; 469 verifyCount++; 470 } 471 472 while (*desc != '