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 /* 18 * Dalvik implementation of JNI interfaces. 19 */ 20 #include "Dalvik.h" 21 #include "JniInternal.h" 22 23 #include <stdlib.h> 24 #include <stdarg.h> 25 #include <limits.h> 26 27 /* 28 Native methods and interaction with the GC 29 30 All JNI methods must start by changing their thread status to 31 THREAD_RUNNING, and finish by changing it back to THREAD_NATIVE before 32 returning to native code. The switch to "running" triggers a thread 33 suspension check. 34 35 With a rudimentary GC we should be able to skip the status change for 36 simple functions, e.g. IsSameObject, GetJavaVM, GetStringLength, maybe 37 even access to fields with primitive types. Our options are more limited 38 with a compacting GC, so we should replace JNI_ENTER with JNI_ENTER_NCGC 39 or somesuch on the "lite" functions if we want to try this optimization. 40 41 For performance reasons we do as little error-checking as possible here. 42 For example, we don't check to make sure the correct type of Object is 43 passed in when setting a field, and we don't prevent you from storing 44 new values in a "final" field. Such things are best handled in the 45 "check" version. For actions that are common, dangerous, and must be 46 checked at runtime, such as array bounds checks, we do the tests here. 47 48 49 General notes on local/global reference tracking 50 51 JNI provides explicit control over natively-held references that the GC 52 needs to know about. These can be local, in which case they're released 53 when the native method returns into the VM, or global, which are held 54 until explicitly released. (There are also weak-global references, 55 which have the lifespan and visibility of global references, but the 56 object they refer to may be collected.) 57 58 The references can be created with explicit JNI NewLocalRef / NewGlobalRef 59 calls. The former is very unusual, the latter is reasonably common 60 (e.g. for caching references to class objects). 61 62 Local references are most often created as a side-effect of JNI functions. 63 For example, the AllocObject/NewObject functions must create local 64 references to the objects returned, because nothing else in the GC root 65 set has a reference to the new objects. 66 67 The most common mode of operation is for a method to create zero or 68 more local references and return. Explicit "local delete" operations 69 are expected to be exceedingly rare, except when walking through an 70 object array, and the Push/PopLocalFrame calls are expected to be used 71 infrequently. For efficient operation, we want to add new local refs 72 with a simple store/increment operation; to avoid infinite growth in 73 pathological situations, we need to reclaim the space used by deleted 74 entries. 75 76 If we just want to maintain a list for the GC root set, we can use an 77 expanding append-only array that compacts when objects are deleted. 78 In typical situations, e.g. running through an array of objects, we will 79 be deleting one of the most recently added entries, so we can minimize 80 the number of elements moved (or avoid having to move any). 81 82 If we want to conceal the pointer values from native code, which is 83 necessary to allow the GC to move JNI-referenced objects around, then we 84 have to use a more complicated indirection mechanism. 85 86 The spec says, "Local references are only valid in the thread in which 87 they are created. The native code must not pass local references from 88 one thread to another." 89 90 91 Pinned objects 92 93 For some large chunks of data, notably primitive arrays and String data, 94 JNI allows the VM to choose whether it wants to pin the array object or 95 make a copy. We currently pin the memory for better execution performance. 96 97 TODO: we're using simple root set references to pin primitive array data, 98 because they have the property we need (i.e. the pointer we return is 99 guaranteed valid until we explicitly release it). However, if we have a 100 compacting GC and don't want to pin all memory held by all global refs, 101 we need to treat these differently. 102 103 104 Global reference tracking 105 106 There should be a small "active" set centered around the most-recently 107 added items. 108 109 Because it's global, access to it has to be synchronized. Additions and 110 removals require grabbing a mutex. If the table serves as an indirection 111 mechanism (i.e. it's not just a list for the benefit of the garbage 112 collector), reference lookups may also require grabbing a mutex. 113 114 The JNI spec does not define any sort of limit, so the list must be able 115 to expand to a reasonable size. It may be useful to log significant 116 increases in usage to help identify resource leaks. 117 118 119 Weak-global reference tracking 120 121 [TBD] 122 123 124 Local reference tracking 125 126 The table of local references can be stored on the interpreted stack or 127 in a parallel data structure (one per thread). 128 129 *** Approach #1: use the interpreted stack 130 131 The easiest place to tuck it is between the frame ptr and the first saved 132 register, which is always in0. (See the ASCII art in Stack.h.) We can 133 shift the "VM-specific goop" and frame ptr down, effectively inserting 134 the JNI local refs in the space normally occupied by local variables. 135 136 (Three things are accessed from the frame pointer: 137 (1) framePtr[N] is register vN, used to get at "ins" and "locals". 138 (2) framePtr - sizeof(StackSaveArea) is the VM frame goop. 139 (3) framePtr - sizeof(StackSaveArea) - numOuts is where the "outs" go. 140 The only thing that isn't determined by an offset from the current FP 141 is the previous frame. However, tucking things below the previous frame 142 can be problematic because the "outs" of the previous frame overlap with 143 the "ins" of the current frame. If the "ins" are altered they must be 144 restored before we return. For a native method call, the easiest and 145 safest thing to disrupt is #1, because there are no locals and the "ins" 146 are all copied to the native stack.) 147 148 We can implement Push/PopLocalFrame with the existing stack frame calls, 149 making sure we copy some goop from the previous frame (notably the method 150 ptr, so that dvmGetCurrentJNIMethod() doesn't require extra effort). 151 152 We can pre-allocate the storage at the time the stack frame is first 153 set up, but we have to be careful. When calling from interpreted code 154 the frame ptr points directly at the arguments we're passing, but we can 155 offset the args pointer when calling the native bridge. 156 157 To manage the local ref collection, we need to be able to find three 158 things: (1) the start of the region, (2) the end of the region, and (3) 159 the next available entry. The last is only required for quick adds. 160 We currently have two easily-accessible pointers, the current FP and the 161 previous frame's FP. (The "stack pointer" shown in the ASCII art doesn't 162 actually exist in the interpreted world.) 163 164 We can't use the current FP to find the first "in", because we want to 165 insert the variable-sized local refs table between them. It's awkward 166 to use the previous frame's FP because native methods invoked via 167 dvmCallMethod() or dvmInvokeMethod() don't have "ins", but native methods 168 invoked from interpreted code do. We can either track the local refs 169 table size with a field in the stack frame, or insert unnecessary items 170 so that all native stack frames have "ins". 171 172 Assuming we can find the region bounds, we still need pointer #3 173 for an efficient implementation. This can be stored in an otherwise 174 unused-for-native field in the frame goop. 175 176 When we run out of room we have to make more space. If we start allocating 177 locals immediately below in0 and grow downward, we will detect end-of-space 178 by running into the current frame's FP. We then memmove() the goop down 179 (memcpy if we guarantee the additional size is larger than the frame). 180 This is nice because we only have to move sizeof(StackSaveArea) bytes 181 each time. 182 183 Stack walking should be okay so long as nothing tries to access the 184 "ins" by an offset from the FP. In theory the "ins" could be read by 185 the debugger or SIGQUIT handler looking for "this" or other arguments, 186 but in practice this behavior isn't expected to work for native methods, 187 so we can simply disallow it. 188 189 A conservative GC can just scan the entire stack from top to bottom to find 190 all references. An exact GC will need to understand the actual layout. 191 192 *** Approach #2: use a parallel stack 193 194 Each Thread/JNIEnv points to a reference table. The struct has 195 a system-heap-allocated array of references and a pointer to the 196 next-available entry ("nextEntry"). 197 198 Each stack frame has a pointer to what it sees as the "bottom" element 199 in the array (we can double-up the "currentPc" field). This is set to 200 "nextEntry" when the frame is pushed on. As local references are added 201 or removed, "nextEntry" is updated. 202 203 We implement Push/PopLocalFrame with actual stack frames. Before a JNI 204 frame gets popped, we set "nextEntry" to the "top" pointer of the current 205 frame, effectively releasing the references. 206 207 The GC will scan all references from the start of the table to the 208 "nextEntry" pointer. 209 210 *** Comparison 211 212 All approaches will return a failure result when they run out of local 213 reference space. For #1 that means blowing out the stack, for #2 it's 214 running out of room in the array. 215 216 Compared to #1, approach #2: 217 - Needs only one pointer in the stack frame goop. 218 - Makes pre-allocating storage unnecessary. 219 - Doesn't contend with interpreted stack depth for space. In most 220 cases, if something blows out the local ref storage, it's because the 221 JNI code was misbehaving rather than called from way down. 222 - Allows the GC to do a linear scan per thread in a buffer that is 100% 223 references. The GC can be slightly less smart when scanning the stack. 224 - Will be easier to work with if we combine native and interpeted stacks. 225 226 - Isn't as clean, especially when popping frames, since we have to do 227 explicit work. Fortunately we only have to do it when popping native 228 method calls off, so it doesn't add overhead to interpreted code paths. 229 - Is awkward to expand dynamically. We'll want to pre-allocate the full 230 amount of space; this is fine, since something on the order of 1KB should 231 be plenty. The JNI spec allows us to limit this. 232 - Requires the GC to scan even more memory. With the references embedded 233 in the stack we get better locality of reference. 234 235 */ 236 237 /* fwd */ 238 static const struct JNINativeInterface gNativeInterface; 239 static jobject addGlobalReference(Object* obj); 240 241 #ifdef WITH_JNI_STACK_CHECK 242 # define COMPUTE_STACK_SUM(_self) computeStackSum(_self); 243 # define CHECK_STACK_SUM(_self) checkStackSum(_self); 244 //static void computeStackSum(Thread* self); 245 //static void checkStackSum(Thread* self); 246 #else 247 # define COMPUTE_STACK_SUM(_self) ((void)0) 248 # define CHECK_STACK_SUM(_self) ((void)0) 249 #endif 250 251 252 /* 253 * =========================================================================== 254 * Utility functions 255 * =========================================================================== 256 */ 257 258 /* 259 * Entry/exit processing for all JNI calls. 260 * 261 * If TRUSTED_JNIENV is set, we get to skip the (curiously expensive) 262 * thread-local storage lookup on our Thread*. If the caller has passed 263 * the wrong JNIEnv in, we're going to be accessing unsynchronized 264 * structures from more than one thread, and things are going to fail 265 * in bizarre ways. This is only sensible if the native code has been 266 * fully exercised with CheckJNI enabled. 267 */ 268 #define TRUSTED_JNIENV 269 #ifdef TRUSTED_JNIENV 270 # define JNI_ENTER() \ 271 Thread* _self = ((JNIEnvExt*)env)->self; \ 272 CHECK_STACK_SUM(_self); \ 273 dvmChangeStatus(_self, THREAD_RUNNING) 274 #else 275 # define JNI_ENTER() \ 276 Thread* _self = dvmThreadSelf(); \ 277 UNUSED_PARAMETER(env); \ 278 CHECK_STACK_SUM(_self); \ 279 dvmChangeStatus(_self, THREAD_RUNNING) 280 #endif 281 #define JNI_EXIT() \ 282 dvmChangeStatus(_self, THREAD_NATIVE); \ 283 COMPUTE_STACK_SUM(_self) 284 285 #define kGlobalRefsTableInitialSize 512 286 #define kGlobalRefsTableMaxSize 51200 /* arbitrary, must be < 64K */ 287 #define kGrefWaterInterval 100 288 #define kTrackGrefUsage true 289 290 #define kPinTableInitialSize 16 291 #define kPinTableMaxSize 1024 292 #define kPinComplainThreshold 10 293 294 /* 295 * Allocate the global references table, and look up some classes for 296 * the benefit of direct buffer access. 297 */ 298 bool dvmJniStartup(void) 299 { 300 #ifdef USE_INDIRECT_REF 301 if (!dvmInitIndirectRefTable(&gDvm.jniGlobalRefTable, 302 kGlobalRefsTableInitialSize, kGlobalRefsTableMaxSize, 303 kIndirectKindGlobal)) 304 return false; 305 #else 306 if (!dvmInitReferenceTable(&gDvm.jniGlobalRefTable, 307 kGlobalRefsTableInitialSize, kGlobalRefsTableMaxSize)) 308 return false; 309 #endif 310 311 dvmInitMutex(&gDvm.jniGlobalRefLock); 312 gDvm.jniGlobalRefLoMark = 0; 313 gDvm.jniGlobalRefHiMark = kGrefWaterInterval * 2; 314 315 if (!dvmInitReferenceTable(&gDvm.jniPinRefTable, 316 kPinTableInitialSize, kPinTableMaxSize)) 317 return false; 318 319 dvmInitMutex(&gDvm.jniPinRefLock); 320 321 Method* meth; 322 323 /* 324 * Grab the PhantomReference constructor. 325 */ 326 gDvm.classJavaLangRefPhantomReference = 327 dvmFindSystemClassNoInit("Ljava/lang/ref/PhantomReference;"); 328 if (gDvm.classJavaLangRefPhantomReference == NULL) { 329 LOGE("Unable to find PhantomReference class\n"); 330 return false; 331 } 332 meth= dvmFindDirectMethodByDescriptor(gDvm.classJavaLangRefPhantomReference, 333 "<init>", "(Ljava/lang/Object;Ljava/lang/ref/ReferenceQueue;)V"); 334 if (meth == NULL) { 335 LOGE("Unable to find constructor for PhantomReference\n"); 336 return false; 337 } 338 gDvm.methJavaLangRefPhantomReference_init = meth; 339 340 341 /* 342 * Look up and cache pointers to some direct buffer classes, fields, 343 * and methods. 344 */ 345 ClassObject* platformAddressClass = 346 dvmFindSystemClassNoInit("Lorg/apache/harmony/luni/platform/PlatformAddress;"); 347 ClassObject* platformAddressFactoryClass = 348 dvmFindSystemClassNoInit("Lorg/apache/harmony/luni/platform/PlatformAddressFactory;"); 349 ClassObject* directBufferClass = 350 dvmFindSystemClassNoInit("Lorg/apache/harmony/nio/internal/DirectBuffer;"); 351 ClassObject* readWriteBufferClass = 352 dvmFindSystemClassNoInit("Ljava/nio/ReadWriteDirectByteBuffer;"); 353 ClassObject* bufferClass = 354 dvmFindSystemClassNoInit("Ljava/nio/Buffer;"); 355 356 if (platformAddressClass == NULL || platformAddressFactoryClass == NULL || 357 directBufferClass == NULL || readWriteBufferClass == NULL || 358 bufferClass == NULL) 359 { 360 LOGE("Unable to find internal direct buffer classes\n"); 361 return false; 362 } 363 gDvm.classJavaNioReadWriteDirectByteBuffer = readWriteBufferClass; 364 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer = directBufferClass; 365 /* need a global reference for extended CheckJNI tests */ 366 gDvm.jclassOrgApacheHarmonyNioInternalDirectBuffer = 367 addGlobalReference((Object*) directBufferClass); 368 369 /* 370 * We need a Method* here rather than a vtable offset, because 371 * DirectBuffer is an interface class. 372 */ 373 meth = dvmFindVirtualMethodByDescriptor( 374 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer, 375 "getEffectiveAddress", 376 "()Lorg/apache/harmony/luni/platform/PlatformAddress;"); 377 if (meth == NULL) { 378 LOGE("Unable to find PlatformAddress.getEffectiveAddress\n"); 379 return false; 380 } 381 gDvm.methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress = meth; 382 383 meth = dvmFindVirtualMethodByDescriptor(platformAddressClass, 384 "toLong", "()J"); 385 if (meth == NULL) { 386 LOGE("Unable to find PlatformAddress.toLong\n"); 387 return false; 388 } 389 gDvm.voffOrgApacheHarmonyLuniPlatformPlatformAddress_toLong = 390 meth->methodIndex; 391 392 meth = dvmFindDirectMethodByDescriptor(platformAddressFactoryClass, 393 "on", 394 "(I)Lorg/apache/harmony/luni/platform/PlatformAddress;"); 395 if (meth == NULL) { 396 LOGE("Unable to find PlatformAddressFactory.on\n"); 397 return false; 398 } 399 gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on = meth; 400 401 meth = dvmFindDirectMethodByDescriptor(readWriteBufferClass, 402 "<init>", 403 "(Lorg/apache/harmony/luni/platform/PlatformAddress;II)V"); 404 if (meth == NULL) { 405 LOGE("Unable to find ReadWriteDirectByteBuffer.<init>\n"); 406 return false; 407 } 408 gDvm.methJavaNioReadWriteDirectByteBuffer_init = meth; 409 410 gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr = 411 dvmFindFieldOffset(platformAddressClass, "osaddr", "I"); 412 if (gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr < 0) { 413 LOGE("Unable to find PlatformAddress.osaddr\n"); 414 return false; 415 } 416 417 gDvm.offJavaNioBuffer_capacity = 418 dvmFindFieldOffset(bufferClass, "capacity", "I"); 419 if (gDvm.offJavaNioBuffer_capacity < 0) { 420 LOGE("Unable to find Buffer.capacity\n"); 421 return false; 422 } 423 424 gDvm.offJavaNioBuffer_effectiveDirectAddress = 425 dvmFindFieldOffset(bufferClass, "effectiveDirectAddress", "I"); 426 if (gDvm.offJavaNioBuffer_effectiveDirectAddress < 0) { 427 LOGE("Unable to find Buffer.effectiveDirectAddress\n"); 428 return false; 429 } 430 431 return true; 432 } 433 434 /* 435 * Free the global references table. 436 */ 437 void dvmJniShutdown(void) 438 { 439 #ifdef USE_INDIRECT_REF 440 dvmClearIndirectRefTable(&gDvm.jniGlobalRefTable); 441 #else 442 dvmClearReferenceTable(&gDvm.jniGlobalRefTable); 443 #endif 444 dvmClearReferenceTable(&gDvm.jniPinRefTable); 445 } 446 447 448 /* 449 * Find the JNIEnv associated with the current thread. 450 * 451 * Currently stored in the Thread struct. Could also just drop this into 452 * thread-local storage. 453 */ 454 JNIEnvExt* dvmGetJNIEnvForThread(void) 455 { 456 Thread* self = dvmThreadSelf(); 457 if (self == NULL) 458 return NULL; 459 return (JNIEnvExt*) dvmGetThreadJNIEnv(self); 460 } 461 462 /* 463 * Create a new JNIEnv struct and add it to the VM's list. 464 * 465 * "self" will be NULL for the main thread, since the VM hasn't started 466 * yet; the value will be filled in later. 467 */ 468 JNIEnv* dvmCreateJNIEnv(Thread* self) 469 { 470 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList; 471 JNIEnvExt* newEnv; 472 473 //if (self != NULL) 474 // LOGI("Ent CreateJNIEnv: threadid=%d %p\n", self->threadId, self); 475 476 assert(vm != NULL); 477 478 newEnv = (JNIEnvExt*) calloc(1, sizeof(JNIEnvExt)); 479 newEnv->funcTable = &gNativeInterface; 480 newEnv->vm = vm; 481 newEnv->forceDataCopy = vm->forceDataCopy; 482 if (self != NULL) { 483 dvmSetJniEnvThreadId((JNIEnv*) newEnv, self); 484 assert(newEnv->envThreadId != 0); 485 } else { 486 /* make it obvious if we fail to initialize these later */ 487 newEnv->envThreadId = 0x77777775; 488 newEnv->self = (Thread*) 0x77777779; 489 } 490 if (vm->useChecked) 491 dvmUseCheckedJniEnv(newEnv); 492 493 dvmLockMutex(&vm->envListLock); 494 495 /* insert at head of list */ 496 newEnv->next = vm->envList; 497 assert(newEnv->prev == NULL); 498 if (vm->envList == NULL) // rare, but possible 499 vm->envList = newEnv; 500 else 501 vm->envList->prev = newEnv; 502 vm->envList = newEnv; 503 504 dvmUnlockMutex(&vm->envListLock); 505 506 //if (self != NULL) 507 // LOGI("Xit CreateJNIEnv: threadid=%d %p\n", self->threadId, self); 508 return (JNIEnv*) newEnv; 509 } 510 511 /* 512 * Remove a JNIEnv struct from the list and free it. 513 */ 514 void dvmDestroyJNIEnv(JNIEnv* env) 515 { 516 JNIEnvExt* extEnv = (JNIEnvExt*) env; 517 JavaVMExt* vm = extEnv->vm; 518 Thread* self; 519 520 if (env == NULL) 521 return; 522 523 self = dvmThreadSelf(); 524 assert(self != NULL); 525 526 //LOGI("Ent DestroyJNIEnv: threadid=%d %p\n", self->threadId, self); 527 528 dvmLockMutex(&vm->envListLock); 529 530 if (extEnv == vm->envList) { 531 assert(extEnv->prev == NULL); 532 vm->envList = extEnv->next; 533 } else { 534 assert(extEnv->prev != NULL); 535 extEnv->prev->next = extEnv->next; 536 } 537 if (extEnv->next != NULL) 538 extEnv->next->prev = extEnv->prev; 539 540 dvmUnlockMutex(&extEnv->vm->envListLock); 541 542 free(env); 543 //LOGI("Xit DestroyJNIEnv: threadid=%d %p\n", self->threadId, self); 544 } 545 546 547 /* 548 * Retrieve the ReferenceTable struct for the current thread. 549 * 550 * Going through "env" rather than dvmThreadSelf() is faster but will 551 * get weird if the JNI code is passing the wrong JNIEnv around. 552 */ 553 #ifdef USE_INDIRECT_REF 554 static inline IndirectRefTable* getLocalRefTable(JNIEnv* env) 555 #else 556 static inline ReferenceTable* getLocalRefTable(JNIEnv* env) 557 #endif 558 { 559 //return &dvmThreadSelf()->jniLocalRefTable; 560 return &((JNIEnvExt*)env)->self->jniLocalRefTable; 561 } 562 563 #ifdef USE_INDIRECT_REF 564 /* 565 * Convert an indirect reference to an Object reference. The indirect 566 * reference may be local, global, or weak-global. 567 * 568 * If "jobj" is NULL or an invalid indirect reference, this returns NULL. 569 */ 570 Object* dvmDecodeIndirectRef(JNIEnv* env, jobject jobj) 571 { 572 if (jobj == NULL) 573 return NULL; 574 575 Object* result; 576 577 switch (dvmGetIndirectRefType(jobj)) { 578 case kIndirectKindLocal: 579 { 580 IndirectRefTable* pRefTable = getLocalRefTable(env); 581 result = dvmGetFromIndirectRefTable(pRefTable, jobj); 582 } 583 break; 584 case kIndirectKindGlobal: 585 { 586 // TODO: find a way to avoid the mutex activity here 587 IndirectRefTable* pRefTable = &gDvm.jniGlobalRefTable; 588 dvmLockMutex(&gDvm.jniGlobalRefLock); 589 result = dvmGetFromIndirectRefTable(pRefTable, jobj); 590 dvmUnlockMutex(&gDvm.jniGlobalRefLock); 591 } 592 break; 593 case kIndirectKindWeakGlobal: 594 { 595 // TODO: implement 596 LOGE("weak-global not yet supported\n"); 597 result = NULL; 598 dvmAbort(); 599 } 600 break; 601 case kIndirectKindInvalid: 602 default: 603 LOGW("Invalid indirect reference %p in decodeIndirectRef\n", jobj); 604 dvmAbort(); 605 result = NULL; 606 break; 607 } 608 609 return result; 610 } 611 #else 612 /* use trivial inline in JniInternal.h for performance */ 613 #endif 614 615 /* 616 * Add a local reference for an object to the current stack frame. When 617 * the native function returns, the reference will be discarded. 618 * 619 * We need to allow the same reference to be added multiple times. 620 * 621 * This will be called on otherwise unreferenced objects. We cannot do 622 * GC allocations here, and it's best if we don't grab a mutex. 623 * 624 * Returns the local reference (currently just the same pointer that was 625 * passed in), or NULL on failure. 626 */ 627 static jobject addLocalReference(JNIEnv* env, Object* obj) 628 { 629 if (obj == NULL) 630 return NULL; 631 632 jobject jobj; 633 634 #ifdef USE_INDIRECT_REF 635 IndirectRefTable* pRefTable = getLocalRefTable(env); 636 void* curFrame = ((JNIEnvExt*)env)->self->curFrame; 637 u4 cookie = SAVEAREA_FROM_FP(curFrame)->xtra.localRefCookie; 638 639 jobj = (jobject) dvmAddToIndirectRefTable(pRefTable, cookie, obj); 640 if (jobj == NULL) { 641 dvmDumpIndirectRefTable(pRefTable, "JNI local"); 642 LOGE("Failed adding to JNI local ref table (has %d entries)\n", 643 (int) dvmIndirectRefTableEntries(pRefTable)); 644 dvmDumpThread(dvmThreadSelf(), false); 645 dvmAbort(); // spec says call FatalError; this is equivalent 646 } else { 647 LOGVV("LREF add %p (%s.%s) (ent=%d)\n", obj, 648 dvmGetCurrentJNIMethod()->clazz->descriptor, 649 dvmGetCurrentJNIMethod()->name, 650 (int) dvmReferenceTableEntries(pRefTable)); 651 } 652 #else 653 ReferenceTable* pRefTable = getLocalRefTable(env); 654 655 if (!dvmAddToReferenceTable(pRefTable, obj)) { 656 dvmDumpReferenceTable(pRefTable, "JNI local"); 657 LOGE("Failed adding to JNI local ref table (has %d entries)\n", 658 (int) dvmReferenceTableEntries(pRefTable)); 659 dvmDumpThread(dvmThreadSelf(), false); 660 dvmAbort(); // spec says call FatalError; this is equivalent 661 } else { 662 LOGVV("LREF add %p (%s.%s) (ent=%d)\n", obj, 663 dvmGetCurrentJNIMethod()->clazz->descriptor, 664 dvmGetCurrentJNIMethod()->name, 665 (int) dvmReferenceTableEntries(pRefTable)); 666 } 667 668 jobj = (jobject) obj; 669 #endif 670 671 return jobj; 672 } 673 674 /* 675 * Ensure that at least "capacity" references can be held in the local 676 * refs table of the current thread. 677 */ 678 static bool ensureLocalCapacity(JNIEnv* env, int capacity) 679 { 680 #ifdef USE_INDIRECT_REF 681 IndirectRefTable* pRefTable = getLocalRefTable(env); 682 int numEntries = dvmIndirectRefTableEntries(pRefTable); 683 // TODO: this isn't quite right, since "numEntries" includes holes 684 return ((kJniLocalRefMax - numEntries) >= capacity); 685 #else 686 ReferenceTable* pRefTable = getLocalRefTable(env); 687 688 return (kJniLocalRefMax - (pRefTable->nextEntry - pRefTable->table) >= capacity); 689 #endif 690 } 691 692 /* 693 * Explicitly delete a reference from the local list. 694 */ 695 static void deleteLocalReference(JNIEnv* env, jobject jobj) 696 { 697 if (jobj == NULL) 698 return; 699 700 #ifdef USE_INDIRECT_REF 701 IndirectRefTable* pRefTable = getLocalRefTable(env); 702 Thread* self = ((JNIEnvExt*)env)->self; 703 u4 cookie = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefCookie; 704 705 if (!dvmRemoveFromIndirectRefTable(pRefTable, cookie, jobj)) { 706 /* 707 * Attempting to delete a local reference that is not in the 708 * topmost local reference frame is a no-op. DeleteLocalRef returns 709 * void and doesn't throw any exceptions, but we should probably 710 * complain about it so the user will notice that things aren't 711 * going quite the way they expect. 712 */ 713 LOGW("JNI WARNING: DeleteLocalRef(%p) failed to find entry\n", jobj); 714 } 715 #else 716 ReferenceTable* pRefTable = getLocalRefTable(env); 717 Thread* self = ((JNIEnvExt*)env)->self; 718 Object** bottom = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefCookie; 719 720 if (!dvmRemoveFromReferenceTable(pRefTable, bottom, (Object*) jobj)) { 721 /* 722 * Attempting to delete a local reference that is not in the 723 * topmost local reference frame is a no-op. DeleteLocalRef returns 724 * void and doesn't throw any exceptions, but we should probably 725 * complain about it so the user will notice that things aren't 726 * going quite the way they expect. 727 */ 728 LOGW("JNI WARNING: DeleteLocalRef(%p) failed to find entry (valid=%d)\n", 729 jobj, dvmIsValidObject((Object*) jobj)); 730 } 731 #endif 732 } 733 734 /* 735 * Add a global reference for an object. 736 * 737 * We may add the same object more than once. Add/remove calls are paired, 738 * so it needs to appear on the list multiple times. 739 */ 740 static jobject addGlobalReference(Object* obj) 741 { 742 if (obj == NULL) 743 return NULL; 744 745 //LOGI("adding obj=%p\n", obj); 746 //dvmDumpThread(dvmThreadSelf(), false); 747 748 if (false && ((Object*)obj)->clazz == gDvm.classJavaLangClass) { 749 ClassObject* clazz = (ClassObject*) obj; 750 LOGI("-------\n"); 751 LOGI("Adding global ref on class %s\n", clazz->descriptor); 752 dvmDumpThread(dvmThreadSelf(), false); 753 } 754 if (false && ((Object*)obj)->clazz == gDvm.classJavaLangString) { 755 StringObject* strObj = (StringObject*) obj; 756 char* str = dvmCreateCstrFromString(strObj); 757 if (strcmp(str, "sync-response") == 0) { 758 LOGI("-------\n"); 759 LOGI("Adding global ref on string '%s'\n", str); 760 dvmDumpThread(dvmThreadSelf(), false); 761 //dvmAbort(); 762 } 763 free(str); 764 } 765 if (false && ((Object*)obj)->clazz == gDvm.classArrayByte) { 766 ArrayObject* arrayObj = (ArrayObject*) obj; 767 if (arrayObj->length == 8192 /*&& 768 dvmReferenceTableEntries(&gDvm.jniGlobalRefTable) > 400*/) 769 { 770 LOGI("Adding global ref on byte array %p (len=%d)\n", 771 arrayObj, arrayObj->length); 772 dvmDumpThread(dvmThreadSelf(), false); 773 } 774 } 775 776 jobject jobj; 777 778 dvmLockMutex(&gDvm.jniGlobalRefLock); 779 780 /* 781 * Throwing an exception on failure is problematic, because JNI code 782 * may not be expecting an exception, and things sort of cascade. We 783 * want to have a hard limit to catch leaks during debugging, but this 784 * otherwise needs to expand until memory is consumed. As a practical 785 * matter, if we have many thousands of global references, chances are 786 * we're either leaking global ref table entries or we're going to 787 * run out of space in the GC heap. 788 */ 789 #ifdef USE_INDIRECT_REF 790 jobj = dvmAddToIndirectRefTable(&gDvm.jniGlobalRefTable, IRT_FIRST_SEGMENT, 791 obj); 792 if (jobj == NULL) { 793 dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable, "JNI global"); 794 LOGE("Failed adding to JNI global ref table (%d entries)\n", 795 (int) dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable)); 796 dvmAbort(); 797 } 798 799 LOGVV("GREF add %p (%s.%s)\n", obj, 800 dvmGetCurrentJNIMethod()->clazz->descriptor, 801 dvmGetCurrentJNIMethod()->name); 802 803 /* GREF usage tracking; should probably be disabled for production env */ 804 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) { 805 int count = dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable); 806 // TODO: adjust for "holes" 807 if (count > gDvm.jniGlobalRefHiMark) { 808 LOGD("GREF has increased to %d\n", count); 809 gDvm.jniGlobalRefHiMark += kGrefWaterInterval; 810 gDvm.jniGlobalRefLoMark += kGrefWaterInterval; 811 812 /* watch for "excessive" use; not generally appropriate */ 813 if (count >= gDvm.jniGrefLimit) { 814 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList; 815 if (vm->warnError) { 816 dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable, 817 "JNI global"); 818 LOGE("Excessive JNI global references (%d)\n", count); 819 dvmAbort(); 820 } else { 821 LOGW("Excessive JNI global references (%d)\n", count); 822 } 823 } 824 } 825 } 826 #else 827 if (!dvmAddToReferenceTable(&gDvm.jniGlobalRefTable, obj)) { 828 dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global"); 829 LOGE("Failed adding to JNI global ref table (%d entries)\n", 830 (int) dvmReferenceTableEntries(&gDvm.jniGlobalRefTable)); 831 dvmAbort(); 832 } 833 jobj = (jobject) obj; 834 835 LOGVV("GREF add %p (%s.%s)\n", obj, 836 dvmGetCurrentJNIMethod()->clazz->descriptor, 837 dvmGetCurrentJNIMethod()->name); 838 839 /* GREF usage tracking; should probably be disabled for production env */ 840 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) { 841 int count = dvmReferenceTableEntries(&gDvm.jniGlobalRefTable); 842 if (count > gDvm.jniGlobalRefHiMark) { 843 LOGD("GREF has increased to %d\n", count); 844 gDvm.jniGlobalRefHiMark += kGrefWaterInterval; 845 gDvm.jniGlobalRefLoMark += kGrefWaterInterval; 846 847 /* watch for "excessive" use; not generally appropriate */ 848 if (count >= gDvm.jniGrefLimit) { 849 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList; 850 if (vm->warnError) { 851 dvmDumpReferenceTable(&gDvm.jniGlobalRefTable,"JNI global"); 852 LOGE("Excessive JNI global references (%d)\n", count); 853 dvmAbort(); 854 } else { 855 LOGW("Excessive JNI global references (%d)\n", count); 856 } 857 } 858 } 859 } 860 #endif 861 862 dvmUnlockMutex(&gDvm.jniGlobalRefLock); 863 return jobj; 864 } 865 866 /* 867 * Remove a global reference. In most cases it's the entry most recently 868 * added, which makes this pretty quick. 869 * 870 * Thought: if it's not the most recent entry, just null it out. When we 871 * fill up, do a compaction pass before we expand the list. 872 */ 873 static void deleteGlobalReference(jobject jobj) 874 { 875 if (jobj == NULL) 876 return; 877 878 dvmLockMutex(&gDvm.jniGlobalRefLock); 879 880 #ifdef USE_INDIRECT_REF 881 if (!dvmRemoveFromIndirectRefTable(&gDvm.jniGlobalRefTable, 882 IRT_FIRST_SEGMENT, jobj)) 883 { 884 LOGW("JNI: DeleteGlobalRef(%p) failed to find entry\n", jobj); 885 goto bail; 886 } 887 888 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) { 889 int count = dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable); 890 // TODO: not quite right, need to subtract holes 891 if (count < gDvm.jniGlobalRefLoMark) { 892 LOGD("GREF has decreased to %d\n", count); 893 gDvm.jniGlobalRefHiMark -= kGrefWaterInterval; 894 gDvm.jniGlobalRefLoMark -= kGrefWaterInterval; 895 } 896 } 897 #else 898 if (!dvmRemoveFromReferenceTable(&gDvm.jniGlobalRefTable, 899 gDvm.jniGlobalRefTable.table, jobj)) 900 { 901 LOGW("JNI: DeleteGlobalRef(%p) failed to find entry (valid=%d)\n", 902 jobj, dvmIsValidObject((Object*) jobj)); 903 goto bail; 904 } 905 906 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) { 907 int count = dvmReferenceTableEntries(&gDvm.jniGlobalRefTable); 908 if (count < gDvm.jniGlobalRefLoMark) { 909 LOGD("GREF has decreased to %d\n", count); 910 gDvm.jniGlobalRefHiMark -= kGrefWaterInterval; 911 gDvm.jniGlobalRefLoMark -= kGrefWaterInterval; 912 } 913 } 914 #endif 915 916 bail: 917 dvmUnlockMutex(&gDvm.jniGlobalRefLock); 918 } 919 920 /* 921 * We create a PhantomReference that references the object, add a 922 * global reference to it, and then flip some bits before returning it. 923 * The last step ensures that we detect it as special and that only 924 * appropriate calls will accept it. 925 * 926 * On failure, returns NULL with an exception pending. 927 */ 928 static jweak createWeakGlobalRef(JNIEnv* env, jobject jobj) 929 { 930 if (jobj == NULL) 931 return NULL; 932 933 Thread* self = ((JNIEnvExt*)env)->self; 934 Object* obj = dvmDecodeIndirectRef(env, jobj); 935 Object* phantomObj; 936 jobject phantomRef; 937 938 /* 939 * Allocate a PhantomReference, then call the constructor to set 940 * the referent and the reference queue. 941 * 942 * We use a "magic" reference queue that the GC knows about; it behaves 943 * more like a queueless WeakReference, clearing the referent and 944 * not calling enqueue(). 945 */ 946 if (!dvmIsClassInitialized(gDvm.classJavaLangRefPhantomReference)) 947 dvmInitClass(gDvm.classJavaLangRefPhantomReference); 948 phantomObj = dvmAllocObject(gDvm.classJavaLangRefPhantomReference, 949 ALLOC_DEFAULT); 950 if (phantomObj == NULL) { 951 assert(dvmCheckException(self)); 952 LOGW("Failed on WeakGlobalRef alloc\n"); 953 return NULL; 954 } 955 956 JValue unused; 957 dvmCallMethod(self, gDvm.methJavaLangRefPhantomReference_init, phantomObj, 958 &unused, obj, NULL); 959 dvmReleaseTrackedAlloc(phantomObj, self); 960 961 if (dvmCheckException(self)) { 962 LOGW("PhantomReference init failed\n"); 963 return NULL; 964 } 965 966 LOGV("+++ WGR: created phantom ref %p for object %p\n", phantomObj, obj); 967 968 /* 969 * Add it to the global reference table, and mangle the pointer. 970 */ 971 phantomRef = addGlobalReference(phantomObj); 972 return dvmObfuscateWeakGlobalRef(phantomRef); 973 } 974 975 /* 976 * Delete the global reference that's keeping the PhantomReference around. 977 * The PhantomReference will eventually be discarded by the GC. 978 */ 979 static void deleteWeakGlobalRef(JNIEnv* env, jweak wref) 980 { 981 if (wref == NULL) 982 return; 983 984 jobject phantomRef = dvmNormalizeWeakGlobalRef(wref); 985 deleteGlobalReference(phantomRef); 986 } 987 988 /* 989 * Extract the referent from a PhantomReference. Used for weak global 990 * references. 991 * 992 * "jwobj" is a "mangled" WGR pointer. 993 */ 994 static Object* getPhantomReferent(JNIEnv* env, jweak jwobj) 995 { 996 jobject jobj = dvmNormalizeWeakGlobalRef(jwobj); 997 Object* obj = dvmDecodeIndirectRef(env, jobj); 998 999 if (obj->clazz != gDvm.classJavaLangRefPhantomReference) { 1000 LOGE("%p is not a phantom reference (%s)\n", 1001 jwobj, obj->clazz->descriptor); 1002 return NULL; 1003 } 1004 1005 return dvmGetFieldObject(obj, gDvm.offJavaLangRefReference_referent); 1006 } 1007 1008 1009 /* 1010 * Objects don't currently move, so we just need to create a reference 1011 * that will ensure the array object isn't collected. 1012 * 1013 * We use a separate reference table, which is part of the GC root set. 1014 */ 1015 static void pinPrimitiveArray(ArrayObject* arrayObj) 1016 { 1017 if (arrayObj == NULL) 1018 return; 1019 1020 dvmLockMutex(&gDvm.jniPinRefLock); 1021 if (!dvmAddToReferenceTable(&gDvm.jniPinRefTable, (Object*)arrayObj)) { 1022 dvmDumpReferenceTable(&gDvm.jniPinRefTable, "JNI pinned array"); 1023 LOGE("Failed adding to JNI pinned array ref table (%d entries)\n", 1024 (int) dvmReferenceTableEntries(&gDvm.jniPinRefTable)); 1025 dvmDumpThread(dvmThreadSelf(), false); 1026 dvmAbort(); 1027 } 1028 1029 /* 1030 * If we're watching global ref usage, also keep an eye on these. 1031 * 1032 * The total number of pinned primitive arrays should be pretty small. 1033 * A single array should not be pinned more than once or twice; any 1034 * more than that is a strong indicator that a Release function is 1035 * not being called. 1036 */ 1037 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) { 1038 int count = 0; 1039 Object** ppObj = gDvm.jniPinRefTable.table; 1040 while (ppObj < gDvm.jniPinRefTable.nextEntry) { 1041 if (*ppObj++ == (Object*) arrayObj) 1042 count++; 1043 } 1044 1045 if (count > kPinComplainThreshold) { 1046 LOGW("JNI: pin count on array %p (%s) is now %d\n", 1047 arrayObj, arrayObj->obj.clazz->descriptor, count); 1048 /* keep going */ 1049 } 1050 } 1051 1052 dvmUnlockMutex(&gDvm.jniPinRefLock); 1053 } 1054 1055 /* 1056 * Un-pin the array object. If an object was pinned twice, it must be 1057 * unpinned twice before it's free to move. 1058 */ 1059 static void unpinPrimitiveArray(ArrayObject* arrayObj) 1060 { 1061 if (arrayObj == NULL) 1062 return; 1063 1064 dvmLockMutex(&gDvm.jniPinRefLock); 1065 if (!dvmRemoveFromReferenceTable(&gDvm.jniPinRefTable, 1066 gDvm.jniPinRefTable.table, (Object*) arrayObj)) 1067 { 1068 LOGW("JNI: unpinPrimitiveArray(%p) failed to find entry (valid=%d)\n", 1069 arrayObj, dvmIsValidObject((Object*) arrayObj)); 1070 goto bail; 1071 } 1072 1073 bail: 1074 dvmUnlockMutex(&gDvm.jniPinRefLock); 1075 } 1076 1077 /* 1078 * Dump the contents of the JNI reference tables to the log file. 1079 * 1080 * We only dump the local refs associated with the current thread. 1081 */ 1082 void dvmDumpJniReferenceTables(void) 1083 { 1084 Thread* self = dvmThreadSelf(); 1085 JNIEnv* env = self->jniEnv; 1086 ReferenceTable* pLocalRefs = getLocalRefTable(env); 1087 1088 #ifdef USE_INDIRECT_REF 1089 dvmDumpIndirectRefTable(pLocalRefs, "JNI local"); 1090 dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable, "JNI global"); 1091 #else 1092 dvmDumpReferenceTable(pLocalRefs, "JNI local"); 1093 dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global"); 1094 #endif 1095 dvmDumpReferenceTable(&gDvm.jniPinRefTable, "JNI pinned array"); 1096 } 1097 1098 /* 1099 * GC helper function to mark all JNI global references. 1100 * 1101 * We're currently handling the "pin" table here too. 1102 */ 1103 void dvmGcMarkJniGlobalRefs() 1104 { 1105 Object** op; 1106 1107 dvmLockMutex(&gDvm.jniGlobalRefLock); 1108 1109 #ifdef USE_INDIRECT_REF 1110 IndirectRefTable* pRefTable = &gDvm.jniGlobalRefTable; 1111 op = pRefTable->table; 1112 int numEntries = dvmIndirectRefTableEntries(pRefTable); 1113 int i; 1114 1115 for (i = 0; i < numEntries; i++) { 1116 Object* obj = *op; 1117 if (obj != NULL) 1118 dvmMarkObjectNonNull(obj); 1119 op++; 1120 } 1121 #else 1122 op = gDvm.jniGlobalRefTable.table; 1123 while ((uintptr_t)op < (uintptr_t)gDvm.jniGlobalRefTable.nextEntry) { 1124 dvmMarkObjectNonNull(*(op++)); 1125 } 1126 #endif 1127 1128 dvmUnlockMutex(&gDvm.jniGlobalRefLock); 1129 1130 1131 dvmLockMutex(&gDvm.jniPinRefLock); 1132 1133 op = gDvm.jniPinRefTable.table; 1134 while ((uintptr_t)op < (uintptr_t)gDvm.jniPinRefTable.nextEntry) { 1135 dvmMarkObjectNonNull(*(op++)); 1136 } 1137 1138 dvmUnlockMutex(&gDvm.jniPinRefLock); 1139 } 1140 1141 1142 #ifndef USE_INDIRECT_REF 1143 /* 1144 * Determine if "obj" appears in the argument list for the native method. 1145 * 1146 * We use the "shorty" signature to determine which argument slots hold 1147 * reference types. 1148 */ 1149 static bool findInArgList(Thread* self, Object* obj) 1150 { 1151 const Method* meth; 1152 u4* fp; 1153 int i; 1154 1155 fp = self->curFrame; 1156 while (1) { 1157 /* 1158 * Back up over JNI PushLocalFrame frames. This works because the 1159 * previous frame on the interpreted stack is either a break frame 1160 * (if we called here via native code) or an interpreted method (if 1161 * we called here via the interpreter). In both cases the method 1162 * pointer won't match. 1163 */ 1164 StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp); 1165 meth = saveArea->method; 1166 if (meth != SAVEAREA_FROM_FP(saveArea->prevFrame)->method) 1167 break; 1168 fp = saveArea->prevFrame; 1169 } 1170 1171 LOGVV("+++ scanning %d args in %s (%s)\n", 1172 meth->insSize, meth->name, meth->shorty); 1173 const char* shorty = meth->shorty +1; /* skip return type char */ 1174 for (i = 0; i < meth->insSize; i++) { 1175 if (i == 0 && !dvmIsStaticMethod(meth)) { 1176 /* first arg is "this" ref, not represented in "shorty" */ 1177 if (fp[i] == (u4) obj) 1178 return true; 1179 } else { 1180 /* if this is a reference type, see if it matches */ 1181 switch (*shorty) { 1182 case 'L': 1183 if (fp[i] == (u4) obj) 1184 return true; 1185 break; 1186 case 'D': 1187 case 'J': 1188 i++; 1189 break; 1190 case '\0': 1191 LOGE("Whoops! ran off the end of %s (%d)\n", 1192 meth->shorty, meth->insSize); 1193 break; 1194 default: 1195 if (fp[i] == (u4) obj) 1196 LOGI("NOTE: ref %p match on arg type %c\n", obj, *shorty); 1197 break; 1198 } 1199 shorty++; 1200 } 1201 } 1202 1203 /* 1204 * For static methods, we also pass a class pointer in. 1205 */ 1206 if (dvmIsStaticMethod(meth)) { 1207 //LOGI("+++ checking class pointer in %s\n", meth->name); 1208 if ((void*)obj == (void*)meth->clazz) 1209 return true; 1210 } 1211 return false; 1212 } 1213 #endif 1214 1215 /* 1216 * Verify that a reference passed in from native code is one that the 1217 * code is allowed to have. 1218 * 1219 * It's okay for native code to pass us a reference that: 1220 * - was passed in as an argument when invoked by native code (and hence 1221 * is in the JNI local refs table) 1222 * - was returned to it from JNI (and is now in the local refs table) 1223 * - is present in the JNI global refs table 1224 * 1225 * Used by -Xcheck:jni and GetObjectRefType. 1226 * 1227 * NOTE: in the current VM, global and local references are identical. If 1228 * something is both global and local, we can't tell them apart, and always 1229 * return "local". 1230 */ 1231 jobjectRefType dvmGetJNIRefType(JNIEnv* env, jobject jobj) 1232 { 1233 #ifdef USE_INDIRECT_REF 1234 /* 1235 * IndirectRefKind is currently defined as an exact match of 1236 * jobjectRefType, so this is easy. We have to decode it to determine 1237 * if it's a valid reference and not merely valid-looking. 1238 */ 1239 Object* obj = dvmDecodeIndirectRef(env, jobj); 1240 1241 if (obj == NULL) { 1242 /* invalid ref, or jobj was NULL */ 1243 return JNIInvalidRefType; 1244 } else { 1245 return (jobjectRefType) dvmGetIndirectRefType(jobj); 1246 } 1247 #else 1248 ReferenceTable* pRefTable = getLocalRefTable(env); 1249 Thread* self = dvmThreadSelf(); 1250 1251 if (dvmIsWeakGlobalRef(jobj)) { 1252 return JNIWeakGlobalRefType; 1253 } 1254 1255 /* check args */ 1256 if (findInArgList(self, jobj)) { 1257 //LOGI("--- REF found %p on stack\n", jobj); 1258 return JNILocalRefType; 1259 } 1260 1261 /* check locals */ 1262 if (dvmFindInReferenceTable(pRefTable, pRefTable->table, jobj) != NULL) { 1263 //LOGI("--- REF found %p in locals\n", jobj); 1264 return JNILocalRefType; 1265 } 1266 1267 /* check globals */ 1268 dvmLockMutex(&gDvm.jniGlobalRefLock); 1269 if (dvmFindInReferenceTable(&gDvm.jniGlobalRefTable, 1270 gDvm.jniGlobalRefTable.table, jobj)) 1271 { 1272 //LOGI("--- REF found %p in globals\n", jobj); 1273 dvmUnlockMutex(&gDvm.jniGlobalRefLock); 1274 return JNIGlobalRefType; 1275 } 1276 dvmUnlockMutex(&gDvm.jniGlobalRefLock); 1277 1278 /* not found! */ 1279 return JNIInvalidRefType; 1280 #endif 1281 } 1282 1283 /* 1284 * Register a method that uses JNI calling conventions. 1285 */ 1286 static bool dvmRegisterJNIMethod(ClassObject* clazz, const char* methodName, 1287 const char* signature, void* fnPtr) 1288 { 1289 Method* method; 1290 bool result = false; 1291 1292 if (fnPtr == NULL) 1293 goto bail; 1294 1295 method = dvmFindDirectMethodByDescriptor(clazz, methodName, signature); 1296 if (method == NULL) 1297 method = dvmFindVirtualMethodByDescriptor(clazz, methodName, signature); 1298 if (method == NULL) { 1299 LOGW("ERROR: Unable to find decl for native %s.%s:%s\n", 1300 clazz->descriptor, methodName, signature); 1301 goto bail; 1302 } 1303 1304 if (!dvmIsNativeMethod(method)) { 1305 LOGW("Unable to register: not native: %s.%s:%s\n", 1306 clazz->descriptor, methodName, signature); 1307 goto bail; 1308 } 1309 1310 if (method->nativeFunc != dvmResolveNativeMethod) { 1311 /* this is allowed, but unusual */ 1312 LOGV("Note: %s.%s:%s was already registered\n", 1313 clazz->descriptor, methodName, signature); 1314 } 1315 1316 dvmUseJNIBridge(method, fnPtr); 1317 1318 LOGV("JNI-registered %s.%s:%s\n", clazz->descriptor, methodName, 1319 signature); 1320 result = true; 1321 1322 bail: 1323 return result; 1324 } 1325 1326 /* 1327 * Returns "true" if CheckJNI is enabled in the VM. 1328 */ 1329 static bool dvmIsCheckJNIEnabled(void) 1330 { 1331 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList; 1332 return vm->useChecked; 1333 } 1334 1335 /* 1336 * Returns the appropriate JNI bridge for 'method', also taking into account 1337 * the -Xcheck:jni setting. 1338 */ 1339 static DalvikBridgeFunc dvmSelectJNIBridge(const Method* method) 1340 { 1341 enum { 1342 kJNIGeneral = 0, 1343 kJNISync = 1, 1344 kJNIVirtualNoRef = 2, 1345 kJNIStaticNoRef = 3, 1346 } kind; 1347 static const DalvikBridgeFunc stdFunc[] = { 1348 dvmCallJNIMethod_general, 1349 dvmCallJNIMethod_synchronized, 1350 dvmCallJNIMethod_virtualNoRef, 1351 dvmCallJNIMethod_staticNoRef 1352 }; 1353 static const DalvikBridgeFunc checkFunc[] = { 1354 dvmCheckCallJNIMethod_general, 1355 dvmCheckCallJNIMethod_synchronized, 1356 dvmCheckCallJNIMethod_virtualNoRef, 1357 dvmCheckCallJNIMethod_staticNoRef 1358 }; 1359 1360 bool hasRefArg = false; 1361 1362 if (dvmIsSynchronizedMethod(method)) { 1363 /* use version with synchronization; calls into general handler */ 1364 kind = kJNISync; 1365 } else { 1366 /* 1367 * Do a quick scan through the "shorty" signature to see if the method 1368 * takes any reference arguments. 1369 */ 1370 const char* cp = method->shorty; 1371 while (*++cp != '\0') { /* pre-incr to skip return type */ 1372 if (*cp == 'L') { 1373 /* 'L' used for both object and array references */ 1374 hasRefArg = true; 1375 break; 1376 } 1377 } 1378 1379 if (hasRefArg) { 1380 /* use general handler to slurp up reference args */ 1381 kind = kJNIGeneral; 1382 } else { 1383 /* virtual methods have a ref in args[0] (not in signature) */ 1384 if (dvmIsStaticMethod(method)) 1385 kind = kJNIStaticNoRef; 1386 else 1387 kind = kJNIVirtualNoRef; 1388 } 1389 } 1390 1391 return dvmIsCheckJNIEnabled() ? checkFunc[kind] : stdFunc[kind]; 1392 } 1393 1394 /* 1395 * Trace a call into native code. 1396 */ 1397 static void dvmTraceCallJNIMethod(const u4* args, JValue* pResult, 1398 const Method* method, Thread* self) 1399 { 1400 dvmLogNativeMethodEntry(method, args); 1401 DalvikBridgeFunc bridge = dvmSelectJNIBridge(method); 1402 (*bridge)(args, pResult, method, self); 1403 dvmLogNativeMethodExit(method, self, *pResult); 1404 } 1405 1406 /** 1407 * Returns true if the -Xjnitrace setting implies we should trace 'method'. 1408 */ 1409 static bool shouldTrace(Method* method) 1410 { 1411 return gDvm.jniTrace && strstr(method->clazz->descriptor, gDvm.jniTrace); 1412 } 1413 1414 /* 1415 * Point "method->nativeFunc" at the JNI bridge, and overload "method->insns" 1416 * to point at the actual function. 1417 */ 1418 void dvmUseJNIBridge(Method* method, void* func) 1419 { 1420 DalvikBridgeFunc bridge = shouldTrace(method) 1421 ? dvmTraceCallJNIMethod 1422 : dvmSelectJNIBridge(method); 1423 dvmSetNativeFunc(method, bridge, func); 1424 } 1425 1426 /* 1427 * Get the method currently being executed by examining the interp stack. 1428 */ 1429 const Method* dvmGetCurrentJNIMethod(void) 1430 { 1431 assert(dvmThreadSelf() != NULL); 1432 1433 void* fp = dvmThreadSelf()->curFrame; 1434 const Method* meth = SAVEAREA_FROM_FP(fp)->method; 1435 1436 assert(meth != NULL); 1437 assert(dvmIsNativeMethod(meth)); 1438 return meth; 1439 } 1440 1441 1442 /* 1443 * Track a JNI MonitorEnter in the current thread. 1444 * 1445 * The goal is to be able to "implicitly" release all JNI-held monitors 1446 * when the thread detaches. 1447 * 1448 * Monitors may be entered multiple times, so we add a new entry for each 1449 * enter call. It would be more efficient to keep a counter. At present 1450 * there's no real motivation to improve this however. 1451 */ 1452 static void trackMonitorEnter(Thread* self, Object* obj) 1453 { 1454 static const int kInitialSize = 16; 1455 ReferenceTable* refTable = &self->jniMonitorRefTable; 1456 1457 /* init table on first use */ 1458 if (refTable->table == NULL) { 1459 assert(refTable->maxEntries == 0); 1460 1461 if (!dvmInitReferenceTable(refTable, kInitialSize, INT_MAX)) { 1462 LOGE("Unable to initialize monitor tracking table\n"); 1463 dvmAbort(); 1464 } 1465 } 1466 1467 if (!dvmAddToReferenceTable(refTable, obj)) { 1468 /* ran out of memory? could throw exception instead */ 1469 LOGE("Unable to add entry to monitor tracking table\n"); 1470 dvmAbort(); 1471 } else { 1472 LOGVV("--- added monitor %p\n", obj); 1473 } 1474 } 1475 1476 1477 /* 1478 * Track a JNI MonitorExit in the current thread. 1479 */ 1480 static void trackMonitorExit(Thread* self, Object* obj) 1481 { 1482 ReferenceTable* pRefTable = &self->jniMonitorRefTable; 1483 1484 if (!dvmRemoveFromReferenceTable(pRefTable, pRefTable->table, obj)) { 1485 LOGE("JNI monitor %p not found in tracking list\n", obj); 1486 /* keep going? */ 1487 } else { 1488 LOGVV("--- removed monitor %p\n", obj); 1489 } 1490 } 1491 1492 /* 1493 * Release all monitors held by the jniMonitorRefTable list. 1494 */ 1495 void dvmReleaseJniMonitors(Thread* self) 1496 { 1497 ReferenceTable* pRefTable = &self->jniMonitorRefTable; 1498 Object** top = pRefTable->table; 1499 1500 if (top == NULL) 1501 return; 1502 1503 Object** ptr = pRefTable->nextEntry; 1504 while (--ptr >= top) { 1505 if (!dvmUnlockObject(self, *ptr)) { 1506 LOGW("Unable to unlock monitor %p at thread detach\n", *ptr); 1507 } else { 1508 LOGVV("--- detach-releasing monitor %p\n", *ptr); 1509 } 1510 } 1511 1512 /* zap it */ 1513 pRefTable->nextEntry = pRefTable->table; 1514 } 1515 1516 /* 1517 * Determine if the specified class can be instantiated from JNI. This 1518 * is used by AllocObject / NewObject, which are documented as throwing 1519 * an exception for abstract and interface classes, and not accepting 1520 * array classes. We also want to reject attempts to create new Class 1521 * objects, since only DefineClass should do that. 1522 */ 1523 static bool canAllocClass(ClassObject* clazz) 1524 { 1525 if (dvmIsAbstractClass(clazz) || dvmIsInterfaceClass(clazz)) { 1526 /* JNI spec defines what this throws */ 1527 dvmThrowExceptionFmt("Ljava/lang/InstantiationException;", 1528 "Can't instantiate %s (abstract or interface)", clazz->descriptor); 1529 return false; 1530 } else if (dvmIsArrayClass(clazz) || clazz == gDvm.classJavaLangClass) { 1531 /* spec says "must not" for arrays, ignores Class */ 1532 dvmThrowExceptionFmt("Ljava/lang/IllegalArgumentException;", 1533 "Can't instantiate %s (array or Class) with this JNI function", 1534 clazz->descriptor); 1535 return false; 1536 } 1537 1538 return true; 1539 } 1540 1541 #ifdef WITH_JNI_STACK_CHECK 1542 /* 1543 * Compute a CRC on the entire interpreted stack. 1544 * 1545 * Would be nice to compute it on "self" as well, but there are parts of 1546 * the Thread that can be altered by other threads (e.g. prev/next pointers). 1547 */ 1548 static void computeStackSum(Thread* self) 1549 { 1550 const u1* low = (const u1*)SAVEAREA_FROM_FP(self->curFrame); 1551 u4 crc = dvmInitCrc32(); 1552 self->stackCrc = 0; 1553 crc = dvmComputeCrc32(crc, low, self->interpStackStart - low); 1554 self->stackCrc = crc; 1555 } 1556 1557 /* 1558 * Compute a CRC on the entire interpreted stack, and compare it to what 1559 * we previously computed. 1560 * 1561 * We can execute JNI directly from native code without calling in from 1562 * interpreted code during VM initialization and immediately after JNI 1563 * thread attachment. Another opportunity exists during JNI_OnLoad. Rather 1564 * than catching these cases we just ignore them here, which is marginally 1565 * less accurate but reduces the amount of code we have to touch with #ifdefs. 1566 */ 1567 static void checkStackSum(Thread* self) 1568 { 1569 const u1* low = (const u1*)SAVEAREA_FROM_FP(self->curFrame); 1570 u4 stackCrc, crc; 1571 1572 stackCrc = self->stackCrc; 1573 self->stackCrc = 0; 1574 crc = dvmInitCrc32(); 1575 crc = dvmComputeCrc32(crc, low, self->interpStackStart - low); 1576 if (crc != stackCrc) { 1577 const Method* meth = dvmGetCurrentJNIMethod(); 1578 if (dvmComputeExactFrameDepth(self->curFrame) == 1) { 1579 LOGD("JNI: bad stack CRC (0x%08x) -- okay during init\n", 1580 stackCrc); 1581 } else if (strcmp(meth->name, "nativeLoad") == 0 && 1582 (strcmp(meth->clazz->descriptor, "Ljava/lang/Runtime;") == 0)) 1583 { 1584 LOGD("JNI: bad stack CRC (0x%08x) -- okay during JNI_OnLoad\n", 1585 stackCrc); 1586 } else { 1587 LOGW("JNI: bad stack CRC (%08x vs %08x)\n", crc, stackCrc); 1588 dvmAbort(); 1589 } 1590 } 1591 self->stackCrc = (u4) -1; /* make logic errors more noticeable */ 1592 } 1593 #endif 1594 1595 1596 /* 1597 * =========================================================================== 1598 * JNI call bridge 1599 * =========================================================================== 1600 */ 1601 1602 /* 1603 * The functions here form a bridge between interpreted code and JNI native 1604 * functions. The basic task is to convert an array of primitives and 1605 * references into C-style function arguments. This is architecture-specific 1606 * and usually requires help from assembly code. 1607 * 1608 * The bridge takes four arguments: the array of parameters, a place to 1609 * store the function result (if any), the method to call, and a pointer 1610 * to the current thread. 1611 * 1612 * These functions aren't called directly from elsewhere in the VM. 1613 * A pointer in the Method struct points to one of these, and when a native 1614 * method is invoked the interpreter jumps to it. 1615 * 1616 * (The "internal native" methods are invoked the same way, but instead 1617 * of calling through a bridge, the target method is called directly.) 1618 * 1619 * The "args" array should not be modified, but we do so anyway for 1620 * performance reasons. We know that it points to the "outs" area on 1621 * the current method's interpreted stack. This area is ignored by the 1622 * precise GC, because there is no register map for a native method (for 1623 * an interpreted method the args would be listed in the argument set). 1624 * We know all of the values exist elsewhere on the interpreted stack, 1625 * because the method call setup copies them right before making the call, 1626 * so we don't have to worry about concealing stuff from the GC. 1627 * 1628 * If we don't want to modify "args", we either have to create a local 1629 * copy and modify it before calling dvmPlatformInvoke, or we have to do 1630 * the local reference replacement within dvmPlatformInvoke. The latter 1631 * has some performance advantages, though if we can inline the local 1632 * reference adds we may win when there's a lot of reference args (unless 1633 * we want to code up some local ref table manipulation in assembly. 1634 */ 1635 1636 /* 1637 * If necessary, convert the value in pResult from a local/global reference 1638 * to an object pointer. 1639 */ 1640 static inline void convertReferenceResult(JNIEnv* env, JValue* pResult, 1641 const Method* method, Thread* self) 1642 { 1643 #ifdef USE_INDIRECT_REF 1644 if (method->shorty[0] == 'L' && !dvmCheckException(self) && 1645 pResult->l != NULL) 1646 { 1647 pResult->l = dvmDecodeIndirectRef(env, pResult->l); 1648 } 1649 #endif 1650 } 1651 1652 /* 1653 * General form, handles all cases. 1654 */ 1655 void dvmCallJNIMethod_general(const u4* args, JValue* pResult, 1656 const Method* method, Thread* self) 1657 { 1658 int oldStatus; 1659 u4* modArgs = (u4*) args; 1660 jclass staticMethodClass; 1661 JNIEnv* env = self->jniEnv; 1662 1663 //LOGI("JNI calling %p (%s.%s:%s):\n", method->insns, 1664 // method->clazz->descriptor, method->name, method->shorty); 1665 1666 #ifdef USE_INDIRECT_REF 1667 /* 1668 * Walk the argument list, creating local references for appropriate 1669 * arguments. 1670 */ 1671 int idx = 0; 1672 if (dvmIsStaticMethod(method)) { 1673 /* add the class object we pass in */ 1674 staticMethodClass = addLocalReference(env, (Object*) method->clazz); 1675 if (staticMethodClass == NULL) { 1676 assert(dvmCheckException(self)); 1677 return; 1678 } 1679 } else { 1680 /* add "this" */ 1681 staticMethodClass = NULL; 1682 jobject thisObj = addLocalReference(env, (Object*) modArgs[0]); 1683 if (thisObj == NULL) { 1684 assert(dvmCheckException(self)); 1685 return; 1686 } 1687 modArgs[idx] = (u4) thisObj; 1688 idx = 1; 1689 } 1690 1691 const char* shorty = &method->shorty[1]; /* skip return type */ 1692 while (*shorty != '\0') { 1693 switch (*shorty++) { 1694 case 'L': 1695 //LOGI(" local %d: 0x%08x\n", idx, modArgs[idx]); 1696 if (modArgs[idx] != 0) { 1697 //if (!dvmIsValidObject((Object*) modArgs[idx])) 1698 // dvmAbort(); 1699 jobject argObj = addLocalReference(env, (Object*) modArgs[idx]); 1700 if (argObj == NULL) { 1701 assert(dvmCheckException(self)); 1702 return; 1703 } 1704 modArgs[idx] = (u4) argObj; 1705 } 1706 break; 1707 case 'D': 1708 case 'J': 1709 idx++; 1710 break; 1711 default: 1712 /* Z B C S I -- do nothing */ 1713 break; 1714 } 1715 1716 idx++; 1717 } 1718 #else 1719 staticMethodClass = dvmIsStaticMethod(method) ? 1720 (jclass) method->clazz : NULL; 1721 #endif 1722 1723 oldStatus = dvmChangeStatus(self, THREAD_NATIVE); 1724 1725 ANDROID_MEMBAR_FULL(); /* guarantee ordering on method->insns */ 1726 assert(method->insns != NULL); 1727 1728 COMPUTE_STACK_SUM(self); 1729 dvmPlatformInvoke(env, staticMethodClass, 1730 method->jniArgInfo, method->insSize, modArgs, method->shorty, 1731 (void*)method->insns, pResult); 1732 CHECK_STACK_SUM(self); 1733 1734 dvmChangeStatus(self, oldStatus); 1735 1736 convertReferenceResult(env, pResult, method, self); 1737 } 1738 1739 /* 1740 * Handler for the unusual case of a synchronized native method. 1741 * 1742 * Lock the object, then call through the general function. 1743 */ 1744 void dvmCallJNIMethod_synchronized(const u4* args, JValue* pResult, 1745 const Method* method, Thread* self) 1746 { 1747 Object* lockObj; 1748 1749 assert(dvmIsSynchronizedMethod(method)); 1750 1751 if (dvmIsStaticMethod(method)) 1752 lockObj = (Object*) method->clazz; 1753 else 1754 lockObj = (Object*) args[0]; 1755 1756 LOGVV("Calling %s.%s: locking %p (%s)\n", 1757 method->clazz->descriptor, method->name, 1758 lockObj, lockObj->clazz->descriptor); 1759 1760 dvmLockObject(self, lockObj); 1761 dvmCallJNIMethod_general(args, pResult, method, self); 1762 dvmUnlockObject(self, lockObj); 1763 } 1764 1765 /* 1766 * Virtual method call, no reference arguments. 1767 * 1768 * We need to local-ref the "this" argument, found in args[0]. 1769 */ 1770 void dvmCallJNIMethod_virtualNoRef(const u4* args, JValue* pResult, 1771 const Method* method, Thread* self) 1772 { 1773 u4* modArgs = (u4*) args; 1774 int oldStatus; 1775 1776 #ifdef USE_INDIRECT_REF 1777 jobject thisObj = addLocalReference(self->jniEnv, (Object*) args[0]); 1778 if (thisObj == NULL) { 1779 assert(dvmCheckException(self)); 1780 return; 1781 } 1782 modArgs[0] = (u4) thisObj; 1783 #endif 1784 1785 oldStatus = dvmChangeStatus(self, THREAD_NATIVE); 1786 1787 ANDROID_MEMBAR_FULL(); /* guarantee ordering on method->insns */ 1788 1789 COMPUTE_STACK_SUM(self); 1790 dvmPlatformInvoke(self->jniEnv, NULL, 1791 method->jniArgInfo, method->insSize, modArgs, method->shorty, 1792 (void*)method->insns, pResult); 1793 CHECK_STACK_SUM(self); 1794 1795 dvmChangeStatus(self, oldStatus); 1796 1797 convertReferenceResult(self->jniEnv, pResult, method, self); 1798 } 1799 1800 /* 1801 * Static method call, no reference arguments. 1802 * 1803 * We need to local-ref the class reference. 1804 */ 1805 void dvmCallJNIMethod_staticNoRef(const u4* args, JValue* pResult, 1806 const Method* method, Thread* self) 1807 { 1808 jclass staticMethodClass; 1809 int oldStatus; 1810 1811 #ifdef USE_INDIRECT_REF 1812 staticMethodClass = addLocalReference(self->jniEnv, (Object*)method->clazz); 1813 if (staticMethodClass == NULL) { 1814 assert(dvmCheckException(self)); 1815 return; 1816 } 1817 #else 1818 staticMethodClass = (jobject) method->clazz; 1819 #endif 1820 1821 oldStatus = dvmChangeStatus(self, THREAD_NATIVE); 1822 1823 ANDROID_MEMBAR_FULL(); /* guarantee ordering on method->insns */ 1824 1825 COMPUTE_STACK_SUM(self); 1826 dvmPlatformInvoke(self->jniEnv, staticMethodClass, 1827 method->jniArgInfo, method->insSize, args, method->shorty, 1828 (void*)method->insns, pResult); 1829 CHECK_STACK_SUM(self); 1830 1831 dvmChangeStatus(self, oldStatus); 1832 1833 convertReferenceResult(self->jniEnv, pResult, method, self); 1834 } 1835 1836 /* 1837 * Extract the return type enum from the "jniArgInfo" field. 1838 */ 1839 DalvikJniReturnType dvmGetArgInfoReturnType(int jniArgInfo) 1840 { 1841 return (jniArgInfo & DALVIK_JNI_RETURN_MASK) >> DALVIK_JNI_RETURN_SHIFT; 1842 } 1843 1844 1845 /* 1846 * =========================================================================== 1847 * JNI implementation 1848 * =========================================================================== 1849 */ 1850 1851 /* 1852 * Return the version of the native method interface. 1853 */ 1854 static jint GetVersion(JNIEnv* env) 1855 { 1856 JNI_ENTER(); 1857 /* 1858 * There is absolutely no need to toggle the mode for correct behavior. 1859 * However, it does provide native code with a simple "suspend self 1860 * if necessary" call. 1861 */ 1862 JNI_EXIT(); 1863 return JNI_VERSION_1_6; 1864 } 1865 1866 /* 1867 * Create a new class from a bag of bytes. 1868 * 1869 * This is not currently supported within Dalvik. 1870 */ 1871 static jclass DefineClass(JNIEnv* env, const char *name, jobject loader, 1872 const jbyte* buf, jsize bufLen) 1873 { 1874 UNUSED_PARAMETER(name); 1875 UNUSED_PARAMETER(loader); 1876 UNUSED_PARAMETER(buf); 1877 UNUSED_PARAMETER(bufLen); 1878 1879 JNI_ENTER(); 1880 LOGW("JNI DefineClass is not supported\n"); 1881 JNI_EXIT(); 1882 return NULL; 1883 } 1884 1885 /* 1886 * Find a class by name. 1887 * 1888 * We have to use the "no init" version of FindClass here, because we might 1889 * be getting the class prior to registering native methods that will be 1890 * used in <clinit>. 1891 * 1892 * We need to get the class loader associated with the current native 1893 * method. If there is no native method, e.g. we're calling this from native 1894 * code right after creating the VM, the spec says we need to use the class 1895 * loader returned by "ClassLoader.getBaseClassLoader". There is no such 1896 * method, but it's likely they meant ClassLoader.getSystemClassLoader. 1897 * We can't get that until after the VM has initialized though. 1898 */ 1899 static jclass FindClass(JNIEnv* env, const char* name) 1900 { 1901 JNI_ENTER(); 1902 1903 const Method* thisMethod; 1904 ClassObject* clazz; 1905 jclass jclazz = NULL; 1906 Object* loader; 1907 char* descriptor = NULL; 1908 1909 thisMethod = dvmGetCurrentJNIMethod(); 1910 assert(thisMethod != NULL); 1911 1912 descriptor = dvmNameToDescriptor(name); 1913 if (descriptor == NULL) { 1914 clazz = NULL; 1915 goto bail; 1916 } 1917 1918 //Thread* self = dvmThreadSelf(); 1919 if (_self->classLoaderOverride != NULL) { 1920 /* hack for JNI_OnLoad */ 1921 assert(strcmp(thisMethod->name, "nativeLoad") == 0); 1922 loader = _self->classLoaderOverride; 1923 } else if (thisMethod == gDvm.methFakeNativeEntry) { 1924 /* start point of invocation interface */ 1925 if (!gDvm.initializing) 1926 loader = dvmGetSystemClassLoader(); 1927 else 1928 loader = NULL; 1929 } else { 1930 loader = thisMethod->clazz->classLoader; 1931 } 1932 1933 clazz = dvmFindClassNoInit(descriptor, loader); 1934 jclazz = addLocalReference(env, (Object*) clazz); 1935 1936 bail: 1937 free(descriptor); 1938 1939 JNI_EXIT(); 1940 return jclazz; 1941 } 1942 1943 /* 1944 * Return the superclass of a class. 1945 */ 1946 static jclass GetSuperclass(JNIEnv* env, jclass jclazz) 1947 { 1948 JNI_ENTER(); 1949 jclass jsuper = NULL; 1950 1951 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 1952 if (clazz != NULL) 1953 jsuper = addLocalReference(env, (Object*)clazz->super); 1954 JNI_EXIT(); 1955 return jsuper; 1956 } 1957 1958 /* 1959 * Determine whether an object of clazz1 can be safely cast to clazz2. 1960 * 1961 * Like IsInstanceOf, but with a pair of class objects instead of obj+class. 1962 */ 1963 static jboolean IsAssignableFrom(JNIEnv* env, jclass jclazz1, jclass jclazz2) 1964 { 1965 JNI_ENTER(); 1966 1967 ClassObject* clazz1 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz1); 1968 ClassObject* clazz2 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz2); 1969 1970 jboolean result = dvmInstanceof(clazz1, clazz2); 1971 1972 JNI_EXIT(); 1973 return result; 1974 } 1975 1976 /* 1977 * Given a java.lang.reflect.Method or .Constructor, return a methodID. 1978 */ 1979 static jmethodID FromReflectedMethod(JNIEnv* env, jobject jmethod) 1980 { 1981 JNI_ENTER(); 1982 jmethodID methodID; 1983 Object* method = dvmDecodeIndirectRef(env, jmethod); 1984 methodID = (jmethodID) dvmGetMethodFromReflectObj(method); 1985 JNI_EXIT(); 1986 return methodID; 1987 } 1988 1989 /* 1990 * Given a java.lang.reflect.Field, return a fieldID. 1991 */ 1992 static jfieldID FromReflectedField(JNIEnv* env, jobject jfield) 1993 { 1994 JNI_ENTER(); 1995 jfieldID fieldID; 1996 Object* field = dvmDecodeIndirectRef(env, jfield); 1997 fieldID = (jfieldID) dvmGetFieldFromReflectObj(field); 1998 JNI_EXIT(); 1999 return fieldID; 2000 } 2001 2002 /* 2003 * Convert a methodID to a java.lang.reflect.Method or .Constructor. 2004 * 2005 * (The "isStatic" field does not appear in the spec.) 2006 * 2007 * Throws OutOfMemory and returns NULL on failure. 2008 */ 2009 static jobject ToReflectedMethod(JNIEnv* env, jclass jcls, jmethodID methodID, 2010 jboolean isStatic) 2011 { 2012 JNI_ENTER(); 2013 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls); 2014 Object* obj = dvmCreateReflectObjForMethod(clazz, (Method*) methodID); 2015 dvmReleaseTrackedAlloc(obj, NULL); 2016 jobject jobj = addLocalReference(env, obj); 2017 JNI_EXIT(); 2018 return jobj; 2019 } 2020 2021 /* 2022 * Convert a fieldID to a java.lang.reflect.Field. 2023 * 2024 * (The "isStatic" field does not appear in the spec.) 2025 * 2026 * Throws OutOfMemory and returns NULL on failure. 2027 */ 2028 static jobject ToReflectedField(JNIEnv* env, jclass jcls, jfieldID fieldID, 2029 jboolean isStatic) 2030 { 2031 JNI_ENTER(); 2032 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls); 2033 Object* obj = dvmCreateReflectObjForField(clazz, (Field*) fieldID); 2034 dvmReleaseTrackedAlloc(obj, NULL); 2035 jobject jobj = addLocalReference(env, obj); 2036 JNI_EXIT(); 2037 return jobj; 2038 } 2039 2040 /* 2041 * Take this exception and throw it. 2042 */ 2043 static jint Throw(JNIEnv* env, jthrowable jobj) 2044 { 2045 JNI_ENTER(); 2046 2047 jint retval; 2048 2049 if (jobj != NULL) { 2050 Object* obj = dvmDecodeIndirectRef(env, jobj); 2051 dvmSetException(_self, obj); 2052 retval = JNI_OK; 2053 } else { 2054 retval = JNI_ERR; 2055 } 2056 2057 JNI_EXIT(); 2058 return retval; 2059 } 2060 2061 /* 2062 * Constructs an exception object from the specified class with the message 2063 * specified by "message", and throws it. 2064 */ 2065 static jint ThrowNew(JNIEnv* env, jclass jclazz, const char* message) 2066 { 2067 JNI_ENTER(); 2068 2069 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 2070 dvmThrowExceptionByClass(clazz, message); 2071 // TODO: should return failure if this didn't work (e.g. OOM) 2072 2073 JNI_EXIT(); 2074 return JNI_OK; 2075 } 2076 2077 /* 2078 * If an exception is being thrown, return the exception object. Otherwise, 2079 * return NULL. 2080 * 2081 * TODO: if there is no pending exception, we should be able to skip the 2082 * enter/exit checks. If we find one, we need to enter and then re-fetch 2083 * the exception (in case it got moved by a compacting GC). 2084 */ 2085 static jthrowable ExceptionOccurred(JNIEnv* env) 2086 { 2087 JNI_ENTER(); 2088 2089 Object* exception; 2090 jobject localException; 2091 2092 exception = dvmGetException(_self); 2093 localException = addLocalReference(env, exception); 2094 if (localException == NULL && exception != NULL) { 2095 /* 2096 * We were unable to add a new local reference, and threw a new 2097 * exception. We can't return "exception", because it's not a 2098 * local reference. So we have to return NULL, indicating that 2099 * there was no exception, even though it's pretty much raining 2100 * exceptions in here. 2101 */ 2102 LOGW("JNI WARNING: addLocal/exception combo\n"); 2103 } 2104 2105 JNI_EXIT(); 2106 return localException; 2107 } 2108 2109 /* 2110 * Print an exception and stack trace to stderr. 2111 */ 2112 static void ExceptionDescribe(JNIEnv* env) 2113 { 2114 JNI_ENTER(); 2115 2116 Object* exception = dvmGetException(_self); 2117 if (exception != NULL) { 2118 dvmPrintExceptionStackTrace(); 2119 } else { 2120 LOGI("Odd: ExceptionDescribe called, but no exception pending\n"); 2121 } 2122 2123 JNI_EXIT(); 2124 } 2125 2126 /* 2127 * Clear the exception currently being thrown. 2128 * 2129 * TODO: we should be able to skip the enter/exit stuff. 2130 */ 2131 static void ExceptionClear(JNIEnv* env) 2132 { 2133 JNI_ENTER(); 2134 dvmClearException(_self); 2135 JNI_EXIT(); 2136 } 2137 2138 /* 2139 * Kill the VM. This function does not return. 2140 */ 2141 static void FatalError(JNIEnv* env, const char* msg) 2142 { 2143 //dvmChangeStatus(NULL, THREAD_RUNNING); 2144 LOGE("JNI posting fatal error: %s\n", msg); 2145 dvmAbort(); 2146 } 2147 2148 /* 2149 * Push a new JNI frame on the stack, with a new set of locals. 2150 * 2151 * The new frame must have the same method pointer. (If for no other 2152 * reason than FindClass needs it to get the appropriate class loader.) 2153 */ 2154 static jint PushLocalFrame(JNIEnv* env, jint capacity) 2155 { 2156 JNI_ENTER(); 2157 int result = JNI_OK; 2158 if (!ensureLocalCapacity(env, capacity) || 2159 !dvmPushLocalFrame(_self /*dvmThreadSelf()*/, dvmGetCurrentJNIMethod())) 2160 { 2161 /* yes, OutOfMemoryError, not StackOverflowError */ 2162 dvmClearException(_self); 2163 dvmThrowException("Ljava/lang/OutOfMemoryError;", 2164 "out of stack in JNI PushLocalFrame"); 2165 result = JNI_ERR; 2166 } 2167 JNI_EXIT(); 2168 return result; 2169 } 2170 2171 /* 2172 * Pop the local frame off. If "result" is not null, add it as a 2173 * local reference on the now-current frame. 2174 */ 2175 static jobject PopLocalFrame(JNIEnv* env, jobject jresult) 2176 { 2177 JNI_ENTER(); 2178 Object* result = dvmDecodeIndirectRef(env, jresult); 2179 if (!dvmPopLocalFrame(_self /*dvmThreadSelf()*/)) { 2180 LOGW("JNI WARNING: too many PopLocalFrame calls\n"); 2181 dvmClearException(_self); 2182 dvmThrowException("Ljava/lang/RuntimeException;", 2183 "too many PopLocalFrame calls"); 2184 } 2185 jresult = addLocalReference(env, result); 2186 JNI_EXIT(); 2187 return result; 2188 } 2189 2190 /* 2191 * Add a reference to the global list. 2192 */ 2193 static jobject NewGlobalRef(JNIEnv* env, jobject jobj) 2194 { 2195 Object* obj; 2196 2197 JNI_ENTER(); 2198 if (dvmIsWeakGlobalRef(jobj)) 2199 obj = getPhantomReferent(env, (jweak) jobj); 2200 else 2201 obj = dvmDecodeIndirectRef(env, jobj); 2202 jobject retval = addGlobalReference(obj); 2203 JNI_EXIT(); 2204 return retval; 2205 } 2206 2207 /* 2208 * Delete a reference from the global list. 2209 */ 2210 static void DeleteGlobalRef(JNIEnv* env, jobject jglobalRef) 2211 { 2212 JNI_ENTER(); 2213 deleteGlobalReference(jglobalRef); 2214 JNI_EXIT(); 2215 } 2216 2217 2218 /* 2219 * Add a reference to the local list. 2220 */ 2221 static jobject NewLocalRef(JNIEnv* env, jobject jobj) 2222 { 2223 Object* obj; 2224 2225 JNI_ENTER(); 2226 if (dvmIsWeakGlobalRef(jobj)) 2227 obj = getPhantomReferent(env, (jweak) jobj); 2228 else 2229 obj = dvmDecodeIndirectRef(env, jobj); 2230 jobject retval = addLocalReference(env, obj); 2231 JNI_EXIT(); 2232 return retval; 2233 } 2234 2235 /* 2236 * Delete a reference from the local list. 2237 */ 2238 static void DeleteLocalRef(JNIEnv* env, jobject jlocalRef) 2239 { 2240 JNI_ENTER(); 2241 deleteLocalReference(env, jlocalRef); 2242 JNI_EXIT(); 2243 } 2244 2245 /* 2246 * Ensure that the local references table can hold at least this many 2247 * references. 2248 */ 2249 static jint EnsureLocalCapacity(JNIEnv* env, jint capacity) 2250 { 2251 JNI_ENTER(); 2252 bool okay = ensureLocalCapacity(env, capacity); 2253 if (!okay) { 2254 dvmThrowException("Ljava/lang/OutOfMemoryError;", 2255 "can't ensure local reference capacity"); 2256 } 2257 JNI_EXIT(); 2258 if (okay) 2259 return 0; 2260 else 2261 return -1; 2262 } 2263 2264 2265 /* 2266 * Determine whether two Object references refer to the same underlying object. 2267 */ 2268 static jboolean IsSameObject(JNIEnv* env, jobject jref1, jobject jref2) 2269 { 2270 JNI_ENTER(); 2271 Object* obj1 = dvmDecodeIndirectRef(env, jref1); 2272 Object* obj2 = dvmDecodeIndirectRef(env, jref2); 2273 jboolean result = (obj1 == obj2); 2274 JNI_EXIT(); 2275 return result; 2276 } 2277 2278 /* 2279 * Allocate a new object without invoking any constructors. 2280 */ 2281 static jobject AllocObject(JNIEnv* env, jclass jclazz) 2282 { 2283 JNI_ENTER(); 2284 2285 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 2286 jobject result; 2287 2288 if (!canAllocClass(clazz) || 2289 (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))) 2290 { 2291 assert(dvmCheckException(_self)); 2292 result = NULL; 2293 } else { 2294 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK); 2295 result = addLocalReference(env, newObj); 2296 } 2297 2298 JNI_EXIT(); 2299 return result; 2300 } 2301 2302 /* 2303 * Allocate a new object and invoke the supplied constructor. 2304 */ 2305 static jobject NewObject(JNIEnv* env, jclass jclazz, jmethodID methodID, ...) 2306 { 2307 JNI_ENTER(); 2308 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 2309 jobject result; 2310 2311 if (!canAllocClass(clazz) || 2312 (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))) 2313 { 2314 assert(dvmCheckException(_self)); 2315 result = NULL; 2316 } else { 2317 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK); 2318 result = addLocalReference(env, newObj); 2319 if (newObj != NULL) { 2320 JValue unused; 2321 va_list args; 2322 va_start(args, methodID); 2323 dvmCallMethodV(_self, (Method*) methodID, newObj, true, &unused, 2324 args); 2325 va_end(args); 2326 } 2327 } 2328 2329 JNI_EXIT(); 2330 return result; 2331 } 2332 static jobject NewObjectV(JNIEnv* env, jclass jclazz, jmethodID methodID, 2333 va_list args) 2334 { 2335 JNI_ENTER(); 2336 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 2337 jobject result; 2338 2339 if (!canAllocClass(clazz) || 2340 (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))) 2341 { 2342 assert(dvmCheckException(_self)); 2343 result = NULL; 2344 } else { 2345 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK); 2346 result = addLocalReference(env, newObj); 2347 if (newObj != NULL) { 2348 JValue unused; 2349 dvmCallMethodV(_self, (Method*) methodID, newObj, true, &unused, 2350 args); 2351 } 2352 } 2353 2354 JNI_EXIT(); 2355 return result; 2356 } 2357 static jobject NewObjectA(JNIEnv* env, jclass jclazz, jmethodID methodID, 2358 jvalue* args) 2359 { 2360 JNI_ENTER(); 2361 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 2362 jobject result; 2363 2364 if (!canAllocClass(clazz) || 2365 (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))) 2366 { 2367 assert(dvmCheckException(_self)); 2368 result = NULL; 2369 } else { 2370 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK); 2371 result = addLocalReference(env, newObj); 2372 if (newObj != NULL) { 2373 JValue unused; 2374 dvmCallMethodA(_self, (Method*) methodID, newObj, true, &unused, 2375 args); 2376 } 2377 } 2378 2379 JNI_EXIT(); 2380 return result; 2381 } 2382 2383 /* 2384 * Returns the class of an object. 2385 * 2386 * JNI spec says: obj must not be NULL. 2387 */ 2388 static jclass GetObjectClass(JNIEnv* env, jobject jobj) 2389 { 2390 JNI_ENTER(); 2391 2392 assert(jobj != NULL); 2393 2394 Object* obj = dvmDecodeIndirectRef(env, jobj); 2395 jclass jclazz = addLocalReference(env, (Object*) obj->clazz); 2396 2397 JNI_EXIT(); 2398 return jclazz; 2399 } 2400 2401 /* 2402 * Determine whether "obj" is an instance of "clazz". 2403 */ 2404 static jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass jclazz) 2405 { 2406 JNI_ENTER(); 2407 2408 assert(jclazz != NULL); 2409 2410 jboolean result; 2411 2412 if (jobj == NULL) { 2413 result = true; 2414 } else { 2415 Object* obj = dvmDecodeIndirectRef(env, jobj); 2416 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 2417 result = dvmInstanceof(obj->clazz, clazz); 2418 } 2419 2420 JNI_EXIT(); 2421 return result; 2422 } 2423 2424 /* 2425 * Get a method ID for an instance method. 2426 * 2427 * JNI defines <init> as an instance method, but Dalvik considers it a 2428 * "direct" method, so we have to special-case it here. 2429 * 2430 * Dalvik also puts all private methods into the "direct" list, so we 2431 * really need to just search both lists. 2432 */ 2433 static jmethodID GetMethodID(JNIEnv* env, jclass jclazz, const char* name, 2434 const char* sig) 2435 { 2436 JNI_ENTER(); 2437 2438 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 2439 jmethodID id = NULL; 2440 2441 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) { 2442 assert(dvmCheckException(_self)); 2443 } else { 2444 Method* meth; 2445 2446 meth = dvmFindVirtualMethodHierByDescriptor(clazz, name, sig); 2447 if (meth == NULL) { 2448 /* search private methods and constructors; non-hierarchical */ 2449 meth = dvmFindDirectMethodByDescriptor(clazz, name, sig); 2450 } 2451 if (meth != NULL && dvmIsStaticMethod(meth)) { 2452 IF_LOGD() { 2453 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype); 2454 LOGD("GetMethodID: not returning static method %s.%s %s\n", 2455 clazz->descriptor, meth->name, desc); 2456 free(desc); 2457 } 2458 meth = NULL; 2459 } 2460 if (meth == NULL) { 2461 LOGD("GetMethodID: method not found: %s.%s:%s\n", 2462 clazz->descriptor, name, sig); 2463 dvmThrowException("Ljava/lang/NoSuchMethodError;", name); 2464 } 2465 2466 /* 2467 * The method's class may not be the same as clazz, but if 2468 * it isn't this must be a virtual method and the class must 2469 * be a superclass (and, hence, already initialized). 2470 */ 2471 if (meth != NULL) { 2472 assert(dvmIsClassInitialized(meth->clazz) || 2473 dvmIsClassInitializing(meth->clazz)); 2474 } 2475 id = (jmethodID) meth; 2476 } 2477 JNI_EXIT(); 2478 return id; 2479 } 2480 2481 /* 2482 * Get a field ID (instance fields). 2483 */ 2484 static jfieldID GetFieldID(JNIEnv* env, jclass jclazz, 2485 const char* name, const char* sig) 2486 { 2487 JNI_ENTER(); 2488 2489 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 2490 jfieldID id; 2491 2492 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) { 2493 assert(dvmCheckException(_self)); 2494 id = NULL; 2495 } else { 2496 id = (jfieldID) dvmFindInstanceFieldHier(clazz, name, sig); 2497 if (id == NULL) { 2498 LOGD("GetFieldID: unable to find field %s.%s:%s\n", 2499 clazz->descriptor, name, sig); 2500 dvmThrowException("Ljava/lang/NoSuchFieldError;", name); 2501 } 2502 } 2503 JNI_EXIT(); 2504 return id; 2505 } 2506 2507 /* 2508 * Get the method ID for a static method in a class. 2509 */ 2510 static jmethodID GetStaticMethodID(JNIEnv* env, jclass jclazz, 2511 const char* name, const char* sig) 2512 { 2513 JNI_ENTER(); 2514 2515 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 2516 jmethodID id; 2517 2518 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) { 2519 assert(dvmCheckException(_self)); 2520 id = NULL; 2521 } else { 2522 Method* meth; 2523 2524 meth = dvmFindDirectMethodHierByDescriptor(clazz, name, sig); 2525 2526 /* make sure it's static, not virtual+private */ 2527 if (meth != NULL && !dvmIsStaticMethod(meth)) { 2528 IF_LOGD() { 2529 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype); 2530 LOGD("GetStaticMethodID: " 2531 "not returning nonstatic method %s.%s %s\n", 2532 clazz->descriptor, meth->name, desc); 2533 free(desc); 2534 } 2535 meth = NULL; 2536 } 2537 2538 id = (jmethodID) meth; 2539 if (id == NULL) 2540 dvmThrowException("Ljava/lang/NoSuchMethodError;", name); 2541 } 2542 2543 JNI_EXIT(); 2544 return id; 2545 } 2546 2547 /* 2548 * Get a field ID (static fields). 2549 */ 2550 static jfieldID GetStaticFieldID(JNIEnv* env, jclass jclazz, 2551 const char* name, const char* sig) 2552 { 2553 JNI_ENTER(); 2554 2555 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 2556 jfieldID id; 2557 2558 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) { 2559 assert(dvmCheckException(_self)); 2560 id = NULL; 2561 } else { 2562 id = (jfieldID) dvmFindStaticField(clazz, name, sig); 2563 if (id == NULL) 2564 dvmThrowException("Ljava/lang/NoSuchFieldError;", name); 2565 } 2566 JNI_EXIT(); 2567 return id; 2568 } 2569 2570 /* 2571 * Get a static field. 2572 * 2573 * If we get an object reference, add it to the local refs list. 2574 */ 2575 #define GET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) \ 2576 static _ctype GetStatic##_jname##Field(JNIEnv* env, jclass jclazz, \ 2577 jfieldID fieldID) \ 2578 { \ 2579 UNUSED_PARAMETER(jclazz); \ 2580 JNI_ENTER(); \ 2581 StaticField* sfield = (StaticField*) fieldID; \ 2582 _ctype value; \ 2583 if (dvmIsVolatileField(&sfield->field)) { \ 2584 if (_isref) { /* only when _ctype==jobject */ \ 2585 Object* obj = dvmGetStaticFieldObjectVolatile(sfield); \ 2586 value = (_ctype)(u4)addLocalReference(env, obj); \ 2587 } else { \ 2588 value = dvmGetStaticField##_jname##Volatile(sfield); \ 2589 } \ 2590 } else { \ 2591 if (_isref) { \ 2592 Object* obj = dvmGetStaticFieldObject(sfield); \ 2593 value = (_ctype)(u4)addLocalReference(env, obj); \ 2594 } else { \ 2595 value = dvmGetStaticField##_jname(sfield); \ 2596 } \ 2597 } \ 2598 JNI_EXIT(); \ 2599 return value; \ 2600 } 2601 GET_STATIC_TYPE_FIELD(jobject, Object, true); 2602 GET_STATIC_TYPE_FIELD(jboolean, Boolean, false); 2603 GET_STATIC_TYPE_FIELD(jbyte, Byte, false); 2604 GET_STATIC_TYPE_FIELD(jchar, Char, false); 2605 GET_STATIC_TYPE_FIELD(jshort, Short, false); 2606 GET_STATIC_TYPE_FIELD(jint, Int, false); 2607 GET_STATIC_TYPE_FIELD(jlong, Long, false); 2608 GET_STATIC_TYPE_FIELD(jfloat, Float, false); 2609 GET_STATIC_TYPE_FIELD(jdouble, Double, false); 2610 2611 /* 2612 * Set a static field. 2613 */ 2614 #define SET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) \ 2615 static void SetStatic##_jname##Field(JNIEnv* env, jclass jclazz, \ 2616 jfieldID fieldID, _ctype value) \ 2617 { \ 2618 UNUSED_PARAMETER(jclazz); \ 2619 JNI_ENTER(); \ 2620 StaticField* sfield = (StaticField*) fieldID; \ 2621 if (dvmIsVolatileField(&sfield->field)) { \ 2622 if (_isref) { /* only when _ctype==jobject */ \ 2623 Object* valObj = \ 2624 dvmDecodeIndirectRef(env, (jobject)(u4)value); \ 2625 dvmSetStaticFieldObjectVolatile(sfield, valObj); \ 2626 } else { \ 2627 dvmSetStaticField##_jname##Volatile(sfield, value); \ 2628 } \ 2629 } else { \ 2630 if (_isref) { \ 2631 Object* valObj = \ 2632 dvmDecodeIndirectRef(env, (jobject)(u4)value); \ 2633 dvmSetStaticFieldObject(sfield, valObj); \ 2634 } else { \ 2635 dvmSetStaticField##_jname(sfield, value); \ 2636 } \ 2637 } \ 2638 JNI_EXIT(); \ 2639 } 2640 SET_STATIC_TYPE_FIELD(jobject, Object, true); 2641 SET_STATIC_TYPE_FIELD(jboolean, Boolean, false); 2642 SET_STATIC_TYPE_FIELD(jbyte, Byte, false); 2643 SET_STATIC_TYPE_FIELD(jchar, Char, false); 2644 SET_STATIC_TYPE_FIELD(jshort, Short, false); 2645 SET_STATIC_TYPE_FIELD(jint, Int, false); 2646 SET_STATIC_TYPE_FIELD(jlong, Long, false); 2647 SET_STATIC_TYPE_FIELD(jfloat, Float, false); 2648 SET_STATIC_TYPE_FIELD(jdouble, Double, false); 2649 2650 /* 2651 * Get an instance field. 2652 * 2653 * If we get an object reference, add it to the local refs list. 2654 */ 2655 #define GET_TYPE_FIELD(_ctype, _jname, _isref) \ 2656 static _ctype Get##_jname##Field(JNIEnv* env, jobject jobj, \ 2657 jfieldID fieldID) \ 2658 { \ 2659 JNI_ENTER(); \ 2660 Object* obj = dvmDecodeIndirectRef(env, jobj); \ 2661 InstField* field = (InstField*) fieldID; \ 2662 _ctype value; \ 2663 if (dvmIsVolatileField(&field->field)) { \ 2664 if (_isref) { /* only when _ctype==jobject */ \ 2665 Object* valObj = \ 2666 dvmGetFieldObjectVolatile(obj, field->byteOffset); \ 2667 value = (_ctype)(u4)addLocalReference(env, valObj); \ 2668 } else { \ 2669 value = \ 2670 dvmGetField##_jname##Volatile(obj, field->byteOffset); \ 2671 } \ 2672 } else { \ 2673 if (_isref) { \ 2674 Object* valObj = dvmGetFieldObject(obj, field->byteOffset); \ 2675 value = (_ctype)(u4)addLocalReference(env, valObj); \ 2676 } else { \ 2677 value = dvmGetField##_jname(obj, field->byteOffset); \ 2678 } \ 2679 } \ 2680 JNI_EXIT(); \ 2681 return value; \ 2682 } 2683 GET_TYPE_FIELD(jobject, Object, true); 2684 GET_TYPE_FIELD(jboolean, Boolean, false); 2685 GET_TYPE_FIELD(jbyte, Byte, false); 2686 GET_TYPE_FIELD(jchar, Char, false); 2687 GET_TYPE_FIELD(jshort, Short, false); 2688 GET_TYPE_FIELD(jint, Int, false); 2689 GET_TYPE_FIELD(jlong, Long, false); 2690 GET_TYPE_FIELD(jfloat, Float, false); 2691 GET_TYPE_FIELD(jdouble, Double, false); 2692 2693 /* 2694 * Set an instance field. 2695 */ 2696 #define SET_TYPE_FIELD(_ctype, _jname, _isref) \ 2697 static void Set##_jname##Field(JNIEnv* env, jobject jobj, \ 2698 jfieldID fieldID, _ctype value) \ 2699 { \ 2700 JNI_ENTER(); \ 2701 Object* obj = dvmDecodeIndirectRef(env, jobj); \ 2702 InstField* field = (InstField*) fieldID; \ 2703 if (dvmIsVolatileField(&field->field)) { \ 2704 if (_isref) { /* only when _ctype==jobject */ \ 2705 Object* valObj = \ 2706 dvmDecodeIndirectRef(env, (jobject)(u4)value); \ 2707 dvmSetFieldObjectVolatile(obj, field->byteOffset, valObj); \ 2708 } else { \ 2709 dvmSetField##_jname##Volatile(obj, \ 2710 field->byteOffset, value); \ 2711 } \ 2712 } else { \ 2713 if (_isref) { \ 2714 Object* valObj = \ 2715 dvmDecodeIndirectRef(env, (jobject)(u4)value); \ 2716 dvmSetFieldObject(obj, field->byteOffset, valObj); \ 2717 } else { \ 2718 dvmSetField##_jname(obj, field->byteOffset, value); \ 2719 } \ 2720 } \ 2721 JNI_EXIT(); \ 2722 } 2723 SET_TYPE_FIELD(jobject, Object, true); 2724 SET_TYPE_FIELD(jboolean, Boolean, false); 2725 SET_TYPE_FIELD(jbyte, Byte, false); 2726 SET_TYPE_FIELD(jchar, Char, false); 2727 SET_TYPE_FIELD(jshort, Short, false); 2728 SET_TYPE_FIELD(jint, Int, false); 2729 SET_TYPE_FIELD(jlong, Long, false); 2730 SET_TYPE_FIELD(jfloat, Float, false); 2731 SET_TYPE_FIELD(jdouble, Double, false); 2732 2733 /* 2734 * Make a virtual method call. 2735 * 2736 * Three versions (..., va_list, jvalue[]) for each return type. If we're 2737 * returning an Object, we have to add it to the local references table. 2738 */ 2739 #define CALL_VIRTUAL(_ctype, _jname, _retfail, _retok, _isref) \ 2740 static _ctype Call##_jname##Method(JNIEnv* env, jobject jobj, \ 2741 jmethodID methodID, ...) \ 2742 { \ 2743 JNI_ENTER(); \ 2744 Object* obj = dvmDecodeIndirectRef(env, jobj); \ 2745 const Method* meth; \ 2746 va_list args; \ 2747 JValue result; \ 2748 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \ 2749 if (meth == NULL) { \ 2750 JNI_EXIT(); \ 2751 return _retfail; \ 2752 } \ 2753 va_start(args, methodID); \ 2754 dvmCallMethodV(_self, meth, obj, true, &result, args); \ 2755 va_end(args); \ 2756 if (_isref && !dvmCheckException(_self)) \ 2757 result.l = addLocalReference(env, result.l); \ 2758 JNI_EXIT(); \ 2759 return _retok; \ 2760 } \ 2761 static _ctype Call##_jname##MethodV(JNIEnv* env, jobject jobj, \ 2762 jmethodID methodID, va_list args) \ 2763 { \ 2764 JNI_ENTER(); \ 2765 Object* obj = dvmDecodeIndirectRef(env, jobj); \ 2766 const Method* meth; \ 2767 JValue result; \ 2768 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \ 2769 if (meth == NULL) { \ 2770 JNI_EXIT(); \ 2771 return _retfail; \ 2772 } \ 2773 dvmCallMethodV(_self, meth, obj, true, &result, args); \ 2774 if (_isref && !dvmCheckException(_self)) \ 2775 result.l = addLocalReference(env, result.l); \ 2776 JNI_EXIT(); \ 2777 return _retok; \ 2778 } \ 2779 static _ctype Call##_jname##MethodA(JNIEnv* env, jobject jobj, \ 2780 jmethodID methodID, jvalue* args) \ 2781 { \ 2782 JNI_ENTER(); \ 2783 Object* obj = dvmDecodeIndirectRef(env, jobj); \ 2784 const Method* meth; \ 2785 JValue result; \ 2786 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \ 2787 if (meth == NULL) { \ 2788 JNI_EXIT(); \ 2789 return _retfail; \ 2790 } \ 2791 dvmCallMethodA(_self, meth, obj, true, &result, args); \ 2792 if (_isref && !dvmCheckException(_self)) \ 2793 result.l = addLocalReference(env, result.l); \ 2794 JNI_EXIT(); \ 2795 return _retok; \ 2796 } 2797 CALL_VIRTUAL(jobject, Object, NULL, result.l, true); 2798 CALL_VIRTUAL(jboolean, Boolean, 0, result.z, false); 2799 CALL_VIRTUAL(jbyte, Byte, 0, result.b, false); 2800 CALL_VIRTUAL(jchar, Char, 0, result.c, false); 2801 CALL_VIRTUAL(jshort, Short, 0, result.s, false); 2802 CALL_VIRTUAL(jint, Int, 0, result.i, false); 2803 CALL_VIRTUAL(jlong, Long, 0, result.j, false); 2804 CALL_VIRTUAL(jfloat, Float, 0.0f, result.f, false); 2805 CALL_VIRTUAL(jdouble, Double, 0.0, result.d, false); 2806 CALL_VIRTUAL(void, Void, , , false); 2807 2808 /* 2809 * Make a "non-virtual" method call. We're still calling a virtual method, 2810 * but this time we're not doing an indirection through the object's vtable. 2811 * The "clazz" parameter defines which implementation of a method we want. 2812 * 2813 * Three versions (..., va_list, jvalue[]) for each return type. 2814 */ 2815 #define CALL_NONVIRTUAL(_ctype, _jname, _retfail, _retok, _isref) \ 2816 static _ctype CallNonvirtual##_jname##Method(JNIEnv* env, jobject jobj, \ 2817 jclass jclazz, jmethodID methodID, ...) \ 2818 { \ 2819 JNI_ENTER(); \ 2820 Object* obj = dvmDecodeIndirectRef(env, jobj); \ 2821 ClassObject* clazz = \ 2822 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \ 2823 const Method* meth; \ 2824 va_list args; \ 2825 JValue result; \ 2826 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \ 2827 if (meth == NULL) { \ 2828 JNI_EXIT(); \ 2829 return _retfail; \ 2830 } \ 2831 va_start(args, methodID); \ 2832 dvmCallMethodV(_self, meth, obj, true, &result, args); \ 2833 if (_isref && !dvmCheckException(_self)) \ 2834 result.l = addLocalReference(env, result.l); \ 2835 va_end(args); \ 2836 JNI_EXIT(); \ 2837 return _retok; \ 2838 } \ 2839 static _ctype CallNonvirtual##_jname##MethodV(JNIEnv* env, jobject jobj,\ 2840 jclass jclazz, jmethodID methodID, va_list args) \ 2841 { \ 2842 JNI_ENTER(); \ 2843 Object* obj = dvmDecodeIndirectRef(env, jobj); \ 2844 ClassObject* clazz = \ 2845 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \ 2846 const Method* meth; \ 2847 JValue result; \ 2848 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \ 2849 if (meth == NULL) { \ 2850 JNI_EXIT(); \ 2851 return _retfail; \ 2852 } \ 2853 dvmCallMethodV(_self, meth, obj, true, &result, args); \ 2854 if (_isref && !dvmCheckException(_self)) \ 2855 result.l = addLocalReference(env, result.l); \ 2856 JNI_EXIT(); \ 2857 return _retok; \ 2858 } \ 2859 static _ctype CallNonvirtual##_jname##MethodA(JNIEnv* env, jobject jobj,\ 2860 jclass jclazz, jmethodID methodID, jvalue* args) \ 2861 { \ 2862 JNI_ENTER(); \ 2863 Object* obj = dvmDecodeIndirectRef(env, jobj); \ 2864 ClassObject* clazz = \ 2865 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \ 2866 const Method* meth; \ 2867 JValue result; \ 2868 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \ 2869 if (meth == NULL) { \ 2870 JNI_EXIT(); \ 2871 return _retfail; \ 2872 } \ 2873 dvmCallMethodA(_self, meth, obj, true, &result, args); \ 2874 if (_isref && !dvmCheckException(_self)) \ 2875 result.l = addLocalReference(env, result.l); \ 2876 JNI_EXIT(); \ 2877 return _retok; \ 2878 } 2879 CALL_NONVIRTUAL(jobject, Object, NULL, result.l, true); 2880 CALL_NONVIRTUAL(jboolean, Boolean, 0, result.z, false); 2881 CALL_NONVIRTUAL(jbyte, Byte, 0, result.b, false); 2882 CALL_NONVIRTUAL(jchar, Char, 0, result.c, false); 2883 CALL_NONVIRTUAL(jshort, Short, 0, result.s, false); 2884 CALL_NONVIRTUAL(jint, Int, 0, result.i, false); 2885 CALL_NONVIRTUAL(jlong, Long, 0, result.j, false); 2886 CALL_NONVIRTUAL(jfloat, Float, 0.0f, result.f, false); 2887 CALL_NONVIRTUAL(jdouble, Double, 0.0, result.d, false); 2888 CALL_NONVIRTUAL(void, Void, , , false); 2889 2890 2891 /* 2892 * Call a static method. 2893 */ 2894 #define CALL_STATIC(_ctype, _jname, _retfail, _retok, _isref) \ 2895 static _ctype CallStatic##_jname##Method(JNIEnv* env, jclass jclazz, \ 2896 jmethodID methodID, ...) \ 2897 { \ 2898 UNUSED_PARAMETER(jclazz); \ 2899 JNI_ENTER(); \ 2900 JValue result; \ 2901 va_list args; \ 2902 va_start(args, methodID); \ 2903 dvmCallMethodV(_self, (Method*)methodID, NULL, true, &result, args);\ 2904 va_end(args); \ 2905 if (_isref && !dvmCheckException(_self)) \ 2906 result.l = addLocalReference(env, result.l); \ 2907 JNI_EXIT(); \ 2908 return _retok; \ 2909 } \ 2910 static _ctype CallStatic##_jname##MethodV(JNIEnv* env, jclass jclazz, \ 2911 jmethodID methodID, va_list args) \ 2912 { \ 2913 UNUSED_PARAMETER(jclazz); \ 2914 JNI_ENTER(); \ 2915 JValue result; \ 2916 dvmCallMethodV(_self, (Method*)methodID, NULL, true, &result, args);\ 2917 if (_isref && !dvmCheckException(_self)) \ 2918 result.l = addLocalReference(env, result.l); \ 2919 JNI_EXIT(); \ 2920 return _retok; \ 2921 } \ 2922 static _ctype CallStatic##_jname##MethodA(JNIEnv* env, jclass jclazz, \ 2923 jmethodID methodID, jvalue* args) \ 2924 { \ 2925 UNUSED_PARAMETER(jclazz); \ 2926 JNI_ENTER(); \ 2927 JValue result; \ 2928 dvmCallMethodA(_self, (Method*)methodID, NULL, true, &result, args);\ 2929 if (_isref && !dvmCheckException(_self)) \ 2930 result.l = addLocalReference(env, result.l); \ 2931 JNI_EXIT(); \ 2932 return _retok; \ 2933 } 2934 CALL_STATIC(jobject, Object, NULL, result.l, true); 2935 CALL_STATIC(jboolean, Boolean, 0, result.z, false); 2936 CALL_STATIC(jbyte, Byte, 0, result.b, false); 2937 CALL_STATIC(jchar, Char, 0, result.c, false); 2938 CALL_STATIC(jshort, Short, 0, result.s, false); 2939 CALL_STATIC(jint, Int, 0, result.i, false); 2940 CALL_STATIC(jlong, Long, 0, result.j, false); 2941 CALL_STATIC(jfloat, Float, 0.0f, result.f, false); 2942 CALL_STATIC(jdouble, Double, 0.0, result.d, false); 2943 CALL_STATIC(void, Void, , , false); 2944 2945 /* 2946 * Create a new String from Unicode data. 2947 * 2948 * If "len" is zero, we will return an empty string even if "unicodeChars" 2949 * is NULL. (The JNI spec is vague here.) 2950 */ 2951 static jstring NewString(JNIEnv* env, const jchar* unicodeChars, jsize len) 2952 { 2953 JNI_ENTER(); 2954 jobject retval; 2955 2956 StringObject* jstr = dvmCreateStringFromUnicode(unicodeChars, len); 2957 if (jstr == NULL) { 2958 retval = NULL; 2959 } else { 2960 dvmReleaseTrackedAlloc((Object*) jstr, NULL); 2961 retval = addLocalReference(env, (Object*) jstr); 2962 } 2963 2964 JNI_EXIT(); 2965 return retval; 2966 } 2967 2968 /* 2969 * Return the length of a String in Unicode character units. 2970 */ 2971 static jsize GetStringLength(JNIEnv* env, jstring jstr) 2972 { 2973 JNI_ENTER(); 2974 2975 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); 2976 jsize len = dvmStringLen(strObj); 2977 2978 JNI_EXIT(); 2979 return len; 2980 } 2981 2982 2983 /* 2984 * Get a string's character data. 2985 * 2986 * The result is guaranteed to be valid until ReleaseStringChars is 2987 * called, which means we have to pin it or return a copy. 2988 */ 2989 static const jchar* GetStringChars(JNIEnv* env, jstring jstr, jboolean* isCopy) 2990 { 2991 JNI_ENTER(); 2992 2993 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); 2994 ArrayObject* strChars = dvmStringCharArray(strObj); 2995 2996 pinPrimitiveArray(strChars); 2997 2998 const u2* data = dvmStringChars(strObj); 2999 if (isCopy != NULL) 3000 *isCopy = JNI_FALSE; 3001 3002 JNI_EXIT(); 3003 return (jchar*)data; 3004 } 3005 3006 /* 3007 * Release our grip on some characters from a string. 3008 */ 3009 static void ReleaseStringChars(JNIEnv* env, jstring jstr, const jchar* chars) 3010 { 3011 JNI_ENTER(); 3012 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); 3013 ArrayObject* strChars = dvmStringCharArray(strObj); 3014 unpinPrimitiveArray(strChars); 3015 JNI_EXIT(); 3016 } 3017 3018 /* 3019 * Create a new java.lang.String object from chars in modified UTF-8 form. 3020 * 3021 * The spec doesn't say how to handle a NULL string. Popular desktop VMs 3022 * accept it and return a NULL pointer in response. 3023 */ 3024 static jstring NewStringUTF(JNIEnv* env, const char* bytes) 3025 { 3026 JNI_ENTER(); 3027 3028 jstring result; 3029 3030 if (bytes == NULL) { 3031 result = NULL; 3032 } else { 3033 /* note newStr could come back NULL on OOM */ 3034 StringObject* newStr = dvmCreateStringFromCstr(bytes); 3035 result = addLocalReference(env, (Object*) newStr); 3036 dvmReleaseTrackedAlloc((Object*)newStr, NULL); 3037 } 3038 3039 JNI_EXIT(); 3040 return result; 3041 } 3042 3043 /* 3044 * Return the length in bytes of the modified UTF-8 form of the string. 3045 */ 3046 static jsize GetStringUTFLength(JNIEnv* env, jstring jstr) 3047 { 3048 JNI_ENTER(); 3049 3050 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); 3051 jsize len = dvmStringUtf8ByteLen(strObj); 3052 3053 JNI_EXIT(); 3054 return len; 3055 } 3056 3057 /* 3058 * Convert "string" to modified UTF-8 and return a pointer. The returned 3059 * value must be released with ReleaseStringUTFChars. 3060 * 3061 * According to the JNI reference, "Returns a pointer to a UTF-8 string, 3062 * or NULL if the operation fails. Returns NULL if and only if an invocation 3063 * of this function has thrown an exception." 3064 * 3065 * The behavior here currently follows that of other open-source VMs, which 3066 * quietly return NULL if "string" is NULL. We should consider throwing an 3067 * NPE. (The CheckJNI code blows up if you try to pass in a NULL string, 3068 * which should catch this sort of thing during development.) Certain other 3069 * VMs will crash with a segmentation fault. 3070 */ 3071 static const char* GetStringUTFChars(JNIEnv* env, jstring jstr, 3072 jboolean* isCopy) 3073 { 3074 JNI_ENTER(); 3075 char* newStr; 3076 3077 if (jstr == NULL) { 3078 /* this shouldn't happen; throw NPE? */ 3079 newStr = NULL; 3080 } else { 3081 if (isCopy != NULL) 3082 *isCopy = JNI_TRUE; 3083 3084 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); 3085 newStr = dvmCreateCstrFromString(strObj); 3086 if (newStr == NULL) { 3087 /* assume memory failure */ 3088 dvmThrowException("Ljava/lang/OutOfMemoryError;", 3089 "native heap string alloc failed"); 3090 } 3091 } 3092 3093 JNI_EXIT(); 3094 return newStr; 3095 } 3096 3097 /* 3098 * Release a string created by GetStringUTFChars(). 3099 */ 3100 static void ReleaseStringUTFChars(JNIEnv* env, jstring jstr, const char* utf) 3101 { 3102 JNI_ENTER(); 3103 free((char*)utf); 3104 JNI_EXIT(); 3105 } 3106 3107 /* 3108 * Return the capacity of the array. 3109 */ 3110 static jsize GetArrayLength(JNIEnv* env, jarray jarr) 3111 { 3112 JNI_ENTER(); 3113 3114 ArrayObject* arrObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr); 3115 jsize length = arrObj->length; 3116 3117 JNI_EXIT(); 3118 return length; 3119 } 3120 3121 /* 3122 * Construct a new array that holds objects from class "elementClass". 3123 */ 3124 static jobjectArray NewObjectArray(JNIEnv* env, jsize length, 3125 jclass jelementClass, jobject jinitialElement) 3126 { 3127 JNI_ENTER(); 3128 3129 jobjectArray newArray = NULL; 3130 ClassObject* elemClassObj = 3131 (ClassObject*) dvmDecodeIndirectRef(env, jelementClass); 3132 3133 if (elemClassObj == NULL) { 3134 dvmThrowException("Ljava/lang/NullPointerException;", 3135 "JNI NewObjectArray"); 3136 goto bail; 3137 } 3138 3139 ArrayObject* newObj = 3140 dvmAllocObjectArray(elemClassObj, length, ALLOC_DEFAULT); 3141 if (newObj == NULL) { 3142 assert(dvmCheckException(_self)); 3143 goto bail; 3144 } 3145 newArray = addLocalReference(env, (Object*) newObj); 3146 dvmReleaseTrackedAlloc((Object*) newObj, NULL); 3147 3148 /* 3149 * Initialize the array. Trashes "length". 3150 */ 3151 if (jinitialElement != NULL) { 3152 Object* initialElement = dvmDecodeIndirectRef(env, jinitialElement); 3153 Object** arrayData = (Object**) newObj->contents; 3154 3155 while (length--) 3156 *arrayData++ = initialElement; 3157 } 3158 3159 3160 bail: 3161 JNI_EXIT(); 3162 return newArray; 3163 } 3164 3165 /* 3166 * Get one element of an Object array. 3167 * 3168 * Add the object to the local references table in case the array goes away. 3169 */ 3170 static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray jarr, 3171 jsize index) 3172 { 3173 JNI_ENTER(); 3174 3175 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr); 3176 jobject retval = NULL; 3177 3178 assert(arrayObj != NULL); 3179 3180 /* check the array bounds */ 3181 if (index < 0 || index >= (int) arrayObj->length) { 3182 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", 3183 arrayObj->obj.clazz->descriptor); 3184 goto bail; 3185 } 3186 3187 Object* value = ((Object**) arrayObj->contents)[index]; 3188 retval = addLocalReference(env, value); 3189 3190 bail: 3191 JNI_EXIT(); 3192 return retval; 3193 } 3194 3195 /* 3196 * Set one element of an Object array. 3197 */ 3198 static void SetObjectArrayElement(JNIEnv* env, jobjectArray jarr, 3199 jsize index, jobject jobj) 3200 { 3201 JNI_ENTER(); 3202 3203 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr); 3204 3205 assert(arrayObj != NULL); 3206 3207 /* check the array bounds */ 3208 if (index < 0 || index >= (int) arrayObj->length) { 3209 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", 3210 arrayObj->obj.clazz->descriptor); 3211 goto bail; 3212 } 3213 3214 //LOGV("JNI: set element %d in array %p to %p\n", index, array, value); 3215 3216 Object* obj = dvmDecodeIndirectRef(env, jobj); 3217 dvmSetObjectArrayElement(arrayObj, index, obj); 3218 3219 bail: 3220 JNI_EXIT(); 3221 } 3222 3223 /* 3224 * Create a new array of primitive elements. 3225 */ 3226 #define NEW_PRIMITIVE_ARRAY(_artype, _jname, _typechar) \ 3227 static _artype New##_jname##Array(JNIEnv* env, jsize length) \ 3228 { \ 3229 JNI_ENTER(); \ 3230 ArrayObject* arrayObj; \ 3231 arrayObj = dvmAllocPrimitiveArray(_typechar, length, \ 3232 ALLOC_DEFAULT); \ 3233 jarray jarr = NULL; \ 3234 if (arrayObj != NULL) { \ 3235 jarr = addLocalReference(env, (Object*) arrayObj); \ 3236 dvmReleaseTrackedAlloc((Object*) arrayObj, NULL); \ 3237 } \ 3238 JNI_EXIT(); \ 3239 return (_artype)jarr; \ 3240 } 3241 NEW_PRIMITIVE_ARRAY(jbooleanArray, Boolean, 'Z'); 3242 NEW_PRIMITIVE_ARRAY(jbyteArray, Byte, 'B'); 3243 NEW_PRIMITIVE_ARRAY(jcharArray, Char, 'C'); 3244 NEW_PRIMITIVE_ARRAY(jshortArray, Short, 'S'); 3245 NEW_PRIMITIVE_ARRAY(jintArray, Int, 'I'); 3246 NEW_PRIMITIVE_ARRAY(jlongArray, Long, 'J'); 3247 NEW_PRIMITIVE_ARRAY(jfloatArray, Float, 'F'); 3248 NEW_PRIMITIVE_ARRAY(jdoubleArray, Double, 'D'); 3249 3250 /* 3251 * Get a pointer to a C array of primitive elements from an array object 3252 * of the matching type. 3253 * 3254 * In a compacting GC, we either need to return a copy of the elements or 3255 * "pin" the memory. Otherwise we run the risk of native code using the 3256 * buffer as the destination of e.g. a blocking read() call that wakes up 3257 * during a GC. 3258 */ 3259 #define GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \ 3260 static _ctype* Get##_jname##ArrayElements(JNIEnv* env, \ 3261 _ctype##Array jarr, jboolean* isCopy) \ 3262 { \ 3263 JNI_ENTER(); \ 3264 _ctype* data; \ 3265 ArrayObject* arrayObj = \ 3266 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \ 3267 pinPrimitiveArray(arrayObj); \ 3268 data = (_ctype*) arrayObj->contents; \ 3269 if (isCopy != NULL) \ 3270 *isCopy = JNI_FALSE; \ 3271 JNI_EXIT(); \ 3272 return data; \ 3273 } 3274 3275 /* 3276 * Release the storage locked down by the "get" function. 3277 * 3278 * The spec says, "'mode' has no effect if 'elems' is not a copy of the 3279 * elements in 'array'." They apparently did not anticipate the need to 3280 * un-pin memory. 3281 */ 3282 #define RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \ 3283 static void Release##_jname##ArrayElements(JNIEnv* env, \ 3284 _ctype##Array jarr, _ctype* elems, jint mode) \ 3285 { \ 3286 UNUSED_PARAMETER(elems); \ 3287 JNI_ENTER(); \ 3288 if (mode != JNI_COMMIT) { \ 3289 ArrayObject* arrayObj = \ 3290 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \ 3291 unpinPrimitiveArray(arrayObj); \ 3292 } \ 3293 JNI_EXIT(); \ 3294 } 3295 3296 /* 3297 * Copy a section of a primitive array to a buffer. 3298 */ 3299 #define GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \ 3300 static void Get##_jname##ArrayRegion(JNIEnv* env, \ 3301 _ctype##Array jarr, jsize start, jsize len, _ctype* buf) \ 3302 { \ 3303 JNI_ENTER(); \ 3304 ArrayObject* arrayObj = \ 3305 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \ 3306 _ctype* data = (_ctype*) arrayObj->contents; \ 3307 if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \ 3308 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \ 3309 arrayObj->obj.clazz->descriptor); \ 3310 } else { \ 3311 memcpy(buf, data + start, len * sizeof(_ctype)); \ 3312 } \ 3313 JNI_EXIT(); \ 3314 } 3315 3316 /* 3317 * Copy a section of a primitive array from a buffer. 3318 */ 3319 #define SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \ 3320 static void Set##_jname##ArrayRegion(JNIEnv* env, \ 3321 _ctype##Array jarr, jsize start, jsize len, const _ctype* buf) \ 3322 { \ 3323 JNI_ENTER(); \ 3324 ArrayObject* arrayObj = \ 3325 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \ 3326 _ctype* data = (_ctype*) arrayObj->contents; \ 3327 if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \ 3328 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \ 3329 arrayObj->obj.clazz->descriptor); \ 3330 } else { \ 3331 memcpy(data + start, buf, len * sizeof(_ctype)); \ 3332 } \ 3333 JNI_EXIT(); \ 3334 } 3335 3336 /* 3337 * 4-in-1: 3338 * Get<Type>ArrayElements 3339 * Release<Type>ArrayElements 3340 * Get<Type>ArrayRegion 3341 * Set<Type>ArrayRegion 3342 */ 3343 #define PRIMITIVE_ARRAY_FUNCTIONS(_ctype, _jname) \ 3344 GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \ 3345 RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \ 3346 GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname); \ 3347 SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname); 3348 3349 PRIMITIVE_ARRAY_FUNCTIONS(jboolean, Boolean); 3350 PRIMITIVE_ARRAY_FUNCTIONS(jbyte, Byte); 3351 PRIMITIVE_ARRAY_FUNCTIONS(jchar, Char); 3352 PRIMITIVE_ARRAY_FUNCTIONS(jshort, Short); 3353 PRIMITIVE_ARRAY_FUNCTIONS(jint, Int); 3354 PRIMITIVE_ARRAY_FUNCTIONS(jlong, Long); 3355 PRIMITIVE_ARRAY_FUNCTIONS(jfloat, Float); 3356 PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double); 3357 3358 /* 3359 * Register one or more native functions in one class. 3360 * 3361 * This can be called multiple times on the same method, allowing the 3362 * caller to redefine the method implementation at will. 3363 */ 3364 static jint RegisterNatives(JNIEnv* env, jclass jclazz, 3365 const JNINativeMethod* methods, jint nMethods) 3366 { 3367 JNI_ENTER(); 3368 3369 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 3370 jint retval = JNI_OK; 3371 int i; 3372 3373 if (gDvm.verboseJni) { 3374 LOGI("[Registering JNI native methods for class %s]\n", 3375 clazz->descriptor); 3376 } 3377 3378 for (i = 0; i < nMethods; i++) { 3379 if (!dvmRegisterJNIMethod(clazz, methods[i].name, 3380 methods[i].signature, methods[i].fnPtr)) 3381 { 3382 retval = JNI_ERR; 3383 } 3384 } 3385 3386 JNI_EXIT(); 3387 return retval; 3388 } 3389 3390 /* 3391 * Un-register all native methods associated with the class. 3392 * 3393 * The JNI docs refer to this as a way to reload/relink native libraries, 3394 * and say it "should not be used in normal native code". In particular, 3395 * there is no need to do this during shutdown, and you do not need to do 3396 * this before redefining a method implementation with RegisterNatives. 3397 * 3398 * It's chiefly useful for a native "plugin"-style library that wasn't 3399 * loaded with System.loadLibrary() (since there's no way to unload those). 3400 * For example, the library could upgrade itself by: 3401 * 3402 * 1. call UnregisterNatives to unbind the old methods 3403 * 2. ensure that no code is still executing inside it (somehow) 3404 * 3. dlclose() the library 3405 * 4. dlopen() the new library 3406 * 5. use RegisterNatives to bind the methods from the new library 3407 * 3408 * The above can work correctly without the UnregisterNatives call, but 3409 * creates a window of opportunity in which somebody might try to call a 3410 * method that is pointing at unmapped memory, crashing the VM. In theory 3411 * the same guards that prevent dlclose() from unmapping executing code could 3412 * prevent that anyway, but with this we can be more thorough and also deal 3413 * with methods that only exist in the old or new form of the library (maybe 3414 * the lib wants to try the call and catch the UnsatisfiedLinkError). 3415 */ 3416 static jint UnregisterNatives(JNIEnv* env, jclass jclazz) 3417 { 3418 JNI_ENTER(); 3419 3420 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 3421 if (gDvm.verboseJni) { 3422 LOGI("[Unregistering JNI native methods for class %s]\n", 3423 clazz->descriptor); 3424 } 3425 dvmUnregisterJNINativeMethods(clazz); 3426 3427 JNI_EXIT(); 3428 return JNI_OK; 3429 } 3430 3431 /* 3432 * Lock the monitor. 3433 * 3434 * We have to track all monitor enters and exits, so that we can undo any 3435 * outstanding synchronization before the thread exits. 3436 */ 3437 static jint MonitorEnter(JNIEnv* env, jobject jobj) 3438 { 3439 JNI_ENTER(); 3440 Object* obj = dvmDecodeIndirectRef(env, jobj); 3441 dvmLockObject(_self, obj); 3442 trackMonitorEnter(_self, obj); 3443 JNI_EXIT(); 3444 return JNI_OK; 3445 } 3446 3447 /* 3448 * Unlock the monitor. 3449 * 3450 * Throws an IllegalMonitorStateException if the current thread 3451 * doesn't own the monitor. (dvmUnlockObject() takes care of the throw.) 3452 * 3453 * According to the 1.6 spec, it's legal to call here with an exception 3454 * pending. If this fails, we'll stomp the original exception. 3455 */ 3456 static jint MonitorExit(JNIEnv* env, jobject jobj) 3457 { 3458 JNI_ENTER(); 3459 Object* obj = dvmDecodeIndirectRef(env, jobj); 3460 bool success = dvmUnlockObject(_self, obj); 3461 if (success) 3462 trackMonitorExit(_self, obj); 3463 JNI_EXIT(); 3464 return success ? JNI_OK : JNI_ERR; 3465 } 3466 3467 /* 3468 * Return the JavaVM interface associated with the current thread. 3469 */ 3470 static jint GetJavaVM(JNIEnv* env, JavaVM** vm) 3471 { 3472 JNI_ENTER(); 3473 //*vm = gDvm.vmList; 3474 *vm = (JavaVM*) ((JNIEnvExt*)env)->vm; 3475 JNI_EXIT(); 3476 if (*vm == NULL) 3477 return JNI_ERR; 3478 else 3479 return JNI_OK; 3480 } 3481 3482 /* 3483 * Copies "len" Unicode characters, from offset "start". 3484 */ 3485 static void GetStringRegion(JNIEnv* env, jstring jstr, jsize start, jsize len, 3486 jchar* buf) 3487 { 3488 JNI_ENTER(); 3489 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); 3490 if (start + len > dvmStringLen(strObj)) 3491 dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL); 3492 else 3493 memcpy(buf, dvmStringChars(strObj) + start, len * sizeof(u2)); 3494 JNI_EXIT(); 3495 } 3496 3497 /* 3498 * Translates "len" Unicode characters, from offset "start", into 3499 * modified UTF-8 encoding. 3500 */ 3501 static void GetStringUTFRegion(JNIEnv* env, jstring jstr, jsize start, 3502 jsize len, char* buf) 3503 { 3504 JNI_ENTER(); 3505 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); 3506 if (start + len > dvmStringLen(strObj)) 3507 dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL); 3508 else 3509 dvmCreateCstrFromStringRegion(strObj, start, len, buf); 3510 JNI_EXIT(); 3511 } 3512 3513 /* 3514 * Get a raw pointer to array data. 3515 * 3516 * The caller is expected to call "release" before doing any JNI calls 3517 * or blocking I/O operations. 3518 * 3519 * We need to pin the memory or block GC. 3520 */ 3521 static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray jarr, 3522 jboolean* isCopy) 3523 { 3524 JNI_ENTER(); 3525 void* data; 3526 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr); 3527 pinPrimitiveArray(arrayObj); 3528 data = arrayObj->contents; 3529 if (isCopy != NULL) 3530 *isCopy = JNI_FALSE; 3531 JNI_EXIT(); 3532 return data; 3533 } 3534 3535 /* 3536 * Release an array obtained with GetPrimitiveArrayCritical. 3537 */ 3538 static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray jarr, 3539 void* carray, jint mode) 3540 { 3541 JNI_ENTER(); 3542 if (mode != JNI_COMMIT) { 3543 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr); 3544 unpinPrimitiveArray(arrayObj); 3545 } 3546 JNI_EXIT(); 3547 } 3548 3549 /* 3550 * Like GetStringChars, but with restricted use. 3551 */ 3552 static const jchar* GetStringCritical(JNIEnv* env, jstring jstr, 3553 jboolean* isCopy) 3554 { 3555 JNI_ENTER(); 3556 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); 3557 ArrayObject* strChars = dvmStringCharArray(strObj); 3558 3559 pinPrimitiveArray(strChars); 3560 3561 const u2* data = dvmStringChars(strObj); 3562 if (isCopy != NULL) 3563 *isCopy = JNI_FALSE; 3564 3565 JNI_EXIT(); 3566 return (jchar*)data; 3567 } 3568 3569 /* 3570 * Like ReleaseStringChars, but with restricted use. 3571 */ 3572 static void ReleaseStringCritical(JNIEnv* env, jstring jstr, 3573 const jchar* carray) 3574 { 3575 JNI_ENTER(); 3576 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); 3577 ArrayObject* strChars = dvmStringCharArray(strObj); 3578 unpinPrimitiveArray(strChars); 3579 JNI_EXIT(); 3580 } 3581 3582 /* 3583 * Create a new weak global reference. 3584 */ 3585 static jweak NewWeakGlobalRef(JNIEnv* env, jobject obj) 3586 { 3587 JNI_ENTER(); 3588 jweak wref = createWeakGlobalRef(env, obj); 3589 JNI_EXIT(); 3590 return wref; 3591 } 3592 3593 /* 3594 * Delete the specified weak global reference. 3595 */ 3596 static void DeleteWeakGlobalRef(JNIEnv* env, jweak wref) 3597 { 3598 JNI_ENTER(); 3599 deleteWeakGlobalRef(env, wref); 3600 JNI_EXIT(); 3601 } 3602 3603 /* 3604 * Quick check for pending exceptions. 3605 * 3606 * TODO: we should be able to skip the enter/exit macros here. 3607 */ 3608 static jboolean ExceptionCheck(JNIEnv* env) 3609 { 3610 JNI_ENTER(); 3611 bool result = dvmCheckException(_self); 3612 JNI_EXIT(); 3613 return result; 3614 } 3615 3616 /* 3617 * Returns the type of the object referred to by "obj". It can be local, 3618 * global, or weak global. 3619 * 3620 * In the current implementation, references can be global and local at 3621 * the same time, so while the return value is accurate it may not tell 3622 * the whole story. 3623 */ 3624 static jobjectRefType GetObjectRefType(JNIEnv* env, jobject jobj) 3625 { 3626 JNI_ENTER(); 3627 jobjectRefType type = dvmGetJNIRefType(env, jobj); 3628 JNI_EXIT(); 3629 return type; 3630 } 3631 3632 /* 3633 * Allocate and return a new java.nio.ByteBuffer for this block of memory. 3634 * 3635 * "address" may not be NULL, and "capacity" must be > 0. (These are only 3636 * verified when CheckJNI is enabled.) 3637 */ 3638 static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity) 3639 { 3640 JNI_ENTER(); 3641 3642 Thread* self = _self /*dvmThreadSelf()*/; 3643 Object* platformAddress = NULL; 3644 JValue callResult; 3645 jobject result = NULL; 3646 ClassObject* tmpClazz; 3647 3648 tmpClazz = gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on->clazz; 3649 if (!dvmIsClassInitialized(tmpClazz) && !dvmInitClass(tmpClazz)) 3650 goto bail; 3651 3652 /* get an instance of PlatformAddress that wraps the provided address */ 3653 dvmCallMethod(self, 3654 gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on, 3655 NULL, &callResult, address); 3656 if (dvmGetException(self) != NULL || callResult.l == NULL) 3657 goto bail; 3658 3659 /* don't let the GC discard it */ 3660 platformAddress = (Object*) callResult.l; 3661 dvmAddTrackedAlloc(platformAddress, self); 3662 LOGV("tracking %p for address=%p\n", platformAddress, address); 3663 3664 /* create an instance of java.nio.ReadWriteDirectByteBuffer */ 3665 tmpClazz = gDvm.classJavaNioReadWriteDirectByteBuffer; 3666 if (!dvmIsClassInitialized(tmpClazz) && !dvmInitClass(tmpClazz)) 3667 goto bail; 3668 Object* newObj = dvmAllocObject(tmpClazz, ALLOC_DONT_TRACK); 3669 if (newObj != NULL) { 3670 /* call the (PlatformAddress, int, int) constructor */ 3671 result = addLocalReference(env, newObj); 3672 dvmCallMethod(self, gDvm.methJavaNioReadWriteDirectByteBuffer_init, 3673 newObj, &callResult, platformAddress, (jint) capacity, (jint) 0); 3674 if (dvmGetException(self) != NULL) { 3675 deleteLocalReference(env, result); 3676 result = NULL; 3677 goto bail; 3678 } 3679 } 3680 3681 bail: 3682 if (platformAddress != NULL) 3683 dvmReleaseTrackedAlloc(platformAddress, self); 3684 JNI_EXIT(); 3685 return result; 3686 } 3687 3688 /* 3689 * Get the starting address of the buffer for the specified java.nio.Buffer. 3690 * 3691 * If this is not a "direct" buffer, we return NULL. 3692 */ 3693 static void* GetDirectBufferAddress(JNIEnv* env, jobject jbuf) 3694 { 3695 JNI_ENTER(); 3696 3697 Object* bufObj = dvmDecodeIndirectRef(env, jbuf); 3698 Thread* self = _self /*dvmThreadSelf()*/; 3699 void* result; 3700 3701 /* 3702 * All Buffer objects have an effectiveDirectAddress field. If it's 3703 * nonzero, we can just return that value. If not, we have to call 3704 * through DirectBuffer.getEffectiveAddress(), which as a side-effect 3705 * will set the effectiveDirectAddress field for direct buffers (and 3706 * things that wrap direct buffers). 3707 */ 3708 result = (void*) dvmGetFieldInt(bufObj, 3709 gDvm.offJavaNioBuffer_effectiveDirectAddress); 3710 if (result != NULL) { 3711 //LOGI("fast path for %p\n", buf); 3712 goto bail; 3713 } 3714 3715 /* 3716 * Start by determining if the object supports the DirectBuffer 3717 * interfaces. Note this does not guarantee that it's a direct buffer. 3718 */ 3719 if (!dvmInstanceof(bufObj->clazz, 3720 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer)) 3721 { 3722 goto bail; 3723 } 3724 3725 /* 3726 * Get a PlatformAddress object with the effective address. 3727 * 3728 * If this isn't a direct buffer, the result will be NULL and/or an 3729 * exception will have been thrown. 3730 */ 3731 JValue callResult; 3732 const Method* meth = dvmGetVirtualizedMethod(bufObj->clazz, 3733 gDvm.methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress); 3734 dvmCallMethodA(self, meth, bufObj, false, &callResult, NULL); 3735 if (dvmGetException(self) != NULL) { 3736 dvmClearException(self); 3737 callResult.l = NULL; 3738 } 3739 3740 Object* platformAddr = callResult.l; 3741 if (platformAddr == NULL) { 3742 LOGV("Got request for address of non-direct buffer\n"); 3743 goto bail; 3744 } 3745 3746 /* 3747 * Extract the address from the PlatformAddress object. Instead of 3748 * calling the toLong() method, just grab the field directly. This 3749 * is faster but more fragile. 3750 */ 3751 result = (void*) dvmGetFieldInt(platformAddr, 3752 gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr); 3753 3754 //LOGI("slow path for %p --> %p\n", buf, result); 3755 3756 bail: 3757 JNI_EXIT(); 3758 return result; 3759 } 3760 3761 /* 3762 * Get the capacity of the buffer for the specified java.nio.Buffer. 3763 * 3764 * Returns -1 if the object is not a direct buffer. (We actually skip 3765 * this check, since it's expensive to determine, and just return the 3766 * capacity regardless.) 3767 */ 3768 static jlong GetDirectBufferCapacity(JNIEnv* env, jobject jbuf) 3769 { 3770 JNI_ENTER(); 3771 3772 /* 3773 * The capacity is always in the Buffer.capacity field. 3774 * 3775 * (The "check" version should verify that this is actually a Buffer, 3776 * but we're not required to do so here.) 3777 */ 3778 Object* buf = dvmDecodeIndirectRef(env, jbuf); 3779 jlong result = dvmGetFieldInt(buf, gDvm.offJavaNioBuffer_capacity); 3780 3781 JNI_EXIT(); 3782 return result; 3783 } 3784 3785 3786 /* 3787 * =========================================================================== 3788 * JNI invocation functions 3789 * =========================================================================== 3790 */ 3791 3792 /* 3793 * Handle AttachCurrentThread{AsDaemon}. 3794 * 3795 * We need to make sure the VM is actually running. For example, if we start 3796 * up, issue an Attach, and the VM exits almost immediately, by the time the 3797 * attaching happens the VM could already be shutting down. 3798 * 3799 * It's hard to avoid a race condition here because we don't want to hold 3800 * a lock across the entire operation. What we can do is temporarily 3801 * increment the thread count to prevent a VM exit. 3802 * 3803 * This could potentially still have problems if a daemon thread calls here 3804 * while the VM is shutting down. dvmThreadSelf() will work, since it just 3805 * uses pthread TLS, but dereferencing "vm" could fail. Such is life when 3806 * you shut down a VM while threads are still running inside it. 3807 * 3808 * Remember that some code may call this as a way to find the per-thread 3809 * JNIEnv pointer. Don't do excess work for that case. 3810 */ 3811 static jint attachThread(JavaVM* vm, JNIEnv** p_env, void* thr_args, 3812 bool isDaemon) 3813 { 3814 JavaVMAttachArgs* args = (JavaVMAttachArgs*) thr_args; 3815 Thread* self; 3816 bool result = false; 3817 3818 /* 3819 * Return immediately if we're already one with the VM. 3820 */ 3821 self = dvmThreadSelf(); 3822 if (self != NULL) { 3823 *p_env = self->jniEnv; 3824 return JNI_OK; 3825 } 3826 3827 /* 3828 * No threads allowed in zygote mode. 3829 */ 3830 if (gDvm.zygote) { 3831 return JNI_ERR; 3832 } 3833 3834 /* increment the count to keep the VM from bailing while we run */ 3835 dvmLockThreadList(NULL); 3836 if (gDvm.nonDaemonThreadCount == 0) { 3837 // dead or dying 3838 LOGV("Refusing to attach thread '%s' -- VM is shutting down\n", 3839 (thr_args == NULL) ? "(unknown)" : args->name); 3840 dvmUnlockThreadList(); 3841 return JNI_ERR; 3842 } 3843 gDvm.nonDaemonThreadCount++; 3844 dvmUnlockThreadList(); 3845 3846 /* tweak the JavaVMAttachArgs as needed */ 3847 JavaVMAttachArgs argsCopy; 3848 if (args == NULL) { 3849 /* allow the v1.1 calling convention */ 3850 argsCopy.version = JNI_VERSION_1_2; 3851 argsCopy.name = NULL; 3852 argsCopy.group = dvmGetMainThreadGroup(); 3853 } else { 3854 assert(args->version >= JNI_VERSION_1_2); 3855 3856 argsCopy.version = args->version; 3857 argsCopy.name = args->name; 3858 if (args->group != NULL) 3859 argsCopy.group = args->group; 3860 else 3861 argsCopy.group = dvmGetMainThreadGroup(); 3862 } 3863 3864 result = dvmAttachCurrentThread(&argsCopy, isDaemon); 3865 3866 /* restore the count */ 3867 dvmLockThreadList(NULL); 3868 gDvm.nonDaemonThreadCount--; 3869 dvmUnlockThreadList(); 3870 3871 /* 3872 * Change the status to indicate that we're out in native code. This 3873 * call is not guarded with state-change macros, so we have to do it 3874 * by hand. 3875 */ 3876 if (result) { 3877 self = dvmThreadSelf(); 3878 assert(self != NULL); 3879 dvmChangeStatus(self, THREAD_NATIVE); 3880 *p_env = self->jniEnv; 3881 return JNI_OK; 3882 } else { 3883 return JNI_ERR; 3884 } 3885 } 3886 3887 /* 3888 * Attach the current thread to the VM. If the thread is already attached, 3889 * this is a no-op. 3890 */ 3891 static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args) 3892 { 3893 return attachThread(vm, p_env, thr_args, false); 3894 } 3895 3896 /* 3897 * Like AttachCurrentThread, but set the "daemon" flag. 3898 */ 3899 static jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env, 3900 void* thr_args) 3901 { 3902 return attachThread(vm, p_env, thr_args, true); 3903 } 3904 3905 /* 3906 * Dissociate the current thread from the VM. 3907 */ 3908 static jint DetachCurrentThread(JavaVM* vm) 3909 { 3910 Thread* self = dvmThreadSelf(); 3911 3912 if (self == NULL) /* not attached, can't do anything */ 3913 return JNI_ERR; 3914 3915 /* switch to "running" to check for suspension */ 3916 dvmChangeStatus(self, THREAD_RUNNING); 3917 3918 /* detach the thread */ 3919 dvmDetachCurrentThread(); 3920 3921 /* (no need to change status back -- we have no status) */ 3922 return JNI_OK; 3923 } 3924 3925 /* 3926 * If current thread is attached to VM, return the associated JNIEnv. 3927 * Otherwise, stuff NULL in and return JNI_EDETACHED. 3928 * 3929 * JVMTI overloads this by specifying a magic value for "version", so we 3930 * do want to check that here. 3931 */ 3932 static jint GetEnv(JavaVM* vm, void** env, jint version) 3933 { 3934 Thread* self = dvmThreadSelf(); 3935 3936 if (version < JNI_VERSION_1_1 || version > JNI_VERSION_1_6) 3937 return JNI_EVERSION; 3938 3939 if (self == NULL) { 3940 *env = NULL; 3941 } else { 3942 /* TODO: status change is probably unnecessary */ 3943 dvmChangeStatus(self, THREAD_RUNNING); 3944 *env = (void*) dvmGetThreadJNIEnv(self); 3945 dvmChangeStatus(self, THREAD_NATIVE); 3946 } 3947 if (*env == NULL) 3948 return JNI_EDETACHED; 3949 else 3950 return JNI_OK; 3951 } 3952 3953 /* 3954 * Destroy the VM. This may be called from any thread. 3955 * 3956 * If the current thread is attached, wait until the current thread is 3957 * the only non-daemon user-level thread. If the current thread is not 3958 * attached, we attach it and do the processing as usual. (If the attach 3959 * fails, it's probably because all the non-daemon threads have already 3960 * exited and the VM doesn't want to let us back in.) 3961 * 3962 * TODO: we don't really deal with the situation where more than one thread 3963 * has called here. One thread wins, the other stays trapped waiting on 3964 * the condition variable forever. Not sure this situation is interesting 3965 * in real life. 3966 */ 3967 static jint DestroyJavaVM(JavaVM* vm) 3968 { 3969 JavaVMExt* ext = (JavaVMExt*) vm; 3970 Thread* self; 3971 3972 if (ext == NULL) 3973 return JNI_ERR; 3974 3975 if (gDvm.verboseShutdown) 3976 LOGD("DestroyJavaVM waiting for non-daemon threads to exit\n"); 3977 3978 /* 3979 * Sleep on a condition variable until it's okay to exit. 3980 */ 3981 self = dvmThreadSelf(); 3982 if (self == NULL) { 3983 JNIEnv* tmpEnv; 3984 if (AttachCurrentThread(vm, &tmpEnv, NULL) != JNI_OK) { 3985 LOGV("Unable to reattach main for Destroy; assuming VM is " 3986 "shutting down (count=%d)\n", 3987 gDvm.nonDaemonThreadCount); 3988 goto shutdown; 3989 } else { 3990 LOGV("Attached to wait for shutdown in Destroy\n"); 3991 } 3992 } 3993 dvmChangeStatus(self, THREAD_VMWAIT); 3994 3995 dvmLockThreadList(self); 3996 gDvm.nonDaemonThreadCount--; // remove current thread from count 3997 3998 while (gDvm.nonDaemonThreadCount > 0) 3999 pthread_cond_wait(&gDvm.vmExitCond, &gDvm.threadListLock); 4000 4001 dvmUnlockThreadList(); 4002 self = NULL; 4003 4004 shutdown: 4005 // TODO: call System.exit() to run any registered shutdown hooks 4006 // (this may not return -- figure out how this should work) 4007 4008 if (gDvm.verboseShutdown) 4009 LOGD("DestroyJavaVM shutting VM down\n"); 4010 dvmShutdown(); 4011 4012 // TODO - free resources associated with JNI-attached daemon threads 4013 free(ext->envList); 4014 free(ext); 4015 4016 return JNI_OK; 4017 } 4018 4019 4020 /* 4021 * =========================================================================== 4022 * Function tables 4023 * =========================================================================== 4024 */ 4025 4026 static const struct JNINativeInterface gNativeInterface = { 4027 NULL, 4028 NULL, 4029 NULL, 4030 NULL, 4031 4032 GetVersion, 4033 4034 DefineClass, 4035 FindClass, 4036 4037 FromReflectedMethod, 4038 FromReflectedField, 4039 ToReflectedMethod, 4040 4041 GetSuperclass, 4042 IsAssignableFrom, 4043 4044 ToReflectedField, 4045 4046 Throw, 4047 ThrowNew, 4048 ExceptionOccurred, 4049 ExceptionDescribe, 4050 ExceptionClear, 4051 FatalError, 4052 4053 PushLocalFrame, 4054 PopLocalFrame, 4055 4056 NewGlobalRef, 4057 DeleteGlobalRef, 4058 DeleteLocalRef, 4059 IsSameObject, 4060 NewLocalRef, 4061 EnsureLocalCapacity, 4062 4063 AllocObject, 4064 NewObject, 4065 NewObjectV, 4066 NewObjectA, 4067 4068 GetObjectClass, 4069 IsInstanceOf, 4070 4071 GetMethodID, 4072 4073 CallObjectMethod, 4074 CallObjectMethodV, 4075 CallObjectMethodA, 4076 CallBooleanMethod, 4077 CallBooleanMethodV, 4078 CallBooleanMethodA, 4079 CallByteMethod, 4080 CallByteMethodV, 4081 CallByteMethodA, 4082 CallCharMethod, 4083 CallCharMethodV, 4084 CallCharMethodA, 4085 CallShortMethod, 4086 CallShortMethodV, 4087 CallShortMethodA, 4088 CallIntMethod, 4089 CallIntMethodV, 4090 CallIntMethodA, 4091 CallLongMethod, 4092 CallLongMethodV, 4093 CallLongMethodA, 4094 CallFloatMethod, 4095 CallFloatMethodV, 4096 CallFloatMethodA, 4097 CallDoubleMethod, 4098 CallDoubleMethodV, 4099 CallDoubleMethodA, 4100 CallVoidMethod, 4101 CallVoidMethodV, 4102 CallVoidMethodA, 4103 4104 CallNonvirtualObjectMethod, 4105 CallNonvirtualObjectMethodV, 4106 CallNonvirtualObjectMethodA, 4107 CallNonvirtualBooleanMethod, 4108 CallNonvirtualBooleanMethodV, 4109 CallNonvirtualBooleanMethodA, 4110 CallNonvirtualByteMethod, 4111 CallNonvirtualByteMethodV, 4112 CallNonvirtualByteMethodA, 4113 CallNonvirtualCharMethod, 4114 CallNonvirtualCharMethodV, 4115 CallNonvirtualCharMethodA, 4116 CallNonvirtualShortMethod, 4117 CallNonvirtualShortMethodV, 4118 CallNonvirtualShortMethodA, 4119 CallNonvirtualIntMethod, 4120 CallNonvirtualIntMethodV, 4121 CallNonvirtualIntMethodA, 4122 CallNonvirtualLongMethod, 4123 CallNonvirtualLongMethodV, 4124 CallNonvirtualLongMethodA, 4125 CallNonvirtualFloatMethod, 4126 CallNonvirtualFloatMethodV, 4127 CallNonvirtualFloatMethodA, 4128 CallNonvirtualDoubleMethod, 4129 CallNonvirtualDoubleMethodV, 4130 CallNonvirtualDoubleMethodA, 4131 CallNonvirtualVoidMethod, 4132 CallNonvirtualVoidMethodV, 4133 CallNonvirtualVoidMethodA, 4134 4135 GetFieldID, 4136 4137 GetObjectField, 4138 GetBooleanField, 4139 GetByteField, 4140 GetCharField, 4141 GetShortField, 4142 GetIntField, 4143 GetLongField, 4144 GetFloatField, 4145 GetDoubleField, 4146 SetObjectField, 4147 SetBooleanField, 4148 SetByteField, 4149 SetCharField, 4150 SetShortField, 4151 SetIntField, 4152 SetLongField, 4153 SetFloatField, 4154 SetDoubleField, 4155 4156 GetStaticMethodID, 4157 4158 CallStaticObjectMethod, 4159 CallStaticObjectMethodV, 4160 CallStaticObjectMethodA, 4161 CallStaticBooleanMethod, 4162 CallStaticBooleanMethodV, 4163 CallStaticBooleanMethodA, 4164 CallStaticByteMethod, 4165 CallStaticByteMethodV, 4166 CallStaticByteMethodA, 4167 CallStaticCharMethod, 4168 CallStaticCharMethodV, 4169 CallStaticCharMethodA, 4170 CallStaticShortMethod, 4171 CallStaticShortMethodV, 4172 CallStaticShortMethodA, 4173 CallStaticIntMethod, 4174 CallStaticIntMethodV, 4175 CallStaticIntMethodA, 4176 CallStaticLongMethod, 4177 CallStaticLongMethodV, 4178 CallStaticLongMethodA, 4179 CallStaticFloatMethod, 4180 CallStaticFloatMethodV, 4181 CallStaticFloatMethodA, 4182 CallStaticDoubleMethod, 4183 CallStaticDoubleMethodV, 4184 CallStaticDoubleMethodA, 4185 CallStaticVoidMethod, 4186 CallStaticVoidMethodV, 4187 CallStaticVoidMethodA, 4188 4189 GetStaticFieldID, 4190 4191 GetStaticObjectField, 4192 GetStaticBooleanField, 4193 GetStaticByteField, 4194 GetStaticCharField, 4195 GetStaticShortField, 4196 GetStaticIntField, 4197 GetStaticLongField, 4198 GetStaticFloatField, 4199 GetStaticDoubleField, 4200 4201 SetStaticObjectField, 4202 SetStaticBooleanField, 4203 SetStaticByteField, 4204 SetStaticCharField, 4205 SetStaticShortField, 4206 SetStaticIntField, 4207 SetStaticLongField, 4208 SetStaticFloatField, 4209 SetStaticDoubleField, 4210 4211 NewString, 4212 4213 GetStringLength, 4214 GetStringChars, 4215 ReleaseStringChars, 4216 4217 NewStringUTF, 4218 GetStringUTFLength, 4219 GetStringUTFChars, 4220 ReleaseStringUTFChars, 4221 4222 GetArrayLength, 4223 NewObjectArray, 4224 GetObjectArrayElement, 4225 SetObjectArrayElement, 4226 4227 NewBooleanArray, 4228 NewByteArray, 4229 NewCharArray, 4230 NewShortArray, 4231 NewIntArray, 4232 NewLongArray, 4233 NewFloatArray, 4234 NewDoubleArray, 4235 4236 GetBooleanArrayElements, 4237 GetByteArrayElements, 4238 GetCharArrayElements, 4239 GetShortArrayElements, 4240 GetIntArrayElements, 4241 GetLongArrayElements, 4242 GetFloatArrayElements, 4243 GetDoubleArrayElements, 4244 4245 ReleaseBooleanArrayElements, 4246 ReleaseByteArrayElements, 4247 ReleaseCharArrayElements, 4248 ReleaseShortArrayElements, 4249 ReleaseIntArrayElements, 4250 ReleaseLongArrayElements, 4251 ReleaseFloatArrayElements, 4252 ReleaseDoubleArrayElements, 4253 4254 GetBooleanArrayRegion, 4255 GetByteArrayRegion, 4256 GetCharArrayRegion, 4257 GetShortArrayRegion, 4258 GetIntArrayRegion, 4259 GetLongArrayRegion, 4260 GetFloatArrayRegion, 4261 GetDoubleArrayRegion, 4262 SetBooleanArrayRegion, 4263 SetByteArrayRegion, 4264 SetCharArrayRegion, 4265 SetShortArrayRegion, 4266 SetIntArrayRegion, 4267 SetLongArrayRegion, 4268 SetFloatArrayRegion, 4269 SetDoubleArrayRegion, 4270 4271 RegisterNatives, 4272 UnregisterNatives, 4273 4274 MonitorEnter, 4275 MonitorExit, 4276 4277 GetJavaVM, 4278 4279 GetStringRegion, 4280 GetStringUTFRegion, 4281 4282 GetPrimitiveArrayCritical, 4283 ReleasePrimitiveArrayCritical, 4284 4285 GetStringCritical, 4286 ReleaseStringCritical, 4287 4288 NewWeakGlobalRef, 4289 DeleteWeakGlobalRef, 4290 4291 ExceptionCheck, 4292 4293 NewDirectByteBuffer, 4294 GetDirectBufferAddress, 4295 GetDirectBufferCapacity, 4296 4297 GetObjectRefType 4298 }; 4299 static const struct JNIInvokeInterface gInvokeInterface = { 4300 NULL, 4301 NULL, 4302 NULL, 4303 4304 DestroyJavaVM, 4305 AttachCurrentThread, 4306 DetachCurrentThread, 4307 4308 GetEnv, 4309 4310 AttachCurrentThreadAsDaemon, 4311 }; 4312 4313 4314 /* 4315 * =========================================================================== 4316 * VM/Env creation 4317 * =========================================================================== 4318 */ 4319 4320 /* 4321 * Enable "checked JNI" after the VM has partially started. This must 4322 * only be called in "zygote" mode, when we have one thread running. 4323 * 4324 * This doesn't attempt to rewrite the JNI call bridge associated with 4325 * native methods, so we won't get those checks for any methods that have 4326 * already been resolved. 4327 */ 4328 void dvmLateEnableCheckedJni(void) 4329 { 4330 JNIEnvExt* extEnv; 4331 JavaVMExt* extVm; 4332 4333 extEnv = dvmGetJNIEnvForThread(); 4334 if (extEnv == NULL) { 4335 LOGE("dvmLateEnableCheckedJni: thread has no JNIEnv\n"); 4336 return; 4337 } 4338 extVm = extEnv->vm; 4339 assert(extVm != NULL); 4340 4341 if (!extVm->useChecked) { 4342 LOGD("Late-enabling CheckJNI\n"); 4343 dvmUseCheckedJniVm(extVm); 4344 extVm->useChecked = true; 4345 dvmUseCheckedJniEnv(extEnv); 4346 4347 /* currently no way to pick up jniopts features */ 4348 } else { 4349 LOGD("Not late-enabling CheckJNI (already on)\n"); 4350 } 4351 } 4352 4353 /* 4354 * Not supported. 4355 */ 4356 jint JNI_GetDefaultJavaVMInitArgs(void* vm_args) 4357 { 4358 return JNI_ERR; 4359 } 4360 4361 /* 4362 * Return a buffer full of created VMs. 4363 * 4364 * We always have zero or one. 4365 */ 4366 jint JNI_GetCreatedJavaVMs(JavaVM** vmBuf, jsize bufLen, jsize* nVMs) 4367 { 4368 if (gDvm.vmList != NULL) { 4369 *nVMs = 1; 4370 4371 if (bufLen > 0) 4372 *vmBuf++ = gDvm.vmList; 4373 } else { 4374 *nVMs = 0; 4375 } 4376 4377 return JNI_OK; 4378 } 4379 4380 4381 /* 4382 * Create a new VM instance. 4383 * 4384 * The current thread becomes the main VM thread. We return immediately, 4385 * which effectively means the caller is executing in a native method. 4386 */ 4387 jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) 4388 { 4389 const JavaVMInitArgs* args = (JavaVMInitArgs*) vm_args; 4390 JNIEnvExt* pEnv = NULL; 4391 JavaVMExt* pVM = NULL; 4392 const char** argv; 4393 int argc = 0; 4394 int i, curOpt; 4395 int result = JNI_ERR; 4396 bool checkJni = false; 4397 bool warnError = true; 4398 bool forceDataCopy = false; 4399 4400 if (args->version < JNI_VERSION_1_2) 4401 return JNI_EVERSION; 4402 4403 // TODO: don't allow creation of multiple VMs -- one per customer for now 4404 4405 /* zero globals; not strictly necessary the first time a VM is started */ 4406 memset(&gDvm, 0, sizeof(gDvm)); 4407 4408 /* 4409 * Set up structures for JNIEnv and VM. 4410 */ 4411 //pEnv = (JNIEnvExt*) malloc(sizeof(JNIEnvExt)); 4412 pVM = (JavaVMExt*) malloc(sizeof(JavaVMExt)); 4413 4414 //memset(pEnv, 0, sizeof(JNIEnvExt)); 4415 //pEnv->funcTable = &gNativeInterface; 4416 //pEnv->vm = pVM; 4417 memset(pVM, 0, sizeof(JavaVMExt)); 4418 pVM->funcTable = &gInvokeInterface; 4419 pVM->envList = pEnv; 4420 dvmInitMutex(&pVM->envListLock); 4421 4422 argv = (const char**) malloc(sizeof(char*) * (args->nOptions)); 4423 memset(argv, 0, sizeof(char*) * (args->nOptions)); 4424 4425 curOpt = 0; 4426 4427 /* 4428 * Convert JNI args to argv. 4429 * 4430 * We have to pull out vfprintf/exit/abort, because they use the 4431 * "extraInfo" field to pass function pointer "hooks" in. We also 4432 * look for the -Xcheck:jni stuff here. 4433 */ 4434 for (i = 0; i < args->nOptions; i++) { 4435 const char* optStr = args->options[i].optionString; 4436 4437 if (optStr == NULL) { 4438 fprintf(stderr, "ERROR: arg %d string was null\n", i); 4439 goto bail; 4440 } else if (strcmp(optStr, "vfprintf") == 0) { 4441 gDvm.vfprintfHook = args->options[i].extraInfo; 4442 } else if (strcmp(optStr, "exit") == 0) { 4443 gDvm.exitHook = args->options[i].extraInfo; 4444 } else if (strcmp(optStr, "abort") == 0) { 4445 gDvm.abortHook = args->options[i].extraInfo; 4446 } else if (strcmp(optStr, "-Xcheck:jni") == 0) { 4447 checkJni = true; 4448 } else if (strncmp(optStr, "-Xjniopts:", 10) == 0) { 4449 const char* jniOpts = optStr + 9; 4450 while (jniOpts != NULL) { 4451 jniOpts++; /* skip past ':' or ',' */ 4452 if (strncmp(jniOpts, "warnonly", 8) == 0) { 4453 warnError = false; 4454 } else if (strncmp(jniOpts, "forcecopy", 9) == 0) { 4455 forceDataCopy = true; 4456 } else { 4457 LOGW("unknown jni opt starting at '%s'\n", jniOpts); 4458 } 4459 jniOpts = strchr(jniOpts, ','); 4460 } 4461 } else { 4462 /* regular option */ 4463 argv[curOpt++] = optStr; 4464 } 4465 } 4466 argc = curOpt; 4467 4468 if (checkJni) { 4469 dvmUseCheckedJniVm(pVM); 4470 pVM->useChecked = true; 4471 } 4472 pVM->warnError = warnError; 4473 pVM->forceDataCopy = forceDataCopy; 4474 4475 /* set this up before initializing VM, so it can create some JNIEnvs */ 4476 gDvm.vmList = (JavaVM*) pVM; 4477 4478 /* 4479 * Create an env for main thread. We need to have something set up 4480 * here because some of the class initialization we do when starting 4481 * up the VM will call into native code. 4482 */ 4483 pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL); 4484 4485 /* initialize VM */ 4486 gDvm.initializing = true; 4487 if (dvmStartup(argc, argv, args->ignoreUnrecognized, (JNIEnv*)pEnv) != 0) { 4488 free(pEnv); 4489 free(pVM); 4490 goto bail; 4491 } 4492 4493 /* 4494 * Success! Return stuff to caller. 4495 */ 4496 dvmChangeStatus(NULL, THREAD_NATIVE); 4497 *p_env = (JNIEnv*) pEnv; 4498 *p_vm = (JavaVM*) pVM; 4499 result = JNI_OK; 4500 4501 bail: 4502 gDvm.initializing = false; 4503 if (result == JNI_OK) 4504 LOGV("JNI_CreateJavaVM succeeded\n"); 4505 else 4506 LOGW("JNI_CreateJavaVM failed\n"); 4507 free(argv); 4508 return result; 4509 } 4510