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