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