Jni.cpp revision 259a8a5154c63a793ea0ee438d146acda7d990b6
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#include "ScopedPthreadMutexLock.h" 23#include "UniquePtr.h" 24 25#include <stdlib.h> 26#include <stdarg.h> 27#include <limits.h> 28 29/* 30Native methods and interaction with the GC 31 32All JNI methods must start by changing their thread status to 33THREAD_RUNNING, and finish by changing it back to THREAD_NATIVE before 34returning to native code. The switch to "running" triggers a thread 35suspension check. 36 37With a rudimentary GC we should be able to skip the status change for 38simple functions, e.g. IsSameObject, GetJavaVM, GetStringLength, maybe 39even access to fields with primitive types. Our options are more limited 40with a compacting GC. 41 42For performance reasons we do as little error-checking as possible here. 43For example, we don't check to make sure the correct type of Object is 44passed in when setting a field, and we don't prevent you from storing 45new values in a "final" field. Such things are best handled in the 46"check" version. For actions that are common, dangerous, and must be 47checked at runtime, such as array bounds checks, we do the tests here. 48 49 50General notes on local/global reference tracking 51 52JNI provides explicit control over natively-held references that the GC 53needs to know about. These can be local, in which case they're released 54when the native method returns into the VM, or global, which are held 55until explicitly released. (There are also weak-global references, 56which have the lifespan and visibility of global references, but the 57object they refer to may be collected.) 58 59The references can be created with explicit JNI NewLocalRef / NewGlobalRef 60calls. The former is very unusual, the latter is reasonably common 61(e.g. for caching references to class objects). 62 63Local references are most often created as a side-effect of JNI functions. 64For example, the AllocObject/NewObject functions must create local 65references to the objects returned, because nothing else in the GC root 66set has a reference to the new objects. 67 68The most common mode of operation is for a method to create zero or 69more local references and return. Explicit "local delete" operations 70are expected to be exceedingly rare, except when walking through an 71object array, and the Push/PopLocalFrame calls are expected to be used 72infrequently. For efficient operation, we want to add new local refs 73with a simple store/increment operation; to avoid infinite growth in 74pathological situations, we need to reclaim the space used by deleted 75entries. 76 77If we just want to maintain a list for the GC root set, we can use an 78expanding append-only array that compacts when objects are deleted. 79In typical situations, e.g. running through an array of objects, we will 80be deleting one of the most recently added entries, so we can minimize 81the number of elements moved (or avoid having to move any). 82 83If we want to conceal the pointer values from native code, which is 84necessary to allow the GC to move JNI-referenced objects around, then we 85have to use a more complicated indirection mechanism. 86 87The spec says, "Local references are only valid in the thread in which 88they are created. The native code must not pass local references from 89one thread to another." 90 91 92Pinned objects 93 94For some large chunks of data, notably primitive arrays and String data, 95JNI allows the VM to choose whether it wants to pin the array object or 96make a copy. We currently pin the memory for better execution performance. 97 98TODO: we're using simple root set references to pin primitive array data, 99because they have the property we need (i.e. the pointer we return is 100guaranteed valid until we explicitly release it). However, if we have a 101compacting GC and don't want to pin all memory held by all global refs, 102we need to treat these differently. 103 104 105Global reference tracking 106 107There should be a small "active" set centered around the most-recently 108added items. 109 110Because it's global, access to it has to be synchronized. Additions and 111removals require grabbing a mutex. If the table serves as an indirection 112mechanism (i.e. it's not just a list for the benefit of the garbage 113collector), reference lookups may also require grabbing a mutex. 114 115The JNI spec does not define any sort of limit, so the list must be able 116to expand to a reasonable size. It may be useful to log significant 117increases in usage to help identify resource leaks. 118 119 120Weak-global reference tracking 121 122[TBD] 123 124 125Local reference tracking 126 127Each Thread/JNIEnv points to an IndirectRefTable. 128 129We implement Push/PopLocalFrame with actual stack frames. Before a JNI 130frame gets popped, we set "nextEntry" to the "top" pointer of the current 131frame, effectively releasing the references. 132 133The GC will scan all references in the table. 134 135*/ 136 137#ifdef WITH_JNI_STACK_CHECK 138# define COMPUTE_STACK_SUM(_self) computeStackSum(_self); 139# define CHECK_STACK_SUM(_self) checkStackSum(_self); 140 141/* 142 * Compute a CRC on the entire interpreted stack. 143 * 144 * Would be nice to compute it on "self" as well, but there are parts of 145 * the Thread that can be altered by other threads (e.g. prev/next pointers). 146 */ 147static void computeStackSum(Thread* self) { 148 const u1* low = (const u1*)SAVEAREA_FROM_FP(self->interpSave.curFrame); 149 u4 crc = dvmInitCrc32(); 150 self->stackCrc = 0; 151 crc = dvmComputeCrc32(crc, low, self->interpStackStart - low); 152 self->stackCrc = crc; 153} 154 155/* 156 * Compute a CRC on the entire interpreted stack, and compare it to what 157 * we previously computed. 158 * 159 * We can execute JNI directly from native code without calling in from 160 * interpreted code during VM initialization and immediately after JNI 161 * thread attachment. Another opportunity exists during JNI_OnLoad. Rather 162 * than catching these cases we just ignore them here, which is marginally 163 * less accurate but reduces the amount of code we have to touch with #ifdefs. 164 */ 165static void checkStackSum(Thread* self) { 166 const u1* low = (const u1*)SAVEAREA_FROM_FP(self->interpSave.curFrame); 167 u4 stackCrc = self->stackCrc; 168 self->stackCrc = 0; 169 u4 crc = dvmInitCrc32(); 170 crc = dvmComputeCrc32(crc, low, self->interpStackStart - low); 171 if (crc != stackCrc) { 172 const Method* meth = dvmGetCurrentJNIMethod(); 173 if (dvmComputeExactFrameDepth(self->interpSave.curFrame) == 1) { 174 LOGD("JNI: bad stack CRC (0x%08x) -- okay during init", stackCrc); 175 } else if (strcmp(meth->name, "nativeLoad") == 0 && 176 (strcmp(meth->clazz->descriptor, "Ljava/lang/Runtime;") == 0)) { 177 LOGD("JNI: bad stack CRC (0x%08x) -- okay during JNI_OnLoad", stackCrc); 178 } else { 179 LOGW("JNI: bad stack CRC (%08x vs %08x)", crc, stackCrc); 180 dvmAbort(); 181 } 182 } 183 self->stackCrc = (u4) -1; /* make logic errors more noticeable */ 184} 185 186#else 187# define COMPUTE_STACK_SUM(_self) ((void)0) 188# define CHECK_STACK_SUM(_self) ((void)0) 189#endif 190 191 192/* 193 * =========================================================================== 194 * Utility functions 195 * =========================================================================== 196 */ 197 198static inline Thread* self(JNIEnv* env) { 199 Thread* envSelf = ((JNIEnvExt*) env)->self; 200 // When emulating direct pointers with indirect references, it's critical 201 // that we use the correct per-thread indirect reference table. 202 Thread* self = gDvmJni.workAroundAppJniBugs ? dvmThreadSelf() : envSelf; 203 if (self != envSelf) { 204 LOGE("JNI ERROR: env->self != thread-self (%p vs. %p); auto-correcting", 205 envSelf, self); 206 } 207 return self; 208} 209 210/* 211 * Entry/exit processing for all JNI calls. 212 * 213 * We skip the (curiously expensive) thread-local storage lookup on our Thread*. 214 * If the caller has passed the wrong JNIEnv in, we're going to be accessing unsynchronized 215 * structures from more than one thread, and things are going to fail 216 * in bizarre ways. This is only sensible if the native code has been 217 * fully exercised with CheckJNI enabled. 218 */ 219class ScopedJniThreadState { 220public: 221 explicit ScopedJniThreadState(JNIEnv* env) { 222 mSelf = ::self(env); 223 CHECK_STACK_SUM(mSelf); 224 dvmChangeStatus(mSelf, THREAD_RUNNING); 225 } 226 227 ~ScopedJniThreadState() { 228 dvmChangeStatus(mSelf, THREAD_NATIVE); 229 COMPUTE_STACK_SUM(mSelf); 230 } 231 232 Thread* self() { 233 return mSelf; 234 } 235 236private: 237 Thread* mSelf; 238 239 // Disallow copy and assignment. 240 ScopedJniThreadState(const ScopedJniThreadState&); 241 void operator=(const ScopedJniThreadState&); 242}; 243 244#define kGlobalRefsTableInitialSize 512 245#define kGlobalRefsTableMaxSize 51200 /* arbitrary, must be < 64K */ 246#define kGrefWaterInterval 100 247#define kTrackGrefUsage true 248 249#define kWeakGlobalRefsTableInitialSize 16 250 251#define kPinTableInitialSize 16 252#define kPinTableMaxSize 1024 253#define kPinComplainThreshold 10 254 255bool dvmJniStartup() { 256 if (!gDvm.jniGlobalRefTable.init(kGlobalRefsTableInitialSize, 257 kGlobalRefsTableMaxSize, 258 kIndirectKindGlobal)) { 259 return false; 260 } 261 if (!gDvm.jniWeakGlobalRefTable.init(kWeakGlobalRefsTableInitialSize, 262 kGlobalRefsTableMaxSize, 263 kIndirectKindWeakGlobal)) { 264 return false; 265 } 266 267 dvmInitMutex(&gDvm.jniGlobalRefLock); 268 dvmInitMutex(&gDvm.jniWeakGlobalRefLock); 269 gDvm.jniGlobalRefLoMark = 0; 270 gDvm.jniGlobalRefHiMark = kGrefWaterInterval * 2; 271 272 if (!dvmInitReferenceTable(&gDvm.jniPinRefTable, kPinTableInitialSize, kPinTableMaxSize)) { 273 return false; 274 } 275 276 dvmInitMutex(&gDvm.jniPinRefLock); 277 278 return true; 279} 280 281void dvmJniShutdown() { 282 gDvm.jniGlobalRefTable.destroy(); 283 gDvm.jniWeakGlobalRefTable.destroy(); 284 dvmClearReferenceTable(&gDvm.jniPinRefTable); 285} 286 287/* 288 * Find the JNIEnv associated with the current thread. 289 * 290 * Currently stored in the Thread struct. Could also just drop this into 291 * thread-local storage. 292 */ 293JNIEnvExt* dvmGetJNIEnvForThread() { 294 Thread* self = dvmThreadSelf(); 295 if (self == NULL) { 296 return NULL; 297 } 298 return (JNIEnvExt*) dvmGetThreadJNIEnv(self); 299} 300 301/* 302 * Retrieve the ReferenceTable struct for the current thread. 303 * 304 * Going through "env" rather than dvmThreadSelf() is faster but will 305 * get weird if the JNI code is passing the wrong JNIEnv around. 306 */ 307static inline IndirectRefTable* getLocalRefTable(JNIEnv* env) { 308 return &self(env)->jniLocalRefTable; 309} 310 311/* 312 * Convert an indirect reference to an Object reference. The indirect 313 * reference may be local, global, or weak-global. 314 * 315 * If "jobj" is NULL, or is a weak global reference whose reference has 316 * been cleared, this returns NULL. If jobj is an invalid indirect 317 * reference, kInvalidIndirectRefObject is returned. 318 * 319 * Note "env" may be NULL when decoding global references. 320 */ 321Object* dvmDecodeIndirectRef(JNIEnv* env, jobject jobj) { 322 if (jobj == NULL) { 323 return NULL; 324 } 325 326 switch (indirectRefKind(jobj)) { 327 case kIndirectKindLocal: 328 { 329 Object* result = getLocalRefTable(env)->get(jobj); 330 if (result == NULL) { 331 LOGE("JNI ERROR (app bug): use of deleted local reference (%p)", jobj); 332 dvmAbort(); 333 } 334 return result; 335 } 336 case kIndirectKindGlobal: 337 { 338 // TODO: find a way to avoid the mutex activity here 339 IndirectRefTable* pRefTable = &gDvm.jniGlobalRefTable; 340 ScopedPthreadMutexLock lock(&gDvm.jniGlobalRefLock); 341 Object* result = pRefTable->get(jobj); 342 if (result == NULL) { 343 LOGE("JNI ERROR (app bug): use of deleted global reference (%p)", jobj); 344 dvmAbort(); 345 } 346 return result; 347 } 348 case kIndirectKindWeakGlobal: 349 { 350 // TODO: find a way to avoid the mutex activity here 351 IndirectRefTable* pRefTable = &gDvm.jniWeakGlobalRefTable; 352 ScopedPthreadMutexLock lock(&gDvm.jniWeakGlobalRefLock); 353 Object* result = pRefTable->get(jobj); 354 if (result == kClearedJniWeakGlobal) { 355 result = NULL; 356 } else if (result == NULL) { 357 LOGE("JNI ERROR (app bug): use of deleted weak global reference (%p)", jobj); 358 dvmAbort(); 359 } 360 return result; 361 } 362 case kIndirectKindInvalid: 363 default: 364 if (gDvmJni.workAroundAppJniBugs) { 365 // Assume an invalid local reference is actually a direct pointer. 366 return reinterpret_cast<Object*>(jobj); 367 } 368 LOGW("Invalid indirect reference %p in decodeIndirectRef", jobj); 369 dvmAbort(); 370 return kInvalidIndirectRefObject; 371 } 372} 373 374/* 375 * Add a local reference for an object to the current stack frame. When 376 * the native function returns, the reference will be discarded. 377 * 378 * We need to allow the same reference to be added multiple times. 379 * 380 * This will be called on otherwise unreferenced objects. We cannot do 381 * GC allocations here, and it's best if we don't grab a mutex. 382 * 383 * Returns the local reference (currently just the same pointer that was 384 * passed in), or NULL on failure. 385 */ 386static jobject addLocalReference(JNIEnv* env, Object* obj) { 387 if (obj == NULL) { 388 return NULL; 389 } 390 391 IndirectRefTable* pRefTable = getLocalRefTable(env); 392 void* curFrame = self(env)->interpSave.curFrame; 393 u4 cookie = SAVEAREA_FROM_FP(curFrame)->xtra.localRefCookie; 394 jobject jobj = (jobject) pRefTable->add(cookie, obj); 395 if (jobj == NULL) { 396 pRefTable->dump("JNI local"); 397 LOGE("Failed adding to JNI local ref table (has %zd entries)", 398 pRefTable->capacity()); 399 dvmDumpThread(dvmThreadSelf(), false); 400 dvmAbort(); // spec says call FatalError; this is equivalent 401 } else { 402 if (false) { 403 LOGI("LREF add %p (%s.%s) (ent=%zd)", obj, 404 dvmGetCurrentJNIMethod()->clazz->descriptor, 405 dvmGetCurrentJNIMethod()->name, 406 pRefTable->capacity()); 407 } 408 } 409 410#if 0 // TODO: fix this to understand PushLocalFrame, so we can turn it on. 411 if (gDvmJni.useCheckJni) { 412 size_t entryCount = pRefTable->capacity(); 413 if (entryCount > 16) { 414 LOGW("Warning: more than 16 JNI local references: %d (most recent was a %s)", entryCount, obj->clazz->descriptor); 415 pRefTable->dump("JNI local"); 416 dvmDumpThread(dvmThreadSelf(), false); 417 //dvmAbort(); 418 } 419 } 420#endif 421 422 if (gDvmJni.workAroundAppJniBugs) { 423 // Hand out direct pointers to support broken old apps. 424 return reinterpret_cast<jobject>(obj); 425 } 426 return jobj; 427} 428 429/* 430 * Ensure that at least "capacity" references can be held in the local 431 * refs table of the current thread. 432 */ 433static bool ensureLocalCapacity(JNIEnv* env, int capacity) { 434 IndirectRefTable* pRefTable = getLocalRefTable(env); 435 int numEntries = pRefTable->capacity(); 436 // TODO: this isn't quite right, since "numEntries" includes holes 437 return ((kJniLocalRefMax - numEntries) >= capacity); 438} 439 440/* 441 * Explicitly delete a reference from the local list. 442 */ 443static void deleteLocalReference(JNIEnv* env, jobject jobj) { 444 if (jobj == NULL) { 445 return; 446 } 447 448 IndirectRefTable* pRefTable = getLocalRefTable(env); 449 void* curFrame = self(env)->interpSave.curFrame; 450 u4 cookie = SAVEAREA_FROM_FP(curFrame)->xtra.localRefCookie; 451 if (!pRefTable->remove(cookie, jobj)) { 452 /* 453 * Attempting to delete a local reference that is not in the 454 * topmost local reference frame is a no-op. DeleteLocalRef returns 455 * void and doesn't throw any exceptions, but we should probably 456 * complain about it so the user will notice that things aren't 457 * going quite the way they expect. 458 */ 459 LOGW("JNI WARNING: DeleteLocalRef(%p) failed to find entry", jobj); 460 } 461} 462 463/* 464 * Add a global reference for an object. 465 * 466 * We may add the same object more than once. Add/remove calls are paired, 467 * so it needs to appear on the list multiple times. 468 */ 469static jobject addGlobalReference(Object* obj) { 470 if (obj == NULL) { 471 return NULL; 472 } 473 474 //LOGI("adding obj=%p", obj); 475 //dvmDumpThread(dvmThreadSelf(), false); 476 477 if (false && dvmIsClassObject((Object*)obj)) { 478 ClassObject* clazz = (ClassObject*) obj; 479 LOGI("-------"); 480 LOGI("Adding global ref on class %s", clazz->descriptor); 481 dvmDumpThread(dvmThreadSelf(), false); 482 } 483 if (false && ((Object*)obj)->clazz == gDvm.classJavaLangString) { 484 StringObject* strObj = (StringObject*) obj; 485 char* str = dvmCreateCstrFromString(strObj); 486 if (strcmp(str, "sync-response") == 0) { 487 LOGI("-------"); 488 LOGI("Adding global ref on string '%s'", str); 489 dvmDumpThread(dvmThreadSelf(), false); 490 //dvmAbort(); 491 } 492 free(str); 493 } 494 if (false && ((Object*)obj)->clazz == gDvm.classArrayByte) { 495 ArrayObject* arrayObj = (ArrayObject*) obj; 496 if (arrayObj->length == 8192 /*&& 497 dvmReferenceTableEntries(&gDvm.jniGlobalRefTable) > 400*/) 498 { 499 LOGI("Adding global ref on byte array %p (len=%d)", 500 arrayObj, arrayObj->length); 501 dvmDumpThread(dvmThreadSelf(), false); 502 } 503 } 504 505 ScopedPthreadMutexLock lock(&gDvm.jniGlobalRefLock); 506 507 /* 508 * Throwing an exception on failure is problematic, because JNI code 509 * may not be expecting an exception, and things sort of cascade. We 510 * want to have a hard limit to catch leaks during debugging, but this 511 * otherwise needs to expand until memory is consumed. As a practical 512 * matter, if we have many thousands of global references, chances are 513 * we're either leaking global ref table entries or we're going to 514 * run out of space in the GC heap. 515 */ 516 jobject jobj = (jobject) gDvm.jniGlobalRefTable.add(IRT_FIRST_SEGMENT, obj); 517 if (jobj == NULL) { 518 gDvm.jniGlobalRefTable.dump("JNI global"); 519 LOGE("Failed adding to JNI global ref table (%zd entries)", 520 gDvm.jniGlobalRefTable.capacity()); 521 dvmAbort(); 522 } 523 524 LOGVV("GREF add %p (%s.%s)", obj, 525 dvmGetCurrentJNIMethod()->clazz->descriptor, 526 dvmGetCurrentJNIMethod()->name); 527 528 /* GREF usage tracking; should probably be disabled for production env */ 529 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) { 530 int count = gDvm.jniGlobalRefTable.capacity(); 531 // TODO: adjust for "holes" 532 if (count > gDvm.jniGlobalRefHiMark) { 533 LOGD("GREF has increased to %d", count); 534 gDvm.jniGlobalRefHiMark += kGrefWaterInterval; 535 gDvm.jniGlobalRefLoMark += kGrefWaterInterval; 536 537 /* watch for "excessive" use; not generally appropriate */ 538 if (count >= gDvm.jniGrefLimit) { 539 if (gDvmJni.warnOnly) { 540 LOGW("Excessive JNI global references (%d)", count); 541 } else { 542 gDvm.jniGlobalRefTable.dump("JNI global"); 543 LOGE("Excessive JNI global references (%d)", count); 544 dvmAbort(); 545 } 546 } 547 } 548 } 549 return jobj; 550} 551 552static jobject addWeakGlobalReference(Object* obj) { 553 if (obj == NULL) { 554 return NULL; 555 } 556 557 ScopedPthreadMutexLock lock(&gDvm.jniWeakGlobalRefLock); 558 IndirectRefTable *table = &gDvm.jniWeakGlobalRefTable; 559 jobject jobj = (jobject) table->add(IRT_FIRST_SEGMENT, obj); 560 if (jobj == NULL) { 561 LOGE("Failed adding to JNI weak global ref table (%zd entries)", table->capacity()); 562 } 563 return jobj; 564} 565 566static void deleteWeakGlobalReference(jobject jobj) { 567 if (jobj == NULL) { 568 return; 569 } 570 571 ScopedPthreadMutexLock lock(&gDvm.jniWeakGlobalRefLock); 572 IndirectRefTable *table = &gDvm.jniWeakGlobalRefTable; 573 if (!table->remove(IRT_FIRST_SEGMENT, jobj)) { 574 LOGW("JNI: DeleteWeakGlobalRef(%p) failed to find entry", jobj); 575 } 576} 577 578/* 579 * Remove a global reference. In most cases it's the entry most recently 580 * added, which makes this pretty quick. 581 * 582 * Thought: if it's not the most recent entry, just null it out. When we 583 * fill up, do a compaction pass before we expand the list. 584 */ 585static void deleteGlobalReference(jobject jobj) { 586 if (jobj == NULL) { 587 return; 588 } 589 590 ScopedPthreadMutexLock lock(&gDvm.jniGlobalRefLock); 591 if (!gDvm.jniGlobalRefTable.remove(IRT_FIRST_SEGMENT, jobj)) { 592 LOGW("JNI: DeleteGlobalRef(%p) failed to find entry", jobj); 593 return; 594 } 595 596 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) { 597 int count = gDvm.jniGlobalRefTable.capacity(); 598 // TODO: not quite right, need to subtract holes 599 if (count < gDvm.jniGlobalRefLoMark) { 600 LOGD("GREF has decreased to %d", count); 601 gDvm.jniGlobalRefHiMark -= kGrefWaterInterval; 602 gDvm.jniGlobalRefLoMark -= kGrefWaterInterval; 603 } 604 } 605} 606 607/* 608 * Objects don't currently move, so we just need to create a reference 609 * that will ensure the array object isn't collected. 610 * 611 * We use a separate reference table, which is part of the GC root set. 612 */ 613static void pinPrimitiveArray(ArrayObject* arrayObj) { 614 if (arrayObj == NULL) { 615 return; 616 } 617 618 ScopedPthreadMutexLock lock(&gDvm.jniPinRefLock); 619 620 if (!dvmAddToReferenceTable(&gDvm.jniPinRefTable, (Object*)arrayObj)) { 621 dvmDumpReferenceTable(&gDvm.jniPinRefTable, "JNI pinned array"); 622 LOGE("Failed adding to JNI pinned array ref table (%d entries)", 623 (int) dvmReferenceTableEntries(&gDvm.jniPinRefTable)); 624 dvmDumpThread(dvmThreadSelf(), false); 625 dvmAbort(); 626 } 627 628 /* 629 * If we're watching global ref usage, also keep an eye on these. 630 * 631 * The total number of pinned primitive arrays should be pretty small. 632 * A single array should not be pinned more than once or twice; any 633 * more than that is a strong indicator that a Release function is 634 * not being called. 635 */ 636 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) { 637 int count = 0; 638 Object** ppObj = gDvm.jniPinRefTable.table; 639 while (ppObj < gDvm.jniPinRefTable.nextEntry) { 640 if (*ppObj++ == (Object*) arrayObj) 641 count++; 642 } 643 644 if (count > kPinComplainThreshold) { 645 LOGW("JNI: pin count on array %p (%s) is now %d", 646 arrayObj, arrayObj->clazz->descriptor, count); 647 /* keep going */ 648 } 649 } 650} 651 652/* 653 * Un-pin the array object. If an object was pinned twice, it must be 654 * unpinned twice before it's free to move. 655 */ 656static void unpinPrimitiveArray(ArrayObject* arrayObj) { 657 if (arrayObj == NULL) { 658 return; 659 } 660 661 ScopedPthreadMutexLock lock(&gDvm.jniPinRefLock); 662 if (!dvmRemoveFromReferenceTable(&gDvm.jniPinRefTable, 663 gDvm.jniPinRefTable.table, (Object*) arrayObj)) 664 { 665 LOGW("JNI: unpinPrimitiveArray(%p) failed to find entry (valid=%d)", 666 arrayObj, dvmIsValidObject((Object*) arrayObj)); 667 return; 668 } 669} 670 671/* 672 * Dump the contents of the JNI reference tables to the log file. 673 * 674 * We only dump the local refs associated with the current thread. 675 */ 676void dvmDumpJniReferenceTables() { 677 Thread* self = dvmThreadSelf(); 678 JNIEnv* env = self->jniEnv; 679 IndirectRefTable* pLocalRefs = getLocalRefTable(env); 680 pLocalRefs->dump("JNI local"); 681 gDvm.jniGlobalRefTable.dump("JNI global"); 682 dvmDumpReferenceTable(&gDvm.jniPinRefTable, "JNI pinned array"); 683} 684 685/* 686 * Verify that a reference passed in from native code is one that the 687 * code is allowed to have. 688 * 689 * It's okay for native code to pass us a reference that: 690 * - was passed in as an argument when invoked by native code (and hence 691 * is in the JNI local refs table) 692 * - was returned to it from JNI (and is now in the local refs table) 693 * - is present in the JNI global refs table 694 * 695 * Used by -Xcheck:jni and GetObjectRefType. 696 */ 697jobjectRefType dvmGetJNIRefType(JNIEnv* env, jobject jobj) { 698 /* 699 * IndirectRefKind is currently defined as an exact match of 700 * jobjectRefType, so this is easy. We have to decode it to determine 701 * if it's a valid reference and not merely valid-looking. 702 */ 703 assert(jobj != NULL); 704 705 Object* obj = dvmDecodeIndirectRef(env, jobj); 706 if (obj == reinterpret_cast<Object*>(jobj) && gDvmJni.workAroundAppJniBugs) { 707 // If we're handing out direct pointers, check whether 'jobj' is a direct reference 708 // to a local reference. 709 return getLocalRefTable(env)->contains(jobj) ? JNILocalRefType : JNIInvalidRefType; 710 } else if (obj == kInvalidIndirectRefObject) { 711 return JNIInvalidRefType; 712 } else { 713 return (jobjectRefType) indirectRefKind(jobj); 714 } 715} 716 717static void dumpMethods(Method* methods, size_t methodCount, const char* name) { 718 size_t i; 719 for (i = 0; i < methodCount; ++i) { 720 Method* method = &methods[i]; 721 if (strcmp(name, method->name) == 0) { 722 char* desc = dexProtoCopyMethodDescriptor(&method->prototype); 723 LOGE("Candidate: %s.%s:%s", method->clazz->descriptor, name, desc); 724 free(desc); 725 } 726 } 727} 728 729static void dumpCandidateMethods(ClassObject* clazz, const char* methodName, const char* signature) { 730 LOGE("ERROR: couldn't find native method"); 731 LOGE("Requested: %s.%s:%s", clazz->descriptor, methodName, signature); 732 dumpMethods(clazz->virtualMethods, clazz->virtualMethodCount, methodName); 733 dumpMethods(clazz->directMethods, clazz->directMethodCount, methodName); 734} 735 736/* 737 * Register a method that uses JNI calling conventions. 738 */ 739static bool dvmRegisterJNIMethod(ClassObject* clazz, const char* methodName, 740 const char* signature, void* fnPtr) 741{ 742 if (fnPtr == NULL) { 743 return false; 744 } 745 746 // If a signature starts with a '!', we take that as a sign that the native code doesn't 747 // need the extra JNI arguments (the JNIEnv* and the jclass). 748 bool fastJni = false; 749 if (*signature == '!') { 750 fastJni = true; 751 ++signature; 752 LOGV("fast JNI method %s.%s:%s detected", clazz->descriptor, methodName, signature); 753 } 754 755 Method* method = dvmFindDirectMethodByDescriptor(clazz, methodName, signature); 756 if (method == NULL) { 757 method = dvmFindVirtualMethodByDescriptor(clazz, methodName, signature); 758 } 759 if (method == NULL) { 760 dumpCandidateMethods(clazz, methodName, signature); 761 return false; 762 } 763 764 if (!dvmIsNativeMethod(method)) { 765 LOGW("Unable to register: not native: %s.%s:%s", clazz->descriptor, methodName, signature); 766 return false; 767 } 768 769 if (fastJni) { 770 // In this case, we have extra constraints to check... 771 if (dvmIsSynchronizedMethod(method)) { 772 // Synchronization is usually provided by the JNI bridge, 773 // but we won't have one. 774 LOGE("fast JNI method %s.%s:%s cannot be synchronized", 775 clazz->descriptor, methodName, signature); 776 return false; 777 } 778 if (!dvmIsStaticMethod(method)) { 779 // There's no real reason for this constraint, but since we won't 780 // be supplying a JNIEnv* or a jobject 'this', you're effectively 781 // static anyway, so it seems clearer to say so. 782 LOGE("fast JNI method %s.%s:%s cannot be non-static", 783 clazz->descriptor, methodName, signature); 784 return false; 785 } 786 } 787 788 if (method->nativeFunc != dvmResolveNativeMethod) { 789 /* this is allowed, but unusual */ 790 LOGV("Note: %s.%s:%s was already registered", clazz->descriptor, methodName, signature); 791 } 792 793 method->fastJni = fastJni; 794 dvmUseJNIBridge(method, fnPtr); 795 796 LOGV("JNI-registered %s.%s:%s", clazz->descriptor, methodName, signature); 797 return true; 798} 799 800static const char* builtInPrefixes[] = { 801 "Landroid/", 802 "Lcom/android/", 803 "Lcom/google/android/", 804 "Ldalvik/", 805 "Ljava/", 806 "Ljavax/", 807 "Llibcore/", 808 "Lorg/apache/harmony/", 809}; 810 811static bool shouldTrace(Method* method) { 812 const char* className = method->clazz->descriptor; 813 // Return true if the -Xjnitrace setting implies we should trace 'method'. 814 if (gDvm.jniTrace && strstr(className, gDvm.jniTrace)) { 815 return true; 816 } 817 // Return true if we're trying to log all third-party JNI activity and 'method' doesn't look 818 // like part of Android. 819 if (gDvmJni.logThirdPartyJni) { 820 for (size_t i = 0; i < NELEM(builtInPrefixes); ++i) { 821 if (strstr(className, builtInPrefixes[i]) == className) { 822 return false; 823 } 824 } 825 return true; 826 } 827 return false; 828} 829 830/* 831 * Point "method->nativeFunc" at the JNI bridge, and overload "method->insns" 832 * to point at the actual function. 833 */ 834void dvmUseJNIBridge(Method* method, void* func) { 835 method->shouldTrace = shouldTrace(method); 836 837 // Does the method take any reference arguments? 838 method->noRef = true; 839 const char* cp = method->shorty; 840 while (*++cp != '\0') { // Pre-increment to skip return type. 841 if (*cp == 'L') { 842 method->noRef = false; 843 break; 844 } 845 } 846 847 DalvikBridgeFunc bridge = gDvmJni.useCheckJni ? dvmCheckCallJNIMethod : dvmCallJNIMethod; 848 dvmSetNativeFunc(method, bridge, (const u2*) func); 849} 850 851// TODO: rewrite this to share code with CheckJNI's tracing... 852static void appendValue(char type, const JValue value, char* buf, size_t n, bool appendComma) 853{ 854 size_t len = strlen(buf); 855 if (len >= n - 32) { // 32 should be longer than anything we could append. 856 buf[len - 1] = '.'; 857 buf[len - 2] = '.'; 858 buf[len - 3] = '.'; 859 return; 860 } 861 char* p = buf + len; 862 switch (type) { 863 case 'B': 864 if (value.b >= 0 && value.b < 10) { 865 sprintf(p, "%d", value.b); 866 } else { 867 sprintf(p, "%#x (%d)", value.b, value.b); 868 } 869 break; 870 case 'C': 871 if (value.c < 0x7f && value.c >= ' ') { 872 sprintf(p, "U+%x ('%c')", value.c, value.c); 873 } else { 874 sprintf(p, "U+%x", value.c); 875 } 876 break; 877 case 'D': 878 sprintf(p, "%g", value.d); 879 break; 880 case 'F': 881 sprintf(p, "%g", value.f); 882 break; 883 case 'I': 884 sprintf(p, "%d", value.i); 885 break; 886 case 'L': 887 sprintf(p, "%#x", value.i); 888 break; 889 case 'J': 890 sprintf(p, "%lld", value.j); 891 break; 892 case 'S': 893 sprintf(p, "%d", value.s); 894 break; 895 case 'V': 896 strcpy(p, "void"); 897 break; 898 case 'Z': 899 strcpy(p, value.z ? "true" : "false"); 900 break; 901 default: 902 sprintf(p, "unknown type '%c'", type); 903 break; 904 } 905 906 if (appendComma) { 907 strcat(p, ", "); 908 } 909} 910 911static void logNativeMethodEntry(const Method* method, const u4* args) 912{ 913 char thisString[32] = { 0 }; 914 const u4* sp = args; 915 if (!dvmIsStaticMethod(method)) { 916 sprintf(thisString, "this=0x%08x ", *sp++); 917 } 918 919 char argsString[128]= { 0 }; 920 const char* desc = &method->shorty[1]; 921 while (*desc != '\0') { 922 char argType = *desc++; 923 JValue value; 924 if (argType == 'D' || argType == 'J') { 925 value.j = dvmGetArgLong(sp, 0); 926 sp += 2; 927 } else { 928 value.i = *sp++; 929 } 930 appendValue(argType, value, argsString, sizeof(argsString), 931 *desc != '\0'); 932 } 933 934 std::string className(dvmHumanReadableDescriptor(method->clazz->descriptor)); 935 char* signature = dexProtoCopyMethodDescriptor(&method->prototype); 936 LOGI("-> %s %s%s %s(%s)", className.c_str(), method->name, signature, thisString, argsString); 937 free(signature); 938} 939 940static void logNativeMethodExit(const Method* method, Thread* self, const JValue returnValue) 941{ 942 std::string className(dvmHumanReadableDescriptor(method->clazz->descriptor)); 943 char* signature = dexProtoCopyMethodDescriptor(&method->prototype); 944 if (dvmCheckException(self)) { 945 Object* exception = dvmGetException(self); 946 std::string exceptionClassName(dvmHumanReadableDescriptor(exception->clazz->descriptor)); 947 LOGI("<- %s %s%s threw %s", className.c_str(), 948 method->name, signature, exceptionClassName.c_str()); 949 } else { 950 char returnValueString[128] = { 0 }; 951 char returnType = method->shorty[0]; 952 appendValue(returnType, returnValue, returnValueString, sizeof(returnValueString), false); 953 LOGI("<- %s %s%s returned %s", className.c_str(), 954 method->name, signature, returnValueString); 955 } 956 free(signature); 957} 958 959/* 960 * Get the method currently being executed by examining the interp stack. 961 */ 962const Method* dvmGetCurrentJNIMethod() { 963 assert(dvmThreadSelf() != NULL); 964 965 void* fp = dvmThreadSelf()->interpSave.curFrame; 966 const Method* meth = SAVEAREA_FROM_FP(fp)->method; 967 968 assert(meth != NULL); 969 assert(dvmIsNativeMethod(meth)); 970 return meth; 971} 972 973/* 974 * Track a JNI MonitorEnter in the current thread. 975 * 976 * The goal is to be able to "implicitly" release all JNI-held monitors 977 * when the thread detaches. 978 * 979 * Monitors may be entered multiple times, so we add a new entry for each 980 * enter call. It would be more efficient to keep a counter. At present 981 * there's no real motivation to improve this however. 982 */ 983static void trackMonitorEnter(Thread* self, Object* obj) { 984 static const int kInitialSize = 16; 985 ReferenceTable* refTable = &self->jniMonitorRefTable; 986 987 /* init table on first use */ 988 if (refTable->table == NULL) { 989 assert(refTable->maxEntries == 0); 990 991 if (!dvmInitReferenceTable(refTable, kInitialSize, INT_MAX)) { 992 LOGE("Unable to initialize monitor tracking table"); 993 dvmAbort(); 994 } 995 } 996 997 if (!dvmAddToReferenceTable(refTable, obj)) { 998 /* ran out of memory? could throw exception instead */ 999 LOGE("Unable to add entry to monitor tracking table"); 1000 dvmAbort(); 1001 } else { 1002 LOGVV("--- added monitor %p", obj); 1003 } 1004} 1005 1006/* 1007 * Track a JNI MonitorExit in the current thread. 1008 */ 1009static void trackMonitorExit(Thread* self, Object* obj) { 1010 ReferenceTable* pRefTable = &self->jniMonitorRefTable; 1011 1012 if (!dvmRemoveFromReferenceTable(pRefTable, pRefTable->table, obj)) { 1013 LOGE("JNI monitor %p not found in tracking list", obj); 1014 /* keep going? */ 1015 } else { 1016 LOGVV("--- removed monitor %p", obj); 1017 } 1018} 1019 1020/* 1021 * Release all monitors held by the jniMonitorRefTable list. 1022 */ 1023void dvmReleaseJniMonitors(Thread* self) { 1024 ReferenceTable* pRefTable = &self->jniMonitorRefTable; 1025 Object** top = pRefTable->table; 1026 1027 if (top == NULL) { 1028 return; 1029 } 1030 Object** ptr = pRefTable->nextEntry; 1031 while (--ptr >= top) { 1032 if (!dvmUnlockObject(self, *ptr)) { 1033 LOGW("Unable to unlock monitor %p at thread detach", *ptr); 1034 } else { 1035 LOGVV("--- detach-releasing monitor %p", *ptr); 1036 } 1037 } 1038 1039 /* zap it */ 1040 pRefTable->nextEntry = pRefTable->table; 1041} 1042 1043/* 1044 * Determine if the specified class can be instantiated from JNI. This 1045 * is used by AllocObject / NewObject, which are documented as throwing 1046 * an exception for abstract and interface classes, and not accepting 1047 * array classes. We also want to reject attempts to create new Class 1048 * objects, since only DefineClass should do that. 1049 */ 1050static bool canAllocClass(ClassObject* clazz) { 1051 if (dvmIsAbstractClass(clazz) || dvmIsInterfaceClass(clazz)) { 1052 /* JNI spec defines what this throws */ 1053 dvmThrowInstantiationException(clazz, "abstract class or interface"); 1054 return false; 1055 } else if (dvmIsArrayClass(clazz) || dvmIsTheClassClass(clazz)) { 1056 /* spec says "must not" for arrays, ignores Class */ 1057 dvmThrowInstantiationException(clazz, "wrong JNI function"); 1058 return false; 1059 } 1060 return true; 1061} 1062 1063 1064/* 1065 * =========================================================================== 1066 * JNI call bridge 1067 * =========================================================================== 1068 */ 1069 1070/* 1071 * The functions here form a bridge between interpreted code and JNI native 1072 * functions. The basic task is to convert an array of primitives and 1073 * references into C-style function arguments. This is architecture-specific 1074 * and usually requires help from assembly code. 1075 * 1076 * The bridge takes four arguments: the array of parameters, a place to 1077 * store the function result (if any), the method to call, and a pointer 1078 * to the current thread. 1079 * 1080 * These functions aren't called directly from elsewhere in the VM. 1081 * A pointer in the Method struct points to one of these, and when a native 1082 * method is invoked the interpreter jumps to it. 1083 * 1084 * (The "internal native" methods are invoked the same way, but instead 1085 * of calling through a bridge, the target method is called directly.) 1086 * 1087 * The "args" array should not be modified, but we do so anyway for 1088 * performance reasons. We know that it points to the "outs" area on 1089 * the current method's interpreted stack. This area is ignored by the 1090 * precise GC, because there is no register map for a native method (for 1091 * an interpreted method the args would be listed in the argument set). 1092 * We know all of the values exist elsewhere on the interpreted stack, 1093 * because the method call setup copies them right before making the call, 1094 * so we don't have to worry about concealing stuff from the GC. 1095 * 1096 * If we don't want to modify "args", we either have to create a local 1097 * copy and modify it before calling dvmPlatformInvoke, or we have to do 1098 * the local reference replacement within dvmPlatformInvoke. The latter 1099 * has some performance advantages, though if we can inline the local 1100 * reference adds we may win when there's a lot of reference args (unless 1101 * we want to code up some local ref table manipulation in assembly. 1102 */ 1103 1104/* 1105 * If necessary, convert the value in pResult from a local/global reference 1106 * to an object pointer. 1107 * 1108 * If the returned reference is invalid, kInvalidIndirectRefObject will 1109 * be returned in pResult. 1110 */ 1111static inline void convertReferenceResult(JNIEnv* env, JValue* pResult, 1112 const Method* method, Thread* self) 1113{ 1114 if (method->shorty[0] == 'L' && !dvmCheckException(self) && pResult->l != NULL) { 1115 pResult->l = dvmDecodeIndirectRef(env, (jobject) pResult->l); 1116 } 1117} 1118 1119/* 1120 * General form, handles all cases. 1121 */ 1122void dvmCallJNIMethod(const u4* args, JValue* pResult, const Method* method, Thread* self) { 1123 u4* modArgs = (u4*) args; 1124 jclass staticMethodClass = NULL; 1125 JNIEnv* env = self->jniEnv; 1126 1127 bool isSynchronized = dvmIsSynchronizedMethod(method); 1128 Object* lockObj; 1129 1130 //LOGI("JNI calling %p (%s.%s:%s):", method->insns, 1131 // method->clazz->descriptor, method->name, method->shorty); 1132 1133 /* 1134 * Walk the argument list, creating local references for appropriate 1135 * arguments. 1136 */ 1137 int idx = 0; 1138 if (dvmIsStaticMethod(method)) { 1139 lockObj = (Object*) method->clazz; 1140 1141 /* add the class object we pass in */ 1142 staticMethodClass = (jclass) addLocalReference(env, (Object*) method->clazz); 1143 if (staticMethodClass == NULL) { 1144 assert(dvmCheckException(self)); 1145 return; 1146 } 1147 } else { 1148 lockObj = (Object*) args[0]; 1149 1150 /* add "this" */ 1151 jobject thisObj = addLocalReference(env, (Object*) modArgs[0]); 1152 if (thisObj == NULL) { 1153 assert(dvmCheckException(self)); 1154 return; 1155 } 1156 modArgs[idx] = (u4) thisObj; 1157 idx = 1; 1158 } 1159 1160 if (!method->noRef) { 1161 const char* shorty = &method->shorty[1]; /* skip return type */ 1162 while (*shorty != '\0') { 1163 switch (*shorty++) { 1164 case 'L': 1165 //LOGI(" local %d: 0x%08x", idx, modArgs[idx]); 1166 if (modArgs[idx] != 0) { 1167 //if (!dvmIsValidObject((Object*) modArgs[idx])) 1168 // dvmAbort(); 1169 jobject argObj = addLocalReference(env, (Object*) modArgs[idx]); 1170 if (argObj == NULL) { 1171 assert(dvmCheckException(self)); 1172 return; 1173 } 1174 modArgs[idx] = (u4) argObj; 1175 } 1176 break; 1177 case 'D': 1178 case 'J': 1179 idx++; 1180 break; 1181 default: 1182 /* Z B C S I -- do nothing */ 1183 break; 1184 } 1185 idx++; 1186 } 1187 } 1188 1189 if (method->shouldTrace) { 1190 logNativeMethodEntry(method, args); 1191 } 1192 if (isSynchronized) { 1193 dvmLockObject(self, lockObj); 1194 } 1195 1196 ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_NATIVE); 1197 1198 ANDROID_MEMBAR_FULL(); /* guarantee ordering on method->insns */ 1199 assert(method->insns != NULL); 1200 1201 COMPUTE_STACK_SUM(self); 1202 dvmPlatformInvoke(method->fastJni ? NULL : env, 1203 (ClassObject*) staticMethodClass, 1204 method->jniArgInfo, method->insSize, modArgs, method->shorty, 1205 (void*) method->insns, pResult); 1206 CHECK_STACK_SUM(self); 1207 1208 dvmChangeStatus(self, oldStatus); 1209 1210 convertReferenceResult(env, pResult, method, self); 1211 1212 if (isSynchronized) { 1213 dvmUnlockObject(self, lockObj); 1214 } 1215 if (method->shouldTrace) { 1216 logNativeMethodExit(method, self, *pResult); 1217 } 1218} 1219 1220/* 1221 * Extract the return type enum from the "jniArgInfo" field. 1222 */ 1223DalvikJniReturnType dvmGetArgInfoReturnType(int jniArgInfo) { 1224 return static_cast<DalvikJniReturnType>((jniArgInfo & DALVIK_JNI_RETURN_MASK) >> DALVIK_JNI_RETURN_SHIFT); 1225} 1226 1227/* 1228 * =========================================================================== 1229 * JNI implementation 1230 * =========================================================================== 1231 */ 1232 1233/* 1234 * Return the version of the native method interface. 1235 */ 1236static jint GetVersion(JNIEnv* env) { 1237 /* 1238 * There is absolutely no need to toggle the mode for correct behavior. 1239 * However, it does provide native code with a simple "suspend self 1240 * if necessary" call. 1241 */ 1242 ScopedJniThreadState ts(env); 1243 return JNI_VERSION_1_6; 1244} 1245 1246/* 1247 * Create a new class from a bag of bytes. 1248 * 1249 * This is not currently supported within Dalvik. 1250 */ 1251static jclass DefineClass(JNIEnv* env, const char *name, jobject loader, 1252 const jbyte* buf, jsize bufLen) 1253{ 1254 UNUSED_PARAMETER(name); 1255 UNUSED_PARAMETER(loader); 1256 UNUSED_PARAMETER(buf); 1257 UNUSED_PARAMETER(bufLen); 1258 1259 ScopedJniThreadState ts(env); 1260 LOGW("JNI DefineClass is not supported"); 1261 return NULL; 1262} 1263 1264/* 1265 * Find a class by name. 1266 * 1267 * We have to use the "no init" version of FindClass here, because we might 1268 * be getting the class prior to registering native methods that will be 1269 * used in <clinit>. 1270 * 1271 * We need to get the class loader associated with the current native 1272 * method. If there is no native method, e.g. we're calling this from native 1273 * code right after creating the VM, the spec says we need to use the class 1274 * loader returned by "ClassLoader.getBaseClassLoader". There is no such 1275 * method, but it's likely they meant ClassLoader.getSystemClassLoader. 1276 * We can't get that until after the VM has initialized though. 1277 */ 1278static jclass FindClass(JNIEnv* env, const char* name) { 1279 ScopedJniThreadState ts(env); 1280 1281 const Method* thisMethod = dvmGetCurrentJNIMethod(); 1282 assert(thisMethod != NULL); 1283 1284 Object* loader; 1285 Object* trackedLoader = NULL; 1286 if (ts.self()->classLoaderOverride != NULL) { 1287 /* hack for JNI_OnLoad */ 1288 assert(strcmp(thisMethod->name, "nativeLoad") == 0); 1289 loader = ts.self()->classLoaderOverride; 1290 } else if (thisMethod == gDvm.methDalvikSystemNativeStart_main || 1291 thisMethod == gDvm.methDalvikSystemNativeStart_run) { 1292 /* start point of invocation interface */ 1293 if (!gDvm.initializing) { 1294 loader = trackedLoader = dvmGetSystemClassLoader(); 1295 } else { 1296 loader = NULL; 1297 } 1298 } else { 1299 loader = thisMethod->clazz->classLoader; 1300 } 1301 1302 char* descriptor = dvmNameToDescriptor(name); 1303 if (descriptor == NULL) { 1304 return NULL; 1305 } 1306 ClassObject* clazz = dvmFindClassNoInit(descriptor, loader); 1307 free(descriptor); 1308 1309 jclass jclazz = (jclass) addLocalReference(env, (Object*) clazz); 1310 dvmReleaseTrackedAlloc(trackedLoader, ts.self()); 1311 return jclazz; 1312} 1313 1314/* 1315 * Return the superclass of a class. 1316 */ 1317static jclass GetSuperclass(JNIEnv* env, jclass jclazz) { 1318 ScopedJniThreadState ts(env); 1319 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 1320 return (jclass) addLocalReference(env, (Object*)clazz->super); 1321} 1322 1323/* 1324 * Determine whether an object of clazz1 can be safely cast to clazz2. 1325 * 1326 * Like IsInstanceOf, but with a pair of class objects instead of obj+class. 1327 */ 1328static jboolean IsAssignableFrom(JNIEnv* env, jclass jclazz1, jclass jclazz2) { 1329 ScopedJniThreadState ts(env); 1330 ClassObject* clazz1 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz1); 1331 ClassObject* clazz2 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz2); 1332 return dvmInstanceof(clazz1, clazz2); 1333} 1334 1335/* 1336 * Given a java.lang.reflect.Method or .Constructor, return a methodID. 1337 */ 1338static jmethodID FromReflectedMethod(JNIEnv* env, jobject jmethod) { 1339 ScopedJniThreadState ts(env); 1340 Object* method = dvmDecodeIndirectRef(env, jmethod); 1341 return (jmethodID) dvmGetMethodFromReflectObj(method); 1342} 1343 1344/* 1345 * Given a java.lang.reflect.Field, return a fieldID. 1346 */ 1347static jfieldID FromReflectedField(JNIEnv* env, jobject jfield) { 1348 ScopedJniThreadState ts(env); 1349 Object* field = dvmDecodeIndirectRef(env, jfield); 1350 return (jfieldID) dvmGetFieldFromReflectObj(field); 1351} 1352 1353/* 1354 * Convert a methodID to a java.lang.reflect.Method or .Constructor. 1355 * 1356 * (The "isStatic" field does not appear in the spec.) 1357 * 1358 * Throws OutOfMemory and returns NULL on failure. 1359 */ 1360static jobject ToReflectedMethod(JNIEnv* env, jclass jcls, jmethodID methodID, jboolean isStatic) { 1361 ScopedJniThreadState ts(env); 1362 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls); 1363 Object* obj = dvmCreateReflectObjForMethod(clazz, (Method*) methodID); 1364 dvmReleaseTrackedAlloc(obj, NULL); 1365 return addLocalReference(env, obj); 1366} 1367 1368/* 1369 * Convert a fieldID to a java.lang.reflect.Field. 1370 * 1371 * (The "isStatic" field does not appear in the spec.) 1372 * 1373 * Throws OutOfMemory and returns NULL on failure. 1374 */ 1375static jobject ToReflectedField(JNIEnv* env, jclass jcls, jfieldID fieldID, jboolean isStatic) { 1376 ScopedJniThreadState ts(env); 1377 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls); 1378 Object* obj = dvmCreateReflectObjForField(clazz, (Field*) fieldID); 1379 dvmReleaseTrackedAlloc(obj, NULL); 1380 return addLocalReference(env, obj); 1381} 1382 1383/* 1384 * Take this exception and throw it. 1385 */ 1386static jint Throw(JNIEnv* env, jthrowable jobj) { 1387 ScopedJniThreadState ts(env); 1388 if (jobj != NULL) { 1389 Object* obj = dvmDecodeIndirectRef(env, jobj); 1390 dvmSetException(ts.self(), obj); 1391 return JNI_OK; 1392 } 1393 return JNI_ERR; 1394} 1395 1396/* 1397 * Constructs an exception object from the specified class with the message 1398 * specified by "message", and throws it. 1399 */ 1400static jint ThrowNew(JNIEnv* env, jclass jclazz, const char* message) { 1401 ScopedJniThreadState ts(env); 1402 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 1403 dvmThrowException(clazz, message); 1404 // TODO: should return failure if this didn't work (e.g. OOM) 1405 return JNI_OK; 1406} 1407 1408/* 1409 * If an exception is being thrown, return the exception object. Otherwise, 1410 * return NULL. 1411 * 1412 * TODO: if there is no pending exception, we should be able to skip the 1413 * enter/exit checks. If we find one, we need to enter and then re-fetch 1414 * the exception (in case it got moved by a compacting GC). 1415 */ 1416static jthrowable ExceptionOccurred(JNIEnv* env) { 1417 ScopedJniThreadState ts(env); 1418 Object* exception = dvmGetException(ts.self()); 1419 jthrowable localException = (jthrowable) addLocalReference(env, exception); 1420 if (localException == NULL && exception != NULL) { 1421 /* 1422 * We were unable to add a new local reference, and threw a new 1423 * exception. We can't return "exception", because it's not a 1424 * local reference. So we have to return NULL, indicating that 1425 * there was no exception, even though it's pretty much raining 1426 * exceptions in here. 1427 */ 1428 LOGW("JNI WARNING: addLocal/exception combo"); 1429 } 1430 return localException; 1431} 1432 1433/* 1434 * Print an exception and stack trace to stderr. 1435 */ 1436static void ExceptionDescribe(JNIEnv* env) { 1437 ScopedJniThreadState ts(env); 1438 Object* exception = dvmGetException(ts.self()); 1439 if (exception != NULL) { 1440 dvmPrintExceptionStackTrace(); 1441 } else { 1442 LOGI("Odd: ExceptionDescribe called, but no exception pending"); 1443 } 1444} 1445 1446/* 1447 * Clear the exception currently being thrown. 1448 * 1449 * TODO: we should be able to skip the enter/exit stuff. 1450 */ 1451static void ExceptionClear(JNIEnv* env) { 1452 ScopedJniThreadState ts(env); 1453 dvmClearException(ts.self()); 1454} 1455 1456/* 1457 * Kill the VM. This function does not return. 1458 */ 1459static void FatalError(JNIEnv* env, const char* msg) { 1460 //dvmChangeStatus(NULL, THREAD_RUNNING); 1461 LOGE("JNI posting fatal error: %s", msg); 1462 dvmAbort(); 1463} 1464 1465/* 1466 * Push a new JNI frame on the stack, with a new set of locals. 1467 * 1468 * The new frame must have the same method pointer. (If for no other 1469 * reason than FindClass needs it to get the appropriate class loader.) 1470 */ 1471static jint PushLocalFrame(JNIEnv* env, jint capacity) { 1472 ScopedJniThreadState ts(env); 1473 if (!ensureLocalCapacity(env, capacity) || 1474 !dvmPushLocalFrame(ts.self(), dvmGetCurrentJNIMethod())) 1475 { 1476 /* yes, OutOfMemoryError, not StackOverflowError */ 1477 dvmClearException(ts.self()); 1478 dvmThrowOutOfMemoryError("out of stack in JNI PushLocalFrame"); 1479 return JNI_ERR; 1480 } 1481 return JNI_OK; 1482} 1483 1484/* 1485 * Pop the local frame off. If "jresult" is not null, add it as a 1486 * local reference on the now-current frame. 1487 */ 1488static jobject PopLocalFrame(JNIEnv* env, jobject jresult) { 1489 ScopedJniThreadState ts(env); 1490 Object* result = dvmDecodeIndirectRef(env, jresult); 1491 if (!dvmPopLocalFrame(ts.self())) { 1492 LOGW("JNI WARNING: too many PopLocalFrame calls"); 1493 dvmClearException(ts.self()); 1494 dvmThrowRuntimeException("too many PopLocalFrame calls"); 1495 } 1496 return addLocalReference(env, result); 1497} 1498 1499/* 1500 * Add a reference to the global list. 1501 */ 1502static jobject NewGlobalRef(JNIEnv* env, jobject jobj) { 1503 ScopedJniThreadState ts(env); 1504 Object* obj = dvmDecodeIndirectRef(env, jobj); 1505 return addGlobalReference(obj); 1506} 1507 1508/* 1509 * Delete a reference from the global list. 1510 */ 1511static void DeleteGlobalRef(JNIEnv* env, jobject jglobalRef) { 1512 ScopedJniThreadState ts(env); 1513 deleteGlobalReference(jglobalRef); 1514} 1515 1516 1517/* 1518 * Add a reference to the local list. 1519 */ 1520static jobject NewLocalRef(JNIEnv* env, jobject jobj) { 1521 ScopedJniThreadState ts(env); 1522 Object* obj = dvmDecodeIndirectRef(env, jobj); 1523 return addLocalReference(env, obj); 1524} 1525 1526/* 1527 * Delete a reference from the local list. 1528 */ 1529static void DeleteLocalRef(JNIEnv* env, jobject jlocalRef) { 1530 ScopedJniThreadState ts(env); 1531 deleteLocalReference(env, jlocalRef); 1532} 1533 1534/* 1535 * Ensure that the local references table can hold at least this many 1536 * references. 1537 */ 1538static jint EnsureLocalCapacity(JNIEnv* env, jint capacity) { 1539 ScopedJniThreadState ts(env); 1540 bool okay = ensureLocalCapacity(env, capacity); 1541 if (!okay) { 1542 dvmThrowOutOfMemoryError("can't ensure local reference capacity"); 1543 } 1544 return okay ? 0 : -1; 1545} 1546 1547 1548/* 1549 * Determine whether two Object references refer to the same underlying object. 1550 */ 1551static jboolean IsSameObject(JNIEnv* env, jobject jref1, jobject jref2) { 1552 ScopedJniThreadState ts(env); 1553 Object* obj1 = dvmDecodeIndirectRef(env, jref1); 1554 Object* obj2 = dvmDecodeIndirectRef(env, jref2); 1555 return (obj1 == obj2); 1556} 1557 1558/* 1559 * Allocate a new object without invoking any constructors. 1560 */ 1561static jobject AllocObject(JNIEnv* env, jclass jclazz) { 1562 ScopedJniThreadState ts(env); 1563 1564 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 1565 if (!canAllocClass(clazz) || 1566 (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))) 1567 { 1568 assert(dvmCheckException(ts.self())); 1569 return NULL; 1570 } 1571 1572 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK); 1573 return addLocalReference(env, newObj); 1574} 1575 1576/* 1577 * Allocate a new object and invoke the supplied constructor. 1578 */ 1579static jobject NewObject(JNIEnv* env, jclass jclazz, jmethodID methodID, ...) { 1580 ScopedJniThreadState ts(env); 1581 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 1582 1583 if (!canAllocClass(clazz) || (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))) { 1584 assert(dvmCheckException(ts.self())); 1585 return NULL; 1586 } 1587 1588 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK); 1589 jobject result = addLocalReference(env, newObj); 1590 if (newObj != NULL) { 1591 JValue unused; 1592 va_list args; 1593 va_start(args, methodID); 1594 dvmCallMethodV(ts.self(), (Method*) methodID, newObj, true, &unused, args); 1595 va_end(args); 1596 } 1597 return result; 1598} 1599 1600static jobject NewObjectV(JNIEnv* env, jclass jclazz, jmethodID methodID, va_list args) { 1601 ScopedJniThreadState ts(env); 1602 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 1603 1604 if (!canAllocClass(clazz) || (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))) { 1605 assert(dvmCheckException(ts.self())); 1606 return NULL; 1607 } 1608 1609 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK); 1610 jobject result = addLocalReference(env, newObj); 1611 if (newObj != NULL) { 1612 JValue unused; 1613 dvmCallMethodV(ts.self(), (Method*) methodID, newObj, true, &unused, args); 1614 } 1615 return result; 1616} 1617 1618static jobject NewObjectA(JNIEnv* env, jclass jclazz, jmethodID methodID, jvalue* args) { 1619 ScopedJniThreadState ts(env); 1620 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 1621 1622 if (!canAllocClass(clazz) || (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))) { 1623 assert(dvmCheckException(ts.self())); 1624 return NULL; 1625 } 1626 1627 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK); 1628 jobject result = addLocalReference(env, newObj); 1629 if (newObj != NULL) { 1630 JValue unused; 1631 dvmCallMethodA(ts.self(), (Method*) methodID, newObj, true, &unused, args); 1632 } 1633 return result; 1634} 1635 1636/* 1637 * Returns the class of an object. 1638 * 1639 * JNI spec says: obj must not be NULL. 1640 */ 1641static jclass GetObjectClass(JNIEnv* env, jobject jobj) { 1642 ScopedJniThreadState ts(env); 1643 1644 assert(jobj != NULL); 1645 1646 Object* obj = dvmDecodeIndirectRef(env, jobj); 1647 return (jclass) addLocalReference(env, (Object*) obj->clazz); 1648} 1649 1650/* 1651 * Determine whether "obj" is an instance of "clazz". 1652 */ 1653static jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass jclazz) { 1654 ScopedJniThreadState ts(env); 1655 1656 assert(jclazz != NULL); 1657 if (jobj == NULL) { 1658 return true; 1659 } 1660 1661 Object* obj = dvmDecodeIndirectRef(env, jobj); 1662 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 1663 return dvmInstanceof(obj->clazz, clazz); 1664} 1665 1666/* 1667 * Get a method ID for an instance method. 1668 * 1669 * While Dalvik bytecode has distinct instructions for virtual, super, 1670 * static, direct, and interface method invocation, JNI only provides 1671 * two functions for acquiring a method ID. This call handles everything 1672 * but static methods. 1673 * 1674 * JNI defines <init> as an instance method, but Dalvik considers it a 1675 * "direct" method, so we have to special-case it here. 1676 * 1677 * Dalvik also puts all private methods into the "direct" list, so we 1678 * really need to just search both lists. 1679 */ 1680static jmethodID GetMethodID(JNIEnv* env, jclass jclazz, const char* name, const char* sig) { 1681 ScopedJniThreadState ts(env); 1682 1683 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 1684 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) { 1685 assert(dvmCheckException(ts.self())); 1686 } else if (dvmIsInterfaceClass(clazz)) { 1687 Method* meth = dvmFindInterfaceMethodHierByDescriptor(clazz, name, sig); 1688 if (meth == NULL) { 1689 dvmThrowExceptionFmt(gDvm.exNoSuchMethodError, 1690 "no method with name='%s' signature='%s' in interface %s", 1691 name, sig, clazz->descriptor); 1692 } 1693 return (jmethodID) meth; 1694 } 1695 Method* meth = dvmFindVirtualMethodHierByDescriptor(clazz, name, sig); 1696 if (meth == NULL) { 1697 /* search private methods and constructors; non-hierarchical */ 1698 meth = dvmFindDirectMethodByDescriptor(clazz, name, sig); 1699 } 1700 if (meth != NULL && dvmIsStaticMethod(meth)) { 1701 IF_LOGD() { 1702 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype); 1703 LOGD("GetMethodID: not returning static method %s.%s %s", 1704 clazz->descriptor, meth->name, desc); 1705 free(desc); 1706 } 1707 meth = NULL; 1708 } 1709 if (meth == NULL) { 1710 dvmThrowExceptionFmt(gDvm.exNoSuchMethodError, 1711 "no method with name='%s' signature='%s' in class %s", 1712 name, sig, clazz->descriptor); 1713 } else { 1714 /* 1715 * The method's class may not be the same as clazz, but if 1716 * it isn't this must be a virtual method and the class must 1717 * be a superclass (and, hence, already initialized). 1718 */ 1719 assert(dvmIsClassInitialized(meth->clazz) || dvmIsClassInitializing(meth->clazz)); 1720 } 1721 return (jmethodID) meth; 1722} 1723 1724/* 1725 * Get a field ID (instance fields). 1726 */ 1727static jfieldID GetFieldID(JNIEnv* env, jclass jclazz, const char* name, const char* sig) { 1728 ScopedJniThreadState ts(env); 1729 1730 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 1731 1732 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) { 1733 assert(dvmCheckException(ts.self())); 1734 return NULL; 1735 } 1736 1737 jfieldID id = (jfieldID) dvmFindInstanceFieldHier(clazz, name, sig); 1738 if (id == NULL) { 1739 dvmThrowExceptionFmt(gDvm.exNoSuchFieldError, 1740 "no field with name='%s' signature='%s' in class %s", 1741 name, sig, clazz->descriptor); 1742 } 1743 return id; 1744} 1745 1746/* 1747 * Get the method ID for a static method in a class. 1748 */ 1749static jmethodID GetStaticMethodID(JNIEnv* env, jclass jclazz, const char* name, const char* sig) { 1750 ScopedJniThreadState ts(env); 1751 1752 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 1753 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) { 1754 assert(dvmCheckException(ts.self())); 1755 return NULL; 1756 } 1757 1758 Method* meth = dvmFindDirectMethodHierByDescriptor(clazz, name, sig); 1759 1760 /* make sure it's static, not virtual+private */ 1761 if (meth != NULL && !dvmIsStaticMethod(meth)) { 1762 IF_LOGD() { 1763 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype); 1764 LOGD("GetStaticMethodID: not returning nonstatic method %s.%s %s", 1765 clazz->descriptor, meth->name, desc); 1766 free(desc); 1767 } 1768 meth = NULL; 1769 } 1770 1771 jmethodID id = (jmethodID) meth; 1772 if (id == NULL) { 1773 dvmThrowExceptionFmt(gDvm.exNoSuchMethodError, 1774 "no static method with name='%s' signature='%s' in class %s", 1775 name, sig, clazz->descriptor); 1776 } 1777 return id; 1778} 1779 1780/* 1781 * Get a field ID (static fields). 1782 */ 1783static jfieldID GetStaticFieldID(JNIEnv* env, jclass jclazz, const char* name, const char* sig) { 1784 ScopedJniThreadState ts(env); 1785 1786 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 1787 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) { 1788 assert(dvmCheckException(ts.self())); 1789 return NULL; 1790 } 1791 1792 jfieldID id = (jfieldID) dvmFindStaticFieldHier(clazz, name, sig); 1793 if (id == NULL) { 1794 dvmThrowExceptionFmt(gDvm.exNoSuchFieldError, 1795 "no static field with name='%s' signature='%s' in class %s", 1796 name, sig, clazz->descriptor); 1797 } 1798 return id; 1799} 1800 1801/* 1802 * Get a static field. 1803 * 1804 * If we get an object reference, add it to the local refs list. 1805 */ 1806#define GET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) \ 1807 static _ctype GetStatic##_jname##Field(JNIEnv* env, jclass jclazz, \ 1808 jfieldID fieldID) \ 1809 { \ 1810 UNUSED_PARAMETER(jclazz); \ 1811 ScopedJniThreadState ts(env); \ 1812 StaticField* sfield = (StaticField*) fieldID; \ 1813 _ctype value; \ 1814 if (dvmIsVolatileField(sfield)) { \ 1815 if (_isref) { /* only when _ctype==jobject */ \ 1816 Object* obj = dvmGetStaticFieldObjectVolatile(sfield); \ 1817 value = (_ctype)(u4)addLocalReference(env, obj); \ 1818 } else { \ 1819 value = (_ctype) dvmGetStaticField##_jname##Volatile(sfield);\ 1820 } \ 1821 } else { \ 1822 if (_isref) { \ 1823 Object* obj = dvmGetStaticFieldObject(sfield); \ 1824 value = (_ctype)(u4)addLocalReference(env, obj); \ 1825 } else { \ 1826 value = (_ctype) dvmGetStaticField##_jname(sfield); \ 1827 } \ 1828 } \ 1829 return value; \ 1830 } 1831GET_STATIC_TYPE_FIELD(jobject, Object, true); 1832GET_STATIC_TYPE_FIELD(jboolean, Boolean, false); 1833GET_STATIC_TYPE_FIELD(jbyte, Byte, false); 1834GET_STATIC_TYPE_FIELD(jchar, Char, false); 1835GET_STATIC_TYPE_FIELD(jshort, Short, false); 1836GET_STATIC_TYPE_FIELD(jint, Int, false); 1837GET_STATIC_TYPE_FIELD(jlong, Long, false); 1838GET_STATIC_TYPE_FIELD(jfloat, Float, false); 1839GET_STATIC_TYPE_FIELD(jdouble, Double, false); 1840 1841/* 1842 * Set a static field. 1843 */ 1844#define SET_STATIC_TYPE_FIELD(_ctype, _ctype2, _jname, _isref) \ 1845 static void SetStatic##_jname##Field(JNIEnv* env, jclass jclazz, \ 1846 jfieldID fieldID, _ctype value) \ 1847 { \ 1848 UNUSED_PARAMETER(jclazz); \ 1849 ScopedJniThreadState ts(env); \ 1850 StaticField* sfield = (StaticField*) fieldID; \ 1851 if (dvmIsVolatileField(sfield)) { \ 1852 if (_isref) { /* only when _ctype==jobject */ \ 1853 Object* valObj = \ 1854 dvmDecodeIndirectRef(env, (jobject)(u4)value); \ 1855 dvmSetStaticFieldObjectVolatile(sfield, valObj); \ 1856 } else { \ 1857 dvmSetStaticField##_jname##Volatile(sfield, (_ctype2)value);\ 1858 } \ 1859 } else { \ 1860 if (_isref) { \ 1861 Object* valObj = \ 1862 dvmDecodeIndirectRef(env, (jobject)(u4)value); \ 1863 dvmSetStaticFieldObject(sfield, valObj); \ 1864 } else { \ 1865 dvmSetStaticField##_jname(sfield, (_ctype2)value); \ 1866 } \ 1867 } \ 1868 } 1869SET_STATIC_TYPE_FIELD(jobject, Object*, Object, true); 1870SET_STATIC_TYPE_FIELD(jboolean, bool, Boolean, false); 1871SET_STATIC_TYPE_FIELD(jbyte, s1, Byte, false); 1872SET_STATIC_TYPE_FIELD(jchar, u2, Char, false); 1873SET_STATIC_TYPE_FIELD(jshort, s2, Short, false); 1874SET_STATIC_TYPE_FIELD(jint, s4, Int, false); 1875SET_STATIC_TYPE_FIELD(jlong, s8, Long, false); 1876SET_STATIC_TYPE_FIELD(jfloat, float, Float, false); 1877SET_STATIC_TYPE_FIELD(jdouble, double, Double, false); 1878 1879/* 1880 * Get an instance field. 1881 * 1882 * If we get an object reference, add it to the local refs list. 1883 */ 1884#define GET_TYPE_FIELD(_ctype, _jname, _isref) \ 1885 static _ctype Get##_jname##Field(JNIEnv* env, jobject jobj, \ 1886 jfieldID fieldID) \ 1887 { \ 1888 ScopedJniThreadState ts(env); \ 1889 Object* obj = dvmDecodeIndirectRef(env, jobj); \ 1890 InstField* field = (InstField*) fieldID; \ 1891 _ctype value; \ 1892 if (dvmIsVolatileField(field)) { \ 1893 if (_isref) { /* only when _ctype==jobject */ \ 1894 Object* valObj = \ 1895 dvmGetFieldObjectVolatile(obj, field->byteOffset); \ 1896 value = (_ctype)(u4)addLocalReference(env, valObj); \ 1897 } else { \ 1898 value = (_ctype) \ 1899 dvmGetField##_jname##Volatile(obj, field->byteOffset); \ 1900 } \ 1901 } else { \ 1902 if (_isref) { \ 1903 Object* valObj = dvmGetFieldObject(obj, field->byteOffset); \ 1904 value = (_ctype)(u4)addLocalReference(env, valObj); \ 1905 } else { \ 1906 value = (_ctype) dvmGetField##_jname(obj, field->byteOffset);\ 1907 } \ 1908 } \ 1909 return value; \ 1910 } 1911GET_TYPE_FIELD(jobject, Object, true); 1912GET_TYPE_FIELD(jboolean, Boolean, false); 1913GET_TYPE_FIELD(jbyte, Byte, false); 1914GET_TYPE_FIELD(jchar, Char, false); 1915GET_TYPE_FIELD(jshort, Short, false); 1916GET_TYPE_FIELD(jint, Int, false); 1917GET_TYPE_FIELD(jlong, Long, false); 1918GET_TYPE_FIELD(jfloat, Float, false); 1919GET_TYPE_FIELD(jdouble, Double, false); 1920 1921/* 1922 * Set an instance field. 1923 */ 1924#define SET_TYPE_FIELD(_ctype, _ctype2, _jname, _isref) \ 1925 static void Set##_jname##Field(JNIEnv* env, jobject jobj, \ 1926 jfieldID fieldID, _ctype value) \ 1927 { \ 1928 ScopedJniThreadState ts(env); \ 1929 Object* obj = dvmDecodeIndirectRef(env, jobj); \ 1930 InstField* field = (InstField*) fieldID; \ 1931 if (dvmIsVolatileField(field)) { \ 1932 if (_isref) { /* only when _ctype==jobject */ \ 1933 Object* valObj = \ 1934 dvmDecodeIndirectRef(env, (jobject)(u4)value); \ 1935 dvmSetFieldObjectVolatile(obj, field->byteOffset, valObj); \ 1936 } else { \ 1937 dvmSetField##_jname##Volatile(obj, \ 1938 field->byteOffset, (_ctype2)value); \ 1939 } \ 1940 } else { \ 1941 if (_isref) { \ 1942 Object* valObj = \ 1943 dvmDecodeIndirectRef(env, (jobject)(u4)value); \ 1944 dvmSetFieldObject(obj, field->byteOffset, valObj); \ 1945 } else { \ 1946 dvmSetField##_jname(obj, \ 1947 field->byteOffset, (_ctype2)value); \ 1948 } \ 1949 } \ 1950 } 1951SET_TYPE_FIELD(jobject, Object*, Object, true); 1952SET_TYPE_FIELD(jboolean, bool, Boolean, false); 1953SET_TYPE_FIELD(jbyte, s1, Byte, false); 1954SET_TYPE_FIELD(jchar, u2, Char, false); 1955SET_TYPE_FIELD(jshort, s2, Short, false); 1956SET_TYPE_FIELD(jint, s4, Int, false); 1957SET_TYPE_FIELD(jlong, s8, Long, false); 1958SET_TYPE_FIELD(jfloat, float, Float, false); 1959SET_TYPE_FIELD(jdouble, double, Double, false); 1960 1961/* 1962 * Make a virtual method call. 1963 * 1964 * Three versions (..., va_list, jvalue[]) for each return type. If we're 1965 * returning an Object, we have to add it to the local references table. 1966 */ 1967#define CALL_VIRTUAL(_ctype, _jname, _retfail, _retok, _isref) \ 1968 static _ctype Call##_jname##Method(JNIEnv* env, jobject jobj, \ 1969 jmethodID methodID, ...) \ 1970 { \ 1971 ScopedJniThreadState ts(env); \ 1972 Object* obj = dvmDecodeIndirectRef(env, jobj); \ 1973 const Method* meth; \ 1974 va_list args; \ 1975 JValue result; \ 1976 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \ 1977 if (meth == NULL) { \ 1978 return _retfail; \ 1979 } \ 1980 va_start(args, methodID); \ 1981 dvmCallMethodV(ts.self(), meth, obj, true, &result, args); \ 1982 va_end(args); \ 1983 if (_isref && !dvmCheckException(ts.self())) \ 1984 result.l = (Object*)addLocalReference(env, result.l); \ 1985 return _retok; \ 1986 } \ 1987 static _ctype Call##_jname##MethodV(JNIEnv* env, jobject jobj, \ 1988 jmethodID methodID, va_list args) \ 1989 { \ 1990 ScopedJniThreadState ts(env); \ 1991 Object* obj = dvmDecodeIndirectRef(env, jobj); \ 1992 const Method* meth; \ 1993 JValue result; \ 1994 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \ 1995 if (meth == NULL) { \ 1996 return _retfail; \ 1997 } \ 1998 dvmCallMethodV(ts.self(), meth, obj, true, &result, args); \ 1999 if (_isref && !dvmCheckException(ts.self())) \ 2000 result.l = (Object*)addLocalReference(env, result.l); \ 2001 return _retok; \ 2002 } \ 2003 static _ctype Call##_jname##MethodA(JNIEnv* env, jobject jobj, \ 2004 jmethodID methodID, jvalue* args) \ 2005 { \ 2006 ScopedJniThreadState ts(env); \ 2007 Object* obj = dvmDecodeIndirectRef(env, jobj); \ 2008 const Method* meth; \ 2009 JValue result; \ 2010 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \ 2011 if (meth == NULL) { \ 2012 return _retfail; \ 2013 } \ 2014 dvmCallMethodA(ts.self(), meth, obj, true, &result, args); \ 2015 if (_isref && !dvmCheckException(ts.self())) \ 2016 result.l = (Object*)addLocalReference(env, result.l); \ 2017 return _retok; \ 2018 } 2019CALL_VIRTUAL(jobject, Object, NULL, (jobject) result.l, true); 2020CALL_VIRTUAL(jboolean, Boolean, 0, result.z, false); 2021CALL_VIRTUAL(jbyte, Byte, 0, result.b, false); 2022CALL_VIRTUAL(jchar, Char, 0, result.c, false); 2023CALL_VIRTUAL(jshort, Short, 0, result.s, false); 2024CALL_VIRTUAL(jint, Int, 0, result.i, false); 2025CALL_VIRTUAL(jlong, Long, 0, result.j, false); 2026CALL_VIRTUAL(jfloat, Float, 0.0f, result.f, false); 2027CALL_VIRTUAL(jdouble, Double, 0.0, result.d, false); 2028CALL_VIRTUAL(void, Void, , , false); 2029 2030/* 2031 * Make a "non-virtual" method call. We're still calling a virtual method, 2032 * but this time we're not doing an indirection through the object's vtable. 2033 * The "clazz" parameter defines which implementation of a method we want. 2034 * 2035 * Three versions (..., va_list, jvalue[]) for each return type. 2036 */ 2037#define CALL_NONVIRTUAL(_ctype, _jname, _retfail, _retok, _isref) \ 2038 static _ctype CallNonvirtual##_jname##Method(JNIEnv* env, jobject jobj, \ 2039 jclass jclazz, jmethodID methodID, ...) \ 2040 { \ 2041 ScopedJniThreadState ts(env); \ 2042 Object* obj = dvmDecodeIndirectRef(env, jobj); \ 2043 ClassObject* clazz = \ 2044 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \ 2045 const Method* meth; \ 2046 va_list args; \ 2047 JValue result; \ 2048 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \ 2049 if (meth == NULL) { \ 2050 return _retfail; \ 2051 } \ 2052 va_start(args, methodID); \ 2053 dvmCallMethodV(ts.self(), meth, obj, true, &result, args); \ 2054 if (_isref && !dvmCheckException(ts.self())) \ 2055 result.l = (Object*)addLocalReference(env, result.l); \ 2056 va_end(args); \ 2057 return _retok; \ 2058 } \ 2059 static _ctype CallNonvirtual##_jname##MethodV(JNIEnv* env, jobject jobj,\ 2060 jclass jclazz, jmethodID methodID, va_list args) \ 2061 { \ 2062 ScopedJniThreadState ts(env); \ 2063 Object* obj = dvmDecodeIndirectRef(env, jobj); \ 2064 ClassObject* clazz = \ 2065 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \ 2066 const Method* meth; \ 2067 JValue result; \ 2068 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \ 2069 if (meth == NULL) { \ 2070 return _retfail; \ 2071 } \ 2072 dvmCallMethodV(ts.self(), meth, obj, true, &result, args); \ 2073 if (_isref && !dvmCheckException(ts.self())) \ 2074 result.l = (Object*)addLocalReference(env, result.l); \ 2075 return _retok; \ 2076 } \ 2077 static _ctype CallNonvirtual##_jname##MethodA(JNIEnv* env, jobject jobj,\ 2078 jclass jclazz, jmethodID methodID, jvalue* args) \ 2079 { \ 2080 ScopedJniThreadState ts(env); \ 2081 Object* obj = dvmDecodeIndirectRef(env, jobj); \ 2082 ClassObject* clazz = \ 2083 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \ 2084 const Method* meth; \ 2085 JValue result; \ 2086 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \ 2087 if (meth == NULL) { \ 2088 return _retfail; \ 2089 } \ 2090 dvmCallMethodA(ts.self(), meth, obj, true, &result, args); \ 2091 if (_isref && !dvmCheckException(ts.self())) \ 2092 result.l = (Object*)addLocalReference(env, result.l); \ 2093 return _retok; \ 2094 } 2095CALL_NONVIRTUAL(jobject, Object, NULL, (jobject) result.l, true); 2096CALL_NONVIRTUAL(jboolean, Boolean, 0, result.z, false); 2097CALL_NONVIRTUAL(jbyte, Byte, 0, result.b, false); 2098CALL_NONVIRTUAL(jchar, Char, 0, result.c, false); 2099CALL_NONVIRTUAL(jshort, Short, 0, result.s, false); 2100CALL_NONVIRTUAL(jint, Int, 0, result.i, false); 2101CALL_NONVIRTUAL(jlong, Long, 0, result.j, false); 2102CALL_NONVIRTUAL(jfloat, Float, 0.0f, result.f, false); 2103CALL_NONVIRTUAL(jdouble, Double, 0.0, result.d, false); 2104CALL_NONVIRTUAL(void, Void, , , false); 2105 2106 2107/* 2108 * Call a static method. 2109 */ 2110#define CALL_STATIC(_ctype, _jname, _retfail, _retok, _isref) \ 2111 static _ctype CallStatic##_jname##Method(JNIEnv* env, jclass jclazz, \ 2112 jmethodID methodID, ...) \ 2113 { \ 2114 UNUSED_PARAMETER(jclazz); \ 2115 ScopedJniThreadState ts(env); \ 2116 JValue result; \ 2117 va_list args; \ 2118 va_start(args, methodID); \ 2119 dvmCallMethodV(ts.self(), (Method*)methodID, NULL, true, &result, args);\ 2120 va_end(args); \ 2121 if (_isref && !dvmCheckException(ts.self())) \ 2122 result.l = (Object*)addLocalReference(env, result.l); \ 2123 return _retok; \ 2124 } \ 2125 static _ctype CallStatic##_jname##MethodV(JNIEnv* env, jclass jclazz, \ 2126 jmethodID methodID, va_list args) \ 2127 { \ 2128 UNUSED_PARAMETER(jclazz); \ 2129 ScopedJniThreadState ts(env); \ 2130 JValue result; \ 2131 dvmCallMethodV(ts.self(), (Method*)methodID, NULL, true, &result, args);\ 2132 if (_isref && !dvmCheckException(ts.self())) \ 2133 result.l = (Object*)addLocalReference(env, result.l); \ 2134 return _retok; \ 2135 } \ 2136 static _ctype CallStatic##_jname##MethodA(JNIEnv* env, jclass jclazz, \ 2137 jmethodID methodID, jvalue* args) \ 2138 { \ 2139 UNUSED_PARAMETER(jclazz); \ 2140 ScopedJniThreadState ts(env); \ 2141 JValue result; \ 2142 dvmCallMethodA(ts.self(), (Method*)methodID, NULL, true, &result, args);\ 2143 if (_isref && !dvmCheckException(ts.self())) \ 2144 result.l = (Object*)addLocalReference(env, result.l); \ 2145 return _retok; \ 2146 } 2147CALL_STATIC(jobject, Object, NULL, (jobject) result.l, true); 2148CALL_STATIC(jboolean, Boolean, 0, result.z, false); 2149CALL_STATIC(jbyte, Byte, 0, result.b, false); 2150CALL_STATIC(jchar, Char, 0, result.c, false); 2151CALL_STATIC(jshort, Short, 0, result.s, false); 2152CALL_STATIC(jint, Int, 0, result.i, false); 2153CALL_STATIC(jlong, Long, 0, result.j, false); 2154CALL_STATIC(jfloat, Float, 0.0f, result.f, false); 2155CALL_STATIC(jdouble, Double, 0.0, result.d, false); 2156CALL_STATIC(void, Void, , , false); 2157 2158/* 2159 * Create a new String from Unicode data. 2160 * 2161 * If "len" is zero, we will return an empty string even if "unicodeChars" 2162 * is NULL. (The JNI spec is vague here.) 2163 */ 2164static jstring NewString(JNIEnv* env, const jchar* unicodeChars, jsize len) { 2165 ScopedJniThreadState ts(env); 2166 StringObject* jstr = dvmCreateStringFromUnicode(unicodeChars, len); 2167 if (jstr == NULL) { 2168 return NULL; 2169 } 2170 dvmReleaseTrackedAlloc((Object*) jstr, NULL); 2171 return (jstring) addLocalReference(env, (Object*) jstr); 2172} 2173 2174/* 2175 * Return the length of a String in Unicode character units. 2176 */ 2177static jsize GetStringLength(JNIEnv* env, jstring jstr) { 2178 ScopedJniThreadState ts(env); 2179 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); 2180 return strObj->length(); 2181} 2182 2183 2184/* 2185 * Get a string's character data. 2186 * 2187 * The result is guaranteed to be valid until ReleaseStringChars is 2188 * called, which means we have to pin it or return a copy. 2189 */ 2190static const jchar* GetStringChars(JNIEnv* env, jstring jstr, jboolean* isCopy) { 2191 ScopedJniThreadState ts(env); 2192 2193 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); 2194 ArrayObject* strChars = strObj->array(); 2195 2196 pinPrimitiveArray(strChars); 2197 2198 const u2* data = strObj->chars(); 2199 if (isCopy != NULL) { 2200 *isCopy = JNI_FALSE; 2201 } 2202 return (jchar*) data; 2203} 2204 2205/* 2206 * Release our grip on some characters from a string. 2207 */ 2208static void ReleaseStringChars(JNIEnv* env, jstring jstr, const jchar* chars) { 2209 ScopedJniThreadState ts(env); 2210 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); 2211 ArrayObject* strChars = strObj->array(); 2212 unpinPrimitiveArray(strChars); 2213} 2214 2215/* 2216 * Create a new java.lang.String object from chars in modified UTF-8 form. 2217 * 2218 * The spec doesn't say how to handle a NULL string. Popular desktop VMs 2219 * accept it and return a NULL pointer in response. 2220 */ 2221static jstring NewStringUTF(JNIEnv* env, const char* bytes) { 2222 ScopedJniThreadState ts(env); 2223 if (bytes == NULL) { 2224 return NULL; 2225 } 2226 /* note newStr could come back NULL on OOM */ 2227 StringObject* newStr = dvmCreateStringFromCstr(bytes); 2228 jstring result = (jstring) addLocalReference(env, (Object*) newStr); 2229 dvmReleaseTrackedAlloc((Object*)newStr, NULL); 2230 return result; 2231} 2232 2233/* 2234 * Return the length in bytes of the modified UTF-8 form of the string. 2235 */ 2236static jsize GetStringUTFLength(JNIEnv* env, jstring jstr) { 2237 ScopedJniThreadState ts(env); 2238 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); 2239 if (strObj == NULL) { 2240 return 0; // Should we throw something or assert? 2241 } 2242 return strObj->utfLength(); 2243} 2244 2245/* 2246 * Convert "string" to modified UTF-8 and return a pointer. The returned 2247 * value must be released with ReleaseStringUTFChars. 2248 * 2249 * According to the JNI reference, "Returns a pointer to a UTF-8 string, 2250 * or NULL if the operation fails. Returns NULL if and only if an invocation 2251 * of this function has thrown an exception." 2252 * 2253 * The behavior here currently follows that of other open-source VMs, which 2254 * quietly return NULL if "string" is NULL. We should consider throwing an 2255 * NPE. (The CheckJNI code blows up if you try to pass in a NULL string, 2256 * which should catch this sort of thing during development.) Certain other 2257 * VMs will crash with a segmentation fault. 2258 */ 2259static const char* GetStringUTFChars(JNIEnv* env, jstring jstr, jboolean* isCopy) { 2260 ScopedJniThreadState ts(env); 2261 if (jstr == NULL) { 2262 /* this shouldn't happen; throw NPE? */ 2263 return NULL; 2264 } 2265 if (isCopy != NULL) { 2266 *isCopy = JNI_TRUE; 2267 } 2268 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); 2269 char* newStr = dvmCreateCstrFromString(strObj); 2270 if (newStr == NULL) { 2271 /* assume memory failure */ 2272 dvmThrowOutOfMemoryError("native heap string alloc failed"); 2273 } 2274 return newStr; 2275} 2276 2277/* 2278 * Release a string created by GetStringUTFChars(). 2279 */ 2280static void ReleaseStringUTFChars(JNIEnv* env, jstring jstr, const char* utf) { 2281 ScopedJniThreadState ts(env); 2282 free((char*) utf); 2283} 2284 2285/* 2286 * Return the capacity of the array. 2287 */ 2288static jsize GetArrayLength(JNIEnv* env, jarray jarr) { 2289 ScopedJniThreadState ts(env); 2290 ArrayObject* arrObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr); 2291 return arrObj->length; 2292} 2293 2294/* 2295 * Construct a new array that holds objects from class "elementClass". 2296 */ 2297static jobjectArray NewObjectArray(JNIEnv* env, jsize length, 2298 jclass jelementClass, jobject jinitialElement) 2299{ 2300 ScopedJniThreadState ts(env); 2301 2302 if (jelementClass == NULL) { 2303 dvmThrowNullPointerException("JNI NewObjectArray elementClass == NULL"); 2304 return NULL; 2305 } 2306 2307 ClassObject* elemClassObj = (ClassObject*) dvmDecodeIndirectRef(env, jelementClass); 2308 ClassObject* arrayClass = dvmFindArrayClassForElement(elemClassObj); 2309 ArrayObject* newObj = dvmAllocArrayByClass(arrayClass, length, ALLOC_DEFAULT); 2310 if (newObj == NULL) { 2311 assert(dvmCheckException(ts.self())); 2312 return NULL; 2313 } 2314 jobjectArray newArray = (jobjectArray) addLocalReference(env, (Object*) newObj); 2315 dvmReleaseTrackedAlloc((Object*) newObj, NULL); 2316 2317 /* 2318 * Initialize the array. 2319 */ 2320 if (jinitialElement != NULL) { 2321 Object* initialElement = dvmDecodeIndirectRef(env, jinitialElement); 2322 Object** arrayData = (Object**) (void*) newObj->contents; 2323 for (jsize i = 0; i < length; ++i) { 2324 arrayData[i] = initialElement; 2325 } 2326 } 2327 2328 return newArray; 2329} 2330 2331static bool checkArrayElementBounds(ArrayObject* arrayObj, jsize index) { 2332 assert(arrayObj != NULL); 2333 if (index < 0 || index >= (int) arrayObj->length) { 2334 dvmThrowArrayIndexOutOfBoundsException(arrayObj->length, index); 2335 return false; 2336 } 2337 return true; 2338} 2339 2340/* 2341 * Get one element of an Object array. 2342 * 2343 * Add the object to the local references table in case the array goes away. 2344 */ 2345static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray jarr, jsize index) { 2346 ScopedJniThreadState ts(env); 2347 2348 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr); 2349 if (!checkArrayElementBounds(arrayObj, index)) { 2350 return NULL; 2351 } 2352 2353 Object* value = ((Object**) (void*) arrayObj->contents)[index]; 2354 return addLocalReference(env, value); 2355} 2356 2357/* 2358 * Set one element of an Object array. 2359 */ 2360static void SetObjectArrayElement(JNIEnv* env, jobjectArray jarr, jsize index, jobject jobj) { 2361 ScopedJniThreadState ts(env); 2362 2363 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr); 2364 if (!checkArrayElementBounds(arrayObj, index)) { 2365 return; 2366 } 2367 2368 //LOGV("JNI: set element %d in array %p to %p", index, array, value); 2369 2370 Object* obj = dvmDecodeIndirectRef(env, jobj); 2371 dvmSetObjectArrayElement(arrayObj, index, obj); 2372} 2373 2374/* 2375 * Create a new array of primitive elements. 2376 */ 2377#define NEW_PRIMITIVE_ARRAY(_artype, _jname, _typechar) \ 2378 static _artype New##_jname##Array(JNIEnv* env, jsize length) { \ 2379 ScopedJniThreadState ts(env); \ 2380 ArrayObject* arrayObj = dvmAllocPrimitiveArray(_typechar, length, ALLOC_DEFAULT); \ 2381 if (arrayObj == NULL) { \ 2382 return NULL; \ 2383 } \ 2384 _artype result = (_artype) addLocalReference(env, (Object*) arrayObj); \ 2385 dvmReleaseTrackedAlloc((Object*) arrayObj, NULL); \ 2386 return result; \ 2387 } 2388NEW_PRIMITIVE_ARRAY(jbooleanArray, Boolean, 'Z'); 2389NEW_PRIMITIVE_ARRAY(jbyteArray, Byte, 'B'); 2390NEW_PRIMITIVE_ARRAY(jcharArray, Char, 'C'); 2391NEW_PRIMITIVE_ARRAY(jshortArray, Short, 'S'); 2392NEW_PRIMITIVE_ARRAY(jintArray, Int, 'I'); 2393NEW_PRIMITIVE_ARRAY(jlongArray, Long, 'J'); 2394NEW_PRIMITIVE_ARRAY(jfloatArray, Float, 'F'); 2395NEW_PRIMITIVE_ARRAY(jdoubleArray, Double, 'D'); 2396 2397/* 2398 * Get a pointer to a C array of primitive elements from an array object 2399 * of the matching type. 2400 * 2401 * In a compacting GC, we either need to return a copy of the elements or 2402 * "pin" the memory. Otherwise we run the risk of native code using the 2403 * buffer as the destination of e.g. a blocking read() call that wakes up 2404 * during a GC. 2405 */ 2406#define GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \ 2407 static _ctype* Get##_jname##ArrayElements(JNIEnv* env, \ 2408 _ctype##Array jarr, jboolean* isCopy) \ 2409 { \ 2410 ScopedJniThreadState ts(env); \ 2411 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \ 2412 pinPrimitiveArray(arrayObj); \ 2413 _ctype* data = (_ctype*) (void*) arrayObj->contents; \ 2414 if (isCopy != NULL) { \ 2415 *isCopy = JNI_FALSE; \ 2416 } \ 2417 return data; \ 2418 } 2419 2420/* 2421 * Release the storage locked down by the "get" function. 2422 * 2423 * The spec says, "'mode' has no effect if 'elems' is not a copy of the 2424 * elements in 'array'." They apparently did not anticipate the need to 2425 * un-pin memory. 2426 */ 2427#define RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \ 2428 static void Release##_jname##ArrayElements(JNIEnv* env, \ 2429 _ctype##Array jarr, _ctype* elems, jint mode) \ 2430 { \ 2431 UNUSED_PARAMETER(elems); \ 2432 if (mode != JNI_COMMIT) { \ 2433 ScopedJniThreadState ts(env); \ 2434 ArrayObject* arrayObj = \ 2435 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \ 2436 unpinPrimitiveArray(arrayObj); \ 2437 } \ 2438 } 2439 2440static void throwArrayRegionOutOfBounds(ArrayObject* arrayObj, jsize start, 2441 jsize len, const char* arrayIdentifier) 2442{ 2443 dvmThrowExceptionFmt(gDvm.exArrayIndexOutOfBoundsException, 2444 "%s offset=%d length=%d %s.length=%d", 2445 arrayObj->clazz->descriptor, start, len, arrayIdentifier, 2446 arrayObj->length); 2447} 2448 2449/* 2450 * Copy a section of a primitive array to a buffer. 2451 */ 2452#define GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \ 2453 static void Get##_jname##ArrayRegion(JNIEnv* env, \ 2454 _ctype##Array jarr, jsize start, jsize len, _ctype* buf) \ 2455 { \ 2456 ScopedJniThreadState ts(env); \ 2457 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \ 2458 _ctype* data = (_ctype*) (void*) arrayObj->contents; \ 2459 if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \ 2460 throwArrayRegionOutOfBounds(arrayObj, start, len, "src"); \ 2461 } else { \ 2462 memcpy(buf, data + start, len * sizeof(_ctype)); \ 2463 } \ 2464 } 2465 2466/* 2467 * Copy a section of a primitive array from a buffer. 2468 */ 2469#define SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \ 2470 static void Set##_jname##ArrayRegion(JNIEnv* env, \ 2471 _ctype##Array jarr, jsize start, jsize len, const _ctype* buf) \ 2472 { \ 2473 ScopedJniThreadState ts(env); \ 2474 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \ 2475 _ctype* data = (_ctype*) (void*) arrayObj->contents; \ 2476 if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \ 2477 throwArrayRegionOutOfBounds(arrayObj, start, len, "dst"); \ 2478 } else { \ 2479 memcpy(data + start, buf, len * sizeof(_ctype)); \ 2480 } \ 2481 } 2482 2483/* 2484 * 4-in-1: 2485 * Get<Type>ArrayElements 2486 * Release<Type>ArrayElements 2487 * Get<Type>ArrayRegion 2488 * Set<Type>ArrayRegion 2489 */ 2490#define PRIMITIVE_ARRAY_FUNCTIONS(_ctype, _jname) \ 2491 GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \ 2492 RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \ 2493 GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname); \ 2494 SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname); 2495 2496PRIMITIVE_ARRAY_FUNCTIONS(jboolean, Boolean); 2497PRIMITIVE_ARRAY_FUNCTIONS(jbyte, Byte); 2498PRIMITIVE_ARRAY_FUNCTIONS(jchar, Char); 2499PRIMITIVE_ARRAY_FUNCTIONS(jshort, Short); 2500PRIMITIVE_ARRAY_FUNCTIONS(jint, Int); 2501PRIMITIVE_ARRAY_FUNCTIONS(jlong, Long); 2502PRIMITIVE_ARRAY_FUNCTIONS(jfloat, Float); 2503PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double); 2504 2505/* 2506 * Register one or more native functions in one class. 2507 * 2508 * This can be called multiple times on the same method, allowing the 2509 * caller to redefine the method implementation at will. 2510 */ 2511static jint RegisterNatives(JNIEnv* env, jclass jclazz, 2512 const JNINativeMethod* methods, jint nMethods) 2513{ 2514 ScopedJniThreadState ts(env); 2515 2516 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 2517 2518 if (gDvm.verboseJni) { 2519 LOGI("[Registering JNI native methods for class %s]", 2520 clazz->descriptor); 2521 } 2522 2523 for (int i = 0; i < nMethods; i++) { 2524 if (!dvmRegisterJNIMethod(clazz, methods[i].name, 2525 methods[i].signature, methods[i].fnPtr)) 2526 { 2527 return JNI_ERR; 2528 } 2529 } 2530 return JNI_OK; 2531} 2532 2533/* 2534 * Un-register all native methods associated with the class. 2535 * 2536 * The JNI docs refer to this as a way to reload/relink native libraries, 2537 * and say it "should not be used in normal native code". In particular, 2538 * there is no need to do this during shutdown, and you do not need to do 2539 * this before redefining a method implementation with RegisterNatives. 2540 * 2541 * It's chiefly useful for a native "plugin"-style library that wasn't 2542 * loaded with System.loadLibrary() (since there's no way to unload those). 2543 * For example, the library could upgrade itself by: 2544 * 2545 * 1. call UnregisterNatives to unbind the old methods 2546 * 2. ensure that no code is still executing inside it (somehow) 2547 * 3. dlclose() the library 2548 * 4. dlopen() the new library 2549 * 5. use RegisterNatives to bind the methods from the new library 2550 * 2551 * The above can work correctly without the UnregisterNatives call, but 2552 * creates a window of opportunity in which somebody might try to call a 2553 * method that is pointing at unmapped memory, crashing the VM. In theory 2554 * the same guards that prevent dlclose() from unmapping executing code could 2555 * prevent that anyway, but with this we can be more thorough and also deal 2556 * with methods that only exist in the old or new form of the library (maybe 2557 * the lib wants to try the call and catch the UnsatisfiedLinkError). 2558 */ 2559static jint UnregisterNatives(JNIEnv* env, jclass jclazz) { 2560 ScopedJniThreadState ts(env); 2561 2562 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz); 2563 if (gDvm.verboseJni) { 2564 LOGI("[Unregistering JNI native methods for class %s]", 2565 clazz->descriptor); 2566 } 2567 dvmUnregisterJNINativeMethods(clazz); 2568 return JNI_OK; 2569} 2570 2571/* 2572 * Lock the monitor. 2573 * 2574 * We have to track all monitor enters and exits, so that we can undo any 2575 * outstanding synchronization before the thread exits. 2576 */ 2577static jint MonitorEnter(JNIEnv* env, jobject jobj) { 2578 ScopedJniThreadState ts(env); 2579 Object* obj = dvmDecodeIndirectRef(env, jobj); 2580 dvmLockObject(ts.self(), obj); 2581 trackMonitorEnter(ts.self(), obj); 2582 return JNI_OK; 2583} 2584 2585/* 2586 * Unlock the monitor. 2587 * 2588 * Throws an IllegalMonitorStateException if the current thread 2589 * doesn't own the monitor. (dvmUnlockObject() takes care of the throw.) 2590 * 2591 * According to the 1.6 spec, it's legal to call here with an exception 2592 * pending. If this fails, we'll stomp the original exception. 2593 */ 2594static jint MonitorExit(JNIEnv* env, jobject jobj) { 2595 ScopedJniThreadState ts(env); 2596 Object* obj = dvmDecodeIndirectRef(env, jobj); 2597 bool success = dvmUnlockObject(ts.self(), obj); 2598 if (success) { 2599 trackMonitorExit(ts.self(), obj); 2600 } 2601 return success ? JNI_OK : JNI_ERR; 2602} 2603 2604/* 2605 * Return the JavaVM interface associated with the current thread. 2606 */ 2607static jint GetJavaVM(JNIEnv* env, JavaVM** vm) { 2608 ScopedJniThreadState ts(env); 2609 *vm = gDvmJni.jniVm; 2610 return (*vm == NULL) ? JNI_ERR : JNI_OK; 2611} 2612 2613/* 2614 * Copies "len" Unicode characters, from offset "start". 2615 */ 2616static void GetStringRegion(JNIEnv* env, jstring jstr, jsize start, jsize len, jchar* buf) { 2617 ScopedJniThreadState ts(env); 2618 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); 2619 int strLen = strObj->length(); 2620 if (((start|len) < 0) || (start + len > strLen)) { 2621 dvmThrowStringIndexOutOfBoundsExceptionWithRegion(strLen, start, len); 2622 return; 2623 } 2624 memcpy(buf, strObj->chars() + start, len * sizeof(u2)); 2625} 2626 2627/* 2628 * Translates "len" Unicode characters, from offset "start", into 2629 * modified UTF-8 encoding. 2630 */ 2631static void GetStringUTFRegion(JNIEnv* env, jstring jstr, jsize start, jsize len, char* buf) { 2632 ScopedJniThreadState ts(env); 2633 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); 2634 int strLen = strObj->length(); 2635 if (((start|len) < 0) || (start + len > strLen)) { 2636 dvmThrowStringIndexOutOfBoundsExceptionWithRegion(strLen, start, len); 2637 return; 2638 } 2639 dvmGetStringUtfRegion(strObj, start, len, buf); 2640} 2641 2642/* 2643 * Get a raw pointer to array data. 2644 * 2645 * The caller is expected to call "release" before doing any JNI calls 2646 * or blocking I/O operations. 2647 * 2648 * We need to pin the memory or block GC. 2649 */ 2650static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray jarr, jboolean* isCopy) { 2651 ScopedJniThreadState ts(env); 2652 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr); 2653 pinPrimitiveArray(arrayObj); 2654 void* data = arrayObj->contents; 2655 if (isCopy != NULL) { 2656 *isCopy = JNI_FALSE; 2657 } 2658 return data; 2659} 2660 2661/* 2662 * Release an array obtained with GetPrimitiveArrayCritical. 2663 */ 2664static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray jarr, void* carray, jint mode) { 2665 if (mode != JNI_COMMIT) { 2666 ScopedJniThreadState ts(env); 2667 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr); 2668 unpinPrimitiveArray(arrayObj); 2669 } 2670} 2671 2672/* 2673 * Like GetStringChars, but with restricted use. 2674 */ 2675static const jchar* GetStringCritical(JNIEnv* env, jstring jstr, jboolean* isCopy) { 2676 ScopedJniThreadState ts(env); 2677 2678 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); 2679 ArrayObject* strChars = strObj->array(); 2680 2681 pinPrimitiveArray(strChars); 2682 2683 const u2* data = strObj->chars(); 2684 if (isCopy != NULL) { 2685 *isCopy = JNI_FALSE; 2686 } 2687 return (jchar*) data; 2688} 2689 2690/* 2691 * Like ReleaseStringChars, but with restricted use. 2692 */ 2693static void ReleaseStringCritical(JNIEnv* env, jstring jstr, const jchar* carray) { 2694 ScopedJniThreadState ts(env); 2695 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr); 2696 ArrayObject* strChars = strObj->array(); 2697 unpinPrimitiveArray(strChars); 2698} 2699 2700/* 2701 * Create a new weak global reference. 2702 */ 2703static jweak NewWeakGlobalRef(JNIEnv* env, jobject jobj) { 2704 ScopedJniThreadState ts(env); 2705 Object *obj = dvmDecodeIndirectRef(env, jobj); 2706 return (jweak) addWeakGlobalReference(obj); 2707} 2708 2709/* 2710 * Delete the specified weak global reference. 2711 */ 2712static void DeleteWeakGlobalRef(JNIEnv* env, jweak wref) { 2713 ScopedJniThreadState ts(env); 2714 deleteWeakGlobalReference(wref); 2715} 2716 2717/* 2718 * Quick check for pending exceptions. 2719 * 2720 * TODO: we should be able to skip the enter/exit macros here. 2721 */ 2722static jboolean ExceptionCheck(JNIEnv* env) { 2723 ScopedJniThreadState ts(env); 2724 return dvmCheckException(ts.self()); 2725} 2726 2727/* 2728 * Returns the type of the object referred to by "obj". It can be local, 2729 * global, or weak global. 2730 * 2731 * In the current implementation, references can be global and local at 2732 * the same time, so while the return value is accurate it may not tell 2733 * the whole story. 2734 */ 2735static jobjectRefType GetObjectRefType(JNIEnv* env, jobject jobj) { 2736 ScopedJniThreadState ts(env); 2737 return dvmGetJNIRefType(env, jobj); 2738} 2739 2740/* 2741 * Allocate and return a new java.nio.ByteBuffer for this block of memory. 2742 * 2743 * "address" may not be NULL, and "capacity" must be > 0. (These are only 2744 * verified when CheckJNI is enabled.) 2745 */ 2746static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity) { 2747 ScopedJniThreadState ts(env); 2748 2749 /* create an instance of java.nio.ReadWriteDirectByteBuffer */ 2750 ClassObject* bufferClazz = gDvm.classJavaNioReadWriteDirectByteBuffer; 2751 if (!dvmIsClassInitialized(bufferClazz) && !dvmInitClass(bufferClazz)) { 2752 return NULL; 2753 } 2754 Object* newObj = dvmAllocObject(bufferClazz, ALLOC_DONT_TRACK); 2755 if (newObj == NULL) { 2756 return NULL; 2757 } 2758 /* call the constructor */ 2759 jobject result = addLocalReference(env, newObj); 2760 JValue unused; 2761 dvmCallMethod(ts.self(), gDvm.methJavaNioReadWriteDirectByteBuffer_init, 2762 newObj, &unused, (jint) address, (jint) capacity); 2763 if (dvmGetException(ts.self()) != NULL) { 2764 deleteLocalReference(env, result); 2765 return NULL; 2766 } 2767 return result; 2768} 2769 2770/* 2771 * Get the starting address of the buffer for the specified java.nio.Buffer. 2772 * 2773 * If this is not a "direct" buffer, we return NULL. 2774 */ 2775static void* GetDirectBufferAddress(JNIEnv* env, jobject jbuf) { 2776 ScopedJniThreadState ts(env); 2777 2778 // All Buffer objects have an effectiveDirectAddress field. 2779 Object* bufObj = dvmDecodeIndirectRef(env, jbuf); 2780 return (void*) dvmGetFieldInt(bufObj, gDvm.offJavaNioBuffer_effectiveDirectAddress); 2781} 2782 2783/* 2784 * Get the capacity of the buffer for the specified java.nio.Buffer. 2785 * 2786 * Returns -1 if the object is not a direct buffer. (We actually skip 2787 * this check, since it's expensive to determine, and just return the 2788 * capacity regardless.) 2789 */ 2790static jlong GetDirectBufferCapacity(JNIEnv* env, jobject jbuf) { 2791 ScopedJniThreadState ts(env); 2792 2793 /* 2794 * The capacity is always in the Buffer.capacity field. 2795 * 2796 * (The "check" version should verify that this is actually a Buffer, 2797 * but we're not required to do so here.) 2798 */ 2799 Object* buf = dvmDecodeIndirectRef(env, jbuf); 2800 return dvmGetFieldInt(buf, gDvm.offJavaNioBuffer_capacity); 2801} 2802 2803 2804/* 2805 * =========================================================================== 2806 * JNI invocation functions 2807 * =========================================================================== 2808 */ 2809 2810/* 2811 * Handle AttachCurrentThread{AsDaemon}. 2812 * 2813 * We need to make sure the VM is actually running. For example, if we start 2814 * up, issue an Attach, and the VM exits almost immediately, by the time the 2815 * attaching happens the VM could already be shutting down. 2816 * 2817 * It's hard to avoid a race condition here because we don't want to hold 2818 * a lock across the entire operation. What we can do is temporarily 2819 * increment the thread count to prevent a VM exit. 2820 * 2821 * This could potentially still have problems if a daemon thread calls here 2822 * while the VM is shutting down. dvmThreadSelf() will work, since it just 2823 * uses pthread TLS, but dereferencing "vm" could fail. Such is life when 2824 * you shut down a VM while threads are still running inside it. 2825 * 2826 * Remember that some code may call this as a way to find the per-thread 2827 * JNIEnv pointer. Don't do excess work for that case. 2828 */ 2829static jint attachThread(JavaVM* vm, JNIEnv** p_env, void* thr_args, bool isDaemon) { 2830 JavaVMAttachArgs* args = (JavaVMAttachArgs*) thr_args; 2831 2832 /* 2833 * Return immediately if we're already one with the VM. 2834 */ 2835 Thread* self = dvmThreadSelf(); 2836 if (self != NULL) { 2837 *p_env = self->jniEnv; 2838 return JNI_OK; 2839 } 2840 2841 /* 2842 * No threads allowed in zygote mode. 2843 */ 2844 if (gDvm.zygote) { 2845 return JNI_ERR; 2846 } 2847 2848 /* increment the count to keep the VM from bailing while we run */ 2849 dvmLockThreadList(NULL); 2850 if (gDvm.nonDaemonThreadCount == 0) { 2851 // dead or dying 2852 LOGV("Refusing to attach thread '%s' -- VM is shutting down", 2853 (thr_args == NULL) ? "(unknown)" : args->name); 2854 dvmUnlockThreadList(); 2855 return JNI_ERR; 2856 } 2857 gDvm.nonDaemonThreadCount++; 2858 dvmUnlockThreadList(); 2859 2860 /* tweak the JavaVMAttachArgs as needed */ 2861 JavaVMAttachArgs argsCopy; 2862 if (args == NULL) { 2863 /* allow the v1.1 calling convention */ 2864 argsCopy.version = JNI_VERSION_1_2; 2865 argsCopy.name = NULL; 2866 argsCopy.group = (jobject) dvmGetMainThreadGroup(); 2867 } else { 2868 assert(args->version >= JNI_VERSION_1_2); 2869 2870 argsCopy.version = args->version; 2871 argsCopy.name = args->name; 2872 if (args->group != NULL) { 2873 argsCopy.group = (jobject) dvmDecodeIndirectRef(NULL, args->group); 2874 } else { 2875 argsCopy.group = (jobject) dvmGetMainThreadGroup(); 2876 } 2877 } 2878 2879 bool result = dvmAttachCurrentThread(&argsCopy, isDaemon); 2880 2881 /* restore the count */ 2882 dvmLockThreadList(NULL); 2883 gDvm.nonDaemonThreadCount--; 2884 dvmUnlockThreadList(); 2885 2886 /* 2887 * Change the status to indicate that we're out in native code. This 2888 * call is not guarded with state-change macros, so we have to do it 2889 * by hand. 2890 */ 2891 if (result) { 2892 self = dvmThreadSelf(); 2893 assert(self != NULL); 2894 dvmChangeStatus(self, THREAD_NATIVE); 2895 *p_env = self->jniEnv; 2896 return JNI_OK; 2897 } else { 2898 return JNI_ERR; 2899 } 2900} 2901 2902/* 2903 * Attach the current thread to the VM. If the thread is already attached, 2904 * this is a no-op. 2905 */ 2906static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args) { 2907 return attachThread(vm, p_env, thr_args, false); 2908} 2909 2910/* 2911 * Like AttachCurrentThread, but set the "daemon" flag. 2912 */ 2913static jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env, void* thr_args) 2914{ 2915 return attachThread(vm, p_env, thr_args, true); 2916} 2917 2918/* 2919 * Dissociate the current thread from the VM. 2920 */ 2921static jint DetachCurrentThread(JavaVM* vm) { 2922 Thread* self = dvmThreadSelf(); 2923 if (self == NULL) { 2924 /* not attached, can't do anything */ 2925 return JNI_ERR; 2926 } 2927 2928 /* switch to "running" to check for suspension */ 2929 dvmChangeStatus(self, THREAD_RUNNING); 2930 2931 /* detach the thread */ 2932 dvmDetachCurrentThread(); 2933 2934 /* (no need to change status back -- we have no status) */ 2935 return JNI_OK; 2936} 2937 2938/* 2939 * If current thread is attached to VM, return the associated JNIEnv. 2940 * Otherwise, stuff NULL in and return JNI_EDETACHED. 2941 * 2942 * JVMTI overloads this by specifying a magic value for "version", so we 2943 * do want to check that here. 2944 */ 2945static jint GetEnv(JavaVM* vm, void** env, jint version) { 2946 Thread* self = dvmThreadSelf(); 2947 2948 if (version < JNI_VERSION_1_1 || version > JNI_VERSION_1_6) { 2949 return JNI_EVERSION; 2950 } 2951 2952 if (self == NULL) { 2953 *env = NULL; 2954 } else { 2955 /* TODO: status change is probably unnecessary */ 2956 dvmChangeStatus(self, THREAD_RUNNING); 2957 *env = (void*) dvmGetThreadJNIEnv(self); 2958 dvmChangeStatus(self, THREAD_NATIVE); 2959 } 2960 return (*env != NULL) ? JNI_OK : JNI_EDETACHED; 2961} 2962 2963/* 2964 * Destroy the VM. This may be called from any thread. 2965 * 2966 * If the current thread is attached, wait until the current thread is 2967 * the only non-daemon user-level thread. If the current thread is not 2968 * attached, we attach it and do the processing as usual. (If the attach 2969 * fails, it's probably because all the non-daemon threads have already 2970 * exited and the VM doesn't want to let us back in.) 2971 * 2972 * TODO: we don't really deal with the situation where more than one thread 2973 * has called here. One thread wins, the other stays trapped waiting on 2974 * the condition variable forever. Not sure this situation is interesting 2975 * in real life. 2976 */ 2977static jint DestroyJavaVM(JavaVM* vm) { 2978 JavaVMExt* ext = (JavaVMExt*) vm; 2979 if (ext == NULL) { 2980 return JNI_ERR; 2981 } 2982 2983 if (gDvm.verboseShutdown) { 2984 LOGD("DestroyJavaVM waiting for non-daemon threads to exit"); 2985 } 2986 2987 /* 2988 * Sleep on a condition variable until it's okay to exit. 2989 */ 2990 Thread* self = dvmThreadSelf(); 2991 if (self == NULL) { 2992 JNIEnv* tmpEnv; 2993 if (AttachCurrentThread(vm, &tmpEnv, NULL) != JNI_OK) { 2994 LOGV("Unable to reattach main for Destroy; assuming VM is shutting down (count=%d)", 2995 gDvm.nonDaemonThreadCount); 2996 goto shutdown; 2997 } else { 2998 LOGV("Attached to wait for shutdown in Destroy"); 2999 } 3000 } 3001 dvmChangeStatus(self, THREAD_VMWAIT); 3002 3003 dvmLockThreadList(self); 3004 gDvm.nonDaemonThreadCount--; // remove current thread from count 3005 3006 while (gDvm.nonDaemonThreadCount > 0) { 3007 pthread_cond_wait(&gDvm.vmExitCond, &gDvm.threadListLock); 3008 } 3009 3010 dvmUnlockThreadList(); 3011 self = NULL; 3012 3013shutdown: 3014 // TODO: call System.exit() to run any registered shutdown hooks 3015 // (this may not return -- figure out how this should work) 3016 3017 if (gDvm.verboseShutdown) { 3018 LOGD("DestroyJavaVM shutting VM down"); 3019 } 3020 dvmShutdown(); 3021 3022 // TODO - free resources associated with JNI-attached daemon threads 3023 free(ext->envList); 3024 free(ext); 3025 3026 return JNI_OK; 3027} 3028 3029 3030/* 3031 * =========================================================================== 3032 * Function tables 3033 * =========================================================================== 3034 */ 3035 3036static const struct JNINativeInterface gNativeInterface = { 3037 NULL, 3038 NULL, 3039 NULL, 3040 NULL, 3041 3042 GetVersion, 3043 3044 DefineClass, 3045 FindClass, 3046 3047 FromReflectedMethod, 3048 FromReflectedField, 3049 ToReflectedMethod, 3050 3051 GetSuperclass, 3052 IsAssignableFrom, 3053 3054 ToReflectedField, 3055 3056 Throw, 3057 ThrowNew, 3058 ExceptionOccurred, 3059 ExceptionDescribe, 3060 ExceptionClear, 3061 FatalError, 3062 3063 PushLocalFrame, 3064 PopLocalFrame, 3065 3066 NewGlobalRef, 3067 DeleteGlobalRef, 3068 DeleteLocalRef, 3069 IsSameObject, 3070 NewLocalRef, 3071 EnsureLocalCapacity, 3072 3073 AllocObject, 3074 NewObject, 3075 NewObjectV, 3076 NewObjectA, 3077 3078 GetObjectClass, 3079 IsInstanceOf, 3080 3081 GetMethodID, 3082 3083 CallObjectMethod, 3084 CallObjectMethodV, 3085 CallObjectMethodA, 3086 CallBooleanMethod, 3087 CallBooleanMethodV, 3088 CallBooleanMethodA, 3089 CallByteMethod, 3090 CallByteMethodV, 3091 CallByteMethodA, 3092 CallCharMethod, 3093 CallCharMethodV, 3094 CallCharMethodA, 3095 CallShortMethod, 3096 CallShortMethodV, 3097 CallShortMethodA, 3098 CallIntMethod, 3099 CallIntMethodV, 3100 CallIntMethodA, 3101 CallLongMethod, 3102 CallLongMethodV, 3103 CallLongMethodA, 3104 CallFloatMethod, 3105 CallFloatMethodV, 3106 CallFloatMethodA, 3107 CallDoubleMethod, 3108 CallDoubleMethodV, 3109 CallDoubleMethodA, 3110 CallVoidMethod, 3111 CallVoidMethodV, 3112 CallVoidMethodA, 3113 3114 CallNonvirtualObjectMethod, 3115 CallNonvirtualObjectMethodV, 3116 CallNonvirtualObjectMethodA, 3117 CallNonvirtualBooleanMethod, 3118 CallNonvirtualBooleanMethodV, 3119 CallNonvirtualBooleanMethodA, 3120 CallNonvirtualByteMethod, 3121 CallNonvirtualByteMethodV, 3122 CallNonvirtualByteMethodA, 3123 CallNonvirtualCharMethod, 3124 CallNonvirtualCharMethodV, 3125 CallNonvirtualCharMethodA, 3126 CallNonvirtualShortMethod, 3127 CallNonvirtualShortMethodV, 3128 CallNonvirtualShortMethodA, 3129 CallNonvirtualIntMethod, 3130 CallNonvirtualIntMethodV, 3131 CallNonvirtualIntMethodA, 3132 CallNonvirtualLongMethod, 3133 CallNonvirtualLongMethodV, 3134 CallNonvirtualLongMethodA, 3135 CallNonvirtualFloatMethod, 3136 CallNonvirtualFloatMethodV, 3137 CallNonvirtualFloatMethodA, 3138 CallNonvirtualDoubleMethod, 3139 CallNonvirtualDoubleMethodV, 3140 CallNonvirtualDoubleMethodA, 3141 CallNonvirtualVoidMethod, 3142 CallNonvirtualVoidMethodV, 3143 CallNonvirtualVoidMethodA, 3144 3145 GetFieldID, 3146 3147 GetObjectField, 3148 GetBooleanField, 3149 GetByteField, 3150 GetCharField, 3151 GetShortField, 3152 GetIntField, 3153 GetLongField, 3154 GetFloatField, 3155 GetDoubleField, 3156 SetObjectField, 3157 SetBooleanField, 3158 SetByteField, 3159 SetCharField, 3160 SetShortField, 3161 SetIntField, 3162 SetLongField, 3163 SetFloatField, 3164 SetDoubleField, 3165 3166 GetStaticMethodID, 3167 3168 CallStaticObjectMethod, 3169 CallStaticObjectMethodV, 3170 CallStaticObjectMethodA, 3171 CallStaticBooleanMethod, 3172 CallStaticBooleanMethodV, 3173 CallStaticBooleanMethodA, 3174 CallStaticByteMethod, 3175 CallStaticByteMethodV, 3176 CallStaticByteMethodA, 3177 CallStaticCharMethod, 3178 CallStaticCharMethodV, 3179 CallStaticCharMethodA, 3180 CallStaticShortMethod, 3181 CallStaticShortMethodV, 3182 CallStaticShortMethodA, 3183 CallStaticIntMethod, 3184 CallStaticIntMethodV, 3185 CallStaticIntMethodA, 3186 CallStaticLongMethod, 3187 CallStaticLongMethodV, 3188 CallStaticLongMethodA, 3189 CallStaticFloatMethod, 3190 CallStaticFloatMethodV, 3191 CallStaticFloatMethodA, 3192 CallStaticDoubleMethod, 3193 CallStaticDoubleMethodV, 3194 CallStaticDoubleMethodA, 3195 CallStaticVoidMethod, 3196 CallStaticVoidMethodV, 3197 CallStaticVoidMethodA, 3198 3199 GetStaticFieldID, 3200 3201 GetStaticObjectField, 3202 GetStaticBooleanField, 3203 GetStaticByteField, 3204 GetStaticCharField, 3205 GetStaticShortField, 3206 GetStaticIntField, 3207 GetStaticLongField, 3208 GetStaticFloatField, 3209 GetStaticDoubleField, 3210 3211 SetStaticObjectField, 3212 SetStaticBooleanField, 3213 SetStaticByteField, 3214 SetStaticCharField, 3215 SetStaticShortField, 3216 SetStaticIntField, 3217 SetStaticLongField, 3218 SetStaticFloatField, 3219 SetStaticDoubleField, 3220 3221 NewString, 3222 3223 GetStringLength, 3224 GetStringChars, 3225 ReleaseStringChars, 3226 3227 NewStringUTF, 3228 GetStringUTFLength, 3229 GetStringUTFChars, 3230 ReleaseStringUTFChars, 3231 3232 GetArrayLength, 3233 NewObjectArray, 3234 GetObjectArrayElement, 3235 SetObjectArrayElement, 3236 3237 NewBooleanArray, 3238 NewByteArray, 3239 NewCharArray, 3240 NewShortArray, 3241 NewIntArray, 3242 NewLongArray, 3243 NewFloatArray, 3244 NewDoubleArray, 3245 3246 GetBooleanArrayElements, 3247 GetByteArrayElements, 3248 GetCharArrayElements, 3249 GetShortArrayElements, 3250 GetIntArrayElements, 3251 GetLongArrayElements, 3252 GetFloatArrayElements, 3253 GetDoubleArrayElements, 3254 3255 ReleaseBooleanArrayElements, 3256 ReleaseByteArrayElements, 3257 ReleaseCharArrayElements, 3258 ReleaseShortArrayElements, 3259 ReleaseIntArrayElements, 3260 ReleaseLongArrayElements, 3261 ReleaseFloatArrayElements, 3262 ReleaseDoubleArrayElements, 3263 3264 GetBooleanArrayRegion, 3265 GetByteArrayRegion, 3266 GetCharArrayRegion, 3267 GetShortArrayRegion, 3268 GetIntArrayRegion, 3269 GetLongArrayRegion, 3270 GetFloatArrayRegion, 3271 GetDoubleArrayRegion, 3272 SetBooleanArrayRegion, 3273 SetByteArrayRegion, 3274 SetCharArrayRegion, 3275 SetShortArrayRegion, 3276 SetIntArrayRegion, 3277 SetLongArrayRegion, 3278 SetFloatArrayRegion, 3279 SetDoubleArrayRegion, 3280 3281 RegisterNatives, 3282 UnregisterNatives, 3283 3284 MonitorEnter, 3285 MonitorExit, 3286 3287 GetJavaVM, 3288 3289 GetStringRegion, 3290 GetStringUTFRegion, 3291 3292 GetPrimitiveArrayCritical, 3293 ReleasePrimitiveArrayCritical, 3294 3295 GetStringCritical, 3296 ReleaseStringCritical, 3297 3298 NewWeakGlobalRef, 3299 DeleteWeakGlobalRef, 3300 3301 ExceptionCheck, 3302 3303 NewDirectByteBuffer, 3304 GetDirectBufferAddress, 3305 GetDirectBufferCapacity, 3306 3307 GetObjectRefType 3308}; 3309 3310static const struct JNIInvokeInterface gInvokeInterface = { 3311 NULL, 3312 NULL, 3313 NULL, 3314 3315 DestroyJavaVM, 3316 AttachCurrentThread, 3317 DetachCurrentThread, 3318 3319 GetEnv, 3320 3321 AttachCurrentThreadAsDaemon, 3322}; 3323 3324/* 3325 * =========================================================================== 3326 * VM/Env creation 3327 * =========================================================================== 3328 */ 3329 3330/* 3331 * Create a new JNIEnv struct and add it to the VM's list. 3332 * 3333 * "self" will be NULL for the main thread, since the VM hasn't started 3334 * yet; the value will be filled in later. 3335 */ 3336JNIEnv* dvmCreateJNIEnv(Thread* self) { 3337 JavaVMExt* vm = (JavaVMExt*) gDvmJni.jniVm; 3338 3339 //if (self != NULL) 3340 // LOGI("Ent CreateJNIEnv: threadid=%d %p", self->threadId, self); 3341 3342 assert(vm != NULL); 3343 3344 JNIEnvExt* newEnv = (JNIEnvExt*) calloc(1, sizeof(JNIEnvExt)); 3345 newEnv->funcTable = &gNativeInterface; 3346 if (self != NULL) { 3347 dvmSetJniEnvThreadId((JNIEnv*) newEnv, self); 3348 assert(newEnv->envThreadId != 0); 3349 } else { 3350 /* make it obvious if we fail to initialize these later */ 3351 newEnv->envThreadId = 0x77777775; 3352 newEnv->self = (Thread*) 0x77777779; 3353 } 3354 if (gDvmJni.useCheckJni) { 3355 dvmUseCheckedJniEnv(newEnv); 3356 } 3357 3358 ScopedPthreadMutexLock lock(&vm->envListLock); 3359 3360 /* insert at head of list */ 3361 newEnv->next = vm->envList; 3362 assert(newEnv->prev == NULL); 3363 if (vm->envList == NULL) { 3364 // rare, but possible 3365 vm->envList = newEnv; 3366 } else { 3367 vm->envList->prev = newEnv; 3368 } 3369 vm->envList = newEnv; 3370 3371 //if (self != NULL) 3372 // LOGI("Xit CreateJNIEnv: threadid=%d %p", self->threadId, self); 3373 return (JNIEnv*) newEnv; 3374} 3375 3376/* 3377 * Remove a JNIEnv struct from the list and free it. 3378 */ 3379void dvmDestroyJNIEnv(JNIEnv* env) { 3380 if (env == NULL) { 3381 return; 3382 } 3383 3384 //LOGI("Ent DestroyJNIEnv: threadid=%d %p", self->threadId, self); 3385 3386 JNIEnvExt* extEnv = (JNIEnvExt*) env; 3387 JavaVMExt* vm = (JavaVMExt*) gDvmJni.jniVm; 3388 3389 ScopedPthreadMutexLock lock(&vm->envListLock); 3390 3391 if (extEnv == vm->envList) { 3392 assert(extEnv->prev == NULL); 3393 vm->envList = extEnv->next; 3394 } else { 3395 assert(extEnv->prev != NULL); 3396 extEnv->prev->next = extEnv->next; 3397 } 3398 if (extEnv->next != NULL) { 3399 extEnv->next->prev = extEnv->prev; 3400 } 3401 3402 free(env); 3403 //LOGI("Xit DestroyJNIEnv: threadid=%d %p", self->threadId, self); 3404} 3405 3406/* 3407 * Enable "checked JNI" after the VM has partially started. This must 3408 * only be called in "zygote" mode, when we have one thread running. 3409 * 3410 * This doesn't attempt to rewrite the JNI call bridge associated with 3411 * native methods, so we won't get those checks for any methods that have 3412 * already been resolved. 3413 */ 3414void dvmLateEnableCheckedJni() { 3415 JNIEnvExt* extEnv = dvmGetJNIEnvForThread(); 3416 if (extEnv == NULL) { 3417 LOGE("dvmLateEnableCheckedJni: thread has no JNIEnv"); 3418 return; 3419 } 3420 JavaVMExt* extVm = (JavaVMExt*) gDvmJni.jniVm; 3421 assert(extVm != NULL); 3422 3423 if (!gDvmJni.useCheckJni) { 3424 LOGD("Late-enabling CheckJNI"); 3425 dvmUseCheckedJniVm(extVm); 3426 dvmUseCheckedJniEnv(extEnv); 3427 } else { 3428 LOGD("Not late-enabling CheckJNI (already on)"); 3429 } 3430} 3431 3432/* 3433 * Not supported. 3434 */ 3435jint JNI_GetDefaultJavaVMInitArgs(void* vm_args) { 3436 return JNI_ERR; 3437} 3438 3439/* 3440 * Return a buffer full of created VMs. 3441 * 3442 * We always have zero or one. 3443 */ 3444jint JNI_GetCreatedJavaVMs(JavaVM** vmBuf, jsize bufLen, jsize* nVMs) { 3445 if (gDvmJni.jniVm != NULL) { 3446 *nVMs = 1; 3447 if (bufLen > 0) { 3448 *vmBuf++ = gDvmJni.jniVm; 3449 } 3450 } else { 3451 *nVMs = 0; 3452 } 3453 return JNI_OK; 3454} 3455 3456/* 3457 * Create a new VM instance. 3458 * 3459 * The current thread becomes the main VM thread. We return immediately, 3460 * which effectively means the caller is executing in a native method. 3461 */ 3462jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) { 3463 const JavaVMInitArgs* args = (JavaVMInitArgs*) vm_args; 3464 if (args->version < JNI_VERSION_1_2) { 3465 return JNI_EVERSION; 3466 } 3467 3468 // TODO: don't allow creation of multiple VMs -- one per customer for now 3469 3470 /* zero globals; not strictly necessary the first time a VM is started */ 3471 memset(&gDvm, 0, sizeof(gDvm)); 3472 3473 /* 3474 * Set up structures for JNIEnv and VM. 3475 */ 3476 JavaVMExt* pVM = (JavaVMExt*) malloc(sizeof(JavaVMExt)); 3477 memset(pVM, 0, sizeof(JavaVMExt)); 3478 pVM->funcTable = &gInvokeInterface; 3479 pVM->envList = NULL; 3480 dvmInitMutex(&pVM->envListLock); 3481 3482 UniquePtr<const char*[]> argv(new const char*[args->nOptions]); 3483 memset(argv.get(), 0, sizeof(char*) * (args->nOptions)); 3484 3485 /* 3486 * Convert JNI args to argv. 3487 * 3488 * We have to pull out vfprintf/exit/abort, because they use the 3489 * "extraInfo" field to pass function pointer "hooks" in. We also 3490 * look for the -Xcheck:jni stuff here. 3491 */ 3492 int argc = 0; 3493 for (int i = 0; i < args->nOptions; i++) { 3494 const char* optStr = args->options[i].optionString; 3495 if (optStr == NULL) { 3496 dvmFprintf(stderr, "ERROR: CreateJavaVM failed: argument %d was NULL\n", i); 3497 return JNI_ERR; 3498 } else if (strcmp(optStr, "vfprintf") == 0) { 3499 gDvm.vfprintfHook = (int (*)(FILE *, const char*, va_list))args->options[i].extraInfo; 3500 } else if (strcmp(optStr, "exit") == 0) { 3501 gDvm.exitHook = (void (*)(int)) args->options[i].extraInfo; 3502 } else if (strcmp(optStr, "abort") == 0) { 3503 gDvm.abortHook = (void (*)(void))args->options[i].extraInfo; 3504 } else if (strcmp(optStr, "sensitiveThread") == 0) { 3505 gDvm.isSensitiveThreadHook = (bool (*)(void))args->options[i].extraInfo; 3506 } else if (strcmp(optStr, "-Xcheck:jni") == 0) { 3507 gDvmJni.useCheckJni = true; 3508 } else if (strncmp(optStr, "-Xjniopts:", 10) == 0) { 3509 char* jniOpts = strdup(optStr + 10); 3510 size_t jniOptCount = 1; 3511 for (char* p = jniOpts; *p != 0; ++p) { 3512 if (*p == ',') { 3513 ++jniOptCount; 3514 *p = 0; 3515 } 3516 } 3517 char* jniOpt = jniOpts; 3518 for (size_t i = 0; i < jniOptCount; ++i) { 3519 if (strcmp(jniOpt, "warnonly") == 0) { 3520 gDvmJni.warnOnly = true; 3521 } else if (strcmp(jniOpt, "forcecopy") == 0) { 3522 gDvmJni.forceCopy = true; 3523 } else if (strcmp(jniOpt, "logThirdPartyJni") == 0) { 3524 gDvmJni.logThirdPartyJni = true; 3525 } else { 3526 dvmFprintf(stderr, "ERROR: CreateJavaVM failed: unknown -Xjniopts option '%s'\n", 3527 jniOpt); 3528 return JNI_ERR; 3529 } 3530 jniOpt += strlen(jniOpt) + 1; 3531 } 3532 free(jniOpts); 3533 } else { 3534 /* regular option */ 3535 argv[argc++] = optStr; 3536 } 3537 } 3538 3539 if (gDvmJni.useCheckJni) { 3540 dvmUseCheckedJniVm(pVM); 3541 } 3542 3543 if (gDvmJni.jniVm != NULL) { 3544 dvmFprintf(stderr, "ERROR: Dalvik only supports one VM per process\n"); 3545 return JNI_ERR; 3546 } 3547 gDvmJni.jniVm = (JavaVM*) pVM; 3548 3549 /* 3550 * Create a JNIEnv for the main thread. We need to have something set up 3551 * here because some of the class initialization we do when starting 3552 * up the VM will call into native code. 3553 */ 3554 JNIEnvExt* pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL); 3555 3556 /* Initialize VM. */ 3557 gDvm.initializing = true; 3558 std::string status = 3559 dvmStartup(argc, argv.get(), args->ignoreUnrecognized, (JNIEnv*)pEnv); 3560 gDvm.initializing = false; 3561 3562 if (!status.empty()) { 3563 free(pEnv); 3564 free(pVM); 3565 LOGW("CreateJavaVM failed: %s", status.c_str()); 3566 return JNI_ERR; 3567 } 3568 3569 /* 3570 * Success! Return stuff to caller. 3571 */ 3572 dvmChangeStatus(NULL, THREAD_NATIVE); 3573 *p_env = (JNIEnv*) pEnv; 3574 *p_vm = (JavaVM*) pVM; 3575 LOGV("CreateJavaVM succeeded"); 3576 return JNI_OK; 3577} 3578