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 bail: 863 dvmUnlockMutex(&gDvm.jniGlobalRefLock); 864 return jobj; 865 } 866 867 /* 868 * Remove a global reference. In most cases it's the entry most recently 869 * added, which makes this pretty quick. 870 * 871 * Thought: if it's not the most recent entry, just null it out. When we 872 * fill up, do a compaction pass before we expand the list. 873 */ 874 static void deleteGlobalReference(jobject jobj) 875 { 876 if (jobj == NULL) 877 return; 878 879 dvmLockMutex(&gDvm.jniGlobalRefLock); 880 881 #ifdef USE_INDIRECT_REF 882 if (!dvmRemoveFromIndirectRefTable(&gDvm.jniGlobalRefTable, 883 IRT_FIRST_SEGMENT, jobj)) 884 { 885 LOGW("JNI: DeleteGlobalRef(%p) failed to find entry\n", jobj); 886 goto bail; 887 } 888 889 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) { 890 int count = dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable); 891 // TODO: not quite right, need to subtract holes 892 if (count < gDvm.jniGlobalRefLoMark) { 893 LOGD("GREF has decreased to %d\n", count); 894 gDvm.jniGlobalRefHiMark -= kGrefWaterInterval; 895 gDvm.jniGlobalRefLoMark -= kGrefWaterInterval; 896 } 897 } 898 #else 899 if (!dvmRemoveFromReferenceTable(&gDvm.jniGlobalRefTable, 900 gDvm.jniGlobalRefTable.table, jobj)) 901 { 902 LOGW("JNI: DeleteGlobalRef(%p) failed to find entry (valid=%d)\n", 903 jobj, dvmIsValidObject((Object*) jobj)); 904 goto bail; 905 } 906 907 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) { 908 int count = dvmReferenceTableEntries(&gDvm.jniGlobalRefTable); 909 if (count < gDvm.jniGlobalRefLoMark) { 910 LOGD("GREF has decreased to %d\n", count); 911 gDvm.jniGlobalRefHiMark -= kGrefWaterInterval; 912 gDvm.jniGlobalRefLoMark -= kGrefWaterInterval; 913 } 914 } 915 #endif 916 917 bail: 918 dvmUnlockMutex(&gDvm.jniGlobalRefLock); 919 } 920 921 922 /* 923 * Get the "magic" JNI weak global ReferenceQueue. It's allocated on 924 * first use. 925 * 926 * Returns NULL with an exception raised if allocation fails. 927 */ 928 static Object* getWeakGlobalRefQueue(void) 929 { 930 /* use an indirect variable to avoid "type-punned pointer" complaints */ 931 Object** pGlobalQ = &gDvm.jniWeakGlobalRefQueue; 932 933 if (*pGlobalQ != NULL) 934 return *pGlobalQ; 935 936 ClassObject* clazz = dvmFindSystemClass("Ljava/lang/ref/ReferenceQueue;"); 937 if (clazz == NULL) { 938 LOGE("Unable to find java.lang.ref.ReferenceQueue"); 939 dvmAbort(); 940 } 941 942 /* 943 * Create an instance of ReferenceQueue. The object is never actually 944 * used for anything, so we don't need to call a constructor. (We could 945 * get away with using an instance of Object, but this is cleaner.) 946 */ 947 Object* queue = dvmAllocObject(clazz, ALLOC_DEFAULT); 948 if (queue == NULL) { 949 LOGW("Failed allocating weak global ref queue\n"); 950 assert(dvmCheckException(dvmThreadSelf())); 951 return NULL; 952 } 953 dvmReleaseTrackedAlloc(queue, NULL); 954 955 /* 956 * Save it, using atomic ops to ensure we don't double-up. The gDvm 957 * field is known to the GC. 958 */ 959 if (!ATOMIC_CMP_SWAP((int*) pGlobalQ, 0, (int) queue)) { 960 LOGD("WOW: lost race to create weak global ref queue\n"); 961 queue = *pGlobalQ; 962 } 963 964 return queue; 965 } 966 967 968 /* 969 * We create a PhantomReference that references the object, add a 970 * global reference to it, and then flip some bits before returning it. 971 * The last step ensures that we detect it as special and that only 972 * appropriate calls will accept it. 973 * 974 * On failure, returns NULL with an exception pending. 975 */ 976 static jweak createWeakGlobalRef(JNIEnv* env, jobject jobj) 977 { 978 if (jobj == NULL) 979 return NULL; 980 981 Thread* self = ((JNIEnvExt*)env)->self; 982 Object* obj = dvmDecodeIndirectRef(env, jobj); 983 Object* weakGlobalQueue = getWeakGlobalRefQueue(); 984 Object* phantomObj; 985 jobject phantomRef; 986 987 /* 988 * Allocate a PhantomReference, then call the constructor to set 989 * the referent and the reference queue. 990 * 991 * We use a "magic" reference queue that the GC knows about; it behaves 992 * more like a queueless WeakReference, clearing the referent and 993 * not calling enqueue(). 994 */ 995 if (!dvmIsClassInitialized(gDvm.classJavaLangRefPhantomReference)) 996 dvmInitClass(gDvm.classJavaLangRefPhantomReference); 997 phantomObj = dvmAllocObject(gDvm.classJavaLangRefPhantomReference, 998 ALLOC_DEFAULT); 999 if (phantomObj == NULL) { 1000 assert(dvmCheckException(self)); 1001 LOGW("Failed on WeakGlobalRef alloc\n"); 1002 return NULL; 1003 } 1004 1005 JValue unused; 1006 dvmCallMethod(self, gDvm.methJavaLangRefPhantomReference_init, phantomObj, 1007 &unused, jobj, weakGlobalQueue); 1008 dvmReleaseTrackedAlloc(phantomObj, self); 1009 1010 if (dvmCheckException(self)) { 1011 LOGW("PhantomReference init failed\n"); 1012 return NULL; 1013 } 1014 1015 LOGV("+++ WGR: created phantom ref %p for object %p\n", phantomObj, obj); 1016 1017 /* 1018 * Add it to the global reference table, and mangle the pointer. 1019 */ 1020 phantomRef = addGlobalReference(phantomObj); 1021 return dvmObfuscateWeakGlobalRef(phantomRef); 1022 } 1023 1024 /* 1025 * Delete the global reference that's keeping the PhantomReference around. 1026 * The PhantomReference will eventually be discarded by the GC. 1027 */ 1028 static void deleteWeakGlobalRef(JNIEnv* env, jweak wref) 1029 { 1030 if (wref == NULL) 1031 return; 1032 1033 jobject phantomRef = dvmNormalizeWeakGlobalRef(wref); 1034 deleteGlobalReference(phantomRef); 1035 } 1036 1037 /* 1038 * Extract the referent from a PhantomReference. Used for weak global 1039 * references. 1040 * 1041 * "jwobj" is a "mangled" WGR pointer. 1042 */ 1043 static Object* getPhantomReferent(JNIEnv* env, jweak jwobj) 1044 { 1045 jobject jobj = dvmNormalizeWeakGlobalRef(jwobj); 1046 Object* obj = dvmDecodeIndirectRef(env, jobj); 1047 1048 if (obj->clazz != gDvm.classJavaLangRefPhantomReference) { 1049 LOGE("%p is not a phantom reference (%s)\n", 1050 jwobj, obj->clazz->descriptor); 1051 return NULL; 1052 } 1053 1054 return dvmGetFieldObject(obj, gDvm.offJavaLangRefReference_referent); 1055 } 1056 1057 1058 /* 1059 * Objects don't currently move, so we just need to create a reference 1060 * that will ensure the array object isn't collected. 1061 * 1062 * We use a separate reference table, which is part of the GC root set. 1063 */ 1064 static void pinPrimitiveArray(ArrayObject* arrayObj) 1065 { 1066 if (arrayObj == NULL) 1067 return; 1068 1069 dvmLockMutex(&gDvm.jniPinRefLock); 1070 if (!dvmAddToReferenceTable(&gDvm.jniPinRefTable, (Object*)arrayObj)) { 1071 dvmDumpReferenceTable(&gDvm.jniPinRefTable, "JNI pinned array"); 1072 LOGE("Failed adding to JNI pinned array ref table (%d entries)\n", 1073 (int) dvmReferenceTableEntries(&gDvm.jniPinRefTable)); 1074 dvmDumpThread(dvmThreadSelf(), false); 1075 dvmAbort(); 1076 } 1077 1078 /* 1079 * If we're watching global ref usage, also keep an eye on these. 1080 * 1081 * The total number of pinned primitive arrays should be pretty small. 1082 * A single array should not be pinned more than once or twice; any 1083 * more than that is a strong indicator that a Release function is 1084 * not being called. 1085 */ 1086 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) { 1087 int count = 0; 1088 Object** ppObj = gDvm.jniPinRefTable.table; 1089 while (ppObj < gDvm.jniPinRefTable.nextEntry) { 1090 if (*ppObj++ == (Object*) arrayObj) 1091 count++; 1092 } 1093 1094 if (count > kPinComplainThreshold) { 1095 LOGW("JNI: pin count on array %p (%s) is now %d\n", 1096 arrayObj, arrayObj->obj.clazz->descriptor, count); 1097 /* keep going */ 1098 } 1099 } 1100 1101 dvmUnlockMutex(&gDvm.jniPinRefLock); 1102 } 1103 1104 /* 1105 * Un-pin the array object. If an object was pinned twice, it must be 1106 * unpinned twice before it's free to move. 1107 */ 1108 static void unpinPrimitiveArray(ArrayObject* arrayObj) 1109 { 1110 if (arrayObj == NULL) 1111 return; 1112 1113 dvmLockMutex(&gDvm.jniPinRefLock); 1114 if (!dvmRemoveFromReferenceTable(&gDvm.jniPinRefTable, 1115 gDvm.jniPinRefTable.table, (Object*) arrayObj)) 1116 { 1117 LOGW("JNI: unpinPrimitiveArray(%p) failed to find entry (valid=%d)\n", 1118 arrayObj, dvmIsValidObject((Object*) arrayObj)); 1119 goto bail; 1120 } 1121 1122 bail: 1123 dvmUnlockMutex(&gDvm.jniPinRefLock); 1124 } 1125 1126 /* 1127 * Dump the contents of the JNI reference tables to the log file. 1128 * 1129 * We only dump the local refs associated with the current thread. 1130 */ 1131 void dvmDumpJniReferenceTables(void) 1132 { 1133 Thread* self = dvmThreadSelf(); 1134 JNIEnv* env = self->jniEnv; 1135 ReferenceTable* pLocalRefs = getLocalRefTable(env); 1136 1137 #ifdef USE_INDIRECT_REF 1138 dvmDumpIndirectRefTable(pLocalRefs, "JNI local"); 1139 dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable, "JNI global"); 1140 #else 1141 dvmDumpReferenceTable(pLocalRefs, "JNI local"); 1142 dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global"); 1143 #endif 1144 dvmDumpReferenceTable(&gDvm.jniPinRefTable, "JNI pinned array"); 1145 } 1146 1147 /* 1148 * GC helper function to mark all JNI global references. 1149 * 1150 * We're currently handling the "pin" table here too. 1151 */ 1152 void dvmGcMarkJniGlobalRefs() 1153 { 1154 Object** op; 1155 1156 dvmLockMutex(&gDvm.jniGlobalRefLock); 1157 1158 #ifdef USE_INDIRECT_REF 1159 IndirectRefTable* pRefTable = &gDvm.jniGlobalRefTable; 1160 op = pRefTable->table; 1161 int numEntries = dvmIndirectRefTableEntries(pRefTable); 1162 int i; 1163 1164 for (i = 0; i < numEntries; i++) { 1165 Object* obj = *op; 1166 if (obj != NULL) 1167 dvmMarkObjectNonNull(obj); 1168 op++; 1169 } 1170 #else 1171 op = gDvm.jniGlobalRefTable.table; 1172 while ((uintptr_t)op < (uintptr_t)gDvm.jniGlobalRefTable.nextEntry) { 1173 dvmMarkObjectNonNull(*(op++)); 1174 } 1175 #endif 1176 1177 dvmUnlockMutex(&gDvm.jniGlobalRefLock); 1178 1179 1180 dvmLockMutex(&gDvm.jniPinRefLock); 1181 1182 op = gDvm.jniPinRefTable.table; 1183 while ((uintptr_t)op < (uintptr_t)gDvm.jniPinRefTable.nextEntry) { 1184 dvmMarkObjectNonNull(*(op++)); 1185 } 1186 1187 dvmUnlockMutex(&gDvm.jniPinRefLock); 1188 } 1189 1190 1191 #ifndef USE_INDIRECT_REF 1192 /* 1193 * Determine if "obj" appears in the argument list for the native method. 1194 * 1195 * We use the "shorty" signature to determine which argument slots hold 1196 * reference types. 1197 */ 1198 static bool findInArgList(Thread* self, Object* obj) 1199 { 1200 const Method* meth; 1201 u4* fp; 1202 int i; 1203 1204 fp = self->curFrame; 1205 while (1) { 1206 /* 1207 * Back up over JNI PushLocalFrame frames. This works because the 1208 * previous frame on the interpreted stack is either a break frame 1209 * (if we called here via native code) or an interpreted method (if 1210 * we called here via the interpreter). In both cases the method 1211 * pointer won't match. 1212 */ 1213 StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp); 1214 meth = saveArea->method; 1215 if (meth != SAVEAREA_FROM_FP(saveArea->prevFrame)->method) 1216 break; 1217 fp = saveArea->prevFrame; 1218 } 1219 1220 LOGVV("+++ scanning %d args in %s (%s)\n", 1221 meth->insSize, meth->name, meth->shorty); 1222 const char* shorty = meth->shorty +1; /* skip return type char */ 1223 for (i = 0; i < meth->insSize; i++) { 1224 if (i == 0 && !dvmIsStaticMethod(meth)) { 1225 /* first arg is "this" ref, not represented in "shorty" */ 1226 if (fp[i] == (u4) obj) 1227 return true; 1228 } else { 1229 /* if this is a reference type, see if it matches */ 1230 switch (*shorty) { 1231 case 'L': 1232 if (fp[i] == (u4) obj) 1233 return true; 1234 break; 1235 case 'D': 1236 case 'J': 1237 i++; 1238 break; 1239 case '\0': 1240 LOGE("Whoops! ran off the end of %s (%d)\n", 1241 meth->shorty, meth->insSize); 1242 break; 1243 default: 1244 if (fp[i] == (u4) obj) 1245 LOGI("NOTE: ref %p match on arg type %c\n", obj, *shorty); 1246 break; 1247 } 1248 shorty++; 1249 } 1250 } 1251 1252 /* 1253 * For static methods, we also pass a class pointer in. 1254 */ 1255 if (dvmIsStaticMethod(meth)) { 1256 //LOGI("+++ checking class pointer in %s\n", meth->name); 1257 if ((void*)obj == (void*)meth->clazz) 1258 return true; 1259 } 1260 return false; 1261 } 1262 #endif 1263 1264 /* 1265 * Verify that a reference passed in from native code is one that the 1266 * code is allowed to have. 1267 * 1268 * It's okay for native code to pass us a reference that: 1269 * - was passed in as an argument when invoked by native code (and hence 1270 * is in the JNI local refs table) 1271 * - was returned to it from JNI (and is now in the local refs table) 1272 * - is present in the JNI global refs table 1273 * 1274 * Used by -Xcheck:jni and GetObjectRefType. 1275 * 1276 * NOTE: in the current VM, global and local references are identical. If 1277 * something is both global and local, we can't tell them apart, and always 1278 * return "local". 1279 */ 1280 jobjectRefType dvmGetJNIRefType(JNIEnv* env, jobject jobj) 1281 { 1282 #ifdef USE_INDIRECT_REF 1283 /* 1284 * IndirectRefKind is currently defined as an exact match of 1285 * jobjectRefType, so this is easy. We have to decode it to determine 1286 * if it's a valid reference and not merely valid-looking. 1287 */ 1288 Object* obj = dvmDecodeIndirectRef(env, jobj); 1289 1290 if (obj == NULL) { 1291 /* invalid ref, or jobj was NULL */ 1292 return JNIInvalidRefType; 1293 } else { 1294 return (jobjectRefType) dvmGetIndirectRefType(jobj); 1295 } 1296 #else 1297 ReferenceTable* pRefTable = getLocalRefTable(env); 1298 Thread* self = dvmThreadSelf(); 1299 //Object** top; 1300 Object** ptr; 1301 1302 if (dvmIsWeakGlobalRef(jobj)) { 1303 return JNIWeakGlobalRefType; 1304 } 1305 1306 /* check args */ 1307 if (findInArgList(self, jobj)) { 1308 //LOGI("--- REF found %p on stack\n", jobj); 1309 return JNILocalRefType; 1310 } 1311 1312 /* check locals */ 1313 if (dvmFindInReferenceTable(pRefTable, pRefTable->table, jobj) != NULL) { 1314 //LOGI("--- REF found %p in locals\n", jobj); 1315 return JNILocalRefType; 1316 } 1317 1318 /* check globals */ 1319 dvmLockMutex(&gDvm.jniGlobalRefLock); 1320 if (dvmFindInReferenceTable(&gDvm.jniGlobalRefTable, 1321 gDvm.jniGlobalRefTable.table, jobj)) 1322 { 1323 //LOGI("--- REF found %p in globals\n", jobj); 1324 dvmUnlockMutex(&gDvm.jniGlobalRefLock); 1325 return JNIGlobalRefType; 1326 } 1327 dvmUnlockMutex(&gDvm.jniGlobalRefLock); 1328 1329 /* not found! */ 1330 return JNIInvalidRefType; 1331 #endif 1332 } 1333 1334 /* 1335 * Register a method that uses JNI calling conventions. 1336 */ 1337 static bool dvmRegisterJNIMethod(ClassObject* clazz, const char* methodName, 1338 const char* signature, void* fnPtr) 1339 { 1340 Method* method; 1341 bool result = false; 1342 1343 if (fnPtr == NULL) 1344 goto bail; 1345 1346 method = dvmFindDirectMethodByDescriptor(clazz, methodName, signature); 1347 if (method == NULL) 1348 method = dvmFindVirtualMethodByDescriptor(clazz, methodName, signature); 1349 if (method == NULL) { 1350 LOGW("ERROR: Unable to find decl for native %s.%s %s\n", 1351 clazz->descriptor, methodName, signature); 1352 goto bail; 1353 } 1354 1355 if (!dvmIsNativeMethod(method)) { 1356 LOGW("Unable to register: not native: %s.%s %s\n", 1357 clazz->descriptor, methodName, signature); 1358 goto bail; 1359 } 1360 1361 if (method->nativeFunc != dvmResolveNativeMethod) { 1362 LOGW("Warning: %s.%s %s was already registered/resolved?\n", 1363 clazz->descriptor, methodName, signature); 1364 /* keep going, I guess */ 1365 } 1366 1367 dvmUseJNIBridge(method, fnPtr); 1368 1369 LOGV("JNI-registered %s.%s %s\n", clazz->descriptor, methodName, 1370 signature); 1371 result = true; 1372 1373 bail: 1374 return result; 1375 } 1376 1377 /* 1378 * Returns "true" if CheckJNI is enabled in the VM. 1379 */ 1380 static bool dvmIsCheckJNIEnabled(void) 1381 { 1382 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList; 1383 return vm->useChecked; 1384 } 1385 1386 /* 1387 * Point "method->nativeFunc" at the JNI bridge, and overload "method->insns" 1388 * to point at the actual function. 1389 */ 1390 void dvmUseJNIBridge(Method* method, void* func) 1391 { 1392 enum { 1393 kJNIGeneral = 0, 1394 kJNISync = 1, 1395 kJNIVirtualNoRef = 2, 1396 kJNIStaticNoRef = 3, 1397 } kind; 1398 static const DalvikBridgeFunc stdFunc[] = { 1399 dvmCallJNIMethod_general, 1400 dvmCallJNIMethod_synchronized, 1401 dvmCallJNIMethod_virtualNoRef, 1402 dvmCallJNIMethod_staticNoRef 1403 }; 1404 static const DalvikBridgeFunc checkFunc[] = { 1405 dvmCheckCallJNIMethod_general, 1406 dvmCheckCallJNIMethod_synchronized, 1407 dvmCheckCallJNIMethod_virtualNoRef, 1408 dvmCheckCallJNIMethod_staticNoRef 1409 }; 1410 1411 bool hasRefArg = false; 1412 1413 if (dvmIsSynchronizedMethod(method)) { 1414 /* use version with synchronization; calls into general handler */ 1415 kind = kJNISync; 1416 } else { 1417 /* 1418 * Do a quick scan through the "shorty" signature to see if the method 1419 * takes any reference arguments. 1420 */ 1421 const char* cp = method->shorty; 1422 while (*++cp != '\0') { /* pre-incr to skip return type */ 1423 if (*cp == 'L') { 1424 /* 'L' used for both object and array references */ 1425 hasRefArg = true; 1426 break; 1427 } 1428 } 1429 1430 if (hasRefArg) { 1431 /* use general handler to slurp up reference args */ 1432 kind = kJNIGeneral; 1433 } else { 1434 /* virtual methods have a ref in args[0] (not in signature) */ 1435 if (dvmIsStaticMethod(method)) 1436 kind = kJNIStaticNoRef; 1437 else 1438 kind = kJNIVirtualNoRef; 1439 } 1440 } 1441 1442 if (dvmIsCheckJNIEnabled()) { 1443 dvmSetNativeFunc(method, checkFunc[kind], func); 1444 } else { 1445 dvmSetNativeFunc(method, stdFunc[kind], func); 1446 } 1447 } 1448 1449 /* 1450 * Get the method currently being executed by examining the interp stack. 1451 */ 1452 const Method* dvmGetCurrentJNIMethod(void) 1453 { 1454 assert(dvmThreadSelf() != NULL); 1455 1456 void* fp = dvmThreadSelf()->curFrame; 1457 const Method* meth = SAVEAREA_FROM_FP(fp)->method; 1458 1459 assert(meth != NULL); 1460 assert(dvmIsNativeMethod(meth)); 1461 return meth; 1462 } 1463 1464 1465 /* 1466 * Track a JNI MonitorEnter in the current thread. 1467 * 1468 * The goal is to be able to "implicitly" release all JNI-held monitors 1469 * when the thread detaches. 1470 * 1471 * Monitors may be entered multiple times, so we add a new entry for each 1472 * enter call. It would be more efficient to keep a counter. At present 1473 * there's no real motivation to improve this however. 1474 */ 1475 static void trackMonitorEnter(Thread* self, Object* obj) 1476 { 1477 static const int kInitialSize = 16; 1478 ReferenceTable* refTable = &self->jniMonitorRefTable; 1479 1480 /* init table on first use */ 1481 if (refTable->table == NULL) { 1482 assert(refTable->maxEntries == 0); 1483 1484 if (!dvmInitReferenceTable(refTable, kInitialSize, INT_MAX)) { 1485 LOGE("Unable to initialize monitor tracking table\n"); 1486 dvmAbort(); 1487 } 1488 } 1489 1490 if (!dvmAddToReferenceTable(refTable, obj)) { 1491 /* ran out of memory? could throw exception instead */ 1492 LOGE("Unable to add entry to monitor tracking table\n"); 1493 dvmAbort(); 1494 } else { 1495 LOGVV("--- added monitor %p\n", obj); 1496 } 1497 } 1498 1499 1500 /* 1501 * Track a JNI MonitorExit in the current thread. 1502 */ 1503 static void trackMonitorExit(Thread* self, Object* obj) 1504 { 1505 ReferenceTable* pRefTable = &self->jniMonitorRefTable; 1506 1507 if (!dvmRemoveFromReferenceTable(pRefTable, pRefTable->table, obj)) { 1508 LOGE("JNI monitor %p not found in tracking list\n", obj); 1509 /* keep going? */ 1510 } else { 1511 LOGVV("--- removed monitor %p\n", obj); 1512 } 1513 } 1514 1515 /* 1516 * Release all monitors held by the jniMonitorRefTable list. 1517 */ 1518 void dvmReleaseJniMonitors(Thread* self) 1519 { 1520 ReferenceTable* pRefTable = &self->jniMonitorRefTable; 1521 Object** top = pRefTable->table; 1522 1523 if (top == NULL) 1524 return; 1525 1526 Object** ptr = pRefTable->nextEntry; 1527 while (--ptr >= top) { 1528 if (!dvmUnlockObject(self, *ptr)) { 1529 LOGW("Unable to unlock monitor %p at thread detach\n", *ptr); 1530 } else { 1531 LOGVV("--- detach-releasing monitor %p\n", *ptr); 1532 } 1533 } 1534 1535 /* zap it */ 1536 pRefTable->nextEntry = pRefTable->table; 1537 } 1538 1539 #ifdef WITH_JNI_STACK_CHECK 1540 /* 1541 * Compute a CRC on the entire interpreted stack. 1542 * 1543 * Would be nice to compute it on "self" as well, but there are parts of 1544 * the Thread that can be altered by other threads (e.g. prev/next pointers). 1545 */ 1546 static void computeStackSum(Thread* self) 1547 { 1548 const u1* low = (const u1*)SAVEAREA_FROM_FP(self->curFrame); 1549 u4 crc = dvmInitCrc32(); 1550 self->stackCrc = 0; 1551 crc = dvmComputeCrc32(crc, low, self->interpStackStart - low); 1552 self->stackCrc = crc; 1553 } 1554 1555 /* 1556 * Compute a CRC on the entire interpreted stack, and compare it to what 1557 * we previously computed. 1558 * 1559 * We can execute JNI directly from native code without calling in from 1560 * interpreted code during VM initialization and immediately after JNI 1561 * thread attachment. Another opportunity exists during JNI_OnLoad. Rather 1562 * than catching these cases we just ignore them here, which is marginally 1563 * less accurate but reduces the amount of code we have to touch with #ifdefs. 1564 */ 1565 static void checkStackSum(Thread* self) 1566 { 1567 const u1* low = (const u1*)SAVEAREA_FROM_FP(self->curFrame); 1568 u4 stackCrc, crc; 1569 1570 stackCrc = self->stackCrc; 1571 self->stackCrc = 0; 1572 crc = dvmInitCrc32(); 1573 crc = dvmComputeCrc32(crc, low, self->interpStackStart - low); 1574 if (crc != stackCrc) { 1575 const Method* meth = dvmGetCurrentJNIMethod(); 1576 if (dvmComputeExactFrameDepth(self->curFrame) == 1) { 1577 LOGD("JNI: bad stack CRC (0x%08x) -- okay during init\n", 1578 stackCrc); 1579 } else if (strcmp(meth->name, "nativeLoad") == 0 && 1580 (strcmp(meth->clazz->descriptor, "Ljava/lang/Runtime;") == 0)) 1581 { 1582 LOGD("JNI: bad stack CRC (0x%08x) -- okay during JNI_OnLoad\n", 1583 stackCrc); 1584 } else { 1585 LOGW("JNI: bad stack CRC (%08x vs %08x)\n", crc, stackCrc); 1586 dvmAbort(); 1587 } 1588 } 1589 self->stackCrc = (u4) -1; /* make logic errors more noticeable */ 1590 } 1591 #endif 1592 1593 1594 /* 1595 * =========================================================================== 1596 * JNI call bridge 1597 * =========================================================================== 1598 */ 1599 1600 /* 1601 * The functions here form a bridge between interpreted code and JNI native 1602 * functions. The basic task is to convert an array of primitives and 1603 * references into C-style function arguments. This is architecture-specific 1604 * and usually requires help from assembly code. 1605 * 1606 * The bridge takes four arguments: the array of parameters, a place to 1607 * store the function result (if any), the method to call, and a pointer 1608 * to the current thread. 1609 * 1610 * These functions aren't called directly from elsewhere in the VM. 1611 * A pointer in the Method struct points to one of these, and when a native 1612 * method is invoked the interpreter jumps to it. 1613 * 1614 * (The "internal native" methods are invoked the same way, but instead 1615 * of calling through a bridge, the target method is called directly.) 1616 * 1617 * The "args" array should not be modified, but we do so anyway for 1618 * performance reasons. We know that it points to the "outs" area on 1619 * the current method's interpreted stack. This area is ignored by the 1620 * precise GC, because there is no register map for a native method (for 1621 * an interpreted method the args would be listed in the argument set). 1622 * We know all of the values exist elsewhere on the interpreted stack, 1623 * because the method call setup copies them right before making the call, 1624 * so we don't have to worry about concealing stuff from the GC. 1625 * 1626 * If we don't want to modify "args", we either have to create a local 1627 * copy and modify it before calling dvmPlatformInvoke, or we have to do 1628 * the local reference replacement within dvmPlatformInvoke. The latter 1629 * has some performance advantages, though if we can inline the local 1630 * reference adds we may win when there's a lot of reference args (unless 1631 * we want to code up some local ref table manipulation in assembly. 1632 */ 1633 1634 /* 1635 * If necessary, convert the value in pResult from a local/global reference 1636 * to an object pointer. 1637 */ 1638 static inline void convertReferenceResult(JNIEnv* env, JValue* pResult, 1639 const Method* method, Thread* self) 1640 { 1641 #ifdef USE_INDIRECT_REF 1642 if (method->shorty[0] == 'L' && !dvmCheckException(self) && 1643 pResult->l != NULL) 1644 { 1645 pResult->l = dvmDecodeIndirectRef(env, pResult->l); 1646 } 1647 #endif 1648 } 1649 1650 /* 1651 * General form, handles all cases. 1652 */ 1653 void dvmCallJNIMethod_general(const u4* args, JValue* pResult, 1654 const Method* method, Thread* self) 1655 { 1656 int oldStatus; 1657 u4* modArgs = (u4*) args; 1658 jclass staticMethodClass; 1659 JNIEnv* env = self->jniEnv; 1660 1661 assert(method->insns != NULL); 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 COMPUTE_STACK_SUM(self); 1726 dvmPlatformInvoke(env, staticMethodClass, 1727 method->jniArgInfo, method->insSize, modArgs, method->shorty, 1728 (void*)method->insns, pResult); 1729 CHECK_STACK_SUM(self); 1730 1731 dvmChangeStatus(self, oldStatus); 1732 1733 convertReferenceResult(env, pResult, method, self); 1734 } 1735 1736 /* 1737 * Handler for the unusual case of a synchronized native method. 1738 * 1739 * Lock the object, then call through the general function. 1740 */ 1741 void dvmCallJNIMethod_synchronized(const u4* args, JValue* pResult, 1742 const Method* method, Thread* self) 1743 { 1744 Object* lockObj; 1745 1746 assert(dvmIsSynchronizedMethod(method)); 1747 1748 if (dvmIsStaticMethod(method)) 1749 lockObj = (Object*) method->clazz; 1750 else 1751 lockObj = (Object*) args[0]; 1752 1753 LOGVV("Calling %s.%s: locking %p (%s)\n", 1754 method->clazz->descriptor, method->name, 1755 lockObj, lockObj->clazz->descriptor); 1756 1757 dvmLockObject(self, lockObj); 1758 dvmCallJNIMethod_general(args, pResult, method, self); 1759 dvmUnlockObject(self, lockObj); 1760 } 1761 1762 /* 1763 * Virtual method call, no reference arguments. 1764 * 1765 * We need to local-ref the "this" argument, found in args[0]. 1766 */ 1767 void dvmCallJNIMethod_virtualNoRef(const u4* args, JValue* pResult, 1768 const Method* method, Thread* self) 1769 { 1770 u4* modArgs = (u4*) args; 1771 int oldStatus; 1772 1773 #ifdef USE_INDIRECT_REF 1774 jobject thisObj = addLocalReference(self->jniEnv, (Object*) args[0]); 1775 if (thisObj == NULL) { 1776 assert(dvmCheckException(self)); 1777 return; 1778 } 1779 modArgs[0] = (u4) thisObj; 1780 #endif 1781 1782 oldStatus = dvmChangeStatus(self, THREAD_NATIVE); 1783 1784 COMPUTE_STACK_SUM(self); 1785 dvmPlatformInvoke(self->jniEnv, NULL, 1786 method->jniArgInfo, method->insSize, modArgs, method->shorty, 1787 (void*)method->insns, pResult); 1788 CHECK_STACK_SUM(self); 1789 1790 dvmChangeStatus(self, oldStatus); 1791 1792 convertReferenceResult(self->jniEnv, pResult, method, self); 1793 } 1794 1795 /* 1796 * Static method call, no reference arguments. 1797 * 1798 * We need to local-ref the class reference. 1799 */ 1800 void dvmCallJNIMethod_staticNoRef(const u4* args, JValue* pResult, 1801 const Method* method, Thread* self) 1802 { 1803 jclass staticMethodClass; 1804 int oldStatus; 1805 1806 #ifdef USE_INDIRECT_REF 1807 staticMethodClass = addLocalReference(self->jniEnv, (Object*)method->clazz); 1808 if (staticMethodClass == NULL) { 1809 assert(dvmCheckException(self)); 1810 return; 1811 } 1812 #else 1813 staticMethodClass = (jobject) method->clazz; 1814 #endif 1815 1816 oldStatus = dvmChangeStatus(self, THREAD_NATIVE); 1817 1818 COMPUTE_STACK_SUM(self); 1819 dvmPlatformInvoke(self->jniEnv, staticMethodClass, 1820 method->jniArgInfo, method->insSize, args, method->shorty, 1821 (void*)method->insns, pResult); 1822 CHECK_STACK_SUM(self); 1823 1824 dvmChangeStatus(self, oldStatus); 1825 1826 convertReferenceResult(self->jniEnv, pResult, method, self); 1827 } 1828 1829 /* 1830 * Extract the return type enum from the "jniArgInfo" field. 1831 */ 1832 DalvikJniReturnType dvmGetArgInfoReturnType(int jniArgInfo) 1833 { 1834 return (jniArgInfo & DALVIK_JNI_RETURN_MASK) >> DALVIK_JNI_RETURN_SHIFT; 1835 } 1836 1837 1838 /* 1839 * =========================================================================== 1840 * JNI implementation 1841 * =========================================================================== 1842 */ 1843 1844 /* 1845 * Return the version of the native method interface. 1846 */ 1847 static jint GetVersion(JNIEnv* env) 1848 { 1849 JNI_ENTER(); 1850 /* 1851 * There is absolutely no need to toggle the mode for correct behavior. 1852 * However, it does provide native code with a simple "suspend self 1853 * if necessary" call. 1854 */ 1855 JNI_EXIT(); 1856 return JNI_VERSION_1_6; 1857 } 1858 1859 /* 1860 * Create a new class from a bag of bytes. 1861 * 1862 * This is not currently supported within Dalvik. 1863 */ 1864 static jclass DefineClass(JNIEnv* env, const char *name, jobject loader, 1865 const jbyte* buf, jsize bufLen) 1866 { 1867 UNUSED_PARAMETER(name); 1868 UNUSED_PARAMETER(loader); 1869 UNUSED_PARAMETER(buf); 1870 UNUSED_PARAMETER(bufLen); 1871 1872 JNI_ENTER(); 1873 LOGW("JNI DefineClass is not supported\n"); 1874 JNI_EXIT(); 1875 return NULL; 1876 } 1877 1878 /* 1879 * Find a class by name. 1880 * 1881 * We have to use the "no init" version of FindClass here, because we might 1882 * be getting the class prior to registering native methods that will be 1883 * used in <clinit>. 1884 * 1885 * We need to get the class loader associated with the current native 1886 * method. If there is no native method, e.g. we're calling this from native 1887 * code right after creating the VM, the spec says we need to use the class 1888 * loader returned by "ClassLoader.getBaseClassLoader". There is no such 1889 * method, but it's likely they meant ClassLoader.getSystemClassLoader. 1890 * We can't get that until after the VM has initialized though. 1891 */ 1892 static jclass FindClass(JNIEnv* env, const char* name) 1893 { 1894 JNI_ENTER(); 1895 1896 const Method* thisMethod; 1897 ClassObject* clazz; 1898 jclass jclazz = NULL; 1899 Object* loader; 1900 char* descriptor = NULL; 1901 1902 thisMethod = dvmGetCurrentJNIMethod(); 1903 assert(thisMethod != NULL); 1904 1905 descriptor = dvmNameToDescriptor(name); 1906 if (descriptor == NULL) { 1907 clazz = NULL; 1908 goto bail; 1909 } 1910 1911 //Thread* self = dvmThreadSelf(); 1912 if (_self->classLoaderOverride != NULL) { 1913 /* hack for JNI_OnLoad */ 1914 assert(strcmp(thisMethod->name, "nativeLoad") == 0); 1915 loader = _self->classLoaderOverride; 1916 } else if (thisMethod == gDvm.methFakeNativeEntry) { 1917 /* start point of invocation interface */ 1918 if (!gDvm.initializing) 1919 loader = dvmGetSystemClassLoader(); 1920 else 1921 loader = NULL; 1922 } else { 1923 loader = thisMethod->clazz->classLoader; 1924 } 1925 1926 clazz = dvmFindClassNoInit(descriptor, loader); 1927 jclazz = addLocalReference(env, (Object*) clazz); 1928 1929 bail: 1930 free(descriptor); 1931 1932 JNI_EXIT(); 1933 return jclazz; 1934 } 1935 1936 /* 1937 * Return the superclass of a class. 1938 */ 1939 static jclass GetSuperclass(JNIEnv* env, jclass jclazz) 1940 { 1941 JNI_ENTER(); 1942 jclass jsuper = NULL; 1943 1944 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 1945 if (clazz != NULL) 1946 jsuper = addLocalReference(env, (Object*)clazz->super); 1947 JNI_EXIT(); 1948 return jsuper; 1949 } 1950 1951 /* 1952 * Determine whether an object of clazz1 can be safely cast to clazz2. 1953 * 1954 * Like IsInstanceOf, but with a pair of class objects instead of obj+class. 1955 */ 1956 static jboolean IsAssignableFrom(JNIEnv* env, jclass jclazz1, jclass jclazz2) 1957 { 1958 JNI_ENTER(); 1959 1960 ClassObject* clazz1 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz1); 1961 ClassObject* clazz2 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz2); 1962 1963 jboolean result = dvmInstanceof(clazz1, clazz2); 1964 1965 JNI_EXIT(); 1966 return result; 1967 } 1968 1969 /* 1970 * Given a java.lang.reflect.Method or .Constructor, return a methodID. 1971 */ 1972 static jmethodID FromReflectedMethod(JNIEnv* env, jobject jmethod) 1973 { 1974 JNI_ENTER(); 1975 jmethodID methodID; 1976 Object* method = dvmDecodeIndirectRef(env, jmethod); 1977 methodID = (jmethodID) dvmGetMethodFromReflectObj(method); 1978 JNI_EXIT(); 1979 return methodID; 1980 } 1981 1982 /* 1983 * Given a java.lang.reflect.Field, return a fieldID. 1984 */ 1985 static jfieldID FromReflectedField(JNIEnv* env, jobject jfield) 1986 { 1987 JNI_ENTER(); 1988 jfieldID fieldID; 1989 Object* field = dvmDecodeIndirectRef(env, jfield); 1990 fieldID = (jfieldID) dvmGetFieldFromReflectObj(field); 1991 JNI_EXIT(); 1992 return fieldID; 1993 } 1994 1995 /* 1996 * Convert a methodID to a java.lang.reflect.Method or .Constructor. 1997 * 1998 * (The "isStatic" field does not appear in the spec.) 1999 * 2000 * Throws OutOfMemory and returns NULL on failure. 2001 */ 2002 static jobject ToReflectedMethod(JNIEnv* env, jclass jcls, jmethodID methodID, 2003 jboolean isStatic) 2004 { 2005 JNI_ENTER(); 2006 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls); 2007 Object* obj = dvmCreateReflectObjForMethod(clazz, (Method*) methodID); 2008 dvmReleaseTrackedAlloc(obj, NULL); 2009 jobject jobj = addLocalReference(env, obj); 2010 JNI_EXIT(); 2011 return jobj; 2012 } 2013 2014 /* 2015 * Convert a fieldID to a java.lang.reflect.Field. 2016 * 2017 * (The "isStatic" field does not appear in the spec.) 2018 * 2019 * Throws OutOfMemory and returns NULL on failure. 2020 */ 2021 static jobject ToReflectedField(JNIEnv* env, jclass jcls, jfieldID fieldID, 2022 jboolean isStatic) 2023 { 2024 JNI_ENTER(); 2025 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls); 2026 Object* obj = dvmCreateReflectObjForField(jcls, (Field*) fieldID); 2027 dvmReleaseTrackedAlloc(obj, NULL); 2028 jobject jobj = addLocalReference(env, obj); 2029 JNI_EXIT(); 2030 return jobj; 2031 } 2032 2033 /* 2034 * Take this exception and throw it. 2035 */ 2036 static jint Throw(JNIEnv* env, jthrowable jobj) 2037 { 2038 JNI_ENTER(); 2039 2040 jint retval; 2041 2042 if (jobj != NULL) { 2043 Object* obj = dvmDecodeIndirectRef(env, jobj); 2044 dvmSetException(_self, obj); 2045 retval = JNI_OK; 2046 } else { 2047 retval = JNI_ERR; 2048 } 2049 2050 JNI_EXIT(); 2051 return retval; 2052 } 2053 2054 /* 2055 * Constructs an exception object from the specified class with the message 2056 * specified by "message", and throws it. 2057 */ 2058 static jint ThrowNew(JNIEnv* env, jclass jclazz, const char* message) 2059 { 2060 JNI_ENTER(); 2061 2062 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 2063 dvmThrowExceptionByClass(clazz, message); 2064 // TODO: should return failure if this didn't work (e.g. OOM) 2065 2066 JNI_EXIT(); 2067 return JNI_OK; 2068 } 2069 2070 /* 2071 * If an exception is being thrown, return the exception object. Otherwise, 2072 * return NULL. 2073 * 2074 * TODO: if there is no pending exception, we should be able to skip the 2075 * enter/exit checks. If we find one, we need to enter and then re-fetch 2076 * the exception (in case it got moved by a compacting GC). 2077 */ 2078 static jthrowable ExceptionOccurred(JNIEnv* env) 2079 { 2080 JNI_ENTER(); 2081 2082 Object* exception; 2083 jobject localException; 2084 2085 exception = dvmGetException(_self); 2086 localException = addLocalReference(env, exception); 2087 if (localException == NULL && exception != NULL) { 2088 /* 2089 * We were unable to add a new local reference, and threw a new 2090 * exception. We can't return "exception", because it's not a 2091 * local reference. So we have to return NULL, indicating that 2092 * there was no exception, even though it's pretty much raining 2093 * exceptions in here. 2094 */ 2095 LOGW("JNI WARNING: addLocal/exception combo\n"); 2096 } 2097 2098 JNI_EXIT(); 2099 return localException; 2100 } 2101 2102 /* 2103 * Print an exception and stack trace to stderr. 2104 */ 2105 static void ExceptionDescribe(JNIEnv* env) 2106 { 2107 JNI_ENTER(); 2108 2109 Object* exception = dvmGetException(_self); 2110 if (exception != NULL) { 2111 dvmPrintExceptionStackTrace(); 2112 } else { 2113 LOGI("Odd: ExceptionDescribe called, but no exception pending\n"); 2114 } 2115 2116 JNI_EXIT(); 2117 } 2118 2119 /* 2120 * Clear the exception currently being thrown. 2121 * 2122 * TODO: we should be able to skip the enter/exit stuff. 2123 */ 2124 static void ExceptionClear(JNIEnv* env) 2125 { 2126 JNI_ENTER(); 2127 dvmClearException(_self); 2128 JNI_EXIT(); 2129 } 2130 2131 /* 2132 * Kill the VM. This function does not return. 2133 */ 2134 static void FatalError(JNIEnv* env, const char* msg) 2135 { 2136 //dvmChangeStatus(NULL, THREAD_RUNNING); 2137 LOGE("JNI posting fatal error: %s\n", msg); 2138 dvmAbort(); 2139 } 2140 2141 /* 2142 * Push a new JNI frame on the stack, with a new set of locals. 2143 * 2144 * The new frame must have the same method pointer. (If for no other 2145 * reason than FindClass needs it to get the appropriate class loader.) 2146 */ 2147 static jint PushLocalFrame(JNIEnv* env, jint capacity) 2148 { 2149 JNI_ENTER(); 2150 int result = JNI_OK; 2151 if (!ensureLocalCapacity(env, capacity) || 2152 !dvmPushLocalFrame(_self /*dvmThreadSelf()*/, dvmGetCurrentJNIMethod())) 2153 { 2154 /* yes, OutOfMemoryError, not StackOverflowError */ 2155 dvmClearException(_self); 2156 dvmThrowException("Ljava/lang/OutOfMemoryError;", 2157 "out of stack in JNI PushLocalFrame"); 2158 result = JNI_ERR; 2159 } 2160 JNI_EXIT(); 2161 return result; 2162 } 2163 2164 /* 2165 * Pop the local frame off. If "result" is not null, add it as a 2166 * local reference on the now-current frame. 2167 */ 2168 static jobject PopLocalFrame(JNIEnv* env, jobject jresult) 2169 { 2170 JNI_ENTER(); 2171 Object* result = dvmDecodeIndirectRef(env, jresult); 2172 if (!dvmPopLocalFrame(_self /*dvmThreadSelf()*/)) { 2173 LOGW("JNI WARNING: too many PopLocalFrame calls\n"); 2174 dvmClearException(_self); 2175 dvmThrowException("Ljava/lang/RuntimeException;", 2176 "too many PopLocalFrame calls"); 2177 } 2178 jresult = addLocalReference(env, result); 2179 JNI_EXIT(); 2180 return result; 2181 } 2182 2183 /* 2184 * Add a reference to the global list. 2185 */ 2186 static jobject NewGlobalRef(JNIEnv* env, jobject jobj) 2187 { 2188 Object* obj; 2189 2190 JNI_ENTER(); 2191 if (dvmIsWeakGlobalRef(jobj)) 2192 obj = getPhantomReferent(env, (jweak) jobj); 2193 else 2194 obj = dvmDecodeIndirectRef(env, jobj); 2195 jobject retval = addGlobalReference(obj); 2196 JNI_EXIT(); 2197 return retval; 2198 } 2199 2200 /* 2201 * Delete a reference from the global list. 2202 */ 2203 static void DeleteGlobalRef(JNIEnv* env, jobject jglobalRef) 2204 { 2205 JNI_ENTER(); 2206 deleteGlobalReference(jglobalRef); 2207 JNI_EXIT(); 2208 } 2209 2210 2211 /* 2212 * Add a reference to the local list. 2213 */ 2214 static jobject NewLocalRef(JNIEnv* env, jobject jobj) 2215 { 2216 Object* obj; 2217 2218 JNI_ENTER(); 2219 if (dvmIsWeakGlobalRef(jobj)) 2220 obj = getPhantomReferent(env, (jweak) jobj); 2221 else 2222 obj = dvmDecodeIndirectRef(env, jobj); 2223 jobject retval = addLocalReference(env, obj); 2224 JNI_EXIT(); 2225 return retval; 2226 } 2227 2228 /* 2229 * Delete a reference from the local list. 2230 */ 2231 static void DeleteLocalRef(JNIEnv* env, jobject jlocalRef) 2232 { 2233 JNI_ENTER(); 2234 deleteLocalReference(env, jlocalRef); 2235 JNI_EXIT(); 2236 } 2237 2238 /* 2239 * Ensure that the local references table can hold at least this many 2240 * references. 2241 */ 2242 static jint EnsureLocalCapacity(JNIEnv* env, jint capacity) 2243 { 2244 JNI_ENTER(); 2245 bool okay = ensureLocalCapacity(env, capacity); 2246 if (!okay) { 2247 dvmThrowException("Ljava/lang/OutOfMemoryError;", 2248 "can't ensure local reference capacity"); 2249 } 2250 JNI_EXIT(); 2251 if (okay) 2252 return 0; 2253 else 2254 return -1; 2255 } 2256 2257 2258 /* 2259 * Determine whether two Object references refer to the same underlying object. 2260 */ 2261 static jboolean IsSameObject(JNIEnv* env, jobject jref1, jobject jref2) 2262 { 2263 JNI_ENTER(); 2264 Object* obj1 = dvmDecodeIndirectRef(env, jref1); 2265 Object* obj2 = dvmDecodeIndirectRef(env, jref2); 2266 jboolean result = (obj1 == obj2); 2267 JNI_EXIT(); 2268 return result; 2269 } 2270 2271 /* 2272 * Allocate a new object without invoking any constructors. 2273 */ 2274 static jobject AllocObject(JNIEnv* env, jclass jclazz) 2275 { 2276 JNI_ENTER(); 2277 2278 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 2279 jobject result; 2280 2281 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) { 2282 assert(dvmCheckException(_self)); 2283 result = NULL; 2284 } else { 2285 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK); 2286 result = addLocalReference(env, newObj); 2287 } 2288 2289 JNI_EXIT(); 2290 return result; 2291 } 2292 2293 /* 2294 * Allocate a new object and invoke the supplied constructor. 2295 */ 2296 static jobject NewObject(JNIEnv* env, jclass jclazz, jmethodID methodID, ...) 2297 { 2298 JNI_ENTER(); 2299 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 2300 jobject result; 2301 2302 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) { 2303 assert(dvmCheckException(_self)); 2304 result = NULL; 2305 } else { 2306 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK); 2307 result = addLocalReference(env, newObj); 2308 if (newObj != NULL) { 2309 JValue unused; 2310 va_list args; 2311 va_start(args, methodID); 2312 dvmCallMethodV(_self, (Method*) methodID, newObj, true, &unused, 2313 args); 2314 va_end(args); 2315 } 2316 } 2317 2318 JNI_EXIT(); 2319 return result; 2320 } 2321 static jobject NewObjectV(JNIEnv* env, jclass jclazz, jmethodID methodID, 2322 va_list args) 2323 { 2324 JNI_ENTER(); 2325 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 2326 jobject result; 2327 2328 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK); 2329 result = addLocalReference(env, newObj); 2330 if (newObj != NULL) { 2331 JValue unused; 2332 dvmCallMethodV(_self, (Method*) methodID, newObj, true, &unused, args); 2333 } 2334 2335 JNI_EXIT(); 2336 return result; 2337 } 2338 static jobject NewObjectA(JNIEnv* env, jclass jclazz, jmethodID methodID, 2339 jvalue* args) 2340 { 2341 JNI_ENTER(); 2342 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 2343 jobject result; 2344 2345 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK); 2346 result = addLocalReference(env, newObj); 2347 if (newObj != NULL) { 2348 JValue unused; 2349 dvmCallMethodA(_self, (Method*) methodID, newObj, true, &unused, args); 2350 } 2351 2352 JNI_EXIT(); 2353 return result; 2354 } 2355 2356 /* 2357 * Returns the class of an object. 2358 * 2359 * JNI spec says: obj must not be NULL. 2360 */ 2361 static jclass GetObjectClass(JNIEnv* env, jobject jobj) 2362 { 2363 JNI_ENTER(); 2364 2365 assert(jobj != NULL); 2366 2367 Object* obj = dvmDecodeIndirectRef(env, jobj); 2368 jclass jclazz = addLocalReference(env, (Object*) obj->clazz); 2369 2370 JNI_EXIT(); 2371 return jclazz; 2372 } 2373 2374 /* 2375 * Determine whether "obj" is an instance of "clazz". 2376 */ 2377 static jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass jclazz) 2378 { 2379 JNI_ENTER(); 2380 2381 assert(jclazz != NULL); 2382 2383 jboolean result; 2384 2385 if (jobj == NULL) { 2386 result = true; 2387 } else { 2388 Object* obj = dvmDecodeIndirectRef(env, jobj); 2389 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 2390 result = dvmInstanceof(obj->clazz, clazz); 2391 } 2392 2393 JNI_EXIT(); 2394 return result; 2395 } 2396 2397 /* 2398 * Get a method ID for an instance method. 2399 * 2400 * JNI defines <init> as an instance method, but Dalvik considers it a 2401 * "direct" method, so we have to special-case it here. 2402 * 2403 * Dalvik also puts all private methods into the "direct" list, so we 2404 * really need to just search both lists. 2405 */ 2406 static jmethodID GetMethodID(JNIEnv* env, jclass jclazz, const char* name, 2407 const char* sig) 2408 { 2409 JNI_ENTER(); 2410 2411 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 2412 jmethodID id = NULL; 2413 2414 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) { 2415 assert(dvmCheckException(_self)); 2416 } else { 2417 Method* meth; 2418 2419 meth = dvmFindVirtualMethodHierByDescriptor(clazz, name, sig); 2420 if (meth == NULL) { 2421 /* search private methods and constructors; non-hierarchical */ 2422 meth = dvmFindDirectMethodByDescriptor(clazz, name, sig); 2423 } 2424 if (meth != NULL && dvmIsStaticMethod(meth)) { 2425 IF_LOGD() { 2426 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype); 2427 LOGD("GetMethodID: not returning static method %s.%s %s\n", 2428 clazz->descriptor, meth->name, desc); 2429 free(desc); 2430 } 2431 meth = NULL; 2432 } 2433 if (meth == NULL) { 2434 LOGD("GetMethodID: method not found: %s.%s:%s\n", 2435 clazz->descriptor, name, sig); 2436 dvmThrowException("Ljava/lang/NoSuchMethodError;", name); 2437 } 2438 2439 /* 2440 * The method's class may not be the same as clazz, but if 2441 * it isn't this must be a virtual method and the class must 2442 * be a superclass (and, hence, already initialized). 2443 */ 2444 if (meth != NULL) { 2445 assert(dvmIsClassInitialized(meth->clazz) || 2446 dvmIsClassInitializing(meth->clazz)); 2447 } 2448 id = (jmethodID) meth; 2449 } 2450 JNI_EXIT(); 2451 return id; 2452 } 2453 2454 /* 2455 * Get a field ID (instance fields). 2456 */ 2457 static jfieldID GetFieldID(JNIEnv* env, jclass jclazz, 2458 const char* name, const char* sig) 2459 { 2460 JNI_ENTER(); 2461 2462 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 2463 jfieldID id; 2464 2465 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) { 2466 assert(dvmCheckException(_self)); 2467 id = NULL; 2468 } else { 2469 id = (jfieldID) dvmFindInstanceFieldHier(clazz, name, sig); 2470 if (id == NULL) { 2471 LOGD("GetFieldID: unable to find field %s.%s:%s\n", 2472 clazz->descriptor, name, sig); 2473 dvmThrowException("Ljava/lang/NoSuchFieldError;", name); 2474 } 2475 } 2476 JNI_EXIT(); 2477 return id; 2478 } 2479 2480 /* 2481 * Get the method ID for a static method in a class. 2482 */ 2483 static jmethodID GetStaticMethodID(JNIEnv* env, jclass jclazz, 2484 const char* name, const char* sig) 2485 { 2486 JNI_ENTER(); 2487 2488 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 2489 jmethodID id; 2490 2491 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) { 2492 assert(dvmCheckException(_self)); 2493 id = NULL; 2494 } else { 2495 Method* meth; 2496 2497 meth = dvmFindDirectMethodHierByDescriptor(clazz, name, sig); 2498 2499 /* make sure it's static, not virtual+private */ 2500 if (meth != NULL && !dvmIsStaticMethod(meth)) { 2501 IF_LOGD() { 2502 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype); 2503 LOGD("GetStaticMethodID: " 2504 "not returning nonstatic method %s.%s %s\n", 2505 clazz->descriptor, meth->name, desc); 2506 free(desc); 2507 } 2508 meth = NULL; 2509 } 2510 2511 id = (jmethodID) meth; 2512 if (id == NULL) 2513 dvmThrowException("Ljava/lang/NoSuchMethodError;", name); 2514 } 2515 2516 JNI_EXIT(); 2517 return id; 2518 } 2519 2520 /* 2521 * Get a field ID (static fields). 2522 */ 2523 static jfieldID GetStaticFieldID(JNIEnv* env, jclass jclazz, 2524 const char* name, const char* sig) 2525 { 2526 JNI_ENTER(); 2527 2528 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 2529 jfieldID id; 2530 2531 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) { 2532 assert(dvmCheckException(_self)); 2533 id = NULL; 2534 } else { 2535 id = (jfieldID) dvmFindStaticField(clazz, name, sig); 2536 if (id == NULL) 2537 dvmThrowException("Ljava/lang/NoSuchFieldError;", name); 2538 } 2539 JNI_EXIT(); 2540 return id; 2541 } 2542 2543 /* 2544 * Get a static field. 2545 * 2546 * If we get an object reference, add it to the local refs list. 2547 */ 2548 #define GET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) \ 2549 static _ctype GetStatic##_jname##Field(JNIEnv* env, jclass jclazz, \ 2550 jfieldID fieldID) \ 2551 { \ 2552 UNUSED_PARAMETER(jclazz); \ 2553 JNI_ENTER(); \ 2554 StaticField* sfield = (StaticField*) fieldID; \ 2555 _ctype value; \ 2556 if (_isref) { /* only when _ctype==jobject */ \ 2557 Object* obj = dvmGetStaticFieldObject(sfield); \ 2558 value = (_ctype)(u4)addLocalReference(env, obj); \ 2559 } else { \ 2560 value = dvmGetStaticField##_jname(sfield); \ 2561 } \ 2562 JNI_EXIT(); \ 2563 return value; \ 2564 } 2565 GET_STATIC_TYPE_FIELD(jobject, Object, true); 2566 GET_STATIC_TYPE_FIELD(jboolean, Boolean, false); 2567 GET_STATIC_TYPE_FIELD(jbyte, Byte, false); 2568 GET_STATIC_TYPE_FIELD(jchar, Char, false); 2569 GET_STATIC_TYPE_FIELD(jshort, Short, false); 2570 GET_STATIC_TYPE_FIELD(jint, Int, false); 2571 GET_STATIC_TYPE_FIELD(jlong, Long, false); 2572 GET_STATIC_TYPE_FIELD(jfloat, Float, false); 2573 GET_STATIC_TYPE_FIELD(jdouble, Double, false); 2574 2575 /* 2576 * Set a static field. 2577 */ 2578 #define SET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) \ 2579 static void SetStatic##_jname##Field(JNIEnv* env, jclass jclazz, \ 2580 jfieldID fieldID, _ctype value) \ 2581 { \ 2582 UNUSED_PARAMETER(jclazz); \ 2583 JNI_ENTER(); \ 2584 StaticField* sfield = (StaticField*) fieldID; \ 2585 if (_isref) { /* only when _ctype==jobject */ \ 2586 Object* valObj = dvmDecodeIndirectRef(env, (jobject)(u4)value); \ 2587 dvmSetStaticFieldObject(sfield, valObj); \ 2588 } else { \ 2589 dvmSetStaticField##_jname(sfield, value); \ 2590 } \ 2591 JNI_EXIT(); \ 2592 } 2593 SET_STATIC_TYPE_FIELD(jobject, Object, true); 2594 SET_STATIC_TYPE_FIELD(jboolean, Boolean, false); 2595 SET_STATIC_TYPE_FIELD(jbyte, Byte, false); 2596 SET_STATIC_TYPE_FIELD(jchar, Char, false); 2597 SET_STATIC_TYPE_FIELD(jshort, Short, false); 2598 SET_STATIC_TYPE_FIELD(jint, Int, false); 2599 SET_STATIC_TYPE_FIELD(jlong, Long, false); 2600 SET_STATIC_TYPE_FIELD(jfloat, Float, false); 2601 SET_STATIC_TYPE_FIELD(jdouble, Double, false); 2602 2603 /* 2604 * Get an instance field. 2605 * 2606 * If we get an object reference, add it to the local refs list. 2607 */ 2608 #define GET_TYPE_FIELD(_ctype, _jname, _isref) \ 2609 static _ctype Get##_jname##Field(JNIEnv* env, jobject jobj, \ 2610 jfieldID fieldID) \ 2611 { \ 2612 JNI_ENTER(); \ 2613 Object* obj = dvmDecodeIndirectRef(env, jobj); \ 2614 InstField* field = (InstField*) fieldID; \ 2615 _ctype value; \ 2616 if (_isref) { /* only when _ctype==jobject */ \ 2617 Object* valObj = dvmGetFieldObject(obj, field->byteOffset); \ 2618 value = (_ctype)(u4)addLocalReference(env, valObj); \ 2619 } else { \ 2620 value = dvmGetField##_jname(obj, field->byteOffset); \ 2621 } \ 2622 JNI_EXIT(); \ 2623 return value; \ 2624 } 2625 GET_TYPE_FIELD(jobject, Object, true); 2626 GET_TYPE_FIELD(jboolean, Boolean, false); 2627 GET_TYPE_FIELD(jbyte, Byte, false); 2628 GET_TYPE_FIELD(jchar, Char, false); 2629 GET_TYPE_FIELD(jshort, Short, false); 2630 GET_TYPE_FIELD(jint, Int, false); 2631 GET_TYPE_FIELD(jlong, Long, false); 2632 GET_TYPE_FIELD(jfloat, Float, false); 2633 GET_TYPE_FIELD(jdouble, Double, false); 2634 2635 /* 2636 * Set an instance field. 2637 */ 2638 #define SET_TYPE_FIELD(_ctype, _jname, _isref) \ 2639 static void Set##_jname##Field(JNIEnv* env, jobject jobj, \ 2640 jfieldID fieldID, _ctype value) \ 2641 { \ 2642 JNI_ENTER(); \ 2643 Object* obj = dvmDecodeIndirectRef(env, jobj); \ 2644 InstField* field = (InstField*) fieldID; \ 2645 if (_isref) { /* only when _ctype==jobject */ \ 2646 Object* valObj = dvmDecodeIndirectRef(env, (jobject)(u4)value); \ 2647 dvmSetFieldObject(obj, field->byteOffset, valObj); \ 2648 } else { \ 2649 dvmSetField##_jname(obj, field->byteOffset, value); \ 2650 } \ 2651 JNI_EXIT(); \ 2652 } 2653 SET_TYPE_FIELD(jobject, Object, true); 2654 SET_TYPE_FIELD(jboolean, Boolean, false); 2655 SET_TYPE_FIELD(jbyte, Byte, false); 2656 SET_TYPE_FIELD(jchar, Char, false); 2657 SET_TYPE_FIELD(jshort, Short, false); 2658 SET_TYPE_FIELD(jint, Int, false); 2659 SET_TYPE_FIELD(jlong, Long, false); 2660 SET_TYPE_FIELD(jfloat, Float, false); 2661 SET_TYPE_FIELD(jdouble, Double, false); 2662 2663 /* 2664 * Make a virtual method call. 2665 * 2666 * Three versions (..., va_list, jvalue[]) for each return type. If we're 2667 * returning an Object, we have to add it to the local references table. 2668 */ 2669 #define CALL_VIRTUAL(_ctype, _jname, _retfail, _retok, _isref) \ 2670 static _ctype Call##_jname##Method(JNIEnv* env, jobject jobj, \ 2671 jmethodID methodID, ...) \ 2672 { \ 2673 JNI_ENTER(); \ 2674 Object* obj = dvmDecodeIndirectRef(env, jobj); \ 2675 const Method* meth; \ 2676 va_list args; \ 2677 JValue result; \ 2678 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \ 2679 if (meth == NULL) { \ 2680 JNI_EXIT(); \ 2681 return _retfail; \ 2682 } \ 2683 va_start(args, methodID); \ 2684 dvmCallMethodV(_self, meth, obj, true, &result, args); \ 2685 va_end(args); \ 2686 if (_isref && !dvmCheckException(_self)) \ 2687 result.l = addLocalReference(env, result.l); \ 2688 JNI_EXIT(); \ 2689 return _retok; \ 2690 } \ 2691 static _ctype Call##_jname##MethodV(JNIEnv* env, jobject jobj, \ 2692 jmethodID methodID, va_list args) \ 2693 { \ 2694 JNI_ENTER(); \ 2695 Object* obj = dvmDecodeIndirectRef(env, jobj); \ 2696 const Method* meth; \ 2697 JValue result; \ 2698 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \ 2699 if (meth == NULL) { \ 2700 JNI_EXIT(); \ 2701 return _retfail; \ 2702 } \ 2703 dvmCallMethodV(_self, meth, obj, true, &result, args); \ 2704 if (_isref && !dvmCheckException(_self)) \ 2705 result.l = addLocalReference(env, result.l); \ 2706 JNI_EXIT(); \ 2707 return _retok; \ 2708 } \ 2709 static _ctype Call##_jname##MethodA(JNIEnv* env, jobject jobj, \ 2710 jmethodID methodID, jvalue* args) \ 2711 { \ 2712 JNI_ENTER(); \ 2713 Object* obj = dvmDecodeIndirectRef(env, jobj); \ 2714 const Method* meth; \ 2715 JValue result; \ 2716 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \ 2717 if (meth == NULL) { \ 2718 JNI_EXIT(); \ 2719 return _retfail; \ 2720 } \ 2721 dvmCallMethodA(_self, meth, obj, true, &result, args); \ 2722 if (_isref && !dvmCheckException(_self)) \ 2723 result.l = addLocalReference(env, result.l); \ 2724 JNI_EXIT(); \ 2725 return _retok; \ 2726 } 2727 CALL_VIRTUAL(jobject, Object, NULL, result.l, true); 2728 CALL_VIRTUAL(jboolean, Boolean, 0, result.z, false); 2729 CALL_VIRTUAL(jbyte, Byte, 0, result.b, false); 2730 CALL_VIRTUAL(jchar, Char, 0, result.c, false); 2731 CALL_VIRTUAL(jshort, Short, 0, result.s, false); 2732 CALL_VIRTUAL(jint, Int, 0, result.i, false); 2733 CALL_VIRTUAL(jlong, Long, 0, result.j, false); 2734 CALL_VIRTUAL(jfloat, Float, 0.0f, result.f, false); 2735 CALL_VIRTUAL(jdouble, Double, 0.0, result.d, false); 2736 CALL_VIRTUAL(void, Void, , , false); 2737 2738 /* 2739 * Make a "non-virtual" method call. We're still calling a virtual method, 2740 * but this time we're not doing an indirection through the object's vtable. 2741 * The "clazz" parameter defines which implementation of a method we want. 2742 * 2743 * Three versions (..., va_list, jvalue[]) for each return type. 2744 */ 2745 #define CALL_NONVIRTUAL(_ctype, _jname, _retfail, _retok, _isref) \ 2746 static _ctype CallNonvirtual##_jname##Method(JNIEnv* env, jobject jobj, \ 2747 jclass jclazz, jmethodID methodID, ...) \ 2748 { \ 2749 JNI_ENTER(); \ 2750 Object* obj = dvmDecodeIndirectRef(env, jobj); \ 2751 ClassObject* clazz = \ 2752 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \ 2753 const Method* meth; \ 2754 va_list args; \ 2755 JValue result; \ 2756 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \ 2757 if (meth == NULL) { \ 2758 JNI_EXIT(); \ 2759 return _retfail; \ 2760 } \ 2761 va_start(args, methodID); \ 2762 dvmCallMethodV(_self, meth, obj, true, &result, args); \ 2763 if (_isref && !dvmCheckException(_self)) \ 2764 result.l = addLocalReference(env, result.l); \ 2765 va_end(args); \ 2766 JNI_EXIT(); \ 2767 return _retok; \ 2768 } \ 2769 static _ctype CallNonvirtual##_jname##MethodV(JNIEnv* env, jobject jobj,\ 2770 jclass jclazz, jmethodID methodID, va_list args) \ 2771 { \ 2772 JNI_ENTER(); \ 2773 Object* obj = dvmDecodeIndirectRef(env, jobj); \ 2774 ClassObject* clazz = \ 2775 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \ 2776 const Method* meth; \ 2777 JValue result; \ 2778 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \ 2779 if (meth == NULL) { \ 2780 JNI_EXIT(); \ 2781 return _retfail; \ 2782 } \ 2783 dvmCallMethodV(_self, meth, obj, true, &result, args); \ 2784 if (_isref && !dvmCheckException(_self)) \ 2785 result.l = addLocalReference(env, result.l); \ 2786 JNI_EXIT(); \ 2787 return _retok; \ 2788 } \ 2789 static _ctype CallNonvirtual##_jname##MethodA(JNIEnv* env, jobject jobj,\ 2790 jclass jclazz, jmethodID methodID, jvalue* args) \ 2791 { \ 2792 JNI_ENTER(); \ 2793 Object* obj = dvmDecodeIndirectRef(env, jobj); \ 2794 ClassObject* clazz = \ 2795 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \ 2796 const Method* meth; \ 2797 JValue result; \ 2798 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \ 2799 if (meth == NULL) { \ 2800 JNI_EXIT(); \ 2801 return _retfail; \ 2802 } \ 2803 dvmCallMethodA(_self, meth, obj, true, &result, args); \ 2804 if (_isref && !dvmCheckException(_self)) \ 2805 result.l = addLocalReference(env, result.l); \ 2806 JNI_EXIT(); \ 2807 return _retok; \ 2808 } 2809 CALL_NONVIRTUAL(jobject, Object, NULL, result.l, true); 2810 CALL_NONVIRTUAL(jboolean, Boolean, 0, result.z, false); 2811 CALL_NONVIRTUAL(jbyte, Byte, 0, result.b, false); 2812 CALL_NONVIRTUAL(jchar, Char, 0, result.c, false); 2813 CALL_NONVIRTUAL(jshort, Short, 0, result.s, false); 2814 CALL_NONVIRTUAL(jint, Int, 0, result.i, false); 2815 CALL_NONVIRTUAL(jlong, Long, 0, result.j, false); 2816 CALL_NONVIRTUAL(jfloat, Float, 0.0f, result.f, false); 2817 CALL_NONVIRTUAL(jdouble, Double, 0.0, result.d, false); 2818 CALL_NONVIRTUAL(void, Void, , , false); 2819 2820 2821 /* 2822 * Call a static method. 2823 */ 2824 #define CALL_STATIC(_ctype, _jname, _retfail, _retok, _isref) \ 2825 static _ctype CallStatic##_jname##Method(JNIEnv* env, jclass jclazz, \ 2826 jmethodID methodID, ...) \ 2827 { \ 2828 UNUSED_PARAMETER(jclazz); \ 2829 JNI_ENTER(); \ 2830 JValue result; \ 2831 va_list args; \ 2832 va_start(args, methodID); \ 2833 dvmCallMethodV(_self, (Method*)methodID, NULL, true, &result, args);\ 2834 va_end(args); \ 2835 if (_isref && !dvmCheckException(_self)) \ 2836 result.l = addLocalReference(env, result.l); \ 2837 JNI_EXIT(); \ 2838 return _retok; \ 2839 } \ 2840 static _ctype CallStatic##_jname##MethodV(JNIEnv* env, jclass jclazz, \ 2841 jmethodID methodID, va_list args) \ 2842 { \ 2843 UNUSED_PARAMETER(jclazz); \ 2844 JNI_ENTER(); \ 2845 JValue result; \ 2846 dvmCallMethodV(_self, (Method*)methodID, NULL, true, &result, args);\ 2847 if (_isref && !dvmCheckException(_self)) \ 2848 result.l = addLocalReference(env, result.l); \ 2849 JNI_EXIT(); \ 2850 return _retok; \ 2851 } \ 2852 static _ctype CallStatic##_jname##MethodA(JNIEnv* env, jclass jclazz, \ 2853 jmethodID methodID, jvalue* args) \ 2854 { \ 2855 UNUSED_PARAMETER(jclazz); \ 2856 JNI_ENTER(); \ 2857 JValue result; \ 2858 dvmCallMethodA(_self, (Method*)methodID, NULL, true, &result, args);\ 2859 if (_isref && !dvmCheckException(_self)) \ 2860 result.l = addLocalReference(env, result.l); \ 2861 JNI_EXIT(); \ 2862 return _retok; \ 2863 } 2864 CALL_STATIC(jobject, Object, NULL, result.l, true); 2865 CALL_STATIC(jboolean, Boolean, 0, result.z, false); 2866 CALL_STATIC(jbyte, Byte, 0, result.b, false); 2867 CALL_STATIC(jchar, Char, 0, result.c, false); 2868 CALL_STATIC(jshort, Short, 0, result.s, false); 2869 CALL_STATIC(jint, Int, 0, result.i, false); 2870 CALL_STATIC(jlong, Long, 0, result.j, false); 2871 CALL_STATIC(jfloat, Float, 0.0f, result.f, false); 2872 CALL_STATIC(jdouble, Double, 0.0, result.d, false); 2873 CALL_STATIC(void, Void, , , false); 2874 2875 /* 2876 * Create a new String from Unicode data. 2877 * 2878 * If "len" is zero, we will return an empty string even if "unicodeChars" 2879 * is NULL. (The JNI spec is vague here.) 2880 */ 2881 static jstring NewString(JNIEnv* env, const jchar* unicodeChars, jsize len) 2882 { 2883 JNI_ENTER(); 2884 jobject retval; 2885 2886 StringObject* jstr = dvmCreateStringFromUnicode(unicodeChars, len); 2887 if (jstr == NULL) { 2888 retval = NULL; 2889 } else { 2890 dvmReleaseTrackedAlloc((Object*) jstr, NULL); 2891 retval = addLocalReference(env, (Object*) jstr); 2892 } 2893 2894 JNI_EXIT(); 2895 return retval; 2896 } 2897 2898 /* 2899 * Return the length of a String in Unicode character units. 2900 */ 2901 static jsize GetStringLength(JNIEnv* env, jstring jstr) 2902 { 2903 JNI_ENTER(); 2904 2905 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); 2906 jsize len = dvmStringLen(strObj); 2907 2908 JNI_EXIT(); 2909 return len; 2910 } 2911 2912 2913 /* 2914 * Get a string's character data. 2915 * 2916 * The result is guaranteed to be valid until ReleaseStringChars is 2917 * called, which means we have to pin it or return a copy. 2918 */ 2919 static const jchar* GetStringChars(JNIEnv* env, jstring jstr, jboolean* isCopy) 2920 { 2921 JNI_ENTER(); 2922 2923 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); 2924 ArrayObject* strChars = dvmStringCharArray(strObj); 2925 2926 pinPrimitiveArray(strChars); 2927 2928 const u2* data = dvmStringChars(strObj); 2929 if (isCopy != NULL) 2930 *isCopy = JNI_FALSE; 2931 2932 JNI_EXIT(); 2933 return (jchar*)data; 2934 } 2935 2936 /* 2937 * Release our grip on some characters from a string. 2938 */ 2939 static void ReleaseStringChars(JNIEnv* env, jstring jstr, const jchar* chars) 2940 { 2941 JNI_ENTER(); 2942 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); 2943 ArrayObject* strChars = dvmStringCharArray(strObj); 2944 unpinPrimitiveArray(strChars); 2945 JNI_EXIT(); 2946 } 2947 2948 /* 2949 * Create a new java.lang.String object from chars in modified UTF-8 form. 2950 * 2951 * The spec doesn't say how to handle a NULL string. Popular desktop VMs 2952 * accept it and return a NULL pointer in response. 2953 */ 2954 static jstring NewStringUTF(JNIEnv* env, const char* bytes) 2955 { 2956 JNI_ENTER(); 2957 2958 jstring result; 2959 2960 if (bytes == NULL) { 2961 result = NULL; 2962 } else { 2963 /* note newStr could come back NULL on OOM */ 2964 StringObject* newStr = dvmCreateStringFromCstr(bytes, ALLOC_DEFAULT); 2965 result = addLocalReference(env, (Object*) newStr); 2966 dvmReleaseTrackedAlloc((Object*)newStr, NULL); 2967 } 2968 2969 JNI_EXIT(); 2970 return result; 2971 } 2972 2973 /* 2974 * Return the length in bytes of the modified UTF-8 form of the string. 2975 */ 2976 static jsize GetStringUTFLength(JNIEnv* env, jstring jstr) 2977 { 2978 JNI_ENTER(); 2979 2980 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); 2981 jsize len = dvmStringUtf8ByteLen(strObj); 2982 2983 JNI_EXIT(); 2984 return len; 2985 } 2986 2987 /* 2988 * Convert "string" to modified UTF-8 and return a pointer. The returned 2989 * value must be released with ReleaseStringUTFChars. 2990 * 2991 * According to the JNI reference, "Returns a pointer to a UTF-8 string, 2992 * or NULL if the operation fails. Returns NULL if and only if an invocation 2993 * of this function has thrown an exception." 2994 * 2995 * The behavior here currently follows that of other open-source VMs, which 2996 * quietly return NULL if "string" is NULL. We should consider throwing an 2997 * NPE. (The CheckJNI code blows up if you try to pass in a NULL string, 2998 * which should catch this sort of thing during development.) Certain other 2999 * VMs will crash with a segmentation fault. 3000 */ 3001 static const char* GetStringUTFChars(JNIEnv* env, jstring jstr, 3002 jboolean* isCopy) 3003 { 3004 JNI_ENTER(); 3005 char* newStr; 3006 3007 if (jstr == NULL) { 3008 /* this shouldn't happen; throw NPE? */ 3009 newStr = NULL; 3010 } else { 3011 if (isCopy != NULL) 3012 *isCopy = JNI_TRUE; 3013 3014 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); 3015 newStr = dvmCreateCstrFromString(strObj); 3016 if (newStr == NULL) { 3017 /* assume memory failure */ 3018 dvmThrowException("Ljava/lang/OutOfMemoryError;", 3019 "native heap string alloc failed"); 3020 } 3021 } 3022 3023 JNI_EXIT(); 3024 return newStr; 3025 } 3026 3027 /* 3028 * Release a string created by GetStringUTFChars(). 3029 */ 3030 static void ReleaseStringUTFChars(JNIEnv* env, jstring jstr, const char* utf) 3031 { 3032 JNI_ENTER(); 3033 free((char*)utf); 3034 JNI_EXIT(); 3035 } 3036 3037 /* 3038 * Return the capacity of the array. 3039 */ 3040 static jsize GetArrayLength(JNIEnv* env, jarray jarr) 3041 { 3042 JNI_ENTER(); 3043 3044 ArrayObject* arrObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr); 3045 jsize length = arrObj->length; 3046 3047 JNI_EXIT(); 3048 return length; 3049 } 3050 3051 /* 3052 * Construct a new array that holds objects from class "elementClass". 3053 */ 3054 static jobjectArray NewObjectArray(JNIEnv* env, jsize length, 3055 jclass jelementClass, jobject jinitialElement) 3056 { 3057 JNI_ENTER(); 3058 3059 jobjectArray newArray = NULL; 3060 ClassObject* elemClassObj = 3061 (ClassObject*) dvmDecodeIndirectRef(env, jelementClass); 3062 3063 if (elemClassObj == NULL) { 3064 dvmThrowException("Ljava/lang/NullPointerException;", 3065 "JNI NewObjectArray"); 3066 goto bail; 3067 } 3068 3069 ArrayObject* newObj = 3070 dvmAllocObjectArray(elemClassObj, length, ALLOC_DEFAULT); 3071 if (newObj == NULL) { 3072 assert(dvmCheckException(_self)); 3073 goto bail; 3074 } 3075 newArray = addLocalReference(env, (Object*) newObj); 3076 dvmReleaseTrackedAlloc((Object*) newObj, NULL); 3077 3078 /* 3079 * Initialize the array. Trashes "length". 3080 */ 3081 if (jinitialElement != NULL) { 3082 Object* initialElement = dvmDecodeIndirectRef(env, jinitialElement); 3083 Object** arrayData = (Object**) newObj->contents; 3084 3085 while (length--) 3086 *arrayData++ = initialElement; 3087 } 3088 3089 3090 bail: 3091 JNI_EXIT(); 3092 return newArray; 3093 } 3094 3095 /* 3096 * Get one element of an Object array. 3097 * 3098 * Add the object to the local references table in case the array goes away. 3099 */ 3100 static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray jarr, 3101 jsize index) 3102 { 3103 JNI_ENTER(); 3104 3105 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr); 3106 jobject retval = NULL; 3107 3108 assert(arrayObj != NULL); 3109 3110 /* check the array bounds */ 3111 if (index < 0 || index >= (int) arrayObj->length) { 3112 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", 3113 arrayObj->obj.clazz->descriptor); 3114 goto bail; 3115 } 3116 3117 Object* value = ((Object**) arrayObj->contents)[index]; 3118 retval = addLocalReference(env, value); 3119 3120 bail: 3121 JNI_EXIT(); 3122 return retval; 3123 } 3124 3125 /* 3126 * Set one element of an Object array. 3127 */ 3128 static void SetObjectArrayElement(JNIEnv* env, jobjectArray jarr, 3129 jsize index, jobject jobj) 3130 { 3131 JNI_ENTER(); 3132 3133 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr); 3134 3135 assert(arrayObj != NULL); 3136 3137 /* check the array bounds */ 3138 if (index < 0 || index >= (int) arrayObj->length) { 3139 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", 3140 arrayObj->obj.clazz->descriptor); 3141 goto bail; 3142 } 3143 3144 //LOGV("JNI: set element %d in array %p to %p\n", index, array, value); 3145 3146 Object* obj = dvmDecodeIndirectRef(env, jobj); 3147 ((Object**) arrayObj->contents)[index] = obj; 3148 3149 bail: 3150 JNI_EXIT(); 3151 } 3152 3153 /* 3154 * Create a new array of primitive elements. 3155 */ 3156 #define NEW_PRIMITIVE_ARRAY(_artype, _jname, _typechar) \ 3157 static _artype New##_jname##Array(JNIEnv* env, jsize length) \ 3158 { \ 3159 JNI_ENTER(); \ 3160 ArrayObject* arrayObj; \ 3161 arrayObj = dvmAllocPrimitiveArray(_typechar, length, \ 3162 ALLOC_DEFAULT); \ 3163 jarray jarr = NULL; \ 3164 if (arrayObj != NULL) { \ 3165 jarr = addLocalReference(env, (Object*) arrayObj); \ 3166 dvmReleaseTrackedAlloc((Object*) arrayObj, NULL); \ 3167 } \ 3168 JNI_EXIT(); \ 3169 return (_artype)jarr; \ 3170 } 3171 NEW_PRIMITIVE_ARRAY(jbooleanArray, Boolean, 'Z'); 3172 NEW_PRIMITIVE_ARRAY(jbyteArray, Byte, 'B'); 3173 NEW_PRIMITIVE_ARRAY(jcharArray, Char, 'C'); 3174 NEW_PRIMITIVE_ARRAY(jshortArray, Short, 'S'); 3175 NEW_PRIMITIVE_ARRAY(jintArray, Int, 'I'); 3176 NEW_PRIMITIVE_ARRAY(jlongArray, Long, 'J'); 3177 NEW_PRIMITIVE_ARRAY(jfloatArray, Float, 'F'); 3178 NEW_PRIMITIVE_ARRAY(jdoubleArray, Double, 'D'); 3179 3180 /* 3181 * Get a pointer to a C array of primitive elements from an array object 3182 * of the matching type. 3183 * 3184 * In a compacting GC, we either need to return a copy of the elements or 3185 * "pin" the memory. Otherwise we run the risk of native code using the 3186 * buffer as the destination of e.g. a blocking read() call that wakes up 3187 * during a GC. 3188 */ 3189 #define GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \ 3190 static _ctype* Get##_jname##ArrayElements(JNIEnv* env, \ 3191 _ctype##Array jarr, jboolean* isCopy) \ 3192 { \ 3193 JNI_ENTER(); \ 3194 _ctype* data; \ 3195 ArrayObject* arrayObj = \ 3196 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \ 3197 pinPrimitiveArray(arrayObj); \ 3198 data = (_ctype*) arrayObj->contents; \ 3199 if (isCopy != NULL) \ 3200 *isCopy = JNI_FALSE; \ 3201 JNI_EXIT(); \ 3202 return data; \ 3203 } 3204 3205 /* 3206 * Release the storage locked down by the "get" function. 3207 * 3208 * The spec says, "'mode' has no effect if 'elems' is not a copy of the 3209 * elements in 'array'." They apparently did not anticipate the need to 3210 * un-pin memory. 3211 */ 3212 #define RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \ 3213 static void Release##_jname##ArrayElements(JNIEnv* env, \ 3214 _ctype##Array jarr, _ctype* elems, jint mode) \ 3215 { \ 3216 UNUSED_PARAMETER(elems); \ 3217 JNI_ENTER(); \ 3218 if (mode != JNI_COMMIT) { \ 3219 ArrayObject* arrayObj = \ 3220 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \ 3221 unpinPrimitiveArray(arrayObj); \ 3222 } \ 3223 JNI_EXIT(); \ 3224 } 3225 3226 /* 3227 * Copy a section of a primitive array to a buffer. 3228 */ 3229 #define GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \ 3230 static void Get##_jname##ArrayRegion(JNIEnv* env, \ 3231 _ctype##Array jarr, jsize start, jsize len, _ctype* buf) \ 3232 { \ 3233 JNI_ENTER(); \ 3234 ArrayObject* arrayObj = \ 3235 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \ 3236 _ctype* data = (_ctype*) arrayObj->contents; \ 3237 if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \ 3238 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \ 3239 arrayObj->obj.clazz->descriptor); \ 3240 } else { \ 3241 memcpy(buf, data + start, len * sizeof(_ctype)); \ 3242 } \ 3243 JNI_EXIT(); \ 3244 } 3245 3246 /* 3247 * Copy a section of a primitive array to a buffer. 3248 */ 3249 #define SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \ 3250 static void Set##_jname##ArrayRegion(JNIEnv* env, \ 3251 _ctype##Array jarr, jsize start, jsize len, const _ctype* buf) \ 3252 { \ 3253 JNI_ENTER(); \ 3254 ArrayObject* arrayObj = \ 3255 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \ 3256 _ctype* data = (_ctype*) arrayObj->contents; \ 3257 if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \ 3258 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \ 3259 arrayObj->obj.clazz->descriptor); \ 3260 } else { \ 3261 memcpy(data + start, buf, len * sizeof(_ctype)); \ 3262 } \ 3263 JNI_EXIT(); \ 3264 } 3265 3266 /* 3267 * 4-in-1: 3268 * Get<Type>ArrayElements 3269 * Release<Type>ArrayElements 3270 * Get<Type>ArrayRegion 3271 * Set<Type>ArrayRegion 3272 */ 3273 #define PRIMITIVE_ARRAY_FUNCTIONS(_ctype, _jname) \ 3274 GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \ 3275 RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \ 3276 GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname); \ 3277 SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname); 3278 3279 PRIMITIVE_ARRAY_FUNCTIONS(jboolean, Boolean); 3280 PRIMITIVE_ARRAY_FUNCTIONS(jbyte, Byte); 3281 PRIMITIVE_ARRAY_FUNCTIONS(jchar, Char); 3282 PRIMITIVE_ARRAY_FUNCTIONS(jshort, Short); 3283 PRIMITIVE_ARRAY_FUNCTIONS(jint, Int); 3284 PRIMITIVE_ARRAY_FUNCTIONS(jlong, Long); 3285 PRIMITIVE_ARRAY_FUNCTIONS(jfloat, Float); 3286 PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double); 3287 3288 /* 3289 * Register one or more native functions in one class. 3290 */ 3291 static jint RegisterNatives(JNIEnv* env, jclass jclazz, 3292 const JNINativeMethod* methods, jint nMethods) 3293 { 3294 JNI_ENTER(); 3295 3296 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 3297 jint retval = JNI_OK; 3298 int i; 3299 3300 if (gDvm.verboseJni) { 3301 LOGI("[Registering JNI native methods for class %s]\n", 3302 clazz->descriptor); 3303 } 3304 3305 for (i = 0; i < nMethods; i++) { 3306 if (!dvmRegisterJNIMethod(clazz, methods[i].name, 3307 methods[i].signature, methods[i].fnPtr)) 3308 { 3309 retval = JNI_ERR; 3310 } 3311 } 3312 3313 JNI_EXIT(); 3314 return retval; 3315 } 3316 3317 /* 3318 * Un-register a native function. 3319 */ 3320 static jint UnregisterNatives(JNIEnv* env, jclass jclazz) 3321 { 3322 JNI_ENTER(); 3323 /* 3324 * The JNI docs refer to this as a way to reload/relink native libraries, 3325 * and say it "should not be used in normal native code". 3326 * 3327 * We can implement it if we decide we need it. 3328 */ 3329 JNI_EXIT(); 3330 return JNI_ERR; 3331 } 3332 3333 /* 3334 * Lock the monitor. 3335 * 3336 * We have to track all monitor enters and exits, so that we can undo any 3337 * outstanding synchronization before the thread exits. 3338 */ 3339 static jint MonitorEnter(JNIEnv* env, jobject jobj) 3340 { 3341 JNI_ENTER(); 3342 Object* obj = dvmDecodeIndirectRef(env, jobj); 3343 dvmLockObject(_self, obj); 3344 trackMonitorEnter(_self, obj); 3345 JNI_EXIT(); 3346 return JNI_OK; 3347 } 3348 3349 /* 3350 * Unlock the monitor. 3351 * 3352 * Throws an IllegalMonitorStateException if the current thread 3353 * doesn't own the monitor. (dvmUnlockObject() takes care of the throw.) 3354 * 3355 * According to the 1.6 spec, it's legal to call here with an exception 3356 * pending. If this fails, we'll stomp the original exception. 3357 */ 3358 static jint MonitorExit(JNIEnv* env, jobject jobj) 3359 { 3360 JNI_ENTER(); 3361 Object* obj = dvmDecodeIndirectRef(env, jobj); 3362 bool success = dvmUnlockObject(_self, obj); 3363 if (success) 3364 trackMonitorExit(_self, obj); 3365 JNI_EXIT(); 3366 return success ? JNI_OK : JNI_ERR; 3367 } 3368 3369 /* 3370 * Return the JavaVM interface associated with the current thread. 3371 */ 3372 static jint GetJavaVM(JNIEnv* env, JavaVM** vm) 3373 { 3374 JNI_ENTER(); 3375 //*vm = gDvm.vmList; 3376 *vm = (JavaVM*) ((JNIEnvExt*)env)->vm; 3377 JNI_EXIT(); 3378 if (*vm == NULL) 3379 return JNI_ERR; 3380 else 3381 return JNI_OK; 3382 } 3383 3384 /* 3385 * Copies "len" Unicode characters, from offset "start". 3386 */ 3387 static void GetStringRegion(JNIEnv* env, jstring jstr, jsize start, jsize len, 3388 jchar* buf) 3389 { 3390 JNI_ENTER(); 3391 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); 3392 if (start + len > dvmStringLen(strObj)) 3393 dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL); 3394 else 3395 memcpy(buf, dvmStringChars(strObj) + start, len * sizeof(u2)); 3396 JNI_EXIT(); 3397 } 3398 3399 /* 3400 * Translates "len" Unicode characters, from offset "start", into 3401 * modified UTF-8 encoding. 3402 */ 3403 static void GetStringUTFRegion(JNIEnv* env, jstring jstr, jsize start, 3404 jsize len, char* buf) 3405 { 3406 JNI_ENTER(); 3407 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); 3408 if (start + len > dvmStringLen(strObj)) 3409 dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL); 3410 else 3411 dvmCreateCstrFromStringRegion(strObj, start, len, buf); 3412 JNI_EXIT(); 3413 } 3414 3415 /* 3416 * Get a raw pointer to array data. 3417 * 3418 * The caller is expected to call "release" before doing any JNI calls 3419 * or blocking I/O operations. 3420 * 3421 * We need to pin the memory or block GC. 3422 */ 3423 static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray jarr, 3424 jboolean* isCopy) 3425 { 3426 JNI_ENTER(); 3427 void* data; 3428 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr); 3429 pinPrimitiveArray(arrayObj); 3430 data = arrayObj->contents; 3431 if (isCopy != NULL) 3432 *isCopy = JNI_FALSE; 3433 JNI_EXIT(); 3434 return data; 3435 } 3436 3437 /* 3438 * Release an array obtained with GetPrimitiveArrayCritical. 3439 */ 3440 static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray jarr, 3441 void* carray, jint mode) 3442 { 3443 JNI_ENTER(); 3444 if (mode != JNI_COMMIT) { 3445 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr); 3446 unpinPrimitiveArray(arrayObj); 3447 } 3448 JNI_EXIT(); 3449 } 3450 3451 /* 3452 * Like GetStringChars, but with restricted use. 3453 */ 3454 static const jchar* GetStringCritical(JNIEnv* env, jstring jstr, 3455 jboolean* isCopy) 3456 { 3457 JNI_ENTER(); 3458 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); 3459 ArrayObject* strChars = dvmStringCharArray(strObj); 3460 3461 pinPrimitiveArray(strChars); 3462 3463 const u2* data = dvmStringChars(strObj); 3464 if (isCopy != NULL) 3465 *isCopy = JNI_FALSE; 3466 3467 JNI_EXIT(); 3468 return (jchar*)data; 3469 } 3470 3471 /* 3472 * Like ReleaseStringChars, but with restricted use. 3473 */ 3474 static void ReleaseStringCritical(JNIEnv* env, jstring jstr, 3475 const jchar* carray) 3476 { 3477 JNI_ENTER(); 3478 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); 3479 ArrayObject* strChars = dvmStringCharArray(strObj); 3480 unpinPrimitiveArray(strChars); 3481 JNI_EXIT(); 3482 } 3483 3484 /* 3485 * Create a new weak global reference. 3486 */ 3487 static jweak NewWeakGlobalRef(JNIEnv* env, jobject obj) 3488 { 3489 JNI_ENTER(); 3490 jweak wref = createWeakGlobalRef(env, obj); 3491 JNI_EXIT(); 3492 return wref; 3493 } 3494 3495 /* 3496 * Delete the specified weak global reference. 3497 */ 3498 static void DeleteWeakGlobalRef(JNIEnv* env, jweak wref) 3499 { 3500 JNI_ENTER(); 3501 deleteWeakGlobalRef(env, wref); 3502 JNI_EXIT(); 3503 } 3504 3505 /* 3506 * Quick check for pending exceptions. 3507 * 3508 * TODO: we should be able to skip the enter/exit macros here. 3509 */ 3510 static jboolean ExceptionCheck(JNIEnv* env) 3511 { 3512 JNI_ENTER(); 3513 bool result = dvmCheckException(_self); 3514 JNI_EXIT(); 3515 return result; 3516 } 3517 3518 /* 3519 * Returns the type of the object referred to by "obj". It can be local, 3520 * global, or weak global. 3521 * 3522 * In the current implementation, references can be global and local at 3523 * the same time, so while the return value is accurate it may not tell 3524 * the whole story. 3525 */ 3526 static jobjectRefType GetObjectRefType(JNIEnv* env, jobject jobj) 3527 { 3528 JNI_ENTER(); 3529 jobjectRefType type = dvmGetJNIRefType(env, jobj); 3530 JNI_EXIT(); 3531 return type; 3532 } 3533 3534 /* 3535 * Allocate and return a new java.nio.ByteBuffer for this block of memory. 3536 * 3537 * "address" may not be NULL, and "capacity" must be > 0. (These are only 3538 * verified when CheckJNI is enabled.) 3539 */ 3540 static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity) 3541 { 3542 JNI_ENTER(); 3543 3544 Thread* self = _self /*dvmThreadSelf()*/; 3545 Object* platformAddress = NULL; 3546 JValue callResult; 3547 jobject result = NULL; 3548 ClassObject* tmpClazz; 3549 3550 tmpClazz = gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on->clazz; 3551 if (!dvmIsClassInitialized(tmpClazz) && !dvmInitClass(tmpClazz)) 3552 goto bail; 3553 3554 /* get an instance of PlatformAddress that wraps the provided address */ 3555 dvmCallMethod(self, 3556 gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on, 3557 NULL, &callResult, address); 3558 if (dvmGetException(self) != NULL || callResult.l == NULL) 3559 goto bail; 3560 3561 /* don't let the GC discard it */ 3562 platformAddress = (Object*) callResult.l; 3563 dvmAddTrackedAlloc(platformAddress, self); 3564 LOGV("tracking %p for address=%p\n", platformAddress, address); 3565 3566 /* create an instance of java.nio.ReadWriteDirectByteBuffer */ 3567 tmpClazz = gDvm.classJavaNioReadWriteDirectByteBuffer; 3568 if (!dvmIsClassInitialized(tmpClazz) && !dvmInitClass(tmpClazz)) 3569 goto bail; 3570 Object* newObj = dvmAllocObject(tmpClazz, ALLOC_DONT_TRACK); 3571 if (newObj != NULL) { 3572 /* call the (PlatformAddress, int, int) constructor */ 3573 result = addLocalReference(env, newObj); 3574 dvmCallMethod(self, gDvm.methJavaNioReadWriteDirectByteBuffer_init, 3575 newObj, &callResult, platformAddress, (jint) capacity, (jint) 0); 3576 if (dvmGetException(self) != NULL) { 3577 deleteLocalReference(env, result); 3578 result = NULL; 3579 goto bail; 3580 } 3581 } 3582 3583 bail: 3584 if (platformAddress != NULL) 3585 dvmReleaseTrackedAlloc(platformAddress, self); 3586 JNI_EXIT(); 3587 return result; 3588 } 3589 3590 /* 3591 * Get the starting address of the buffer for the specified java.nio.Buffer. 3592 * 3593 * If this is not a "direct" buffer, we return NULL. 3594 */ 3595 static void* GetDirectBufferAddress(JNIEnv* env, jobject jbuf) 3596 { 3597 JNI_ENTER(); 3598 3599 Object* bufObj = dvmDecodeIndirectRef(env, jbuf); 3600 Thread* self = _self /*dvmThreadSelf()*/; 3601 void* result; 3602 3603 /* 3604 * All Buffer objects have an effectiveDirectAddress field. If it's 3605 * nonzero, we can just return that value. If not, we have to call 3606 * through DirectBuffer.getEffectiveAddress(), which as a side-effect 3607 * will set the effectiveDirectAddress field for direct buffers (and 3608 * things that wrap direct buffers). 3609 */ 3610 result = (void*) dvmGetFieldInt(bufObj, 3611 gDvm.offJavaNioBuffer_effectiveDirectAddress); 3612 if (result != NULL) { 3613 //LOGI("fast path for %p\n", buf); 3614 goto bail; 3615 } 3616 3617 /* 3618 * Start by determining if the object supports the DirectBuffer 3619 * interfaces. Note this does not guarantee that it's a direct buffer. 3620 */ 3621 if (!dvmInstanceof(bufObj->clazz, 3622 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer)) 3623 { 3624 goto bail; 3625 } 3626 3627 /* 3628 * Get a PlatformAddress object with the effective address. 3629 * 3630 * If this isn't a direct buffer, the result will be NULL and/or an 3631 * exception will have been thrown. 3632 */ 3633 JValue callResult; 3634 const Method* meth = dvmGetVirtualizedMethod(bufObj->clazz, 3635 gDvm.methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress); 3636 dvmCallMethodA(self, meth, bufObj, false, &callResult, NULL); 3637 if (dvmGetException(self) != NULL) { 3638 dvmClearException(self); 3639 callResult.l = NULL; 3640 } 3641 3642 Object* platformAddr = callResult.l; 3643 if (platformAddr == NULL) { 3644 LOGV("Got request for address of non-direct buffer\n"); 3645 goto bail; 3646 } 3647 3648 /* 3649 * Extract the address from the PlatformAddress object. Instead of 3650 * calling the toLong() method, just grab the field directly. This 3651 * is faster but more fragile. 3652 */ 3653 result = (void*) dvmGetFieldInt(platformAddr, 3654 gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr); 3655 3656 //LOGI("slow path for %p --> %p\n", buf, result); 3657 3658 bail: 3659 JNI_EXIT(); 3660 return result; 3661 } 3662 3663 /* 3664 * Get the capacity of the buffer for the specified java.nio.Buffer. 3665 * 3666 * Returns -1 if the object is not a direct buffer. (We actually skip 3667 * this check, since it's expensive to determine, and just return the 3668 * capacity regardless.) 3669 */ 3670 static jlong GetDirectBufferCapacity(JNIEnv* env, jobject jbuf) 3671 { 3672 JNI_ENTER(); 3673 3674 /* 3675 * The capacity is always in the Buffer.capacity field. 3676 * 3677 * (The "check" version should verify that this is actually a Buffer, 3678 * but we're not required to do so here.) 3679 */ 3680 Object* buf = dvmDecodeIndirectRef(env, jbuf); 3681 jlong result = dvmGetFieldInt(buf, gDvm.offJavaNioBuffer_capacity); 3682 3683 JNI_EXIT(); 3684 return result; 3685 } 3686 3687 3688 /* 3689 * =========================================================================== 3690 * JNI invocation functions 3691 * =========================================================================== 3692 */ 3693 3694 /* 3695 * Handle AttachCurrentThread{AsDaemon}. 3696 * 3697 * We need to make sure the VM is actually running. For example, if we start 3698 * up, issue an Attach, and the VM exits almost immediately, by the time the 3699 * attaching happens the VM could already be shutting down. 3700 * 3701 * It's hard to avoid a race condition here because we don't want to hold 3702 * a lock across the entire operation. What we can do is temporarily 3703 * increment the thread count to prevent a VM exit. 3704 * 3705 * This could potentially still have problems if a daemon thread calls here 3706 * while the VM is shutting down. dvmThreadSelf() will work, since it just 3707 * uses pthread TLS, but dereferencing "vm" could fail. Such is life when 3708 * you shut down a VM while threads are still running inside it. 3709 * 3710 * Remember that some code may call this as a way to find the per-thread 3711 * JNIEnv pointer. Don't do excess work for that case. 3712 */ 3713 static jint attachThread(JavaVM* vm, JNIEnv** p_env, void* thr_args, 3714 bool isDaemon) 3715 { 3716 JavaVMAttachArgs* args = (JavaVMAttachArgs*) thr_args; 3717 Thread* self; 3718 bool result = false; 3719 3720 /* 3721 * Return immediately if we're already one with the VM. 3722 */ 3723 self = dvmThreadSelf(); 3724 if (self != NULL) { 3725 *p_env = self->jniEnv; 3726 return JNI_OK; 3727 } 3728 3729 /* 3730 * No threads allowed in zygote mode. 3731 */ 3732 if (gDvm.zygote) { 3733 return JNI_ERR; 3734 } 3735 3736 /* increment the count to keep the VM from bailing while we run */ 3737 dvmLockThreadList(NULL); 3738 if (gDvm.nonDaemonThreadCount == 0) { 3739 // dead or dying 3740 LOGV("Refusing to attach thread '%s' -- VM is shutting down\n", 3741 (thr_args == NULL) ? "(unknown)" : args->name); 3742 dvmUnlockThreadList(); 3743 return JNI_ERR; 3744 } 3745 gDvm.nonDaemonThreadCount++; 3746 dvmUnlockThreadList(); 3747 3748 /* tweak the JavaVMAttachArgs as needed */ 3749 JavaVMAttachArgs argsCopy; 3750 if (args == NULL) { 3751 /* allow the v1.1 calling convention */ 3752 argsCopy.version = JNI_VERSION_1_2; 3753 argsCopy.name = NULL; 3754 argsCopy.group = dvmGetMainThreadGroup(); 3755 } else { 3756 assert(args->version >= JNI_VERSION_1_2); 3757 3758 argsCopy.version = args->version; 3759 argsCopy.name = args->name; 3760 if (args->group != NULL) 3761 argsCopy.group = args->group; 3762 else 3763 argsCopy.group = dvmGetMainThreadGroup(); 3764 } 3765 3766 result = dvmAttachCurrentThread(&argsCopy, isDaemon); 3767 3768 /* restore the count */ 3769 dvmLockThreadList(NULL); 3770 gDvm.nonDaemonThreadCount--; 3771 dvmUnlockThreadList(); 3772 3773 /* 3774 * Change the status to indicate that we're out in native code. This 3775 * call is not guarded with state-change macros, so we have to do it 3776 * by hand. 3777 */ 3778 if (result) { 3779 self = dvmThreadSelf(); 3780 assert(self != NULL); 3781 dvmChangeStatus(self, THREAD_NATIVE); 3782 *p_env = self->jniEnv; 3783 return JNI_OK; 3784 } else { 3785 return JNI_ERR; 3786 } 3787 } 3788 3789 /* 3790 * Attach the current thread to the VM. If the thread is already attached, 3791 * this is a no-op. 3792 */ 3793 static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args) 3794 { 3795 return attachThread(vm, p_env, thr_args, false); 3796 } 3797 3798 /* 3799 * Like AttachCurrentThread, but set the "daemon" flag. 3800 */ 3801 static jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env, 3802 void* thr_args) 3803 { 3804 return attachThread(vm, p_env, thr_args, true); 3805 } 3806 3807 /* 3808 * Dissociate the current thread from the VM. 3809 */ 3810 static jint DetachCurrentThread(JavaVM* vm) 3811 { 3812 Thread* self = dvmThreadSelf(); 3813 3814 if (self == NULL) /* not attached, can't do anything */ 3815 return JNI_ERR; 3816 3817 /* switch to "running" to check for suspension */ 3818 dvmChangeStatus(self, THREAD_RUNNING); 3819 3820 /* detach the thread */ 3821 dvmDetachCurrentThread(); 3822 3823 /* (no need to change status back -- we have no status) */ 3824 return JNI_OK; 3825 } 3826 3827 /* 3828 * If current thread is attached to VM, return the associated JNIEnv. 3829 * Otherwise, stuff NULL in and return JNI_EDETACHED. 3830 * 3831 * JVMTI overloads this by specifying a magic value for "version", so we 3832 * do want to check that here. 3833 */ 3834 static jint GetEnv(JavaVM* vm, void** env, jint version) 3835 { 3836 Thread* self = dvmThreadSelf(); 3837 3838 if (version < JNI_VERSION_1_1 || version > JNI_VERSION_1_6) 3839 return JNI_EVERSION; 3840 3841 if (self == NULL) { 3842 *env = NULL; 3843 } else { 3844 /* TODO: status change is probably unnecessary */ 3845 dvmChangeStatus(self, THREAD_RUNNING); 3846 *env = (void*) dvmGetThreadJNIEnv(self); 3847 dvmChangeStatus(self, THREAD_NATIVE); 3848 } 3849 if (*env == NULL) 3850 return JNI_EDETACHED; 3851 else 3852 return JNI_OK; 3853 } 3854 3855 /* 3856 * Destroy the VM. This may be called from any thread. 3857 * 3858 * If the current thread is attached, wait until the current thread is 3859 * the only non-daemon user-level thread. If the current thread is not 3860 * attached, we attach it and do the processing as usual. (If the attach 3861 * fails, it's probably because all the non-daemon threads have already 3862 * exited and the VM doesn't want to let us back in.) 3863 * 3864 * TODO: we don't really deal with the situation where more than one thread 3865 * has called here. One thread wins, the other stays trapped waiting on 3866 * the condition variable forever. Not sure this situation is interesting 3867 * in real life. 3868 */ 3869 static jint DestroyJavaVM(JavaVM* vm) 3870 { 3871 JavaVMExt* ext = (JavaVMExt*) vm; 3872 Thread* self; 3873 3874 if (ext == NULL) 3875 return JNI_ERR; 3876 3877 if (gDvm.verboseShutdown) 3878 LOGD("DestroyJavaVM waiting for non-daemon threads to exit\n"); 3879 3880 /* 3881 * Sleep on a condition variable until it's okay to exit. 3882 */ 3883 self = dvmThreadSelf(); 3884 if (self == NULL) { 3885 JNIEnv* tmpEnv; 3886 if (AttachCurrentThread(vm, &tmpEnv, NULL) != JNI_OK) { 3887 LOGV("Unable to reattach main for Destroy; assuming VM is " 3888 "shutting down (count=%d)\n", 3889 gDvm.nonDaemonThreadCount); 3890 goto shutdown; 3891 } else { 3892 LOGV("Attached to wait for shutdown in Destroy\n"); 3893 } 3894 } 3895 dvmChangeStatus(self, THREAD_VMWAIT); 3896 3897 dvmLockThreadList(self); 3898 gDvm.nonDaemonThreadCount--; // remove current thread from count 3899 3900 while (gDvm.nonDaemonThreadCount > 0) 3901 pthread_cond_wait(&gDvm.vmExitCond, &gDvm.threadListLock); 3902 3903 dvmUnlockThreadList(); 3904 self = NULL; 3905 3906 shutdown: 3907 // TODO: call System.exit() to run any registered shutdown hooks 3908 // (this may not return -- figure out how this should work) 3909 3910 if (gDvm.verboseShutdown) 3911 LOGD("DestroyJavaVM shutting VM down\n"); 3912 dvmShutdown(); 3913 3914 // TODO - free resources associated with JNI-attached daemon threads 3915 free(ext->envList); 3916 free(ext); 3917 3918 return JNI_OK; 3919 } 3920 3921 3922 /* 3923 * =========================================================================== 3924 * Function tables 3925 * =========================================================================== 3926 */ 3927 3928 static const struct JNINativeInterface gNativeInterface = { 3929 NULL, 3930 NULL, 3931 NULL, 3932 NULL, 3933 3934 GetVersion, 3935 3936 DefineClass, 3937 FindClass, 3938 3939 FromReflectedMethod, 3940 FromReflectedField, 3941 ToReflectedMethod, 3942 3943 GetSuperclass, 3944 IsAssignableFrom, 3945 3946 ToReflectedField, 3947 3948 Throw, 3949 ThrowNew, 3950 ExceptionOccurred, 3951 ExceptionDescribe, 3952 ExceptionClear, 3953 FatalError, 3954 3955 PushLocalFrame, 3956 PopLocalFrame, 3957 3958 NewGlobalRef, 3959 DeleteGlobalRef, 3960 DeleteLocalRef, 3961 IsSameObject, 3962 NewLocalRef, 3963 EnsureLocalCapacity, 3964 3965 AllocObject, 3966 NewObject, 3967 NewObjectV, 3968 NewObjectA, 3969 3970 GetObjectClass, 3971 IsInstanceOf, 3972 3973 GetMethodID, 3974 3975 CallObjectMethod, 3976 CallObjectMethodV, 3977 CallObjectMethodA, 3978 CallBooleanMethod, 3979 CallBooleanMethodV, 3980 CallBooleanMethodA, 3981 CallByteMethod, 3982 CallByteMethodV, 3983 CallByteMethodA, 3984 CallCharMethod, 3985 CallCharMethodV, 3986 CallCharMethodA, 3987 CallShortMethod, 3988 CallShortMethodV, 3989 CallShortMethodA, 3990 CallIntMethod, 3991 CallIntMethodV, 3992 CallIntMethodA, 3993 CallLongMethod, 3994 CallLongMethodV, 3995 CallLongMethodA, 3996 CallFloatMethod, 3997 CallFloatMethodV, 3998 CallFloatMethodA, 3999 CallDoubleMethod, 4000 CallDoubleMethodV, 4001 CallDoubleMethodA, 4002 CallVoidMethod, 4003 CallVoidMethodV, 4004 CallVoidMethodA, 4005 4006 CallNonvirtualObjectMethod, 4007 CallNonvirtualObjectMethodV, 4008 CallNonvirtualObjectMethodA, 4009 CallNonvirtualBooleanMethod, 4010 CallNonvirtualBooleanMethodV, 4011 CallNonvirtualBooleanMethodA, 4012 CallNonvirtualByteMethod, 4013 CallNonvirtualByteMethodV, 4014 CallNonvirtualByteMethodA, 4015 CallNonvirtualCharMethod, 4016 CallNonvirtualCharMethodV, 4017 CallNonvirtualCharMethodA, 4018 CallNonvirtualShortMethod, 4019 CallNonvirtualShortMethodV, 4020 CallNonvirtualShortMethodA, 4021 CallNonvirtualIntMethod, 4022 CallNonvirtualIntMethodV, 4023 CallNonvirtualIntMethodA, 4024 CallNonvirtualLongMethod, 4025 CallNonvirtualLongMethodV, 4026 CallNonvirtualLongMethodA, 4027 CallNonvirtualFloatMethod, 4028 CallNonvirtualFloatMethodV, 4029 CallNonvirtualFloatMethodA, 4030 CallNonvirtualDoubleMethod, 4031 CallNonvirtualDoubleMethodV, 4032 CallNonvirtualDoubleMethodA, 4033 CallNonvirtualVoidMethod, 4034 CallNonvirtualVoidMethodV, 4035 CallNonvirtualVoidMethodA, 4036 4037 GetFieldID, 4038 4039 GetObjectField, 4040 GetBooleanField, 4041 GetByteField, 4042 GetCharField, 4043 GetShortField, 4044 GetIntField, 4045 GetLongField, 4046 GetFloatField, 4047 GetDoubleField, 4048 SetObjectField, 4049 SetBooleanField, 4050 SetByteField, 4051 SetCharField, 4052 SetShortField, 4053 SetIntField, 4054 SetLongField, 4055 SetFloatField, 4056 SetDoubleField, 4057 4058 GetStaticMethodID, 4059 4060 CallStaticObjectMethod, 4061 CallStaticObjectMethodV, 4062 CallStaticObjectMethodA, 4063 CallStaticBooleanMethod, 4064 CallStaticBooleanMethodV, 4065 CallStaticBooleanMethodA, 4066 CallStaticByteMethod, 4067 CallStaticByteMethodV, 4068 CallStaticByteMethodA, 4069 CallStaticCharMethod, 4070 CallStaticCharMethodV, 4071 CallStaticCharMethodA, 4072 CallStaticShortMethod, 4073 CallStaticShortMethodV, 4074 CallStaticShortMethodA, 4075 CallStaticIntMethod, 4076 CallStaticIntMethodV, 4077 CallStaticIntMethodA, 4078 CallStaticLongMethod, 4079 CallStaticLongMethodV, 4080 CallStaticLongMethodA, 4081 CallStaticFloatMethod, 4082 CallStaticFloatMethodV, 4083 CallStaticFloatMethodA, 4084 CallStaticDoubleMethod, 4085 CallStaticDoubleMethodV, 4086 CallStaticDoubleMethodA, 4087 CallStaticVoidMethod, 4088 CallStaticVoidMethodV, 4089 CallStaticVoidMethodA, 4090 4091 GetStaticFieldID, 4092 4093 GetStaticObjectField, 4094 GetStaticBooleanField, 4095 GetStaticByteField, 4096 GetStaticCharField, 4097 GetStaticShortField, 4098 GetStaticIntField, 4099 GetStaticLongField, 4100 GetStaticFloatField, 4101 GetStaticDoubleField, 4102 4103 SetStaticObjectField, 4104 SetStaticBooleanField, 4105 SetStaticByteField, 4106 SetStaticCharField, 4107 SetStaticShortField, 4108 SetStaticIntField, 4109 SetStaticLongField, 4110 SetStaticFloatField, 4111 SetStaticDoubleField, 4112 4113 NewString, 4114 4115 GetStringLength, 4116 GetStringChars, 4117 ReleaseStringChars, 4118 4119 NewStringUTF, 4120 GetStringUTFLength, 4121 GetStringUTFChars, 4122 ReleaseStringUTFChars, 4123 4124 GetArrayLength, 4125 NewObjectArray, 4126 GetObjectArrayElement, 4127 SetObjectArrayElement, 4128 4129 NewBooleanArray, 4130 NewByteArray, 4131 NewCharArray, 4132 NewShortArray, 4133 NewIntArray, 4134 NewLongArray, 4135 NewFloatArray, 4136 NewDoubleArray, 4137 4138 GetBooleanArrayElements, 4139 GetByteArrayElements, 4140 GetCharArrayElements, 4141 GetShortArrayElements, 4142 GetIntArrayElements, 4143 GetLongArrayElements, 4144 GetFloatArrayElements, 4145 GetDoubleArrayElements, 4146 4147 ReleaseBooleanArrayElements, 4148 ReleaseByteArrayElements, 4149 ReleaseCharArrayElements, 4150 ReleaseShortArrayElements, 4151 ReleaseIntArrayElements, 4152 ReleaseLongArrayElements, 4153 ReleaseFloatArrayElements, 4154 ReleaseDoubleArrayElements, 4155 4156 GetBooleanArrayRegion, 4157 GetByteArrayRegion, 4158 GetCharArrayRegion, 4159 GetShortArrayRegion, 4160 GetIntArrayRegion, 4161 GetLongArrayRegion, 4162 GetFloatArrayRegion, 4163 GetDoubleArrayRegion, 4164 SetBooleanArrayRegion, 4165 SetByteArrayRegion, 4166 SetCharArrayRegion, 4167 SetShortArrayRegion, 4168 SetIntArrayRegion, 4169 SetLongArrayRegion, 4170 SetFloatArrayRegion, 4171 SetDoubleArrayRegion, 4172 4173 RegisterNatives, 4174 UnregisterNatives, 4175 4176 MonitorEnter, 4177 MonitorExit, 4178 4179 GetJavaVM, 4180 4181 GetStringRegion, 4182 GetStringUTFRegion, 4183 4184 GetPrimitiveArrayCritical, 4185 ReleasePrimitiveArrayCritical, 4186 4187 GetStringCritical, 4188 ReleaseStringCritical, 4189 4190 NewWeakGlobalRef, 4191 DeleteWeakGlobalRef, 4192 4193 ExceptionCheck, 4194 4195 NewDirectByteBuffer, 4196 GetDirectBufferAddress, 4197 GetDirectBufferCapacity, 4198 4199 GetObjectRefType 4200 }; 4201 static const struct JNIInvokeInterface gInvokeInterface = { 4202 NULL, 4203 NULL, 4204 NULL, 4205 4206 DestroyJavaVM, 4207 AttachCurrentThread, 4208 DetachCurrentThread, 4209 4210 GetEnv, 4211 4212 AttachCurrentThreadAsDaemon, 4213 }; 4214 4215 4216 /* 4217 * =========================================================================== 4218 * VM/Env creation 4219 * =========================================================================== 4220 */ 4221 4222 /* 4223 * Enable "checked JNI" after the VM has partially started. This must 4224 * only be called in "zygote" mode, when we have one thread running. 4225 * 4226 * This doesn't attempt to rewrite the JNI call bridge associated with 4227 * native methods, so we won't get those checks for any methods that have 4228 * already been resolved. 4229 */ 4230 void dvmLateEnableCheckedJni(void) 4231 { 4232 JNIEnvExt* extEnv; 4233 JavaVMExt* extVm; 4234 4235 extEnv = dvmGetJNIEnvForThread(); 4236 if (extEnv == NULL) { 4237 LOGE("dvmLateEnableCheckedJni: thread has no JNIEnv\n"); 4238 return; 4239 } 4240 extVm = extEnv->vm; 4241 assert(extVm != NULL); 4242 4243 if (!extVm->useChecked) { 4244 LOGD("Late-enabling CheckJNI\n"); 4245 dvmUseCheckedJniVm(extVm); 4246 extVm->useChecked = true; 4247 dvmUseCheckedJniEnv(extEnv); 4248 4249 /* currently no way to pick up jniopts features */ 4250 } else { 4251 LOGD("Not late-enabling CheckJNI (already on)\n"); 4252 } 4253 } 4254 4255 /* 4256 * Not supported. 4257 */ 4258 jint JNI_GetDefaultJavaVMInitArgs(void* vm_args) 4259 { 4260 return JNI_ERR; 4261 } 4262 4263 /* 4264 * Return a buffer full of created VMs. 4265 * 4266 * We always have zero or one. 4267 */ 4268 jint JNI_GetCreatedJavaVMs(JavaVM** vmBuf, jsize bufLen, jsize* nVMs) 4269 { 4270 if (gDvm.vmList != NULL) { 4271 *nVMs = 1; 4272 4273 if (bufLen > 0) 4274 *vmBuf++ = gDvm.vmList; 4275 } else { 4276 *nVMs = 0; 4277 } 4278 4279 return JNI_OK; 4280 } 4281 4282 4283 /* 4284 * Create a new VM instance. 4285 * 4286 * The current thread becomes the main VM thread. We return immediately, 4287 * which effectively means the caller is executing in a native method. 4288 */ 4289 jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) 4290 { 4291 const JavaVMInitArgs* args = (JavaVMInitArgs*) vm_args; 4292 JNIEnvExt* pEnv = NULL; 4293 JavaVMExt* pVM = NULL; 4294 const char** argv; 4295 int argc = 0; 4296 int i, curOpt; 4297 int result = JNI_ERR; 4298 bool checkJni = false; 4299 bool warnError = true; 4300 bool forceDataCopy = false; 4301 4302 if (args->version < JNI_VERSION_1_2) 4303 return JNI_EVERSION; 4304 4305 // TODO: don't allow creation of multiple VMs -- one per customer for now 4306 4307 /* zero globals; not strictly necessary the first time a VM is started */ 4308 memset(&gDvm, 0, sizeof(gDvm)); 4309 4310 /* 4311 * Set up structures for JNIEnv and VM. 4312 */ 4313 //pEnv = (JNIEnvExt*) malloc(sizeof(JNIEnvExt)); 4314 pVM = (JavaVMExt*) malloc(sizeof(JavaVMExt)); 4315 4316 //memset(pEnv, 0, sizeof(JNIEnvExt)); 4317 //pEnv->funcTable = &gNativeInterface; 4318 //pEnv->vm = pVM; 4319 memset(pVM, 0, sizeof(JavaVMExt)); 4320 pVM->funcTable = &gInvokeInterface; 4321 pVM->envList = pEnv; 4322 dvmInitMutex(&pVM->envListLock); 4323 4324 argv = (const char**) malloc(sizeof(char*) * (args->nOptions)); 4325 memset(argv, 0, sizeof(char*) * (args->nOptions)); 4326 4327 curOpt = 0; 4328 4329 /* 4330 * Convert JNI args to argv. 4331 * 4332 * We have to pull out vfprintf/exit/abort, because they use the 4333 * "extraInfo" field to pass function pointer "hooks" in. We also 4334 * look for the -Xcheck:jni stuff here. 4335 */ 4336 for (i = 0; i < args->nOptions; i++) { 4337 const char* optStr = args->options[i].optionString; 4338 4339 if (optStr == NULL) { 4340 fprintf(stderr, "ERROR: arg %d string was null\n", i); 4341 goto bail; 4342 } else if (strcmp(optStr, "vfprintf") == 0) { 4343 gDvm.vfprintfHook = args->options[i].extraInfo; 4344 } else if (strcmp(optStr, "exit") == 0) { 4345 gDvm.exitHook = args->options[i].extraInfo; 4346 } else if (strcmp(optStr, "abort") == 0) { 4347 gDvm.abortHook = args->options[i].extraInfo; 4348 } else if (strcmp(optStr, "-Xcheck:jni") == 0) { 4349 checkJni = true; 4350 } else if (strncmp(optStr, "-Xjniopts:", 10) == 0) { 4351 const char* jniOpts = optStr + 9; 4352 while (jniOpts != NULL) { 4353 jniOpts++; /* skip past ':' or ',' */ 4354 if (strncmp(jniOpts, "warnonly", 8) == 0) { 4355 warnError = false; 4356 } else if (strncmp(jniOpts, "forcecopy", 9) == 0) { 4357 forceDataCopy = true; 4358 } else { 4359 LOGW("unknown jni opt starting at '%s'\n", jniOpts); 4360 } 4361 jniOpts = strchr(jniOpts, ','); 4362 } 4363 } else { 4364 /* regular option */ 4365 argv[curOpt++] = optStr; 4366 } 4367 } 4368 argc = curOpt; 4369 4370 if (checkJni) { 4371 dvmUseCheckedJniVm(pVM); 4372 pVM->useChecked = true; 4373 } 4374 pVM->warnError = warnError; 4375 pVM->forceDataCopy = forceDataCopy; 4376 4377 /* set this up before initializing VM, so it can create some JNIEnvs */ 4378 gDvm.vmList = (JavaVM*) pVM; 4379 4380 /* 4381 * Create an env for main thread. We need to have something set up 4382 * here because some of the class initialization we do when starting 4383 * up the VM will call into native code. 4384 */ 4385 pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL); 4386 4387 /* initialize VM */ 4388 gDvm.initializing = true; 4389 if (dvmStartup(argc, argv, args->ignoreUnrecognized, (JNIEnv*)pEnv) != 0) { 4390 free(pEnv); 4391 free(pVM); 4392 goto bail; 4393 } 4394 4395 /* 4396 * Success! Return stuff to caller. 4397 */ 4398 dvmChangeStatus(NULL, THREAD_NATIVE); 4399 *p_env = (JNIEnv*) pEnv; 4400 *p_vm = (JavaVM*) pVM; 4401 result = JNI_OK; 4402 4403 bail: 4404 gDvm.initializing = false; 4405 if (result == JNI_OK) 4406 LOGV("JNI_CreateJavaVM succeeded\n"); 4407 else 4408 LOGW("JNI_CreateJavaVM failed\n"); 4409 free(argv); 4410 return result; 4411 } 4412