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