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