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