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