blob: 4df32b1aa4eccee086f6692c9b298f761f106696 [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;
Andy McFaddenab00d452009-08-19 07:21:41 -0700223static jobject addGlobalReference(Object* 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
Andy McFaddenab00d452009-08-19 07:21:41 -0700350#define kGlobalRefsTableMaxSize 51200 /* arbitrary, must be < 64K */
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800351#define kGrefWaterInterval 100
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800352#define kTrackGrefUsage true
353
Andy McFaddenc26bb632009-08-21 12:01:31 -0700354#define kPinTableInitialSize 16
355#define kPinTableMaxSize 1024
356#define kPinComplainThreshold 10
357
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800358/*
Andy McFadden5f612b82009-07-22 15:07:27 -0700359 * Allocate the global references table, and look up some classes for
360 * the benefit of direct buffer access.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800361 */
362bool dvmJniStartup(void)
363{
364 if (!dvmInitReferenceTable(&gDvm.jniGlobalRefTable,
365 kGlobalRefsTableInitialSize, kGlobalRefsTableMaxSize))
366 return false;
367
368 dvmInitMutex(&gDvm.jniGlobalRefLock);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800369 gDvm.jniGlobalRefLoMark = 0;
370 gDvm.jniGlobalRefHiMark = kGrefWaterInterval * 2;
371
Andy McFaddenc26bb632009-08-21 12:01:31 -0700372 if (!dvmInitReferenceTable(&gDvm.jniPinRefTable,
373 kPinTableInitialSize, kPinTableMaxSize))
374 return false;
375
376 dvmInitMutex(&gDvm.jniPinRefLock);
377
Andy McFadden8e5c7842009-07-23 17:47:18 -0700378 /*
379 * Look up and cache pointers to some direct buffer classes, fields,
380 * and methods.
381 */
382 Method* meth;
383
Andy McFadden5f612b82009-07-22 15:07:27 -0700384 ClassObject* platformAddressClass =
385 dvmFindSystemClassNoInit("Lorg/apache/harmony/luni/platform/PlatformAddress;");
Andy McFadden8e5c7842009-07-23 17:47:18 -0700386 ClassObject* platformAddressFactoryClass =
387 dvmFindSystemClassNoInit("Lorg/apache/harmony/luni/platform/PlatformAddressFactory;");
Andy McFadden5f612b82009-07-22 15:07:27 -0700388 ClassObject* directBufferClass =
389 dvmFindSystemClassNoInit("Lorg/apache/harmony/nio/internal/DirectBuffer;");
Andy McFadden8e5c7842009-07-23 17:47:18 -0700390 ClassObject* readWriteBufferClass =
391 dvmFindSystemClassNoInit("Ljava/nio/ReadWriteDirectByteBuffer;");
392 ClassObject* bufferClass =
393 dvmFindSystemClassNoInit("Ljava/nio/Buffer;");
394
395 if (platformAddressClass == NULL || platformAddressFactoryClass == NULL ||
396 directBufferClass == NULL || readWriteBufferClass == NULL ||
397 bufferClass == NULL)
398 {
Andy McFadden5f612b82009-07-22 15:07:27 -0700399 LOGE("Unable to find internal direct buffer classes\n");
400 return false;
401 }
402 /* needs to be a global ref so CheckJNI thinks we're allowed to see it */
403 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer =
404 addGlobalReference((Object*) directBufferClass);
Andy McFadden8e5c7842009-07-23 17:47:18 -0700405 gDvm.classJavaNioReadWriteDirectByteBuffer = readWriteBufferClass;
Andy McFadden5f612b82009-07-22 15:07:27 -0700406
Andy McFadden8e5c7842009-07-23 17:47:18 -0700407 /*
408 * We need a Method* here rather than a vtable offset, because
409 * DirectBuffer is an interface class.
410 */
Andy McFadden5f612b82009-07-22 15:07:27 -0700411 meth = dvmFindVirtualMethodByDescriptor(
412 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer,
413 "getEffectiveAddress",
414 "()Lorg/apache/harmony/luni/platform/PlatformAddress;");
415 if (meth == NULL) {
416 LOGE("Unable to find PlatformAddress.getEffectiveAddress\n");
417 return false;
418 }
419 gDvm.methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress = meth;
420
421 meth = dvmFindVirtualMethodByDescriptor(platformAddressClass,
422 "toLong", "()J");
423 if (meth == NULL) {
424 LOGE("Unable to find PlatformAddress.toLong\n");
425 return false;
426 }
Andy McFadden8e5c7842009-07-23 17:47:18 -0700427 gDvm.voffOrgApacheHarmonyLuniPlatformPlatformAddress_toLong =
428 meth->methodIndex;
429
430 meth = dvmFindDirectMethodByDescriptor(platformAddressFactoryClass,
431 "on",
432 "(I)Lorg/apache/harmony/luni/platform/PlatformAddress;");
433 if (meth == NULL) {
434 LOGE("Unable to find PlatformAddressFactory.on\n");
435 return false;
436 }
437 gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on = meth;
438
439 meth = dvmFindDirectMethodByDescriptor(readWriteBufferClass,
440 "<init>",
441 "(Lorg/apache/harmony/luni/platform/PlatformAddress;II)V");
442 if (meth == NULL) {
443 LOGE("Unable to find ReadWriteDirectByteBuffer.<init>\n");
444 return false;
445 }
446 gDvm.methJavaNioReadWriteDirectByteBuffer_init = meth;
447
448 gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr =
449 dvmFindFieldOffset(platformAddressClass, "osaddr", "I");
450 if (gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr < 0) {
451 LOGE("Unable to find PlatformAddress.osaddr\n");
452 return false;
453 }
454
455 gDvm.offJavaNioBuffer_capacity =
456 dvmFindFieldOffset(bufferClass, "capacity", "I");
457 if (gDvm.offJavaNioBuffer_capacity < 0) {
458 LOGE("Unable to find Buffer.capacity\n");
459 return false;
460 }
Andy McFadden5f612b82009-07-22 15:07:27 -0700461
Andy McFadden8e696dc2009-07-24 15:28:16 -0700462 gDvm.offJavaNioBuffer_effectiveDirectAddress =
463 dvmFindFieldOffset(bufferClass, "effectiveDirectAddress", "I");
464 if (gDvm.offJavaNioBuffer_effectiveDirectAddress < 0) {
465 LOGE("Unable to find Buffer.effectiveDirectAddress\n");
466 return false;
467 }
468
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800469 return true;
470}
471
472/*
473 * Free the global references table.
474 */
475void dvmJniShutdown(void)
476{
477 dvmClearReferenceTable(&gDvm.jniGlobalRefTable);
478}
479
480
481/*
482 * Find the JNIEnv associated with the current thread.
483 *
484 * Currently stored in the Thread struct. Could also just drop this into
485 * thread-local storage.
486 */
487JNIEnvExt* dvmGetJNIEnvForThread(void)
488{
489 Thread* self = dvmThreadSelf();
490 if (self == NULL)
491 return NULL;
492 return (JNIEnvExt*) dvmGetThreadJNIEnv(self);
493}
494
495/*
496 * Create a new JNIEnv struct and add it to the VM's list.
497 *
498 * "self" will be NULL for the main thread, since the VM hasn't started
499 * yet; the value will be filled in later.
500 */
501JNIEnv* dvmCreateJNIEnv(Thread* self)
502{
503 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
504 JNIEnvExt* newEnv;
505
506 //if (self != NULL)
507 // LOGI("Ent CreateJNIEnv: threadid=%d %p\n", self->threadId, self);
508
509 assert(vm != NULL);
510
511 newEnv = (JNIEnvExt*) calloc(1, sizeof(JNIEnvExt));
512 newEnv->funcTable = &gNativeInterface;
513 newEnv->vm = vm;
514 newEnv->forceDataCopy = vm->forceDataCopy;
515 if (self != NULL) {
516 dvmSetJniEnvThreadId((JNIEnv*) newEnv, self);
517 assert(newEnv->envThreadId != 0);
518 } else {
519 /* make it obvious if we fail to initialize these later */
520 newEnv->envThreadId = 0x77777775;
521 newEnv->self = (Thread*) 0x77777779;
522 }
523 if (vm->useChecked)
524 dvmUseCheckedJniEnv(newEnv);
525
526 dvmLockMutex(&vm->envListLock);
527
528 /* insert at head of list */
529 newEnv->next = vm->envList;
530 assert(newEnv->prev == NULL);
531 if (vm->envList == NULL) // rare, but possible
532 vm->envList = newEnv;
533 else
534 vm->envList->prev = newEnv;
535 vm->envList = newEnv;
536
537 dvmUnlockMutex(&vm->envListLock);
538
539 //if (self != NULL)
540 // LOGI("Xit CreateJNIEnv: threadid=%d %p\n", self->threadId, self);
541 return (JNIEnv*) newEnv;
542}
543
544/*
545 * Remove a JNIEnv struct from the list and free it.
546 */
547void dvmDestroyJNIEnv(JNIEnv* env)
548{
549 JNIEnvExt* extEnv = (JNIEnvExt*) env;
550 JavaVMExt* vm = extEnv->vm;
551 Thread* self;
552
553 if (env == NULL)
554 return;
555
556 self = dvmThreadSelf();
557 assert(self != NULL);
558
559 //LOGI("Ent DestroyJNIEnv: threadid=%d %p\n", self->threadId, self);
560
561 dvmLockMutex(&vm->envListLock);
562
563 if (extEnv == vm->envList) {
564 assert(extEnv->prev == NULL);
565 vm->envList = extEnv->next;
566 } else {
567 assert(extEnv->prev != NULL);
568 extEnv->prev->next = extEnv->next;
569 }
570 if (extEnv->next != NULL)
571 extEnv->next->prev = extEnv->prev;
572
573 dvmUnlockMutex(&extEnv->vm->envListLock);
574
575 free(env);
576 //LOGI("Xit DestroyJNIEnv: threadid=%d %p\n", self->threadId, self);
577}
578
579
580/*
581 * Retrieve the ReferenceTable struct for the current thread.
582 *
Andy McFaddenab00d452009-08-19 07:21:41 -0700583 * Going through "env" rather than dvmThreadSelf() is faster but will
584 * get weird if the JNI code is passing the wrong JNIEnv around.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800585 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700586static inline ReferenceTable* getLocalRefTable(JNIEnv* env)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800587{
Andy McFaddenab00d452009-08-19 07:21:41 -0700588 //return &dvmThreadSelf()->jniLocalRefTable;
589 return &((JNIEnvExt*)env)->self->jniLocalRefTable;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800590}
591
592/*
593 * Add a local reference for an object to the current stack frame. When
594 * the native function returns, the reference will be discarded.
595 *
596 * We need to allow the same reference to be added multiple times.
597 *
598 * This will be called on otherwise unreferenced objects. We cannot do
599 * GC allocations here, and it's best if we don't grab a mutex.
600 *
601 * Returns the local reference (currently just the same pointer that was
602 * passed in), or NULL on failure.
603 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700604static jobject addLocalReference(JNIEnv* env, Object* obj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800605{
606 if (obj == NULL)
607 return NULL;
608
Andy McFaddenab00d452009-08-19 07:21:41 -0700609 ReferenceTable* pRefTable = getLocalRefTable(env);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800610
Andy McFaddenab00d452009-08-19 07:21:41 -0700611 if (!dvmAddToReferenceTable(pRefTable, obj)) {
612 dvmDumpReferenceTable(pRefTable, "JNI local");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800613 LOGE("Failed adding to JNI local ref table (has %d entries)\n",
Andy McFaddenab00d452009-08-19 07:21:41 -0700614 (int) dvmReferenceTableEntries(pRefTable));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800615 dvmDumpThread(dvmThreadSelf(), false);
616 dvmAbort(); // spec says call FatalError; this is equivalent
617 } else {
618 LOGVV("LREF add %p (%s.%s)\n", obj,
619 dvmGetCurrentJNIMethod()->clazz->descriptor,
620 dvmGetCurrentJNIMethod()->name);
621 }
622
623 return obj;
624}
625
626/*
Andy McFaddenab00d452009-08-19 07:21:41 -0700627 * Convert an indirect reference to an Object reference. The indirect
628 * reference may be local, global, or weak-global.
629 *
630 * If "jobj" is NULL or an invalid indirect reference, this returns NULL.
631 *
632 * [ this is currently a no-op ]
633 */
634Object* dvmDecodeIndirectRef(JNIEnv* env, jobject jobj)
635{
636 return (Object*) jobj;
637}
638
639/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800640 * Ensure that at least "capacity" references can be held in the local
641 * refs table of the current thread.
642 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700643static bool ensureLocalCapacity(JNIEnv* env, int capacity)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800644{
Andy McFaddenab00d452009-08-19 07:21:41 -0700645 ReferenceTable* pRefTable = getLocalRefTable(env);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800646
Andy McFaddenab00d452009-08-19 07:21:41 -0700647 return (kJniLocalRefMax - (pRefTable->nextEntry - pRefTable->table) >= capacity);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800648}
649
650/*
651 * Explicitly delete a reference from the local list.
652 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700653static void deleteLocalReference(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800654{
Andy McFaddenab00d452009-08-19 07:21:41 -0700655 if (jobj == NULL)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800656 return;
657
Andy McFaddenab00d452009-08-19 07:21:41 -0700658 ReferenceTable* pRefTable = getLocalRefTable(env);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800659 Thread* self = dvmThreadSelf();
660 Object** top = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefTop;
661
Andy McFaddenab00d452009-08-19 07:21:41 -0700662 if (!dvmRemoveFromReferenceTable(pRefTable, top, (Object*) jobj)) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800663 /*
664 * Attempting to delete a local reference that is not in the
665 * topmost local reference frame is a no-op. DeleteLocalRef returns
666 * void and doesn't throw any exceptions, but we should probably
667 * complain about it so the user will notice that things aren't
668 * going quite the way they expect.
669 */
670 LOGW("JNI WARNING: DeleteLocalRef(%p) failed to find entry (valid=%d)\n",
Andy McFaddenab00d452009-08-19 07:21:41 -0700671 jobj, dvmIsValidObject((Object*) jobj));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800672 }
673}
674
675/*
676 * Add a global reference for an object.
677 *
678 * We may add the same object more than once. Add/remove calls are paired,
679 * so it needs to appear on the list multiple times.
680 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700681static jobject addGlobalReference(Object* obj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800682{
683 if (obj == NULL)
684 return NULL;
685
686 //LOGI("adding obj=%p\n", obj);
687 //dvmDumpThread(dvmThreadSelf(), false);
688
689 if (false && ((Object*)obj)->clazz == gDvm.classJavaLangClass) {
690 ClassObject* clazz = (ClassObject*) obj;
691 LOGI("-------\n");
692 LOGI("Adding global ref on class %s\n", clazz->descriptor);
693 dvmDumpThread(dvmThreadSelf(), false);
694 }
695 if (false && ((Object*)obj)->clazz == gDvm.classJavaLangString) {
696 StringObject* strObj = (StringObject*) obj;
697 char* str = dvmCreateCstrFromString(strObj);
698 if (strcmp(str, "sync-response") == 0) {
699 LOGI("-------\n");
700 LOGI("Adding global ref on string '%s'\n", str);
701 dvmDumpThread(dvmThreadSelf(), false);
702 //dvmAbort();
703 }
704 free(str);
705 }
706 if (false && ((Object*)obj)->clazz == gDvm.classArrayByte) {
707 ArrayObject* arrayObj = (ArrayObject*) obj;
708 if (arrayObj->length == 8192 &&
709 dvmReferenceTableEntries(&gDvm.jniGlobalRefTable) > 400)
710 {
711 LOGI("Adding global ref on byte array %p (len=%d)\n",
712 arrayObj, arrayObj->length);
713 dvmDumpThread(dvmThreadSelf(), false);
714 }
715 }
716
717 dvmLockMutex(&gDvm.jniGlobalRefLock);
718
719 /*
720 * Expanding the table should happen rarely, so I'm not overly
721 * concerned about the performance impact of copying the old list
722 * over. We shouldn't see one-time activity spikes, so freeing
723 * up storage shouldn't be required.
724 *
725 * Throwing an exception on failure is problematic, because JNI code
726 * may not be expecting an exception, and things sort of cascade. We
727 * want to have a hard limit to catch leaks during debugging, but this
728 * otherwise needs to expand until memory is consumed. As a practical
729 * matter, if we have many thousands of global references, chances are
730 * we're either leaking global ref table entries or we're going to
731 * run out of space in the GC heap.
732 */
733 if (!dvmAddToReferenceTable(&gDvm.jniGlobalRefTable, (Object*)obj)) {
734 dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global");
735 LOGE("Failed adding to JNI global ref table (%d entries)\n",
736 (int) dvmReferenceTableEntries(&gDvm.jniGlobalRefTable));
737 dvmAbort();
738 }
739
740 LOGVV("GREF add %p (%s.%s)\n", obj,
741 dvmGetCurrentJNIMethod()->clazz->descriptor,
742 dvmGetCurrentJNIMethod()->name);
743
744 /* GREF usage tracking; should probably be disabled for production env */
745 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
746 int count = dvmReferenceTableEntries(&gDvm.jniGlobalRefTable);
747 if (count > gDvm.jniGlobalRefHiMark) {
748 LOGD("GREF has increased to %d\n", count);
749 gDvm.jniGlobalRefHiMark += kGrefWaterInterval;
750 gDvm.jniGlobalRefLoMark += kGrefWaterInterval;
751
752 /* watch for "excessive" use; not generally appropriate */
753 if (count >= gDvm.jniGrefLimit) {
754 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
755 if (vm->warnError) {
756 dvmDumpReferenceTable(&gDvm.jniGlobalRefTable,"JNI global");
757 LOGE("Excessive JNI global references (%d)\n", count);
758 dvmAbort();
759 } else {
760 LOGW("Excessive JNI global references (%d)\n", count);
761 }
762 }
763 }
764 }
765
766bail:
767 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
Andy McFaddenab00d452009-08-19 07:21:41 -0700768 return (jobject) obj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800769}
770
771/*
772 * Remove a global reference. In most cases it's the entry most recently
773 * added, which makes this pretty quick.
774 *
775 * Thought: if it's not the most recent entry, just null it out. When we
776 * fill up, do a compaction pass before we expand the list.
777 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700778static void deleteGlobalReference(jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800779{
Andy McFaddenab00d452009-08-19 07:21:41 -0700780 if (jobj == NULL)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800781 return;
782
783 dvmLockMutex(&gDvm.jniGlobalRefLock);
784
785 if (!dvmRemoveFromReferenceTable(&gDvm.jniGlobalRefTable,
Andy McFaddenab00d452009-08-19 07:21:41 -0700786 gDvm.jniGlobalRefTable.table, jobj))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800787 {
788 LOGW("JNI: DeleteGlobalRef(%p) failed to find entry (valid=%d)\n",
Andy McFaddenab00d452009-08-19 07:21:41 -0700789 jobj, dvmIsValidObject((Object*) jobj));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800790 goto bail;
791 }
792
793 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
794 int count = dvmReferenceTableEntries(&gDvm.jniGlobalRefTable);
795 if (count < gDvm.jniGlobalRefLoMark) {
796 LOGD("GREF has decreased to %d\n", count);
797 gDvm.jniGlobalRefHiMark -= kGrefWaterInterval;
798 gDvm.jniGlobalRefLoMark -= kGrefWaterInterval;
799 }
800 }
801
802bail:
803 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
804}
805
806/*
Andy McFaddenab00d452009-08-19 07:21:41 -0700807 * Objects don't currently move, so we just need to create a reference
808 * that will ensure the array object isn't collected.
809 *
Andy McFaddenc26bb632009-08-21 12:01:31 -0700810 * We use a separate reference table, which is part of the GC root set.
Andy McFaddenab00d452009-08-19 07:21:41 -0700811 */
812static void pinPrimitiveArray(ArrayObject* arrayObj)
813{
Andy McFaddenc26bb632009-08-21 12:01:31 -0700814 if (arrayObj == NULL)
815 return;
816
817 dvmLockMutex(&gDvm.jniPinRefLock);
818 if (!dvmAddToReferenceTable(&gDvm.jniPinRefTable, (Object*)arrayObj)) {
819 dvmDumpReferenceTable(&gDvm.jniPinRefTable, "JNI pinned array");
820 LOGE("Failed adding to JNI pinned array ref table (%d entries)\n",
821 (int) dvmReferenceTableEntries(&gDvm.jniPinRefTable));
822 dvmDumpThread(dvmThreadSelf(), false);
823 dvmAbort();
824 }
825
826 /*
827 * If we're watching global ref usage, also keep an eye on these.
828 *
829 * The total number of pinned primitive arrays should be pretty small.
830 * A single array should not be pinned more than once or twice; any
831 * more than that is a strong indicator that a Release function is
832 * not being called.
833 */
834 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
835 int count = 0;
836 Object** ppObj = gDvm.jniPinRefTable.table;
837 while (ppObj < gDvm.jniPinRefTable.nextEntry) {
838 if (*ppObj++ == (Object*) arrayObj)
839 count++;
840 }
841
842 if (count > kPinComplainThreshold) {
843 LOGW("JNI: pin count on array %p (%s) is now %d\n",
844 arrayObj, arrayObj->obj.clazz->descriptor, count);
845 /* keep going */
846 }
847 }
848
849 dvmUnlockMutex(&gDvm.jniPinRefLock);
Andy McFaddenab00d452009-08-19 07:21:41 -0700850}
851
852/*
853 * Un-pin the array object. If an object was pinned twice, it must be
854 * unpinned twice before it's free to move.
Andy McFaddenab00d452009-08-19 07:21:41 -0700855 */
856static void unpinPrimitiveArray(ArrayObject* arrayObj)
857{
Andy McFaddenc26bb632009-08-21 12:01:31 -0700858 if (arrayObj == NULL)
859 return;
860
861 dvmLockMutex(&gDvm.jniPinRefLock);
862 if (!dvmRemoveFromReferenceTable(&gDvm.jniPinRefTable,
863 gDvm.jniPinRefTable.table, (Object*) arrayObj))
864 {
865 LOGW("JNI: unpinPrimitiveArray(%p) failed to find entry (valid=%d)\n",
866 arrayObj, dvmIsValidObject((Object*) arrayObj));
867 goto bail;
868 }
869
870bail:
871 dvmUnlockMutex(&gDvm.jniPinRefLock);
Andy McFaddenab00d452009-08-19 07:21:41 -0700872}
873
874/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800875 * GC helper function to mark all JNI global references.
Andy McFaddenc26bb632009-08-21 12:01:31 -0700876 *
877 * We're currently handling the "pin" table here too.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800878 */
879void dvmGcMarkJniGlobalRefs()
880{
881 Object **op;
882
883 dvmLockMutex(&gDvm.jniGlobalRefLock);
884
885 op = gDvm.jniGlobalRefTable.table;
886 while ((uintptr_t)op < (uintptr_t)gDvm.jniGlobalRefTable.nextEntry) {
887 dvmMarkObjectNonNull(*(op++));
888 }
889
890 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
Andy McFaddenc26bb632009-08-21 12:01:31 -0700891
892
893 dvmLockMutex(&gDvm.jniPinRefLock);
894
895 op = gDvm.jniPinRefTable.table;
896 while ((uintptr_t)op < (uintptr_t)gDvm.jniPinRefTable.nextEntry) {
897 dvmMarkObjectNonNull(*(op++));
898 }
899
900 dvmUnlockMutex(&gDvm.jniPinRefLock);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800901}
902
903
904/*
905 * Determine if "obj" appears in the argument list for the native method.
906 *
907 * We use the "shorty" signature to determine which argument slots hold
908 * reference types.
909 */
910static bool findInArgList(Thread* self, Object* obj)
911{
912 const Method* meth;
913 u4* fp;
914 int i;
915
916 fp = self->curFrame;
917 while (1) {
918 /*
919 * Back up over JNI PushLocalFrame frames. This works because the
920 * previous frame on the interpreted stack is either a break frame
921 * (if we called here via native code) or an interpreted method (if
922 * we called here via the interpreter). In both cases the method
923 * pointer won't match.
924 */
925 StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
926 meth = saveArea->method;
927 if (meth != SAVEAREA_FROM_FP(saveArea->prevFrame)->method)
928 break;
929 fp = saveArea->prevFrame;
930 }
931
932 LOGVV("+++ scanning %d args in %s (%s)\n",
933 meth->insSize, meth->name, meth->shorty);
934 const char* shorty = meth->shorty +1; /* skip return type char */
935 for (i = 0; i < meth->insSize; i++) {
936 if (i == 0 && !dvmIsStaticMethod(meth)) {
937 /* first arg is "this" ref, not represented in "shorty" */
938 if (fp[i] == (u4) obj)
939 return true;
940 } else {
941 /* if this is a reference type, see if it matches */
942 switch (*shorty) {
943 case 'L':
944 if (fp[i] == (u4) obj)
945 return true;
946 break;
947 case 'D':
948 case 'J':
949 i++;
950 break;
951 case '\0':
952 LOGE("Whoops! ran off the end of %s (%d)\n",
953 meth->shorty, meth->insSize);
954 break;
955 default:
956 if (fp[i] == (u4) obj)
957 LOGI("NOTE: ref %p match on arg type %c\n", obj, *shorty);
958 break;
959 }
960 shorty++;
961 }
962 }
963
964 /*
965 * For static methods, we also pass a class pointer in.
966 */
967 if (dvmIsStaticMethod(meth)) {
968 //LOGI("+++ checking class pointer in %s\n", meth->name);
969 if ((void*)obj == (void*)meth->clazz)
970 return true;
971 }
972 return false;
973}
974
975/*
976 * Verify that a reference passed in from native code is one that the
977 * code is allowed to have.
978 *
979 * It's okay for native code to pass us a reference that:
980 * - was just passed in as an argument when invoked by native code
981 * - was returned to it from JNI (and is now in the JNI local refs table)
982 * - is present in the JNI global refs table
983 * The first one is a little awkward. The latter two are just table lookups.
984 *
985 * Used by -Xcheck:jni and GetObjectRefType.
986 *
987 * NOTE: in the current VM, global and local references are identical. If
988 * something is both global and local, we can't tell them apart, and always
989 * return "local".
990 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700991jobjectRefType dvmGetJNIRefType(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800992{
Andy McFaddenab00d452009-08-19 07:21:41 -0700993 ReferenceTable* pRefTable = getLocalRefTable(env);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800994 Thread* self = dvmThreadSelf();
995 //Object** top;
996 Object** ptr;
997
998 /* check args */
Andy McFaddenab00d452009-08-19 07:21:41 -0700999 if (findInArgList(self, jobj)) {
1000 //LOGI("--- REF found %p on stack\n", jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001001 return JNILocalRefType;
1002 }
1003
1004 /* check locals */
1005 //top = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefTop;
Andy McFaddenab00d452009-08-19 07:21:41 -07001006 if (dvmFindInReferenceTable(pRefTable, pRefTable->table, jobj) != NULL) {
1007 //LOGI("--- REF found %p in locals\n", jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001008 return JNILocalRefType;
1009 }
1010
1011 /* check globals */
1012 dvmLockMutex(&gDvm.jniGlobalRefLock);
1013 if (dvmFindInReferenceTable(&gDvm.jniGlobalRefTable,
Andy McFaddenab00d452009-08-19 07:21:41 -07001014 gDvm.jniGlobalRefTable.table, jobj))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001015 {
Andy McFaddenab00d452009-08-19 07:21:41 -07001016 //LOGI("--- REF found %p in globals\n", jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001017 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
1018 return JNIGlobalRefType;
1019 }
1020 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
1021
1022 /* not found! */
1023 return JNIInvalidRefType;
1024}
1025
1026/*
1027 * Register a method that uses JNI calling conventions.
1028 */
1029static bool dvmRegisterJNIMethod(ClassObject* clazz, const char* methodName,
1030 const char* signature, void* fnPtr)
1031{
1032 Method* method;
1033 bool result = false;
1034
1035 if (fnPtr == NULL)
1036 goto bail;
1037
1038 method = dvmFindDirectMethodByDescriptor(clazz, methodName, signature);
1039 if (method == NULL)
1040 method = dvmFindVirtualMethodByDescriptor(clazz, methodName, signature);
1041 if (method == NULL) {
1042 LOGW("ERROR: Unable to find decl for native %s.%s %s\n",
1043 clazz->descriptor, methodName, signature);
1044 goto bail;
1045 }
1046
1047 if (!dvmIsNativeMethod(method)) {
1048 LOGW("Unable to register: not native: %s.%s %s\n",
1049 clazz->descriptor, methodName, signature);
1050 goto bail;
1051 }
1052
1053 if (method->nativeFunc != dvmResolveNativeMethod) {
1054 LOGW("Warning: %s.%s %s was already registered/resolved?\n",
1055 clazz->descriptor, methodName, signature);
1056 /* keep going, I guess */
1057 }
1058
Andy McFadden59b61772009-05-13 16:44:34 -07001059 dvmUseJNIBridge(method, fnPtr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001060
1061 LOGV("JNI-registered %s.%s %s\n", clazz->descriptor, methodName,
1062 signature);
1063 result = true;
1064
1065bail:
1066 return result;
1067}
1068
1069/*
Andy McFadden59b61772009-05-13 16:44:34 -07001070 * Returns "true" if CheckJNI is enabled in the VM.
1071 */
1072static bool dvmIsCheckJNIEnabled(void)
1073{
1074 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
1075 return vm->useChecked;
1076}
1077
1078/*
1079 * Point "method->nativeFunc" at the JNI bridge, and overload "method->insns"
1080 * to point at the actual function.
1081 */
1082void dvmUseJNIBridge(Method* method, void* func)
1083{
1084 if (dvmIsCheckJNIEnabled()) {
1085 if (dvmIsSynchronizedMethod(method))
1086 dvmSetNativeFunc(method, dvmCheckCallSynchronizedJNIMethod, func);
1087 else
1088 dvmSetNativeFunc(method, dvmCheckCallJNIMethod, func);
1089 } else {
1090 if (dvmIsSynchronizedMethod(method))
1091 dvmSetNativeFunc(method, dvmCallSynchronizedJNIMethod, func);
1092 else
1093 dvmSetNativeFunc(method, dvmCallJNIMethod, func);
1094 }
1095}
1096
1097/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001098 * Get the method currently being executed by examining the interp stack.
1099 */
1100const Method* dvmGetCurrentJNIMethod(void)
1101{
1102 assert(dvmThreadSelf() != NULL);
1103
1104 void* fp = dvmThreadSelf()->curFrame;
1105 const Method* meth = SAVEAREA_FROM_FP(fp)->method;
1106
1107 assert(meth != NULL);
1108 assert(dvmIsNativeMethod(meth));
1109 return meth;
1110}
1111
1112
1113/*
1114 * Track a JNI MonitorEnter in the current thread.
1115 *
1116 * The goal is to be able to "implicitly" release all JNI-held monitors
1117 * when the thread detaches.
1118 *
1119 * Monitors may be entered multiple times, so we add a new entry for each
1120 * enter call. It would be more efficient to keep a counter. At present
1121 * there's no real motivation to improve this however.
1122 */
1123static void trackMonitorEnter(Thread* self, Object* obj)
1124{
1125 static const int kInitialSize = 16;
1126 ReferenceTable* refTable = &self->jniMonitorRefTable;
1127
1128 /* init table on first use */
1129 if (refTable->table == NULL) {
1130 assert(refTable->maxEntries == 0);
1131
1132 if (!dvmInitReferenceTable(refTable, kInitialSize, INT_MAX)) {
1133 LOGE("Unable to initialize monitor tracking table\n");
1134 dvmAbort();
1135 }
1136 }
1137
1138 if (!dvmAddToReferenceTable(refTable, obj)) {
1139 /* ran out of memory? could throw exception instead */
1140 LOGE("Unable to add entry to monitor tracking table\n");
1141 dvmAbort();
1142 } else {
1143 LOGVV("--- added monitor %p\n", obj);
1144 }
1145}
1146
Andy McFaddenab00d452009-08-19 07:21:41 -07001147
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001148/*
1149 * Track a JNI MonitorExit in the current thread.
1150 */
1151static void trackMonitorExit(Thread* self, Object* obj)
1152{
Andy McFaddenab00d452009-08-19 07:21:41 -07001153 ReferenceTable* pRefTable = &self->jniMonitorRefTable;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001154
Andy McFaddenab00d452009-08-19 07:21:41 -07001155 if (!dvmRemoveFromReferenceTable(pRefTable, pRefTable->table, obj)) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001156 LOGE("JNI monitor %p not found in tracking list\n", obj);
1157 /* keep going? */
1158 } else {
1159 LOGVV("--- removed monitor %p\n", obj);
1160 }
1161}
1162
1163/*
1164 * Release all monitors held by the jniMonitorRefTable list.
1165 */
1166void dvmReleaseJniMonitors(Thread* self)
1167{
Andy McFaddenab00d452009-08-19 07:21:41 -07001168 ReferenceTable* pRefTable = &self->jniMonitorRefTable;
1169 Object** top = pRefTable->table;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001170
1171 if (top == NULL)
1172 return;
1173
Andy McFaddenab00d452009-08-19 07:21:41 -07001174 Object** ptr = pRefTable->nextEntry;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001175 while (--ptr >= top) {
1176 if (!dvmUnlockObject(self, *ptr)) {
1177 LOGW("Unable to unlock monitor %p at thread detach\n", *ptr);
1178 } else {
1179 LOGVV("--- detach-releasing monitor %p\n", *ptr);
1180 }
1181 }
1182
1183 /* zap it */
Andy McFaddenab00d452009-08-19 07:21:41 -07001184 pRefTable->nextEntry = pRefTable->table;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001185}
1186
1187#ifdef WITH_JNI_STACK_CHECK
1188/*
1189 * Compute a CRC on the entire interpreted stack.
1190 *
1191 * Would be nice to compute it on "self" as well, but there are parts of
1192 * the Thread that can be altered by other threads (e.g. prev/next pointers).
1193 */
1194static void computeStackSum(Thread* self)
1195{
1196 const u1* low = (const u1*)SAVEAREA_FROM_FP(self->curFrame);
1197 u4 crc = dvmInitCrc32();
1198 self->stackCrc = 0;
1199 crc = dvmComputeCrc32(crc, low, self->interpStackStart - low);
1200 self->stackCrc = crc;
1201}
1202
1203/*
1204 * Compute a CRC on the entire interpreted stack, and compare it to what
1205 * we previously computed.
1206 *
1207 * We can execute JNI directly from native code without calling in from
1208 * interpreted code during VM initialization and immediately after JNI
1209 * thread attachment. Another opportunity exists during JNI_OnLoad. Rather
1210 * than catching these cases we just ignore them here, which is marginally
1211 * less accurate but reduces the amount of code we have to touch with #ifdefs.
1212 */
1213static void checkStackSum(Thread* self)
1214{
1215 const u1* low = (const u1*)SAVEAREA_FROM_FP(self->curFrame);
1216 u4 stackCrc, crc;
1217
1218 stackCrc = self->stackCrc;
1219 self->stackCrc = 0;
1220 crc = dvmInitCrc32();
1221 crc = dvmComputeCrc32(crc, low, self->interpStackStart - low);
1222 if (crc != stackCrc) {
1223 const Method* meth = dvmGetCurrentJNIMethod();
1224 if (dvmComputeExactFrameDepth(self->curFrame) == 1) {
1225 LOGD("JNI: bad stack CRC (0x%08x) -- okay during init\n",
1226 stackCrc);
1227 } else if (strcmp(meth->name, "nativeLoad") == 0 &&
1228 (strcmp(meth->clazz->descriptor, "Ljava/lang/Runtime;") == 0))
1229 {
1230 LOGD("JNI: bad stack CRC (0x%08x) -- okay during JNI_OnLoad\n",
1231 stackCrc);
1232 } else {
1233 LOGW("JNI: bad stack CRC (%08x vs %08x)\n", crc, stackCrc);
1234 dvmAbort();
1235 }
1236 }
1237 self->stackCrc = (u4) -1; /* make logic errors more noticeable */
1238}
1239#endif
1240
1241
1242/*
1243 * ===========================================================================
1244 * JNI implementation
1245 * ===========================================================================
1246 */
1247
1248/*
1249 * Return the version of the native method interface.
1250 */
1251static jint GetVersion(JNIEnv* env)
1252{
1253 JNI_ENTER();
1254 /*
1255 * There is absolutely no need to toggle the mode for correct behavior.
1256 * However, it does provide native code with a simple "suspend self
1257 * if necessary" call.
1258 */
1259 JNI_EXIT();
1260 return JNI_VERSION_1_6;
1261}
1262
1263/*
1264 * Create a new class from a bag of bytes.
1265 *
1266 * This is not currently supported within Dalvik.
1267 */
1268static jclass DefineClass(JNIEnv* env, const char *name, jobject loader,
1269 const jbyte* buf, jsize bufLen)
1270{
1271 UNUSED_PARAMETER(name);
1272 UNUSED_PARAMETER(loader);
1273 UNUSED_PARAMETER(buf);
1274 UNUSED_PARAMETER(bufLen);
1275
1276 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001277 LOGW("JNI DefineClass is not supported\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001278 JNI_EXIT();
1279 return NULL;
1280}
1281
1282/*
1283 * Find a class by name.
1284 *
1285 * We have to use the "no init" version of FindClass here, because we might
1286 * be getting the class prior to registering native methods that will be
1287 * used in <clinit>.
1288 *
1289 * We need to get the class loader associated with the current native
1290 * method. If there is no native method, e.g. we're calling this from native
1291 * code right after creating the VM, the spec says we need to use the class
1292 * loader returned by "ClassLoader.getBaseClassLoader". There is no such
1293 * method, but it's likely they meant ClassLoader.getSystemClassLoader.
1294 * We can't get that until after the VM has initialized though.
1295 */
1296static jclass FindClass(JNIEnv* env, const char* name)
1297{
1298 JNI_ENTER();
1299
1300 const Method* thisMethod;
1301 ClassObject* clazz;
Andy McFaddenab00d452009-08-19 07:21:41 -07001302 jclass jclazz = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001303 Object* loader;
1304 char* descriptor = NULL;
1305
1306 thisMethod = dvmGetCurrentJNIMethod();
1307 assert(thisMethod != NULL);
1308
1309 descriptor = dvmNameToDescriptor(name);
1310 if (descriptor == NULL) {
1311 clazz = NULL;
1312 goto bail;
1313 }
1314
1315 //Thread* self = dvmThreadSelf();
1316 if (_self->classLoaderOverride != NULL) {
1317 /* hack for JNI_OnLoad */
1318 assert(strcmp(thisMethod->name, "nativeLoad") == 0);
1319 loader = _self->classLoaderOverride;
1320 } else if (thisMethod == gDvm.methFakeNativeEntry) {
1321 /* start point of invocation interface */
1322 if (!gDvm.initializing)
1323 loader = dvmGetSystemClassLoader();
1324 else
1325 loader = NULL;
1326 } else {
1327 loader = thisMethod->clazz->classLoader;
1328 }
1329
1330 clazz = dvmFindClassNoInit(descriptor, loader);
Andy McFaddenab00d452009-08-19 07:21:41 -07001331 jclazz = addLocalReference(env, (Object*) clazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001332
1333bail:
1334 free(descriptor);
Andy McFaddenab00d452009-08-19 07:21:41 -07001335
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001336 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001337 return jclazz;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001338}
1339
1340/*
1341 * Return the superclass of a class.
1342 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001343static jclass GetSuperclass(JNIEnv* env, jclass jclazz)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001344{
1345 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001346 jclass jsuper;
1347
1348 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1349 if (clazz == NULL)
1350 jsuper = NULL;
1351 else
1352 jsuper = addLocalReference(env, (Object*)clazz->super);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001353 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001354 return jsuper;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001355}
1356
1357/*
1358 * Determine whether an object of clazz1 can be safely cast to clazz2.
1359 *
1360 * Like IsInstanceOf, but with a pair of class objects instead of obj+class.
1361 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001362static jboolean IsAssignableFrom(JNIEnv* env, jclass jclazz1, jclass jclazz2)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001363{
1364 JNI_ENTER();
1365
Andy McFaddenab00d452009-08-19 07:21:41 -07001366 ClassObject* clazz1 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz1);
1367 ClassObject* clazz2 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz2);
1368
1369 jboolean result = dvmInstanceof(clazz1, clazz2);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001370
1371 JNI_EXIT();
1372 return result;
1373}
1374
1375/*
1376 * Given a java.lang.reflect.Method or .Constructor, return a methodID.
1377 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001378static jmethodID FromReflectedMethod(JNIEnv* env, jobject jmethod)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001379{
1380 JNI_ENTER();
1381 jmethodID methodID;
Andy McFaddenab00d452009-08-19 07:21:41 -07001382 Object* method = dvmDecodeIndirectRef(env, jmethod);
1383 methodID = (jmethodID) dvmGetMethodFromReflectObj(method);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001384 JNI_EXIT();
1385 return methodID;
1386}
1387
1388/*
1389 * Given a java.lang.reflect.Field, return a fieldID.
1390 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001391static jfieldID FromReflectedField(JNIEnv* env, jobject jfield)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001392{
1393 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001394 jfieldID fieldID;
1395 Object* field = dvmDecodeIndirectRef(env, jfield);
1396 fieldID = (jfieldID) dvmGetFieldFromReflectObj(field);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001397 JNI_EXIT();
1398 return fieldID;
1399}
1400
1401/*
1402 * Convert a methodID to a java.lang.reflect.Method or .Constructor.
1403 *
1404 * (The "isStatic" field does not appear in the spec.)
1405 *
1406 * Throws OutOfMemory and returns NULL on failure.
1407 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001408static jobject ToReflectedMethod(JNIEnv* env, jclass jcls, jmethodID methodID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001409 jboolean isStatic)
1410{
1411 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001412 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls);
1413 Object* obj = dvmCreateReflectObjForMethod(clazz, (Method*) methodID);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001414 dvmReleaseTrackedAlloc(obj, NULL);
Andy McFaddenab00d452009-08-19 07:21:41 -07001415 jobject jobj = addLocalReference(env, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001416 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001417 return jobj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001418}
1419
1420/*
1421 * Convert a fieldID to a java.lang.reflect.Field.
1422 *
1423 * (The "isStatic" field does not appear in the spec.)
1424 *
1425 * Throws OutOfMemory and returns NULL on failure.
1426 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001427static jobject ToReflectedField(JNIEnv* env, jclass jcls, jfieldID fieldID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001428 jboolean isStatic)
1429{
1430 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001431 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls);
1432 Object* obj = dvmCreateReflectObjForField(jcls, (Field*) fieldID);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001433 dvmReleaseTrackedAlloc(obj, NULL);
Andy McFaddenab00d452009-08-19 07:21:41 -07001434 jobject jobj = addLocalReference(env, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001435 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001436 return jobj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001437}
1438
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001439/*
1440 * Take this exception and throw it.
1441 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001442static jint Throw(JNIEnv* env, jthrowable jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001443{
1444 JNI_ENTER();
1445
1446 jint retval;
1447
Andy McFaddenab00d452009-08-19 07:21:41 -07001448 if (jobj != NULL) {
1449 Object* obj = dvmDecodeIndirectRef(env, jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001450 dvmSetException(_self, obj);
1451 retval = JNI_OK;
Andy McFaddenab00d452009-08-19 07:21:41 -07001452 } else {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001453 retval = JNI_ERR;
Andy McFaddenab00d452009-08-19 07:21:41 -07001454 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001455
1456 JNI_EXIT();
1457 return retval;
1458}
1459
1460/*
Andy McFaddenab00d452009-08-19 07:21:41 -07001461 * Constructs an exception object from the specified class with the message
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001462 * specified by "message", and throws it.
1463 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001464static jint ThrowNew(JNIEnv* env, jclass jclazz, const char* message)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001465{
1466 JNI_ENTER();
1467
Andy McFaddenab00d452009-08-19 07:21:41 -07001468 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1469 dvmThrowExceptionByClass(clazz, message);
1470 // TODO: should return failure if this didn't work (e.g. OOM)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001471
1472 JNI_EXIT();
1473 return JNI_OK;
1474}
1475
1476/*
1477 * If an exception is being thrown, return the exception object. Otherwise,
1478 * return NULL.
1479 *
1480 * TODO: if there is no pending exception, we should be able to skip the
1481 * enter/exit checks. If we find one, we need to enter and then re-fetch
1482 * the exception (in case it got moved by a compacting GC).
1483 */
1484static jthrowable ExceptionOccurred(JNIEnv* env)
1485{
1486 JNI_ENTER();
1487
1488 Object* exception;
Andy McFaddenab00d452009-08-19 07:21:41 -07001489 jobject localException;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001490
Andy McFaddenab00d452009-08-19 07:21:41 -07001491 exception = dvmGetException(_self);
1492 localException = addLocalReference(env, exception);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001493 if (localException == NULL && exception != NULL) {
1494 /*
1495 * We were unable to add a new local reference, and threw a new
1496 * exception. We can't return "exception", because it's not a
1497 * local reference. So we have to return NULL, indicating that
1498 * there was no exception, even though it's pretty much raining
1499 * exceptions in here.
1500 */
1501 LOGW("JNI WARNING: addLocal/exception combo\n");
1502 }
1503
1504 JNI_EXIT();
1505 return localException;
1506}
1507
1508/*
1509 * Print an exception and stack trace to stderr.
1510 */
1511static void ExceptionDescribe(JNIEnv* env)
1512{
1513 JNI_ENTER();
1514
1515 Object* exception = dvmGetException(_self);
1516 if (exception != NULL) {
1517 dvmPrintExceptionStackTrace();
1518 } else {
1519 LOGI("Odd: ExceptionDescribe called, but no exception pending\n");
1520 }
1521
1522 JNI_EXIT();
1523}
1524
1525/*
1526 * Clear the exception currently being thrown.
1527 *
1528 * TODO: we should be able to skip the enter/exit stuff.
1529 */
1530static void ExceptionClear(JNIEnv* env)
1531{
1532 JNI_ENTER();
1533 dvmClearException(_self);
1534 JNI_EXIT();
1535}
1536
1537/*
1538 * Kill the VM. This function does not return.
1539 */
1540static void FatalError(JNIEnv* env, const char* msg)
1541{
1542 //dvmChangeStatus(NULL, THREAD_RUNNING);
1543 LOGE("JNI posting fatal error: %s\n", msg);
1544 dvmAbort();
1545}
1546
1547/*
1548 * Push a new JNI frame on the stack, with a new set of locals.
1549 *
1550 * The new frame must have the same method pointer. (If for no other
1551 * reason than FindClass needs it to get the appropriate class loader.)
1552 */
1553static jint PushLocalFrame(JNIEnv* env, jint capacity)
1554{
1555 JNI_ENTER();
1556 int result = JNI_OK;
Andy McFaddenab00d452009-08-19 07:21:41 -07001557 if (!ensureLocalCapacity(env, capacity) ||
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001558 !dvmPushLocalFrame(_self /*dvmThreadSelf()*/, dvmGetCurrentJNIMethod()))
1559 {
1560 /* yes, OutOfMemoryError, not StackOverflowError */
1561 dvmClearException(_self);
1562 dvmThrowException("Ljava/lang/OutOfMemoryError;",
1563 "out of stack in JNI PushLocalFrame");
1564 result = JNI_ERR;
1565 }
1566 JNI_EXIT();
1567 return result;
1568}
1569
1570/*
1571 * Pop the local frame off. If "result" is not null, add it as a
1572 * local reference on the now-current frame.
1573 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001574static jobject PopLocalFrame(JNIEnv* env, jobject jresult)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001575{
1576 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001577 Object* result = dvmDecodeIndirectRef(env, jresult);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001578 if (!dvmPopLocalFrame(_self /*dvmThreadSelf()*/)) {
1579 LOGW("JNI WARNING: too many PopLocalFrame calls\n");
1580 dvmClearException(_self);
1581 dvmThrowException("Ljava/lang/RuntimeException;",
1582 "too many PopLocalFrame calls");
1583 }
Andy McFaddenab00d452009-08-19 07:21:41 -07001584 jresult = addLocalReference(env, result);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001585 JNI_EXIT();
1586 return result;
1587}
1588
1589/*
1590 * Add a reference to the global list.
1591 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001592static jobject NewGlobalRef(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001593{
1594 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001595 Object* obj = dvmDecodeIndirectRef(env, jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001596 jobject retval = addGlobalReference(obj);
1597 JNI_EXIT();
1598 return retval;
1599}
1600
1601/*
1602 * Delete a reference from the global list.
1603 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001604static void DeleteGlobalRef(JNIEnv* env, jobject jglobalRef)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001605{
1606 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001607 deleteGlobalReference(jglobalRef);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001608 JNI_EXIT();
1609}
1610
1611
1612/*
1613 * Add a reference to the local list.
1614 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001615static jobject NewLocalRef(JNIEnv* env, jobject jref)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001616{
1617 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001618 Object* obj = dvmDecodeIndirectRef(env, jref);
1619 jobject retval = addLocalReference(env, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001620 JNI_EXIT();
1621 return retval;
1622}
1623
1624/*
1625 * Delete a reference from the local list.
1626 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001627static void DeleteLocalRef(JNIEnv* env, jobject jlocalRef)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001628{
1629 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001630 deleteLocalReference(env, jlocalRef);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001631 JNI_EXIT();
1632}
1633
1634/*
1635 * Ensure that the local references table can hold at least this many
1636 * references.
1637 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001638static jint EnsureLocalCapacity(JNIEnv* env, jint capacity)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001639{
1640 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001641 bool okay = ensureLocalCapacity(env, capacity);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001642 if (!okay) {
1643 dvmThrowException("Ljava/lang/OutOfMemoryError;",
1644 "can't ensure local reference capacity");
1645 }
1646 JNI_EXIT();
1647 if (okay)
1648 return 0;
1649 else
1650 return -1;
1651}
1652
1653
1654/*
1655 * Determine whether two Object references refer to the same underlying object.
1656 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001657static jboolean IsSameObject(JNIEnv* env, jobject jref1, jobject jref2)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001658{
1659 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001660 Object* obj1 = dvmDecodeIndirectRef(env, jref1);
1661 Object* obj2 = dvmDecodeIndirectRef(env, jref2);
1662 jboolean result = (obj1 == obj2);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001663 JNI_EXIT();
1664 return result;
1665}
1666
1667/*
1668 * Allocate a new object without invoking any constructors.
1669 */
1670static jobject AllocObject(JNIEnv* env, jclass jclazz)
1671{
1672 JNI_ENTER();
1673
Andy McFaddenab00d452009-08-19 07:21:41 -07001674 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1675 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001676
1677 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
1678 assert(dvmCheckException(_self));
Andy McFaddenab00d452009-08-19 07:21:41 -07001679 result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001680 } else {
Andy McFaddenab00d452009-08-19 07:21:41 -07001681 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
1682 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001683 }
1684
1685 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001686 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001687}
1688
1689/*
Andy McFaddenab00d452009-08-19 07:21:41 -07001690 * Allocate a new object and invoke the supplied constructor.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001691 */
1692static jobject NewObject(JNIEnv* env, jclass jclazz, jmethodID methodID, ...)
1693{
1694 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001695 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1696 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001697
1698 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
1699 assert(dvmCheckException(_self));
Andy McFaddenab00d452009-08-19 07:21:41 -07001700 result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001701 } else {
Andy McFaddenab00d452009-08-19 07:21:41 -07001702 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
1703 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001704 if (newObj != NULL) {
1705 JValue unused;
1706 va_list args;
1707 va_start(args, methodID);
Andy McFaddenab00d452009-08-19 07:21:41 -07001708 dvmCallMethodV(_self, (Method*) methodID, newObj, &unused, args);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001709 va_end(args);
1710 }
1711 }
1712
1713 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001714 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001715}
Andy McFaddenab00d452009-08-19 07:21:41 -07001716static jobject NewObjectV(JNIEnv* env, jclass jclazz, jmethodID methodID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001717 va_list args)
1718{
1719 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001720 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1721 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001722
Andy McFaddenab00d452009-08-19 07:21:41 -07001723 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
1724 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001725 if (newObj != NULL) {
1726 JValue unused;
Andy McFaddenab00d452009-08-19 07:21:41 -07001727 dvmCallMethodV(_self, (Method*) methodID, newObj, &unused, args);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001728 }
1729
1730 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001731 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001732}
Andy McFaddenab00d452009-08-19 07:21:41 -07001733static jobject NewObjectA(JNIEnv* env, jclass jclazz, jmethodID methodID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001734 jvalue* args)
1735{
1736 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001737 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1738 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001739
Andy McFaddenab00d452009-08-19 07:21:41 -07001740 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
1741 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001742 if (newObj != NULL) {
1743 JValue unused;
Andy McFaddenab00d452009-08-19 07:21:41 -07001744 dvmCallMethodA(_self, (Method*) methodID, newObj, &unused, args);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001745 }
1746
1747 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001748 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001749}
1750
1751/*
1752 * Returns the class of an object.
1753 *
1754 * JNI spec says: obj must not be NULL.
1755 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001756static jclass GetObjectClass(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001757{
1758 JNI_ENTER();
1759
Andy McFaddenab00d452009-08-19 07:21:41 -07001760 assert(jobj != NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001761
Andy McFaddenab00d452009-08-19 07:21:41 -07001762 Object* obj = dvmDecodeIndirectRef(env, jobj);
1763 jclass jclazz = addLocalReference(env, (Object*) obj->clazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001764
1765 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001766 return jclazz;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001767}
1768
1769/*
1770 * Determine whether "obj" is an instance of "clazz".
1771 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001772static jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass jclazz)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001773{
1774 JNI_ENTER();
1775
Andy McFaddenab00d452009-08-19 07:21:41 -07001776 assert(jclazz != NULL);
1777
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001778 jboolean result;
1779
Andy McFaddenab00d452009-08-19 07:21:41 -07001780 if (jobj == NULL) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001781 result = true;
Andy McFaddenab00d452009-08-19 07:21:41 -07001782 } else {
1783 Object* obj = dvmDecodeIndirectRef(env, jobj);
1784 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1785 result = dvmInstanceof(obj->clazz, clazz);
1786 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001787
1788 JNI_EXIT();
1789 return result;
1790}
1791
1792/*
1793 * Get a method ID for an instance method.
1794 *
1795 * JNI defines <init> as an instance method, but Dalvik considers it a
1796 * "direct" method, so we have to special-case it here.
1797 *
1798 * Dalvik also puts all private methods into the "direct" list, so we
1799 * really need to just search both lists.
1800 */
1801static jmethodID GetMethodID(JNIEnv* env, jclass jclazz, const char* name,
1802 const char* sig)
1803{
1804 JNI_ENTER();
1805
Andy McFaddenab00d452009-08-19 07:21:41 -07001806 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001807 jmethodID id = NULL;
1808
1809 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
1810 assert(dvmCheckException(_self));
1811 } else {
1812 Method* meth;
1813
1814 meth = dvmFindVirtualMethodHierByDescriptor(clazz, name, sig);
1815 if (meth == NULL) {
1816 /* search private methods and constructors; non-hierarchical */
1817 meth = dvmFindDirectMethodByDescriptor(clazz, name, sig);
1818 }
1819 if (meth != NULL && dvmIsStaticMethod(meth)) {
1820 IF_LOGD() {
1821 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
1822 LOGD("GetMethodID: not returning static method %s.%s %s\n",
1823 clazz->descriptor, meth->name, desc);
1824 free(desc);
1825 }
1826 meth = NULL;
1827 }
1828 if (meth == NULL) {
Andy McFadden03bd0d52009-08-18 15:32:27 -07001829 LOGD("GetMethodID: method not found: %s.%s:%s\n",
1830 clazz->descriptor, name, sig);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001831 dvmThrowException("Ljava/lang/NoSuchMethodError;", name);
1832 }
1833
1834 /*
1835 * The method's class may not be the same as clazz, but if
1836 * it isn't this must be a virtual method and the class must
1837 * be a superclass (and, hence, already initialized).
1838 */
1839 if (meth != NULL) {
1840 assert(dvmIsClassInitialized(meth->clazz) ||
1841 dvmIsClassInitializing(meth->clazz));
1842 }
1843 id = (jmethodID) meth;
1844 }
1845 JNI_EXIT();
1846 return id;
1847}
1848
1849/*
1850 * Get a field ID (instance fields).
1851 */
1852static jfieldID GetFieldID(JNIEnv* env, jclass jclazz,
1853 const char* name, const char* sig)
1854{
1855 JNI_ENTER();
1856
Andy McFaddenab00d452009-08-19 07:21:41 -07001857 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001858 jfieldID id;
1859
1860 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
1861 assert(dvmCheckException(_self));
1862 id = NULL;
1863 } else {
1864 id = (jfieldID) dvmFindInstanceFieldHier(clazz, name, sig);
Andy McFadden03bd0d52009-08-18 15:32:27 -07001865 if (id == NULL) {
1866 LOGD("GetFieldID: unable to find field %s.%s:%s\n",
1867 clazz->descriptor, name, sig);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001868 dvmThrowException("Ljava/lang/NoSuchFieldError;", name);
Andy McFadden03bd0d52009-08-18 15:32:27 -07001869 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001870 }
1871 JNI_EXIT();
1872 return id;
1873}
1874
1875/*
1876 * Get the method ID for a static method in a class.
1877 */
1878static jmethodID GetStaticMethodID(JNIEnv* env, jclass jclazz,
1879 const char* name, const char* sig)
1880{
1881 JNI_ENTER();
1882
Andy McFaddenab00d452009-08-19 07:21:41 -07001883 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001884 jmethodID id;
1885
1886 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
1887 assert(dvmCheckException(_self));
1888 id = NULL;
1889 } else {
1890 Method* meth;
1891
1892 meth = dvmFindDirectMethodHierByDescriptor(clazz, name, sig);
1893
1894 /* make sure it's static, not virtual+private */
1895 if (meth != NULL && !dvmIsStaticMethod(meth)) {
1896 IF_LOGD() {
1897 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
1898 LOGD("GetStaticMethodID: "
1899 "not returning nonstatic method %s.%s %s\n",
1900 clazz->descriptor, meth->name, desc);
1901 free(desc);
1902 }
1903 meth = NULL;
1904 }
1905
1906 id = (jmethodID) meth;
1907 if (id == NULL)
1908 dvmThrowException("Ljava/lang/NoSuchMethodError;", name);
1909 }
1910
1911 JNI_EXIT();
1912 return id;
1913}
1914
1915/*
1916 * Get a field ID (static fields).
1917 */
1918static jfieldID GetStaticFieldID(JNIEnv* env, jclass jclazz,
1919 const char* name, const char* sig)
1920{
1921 JNI_ENTER();
1922
Andy McFaddenab00d452009-08-19 07:21:41 -07001923 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001924 jfieldID id;
1925
1926 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
1927 assert(dvmCheckException(_self));
1928 id = NULL;
1929 } else {
1930 id = (jfieldID) dvmFindStaticField(clazz, name, sig);
1931 if (id == NULL)
1932 dvmThrowException("Ljava/lang/NoSuchFieldError;", name);
1933 }
1934 JNI_EXIT();
1935 return id;
1936}
1937
1938/*
1939 * Get a static field.
1940 *
1941 * If we get an object reference, add it to the local refs list.
1942 */
1943#define GET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07001944 static _ctype GetStatic##_jname##Field(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001945 jfieldID fieldID) \
1946 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07001947 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001948 JNI_ENTER(); \
1949 StaticField* sfield = (StaticField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07001950 _ctype value; \
1951 if (_isref) { /* only when _ctype==jobject */ \
1952 Object* obj = dvmGetStaticFieldObject(sfield); \
1953 value = (_ctype)(u4)addLocalReference(env, obj); \
1954 } else { \
1955 value = dvmGetStaticField##_jname(sfield); \
1956 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001957 JNI_EXIT(); \
1958 return value; \
1959 }
1960GET_STATIC_TYPE_FIELD(jobject, Object, true);
1961GET_STATIC_TYPE_FIELD(jboolean, Boolean, false);
1962GET_STATIC_TYPE_FIELD(jbyte, Byte, false);
1963GET_STATIC_TYPE_FIELD(jchar, Char, false);
1964GET_STATIC_TYPE_FIELD(jshort, Short, false);
1965GET_STATIC_TYPE_FIELD(jint, Int, false);
1966GET_STATIC_TYPE_FIELD(jlong, Long, false);
1967GET_STATIC_TYPE_FIELD(jfloat, Float, false);
1968GET_STATIC_TYPE_FIELD(jdouble, Double, false);
1969
1970/*
1971 * Set a static field.
1972 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001973#define SET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) \
1974 static void SetStatic##_jname##Field(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001975 jfieldID fieldID, _ctype value) \
1976 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07001977 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001978 JNI_ENTER(); \
1979 StaticField* sfield = (StaticField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07001980 if (_isref) { /* only when _ctype==jobject */ \
1981 Object* valObj = dvmDecodeIndirectRef(env, (jobject)(u4)value); \
1982 dvmSetStaticFieldObject(sfield, valObj); \
1983 } else { \
1984 dvmSetStaticField##_jname(sfield, value); \
1985 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001986 JNI_EXIT(); \
1987 }
Andy McFaddenab00d452009-08-19 07:21:41 -07001988SET_STATIC_TYPE_FIELD(jobject, Object, true);
1989SET_STATIC_TYPE_FIELD(jboolean, Boolean, false);
1990SET_STATIC_TYPE_FIELD(jbyte, Byte, false);
1991SET_STATIC_TYPE_FIELD(jchar, Char, false);
1992SET_STATIC_TYPE_FIELD(jshort, Short, false);
1993SET_STATIC_TYPE_FIELD(jint, Int, false);
1994SET_STATIC_TYPE_FIELD(jlong, Long, false);
1995SET_STATIC_TYPE_FIELD(jfloat, Float, false);
1996SET_STATIC_TYPE_FIELD(jdouble, Double, false);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001997
1998/*
1999 * Get an instance field.
2000 *
2001 * If we get an object reference, add it to the local refs list.
2002 */
2003#define GET_TYPE_FIELD(_ctype, _jname, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002004 static _ctype Get##_jname##Field(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002005 jfieldID fieldID) \
2006 { \
2007 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002008 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002009 InstField* field = (InstField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002010 _ctype value; \
2011 if (_isref) { /* only when _ctype==jobject */ \
2012 Object* valObj = dvmGetFieldObject(obj, field->byteOffset); \
2013 value = (_ctype)(u4)addLocalReference(env, valObj); \
2014 } else { \
2015 value = dvmGetField##_jname(obj, field->byteOffset); \
2016 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002017 JNI_EXIT(); \
2018 return value; \
2019 }
2020GET_TYPE_FIELD(jobject, Object, true);
2021GET_TYPE_FIELD(jboolean, Boolean, false);
2022GET_TYPE_FIELD(jbyte, Byte, false);
2023GET_TYPE_FIELD(jchar, Char, false);
2024GET_TYPE_FIELD(jshort, Short, false);
2025GET_TYPE_FIELD(jint, Int, false);
2026GET_TYPE_FIELD(jlong, Long, false);
2027GET_TYPE_FIELD(jfloat, Float, false);
2028GET_TYPE_FIELD(jdouble, Double, false);
2029
2030/*
2031 * Set an instance field.
2032 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002033#define SET_TYPE_FIELD(_ctype, _jname, _isref) \
2034 static void Set##_jname##Field(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002035 jfieldID fieldID, _ctype value) \
2036 { \
2037 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002038 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002039 InstField* field = (InstField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002040 if (_isref) { /* only when _ctype==jobject */ \
2041 Object* valObj = dvmDecodeIndirectRef(env, (jobject)(u4)value); \
2042 dvmSetFieldObject(obj, field->byteOffset, valObj); \
2043 } else { \
2044 dvmSetField##_jname(obj, field->byteOffset, value); \
2045 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002046 JNI_EXIT(); \
2047 }
Andy McFaddenab00d452009-08-19 07:21:41 -07002048SET_TYPE_FIELD(jobject, Object, true);
2049SET_TYPE_FIELD(jboolean, Boolean, false);
2050SET_TYPE_FIELD(jbyte, Byte, false);
2051SET_TYPE_FIELD(jchar, Char, false);
2052SET_TYPE_FIELD(jshort, Short, false);
2053SET_TYPE_FIELD(jint, Int, false);
2054SET_TYPE_FIELD(jlong, Long, false);
2055SET_TYPE_FIELD(jfloat, Float, false);
2056SET_TYPE_FIELD(jdouble, Double, false);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002057
2058/*
2059 * Make a virtual method call.
2060 *
2061 * Three versions (..., va_list, jvalue[]) for each return type. If we're
2062 * returning an Object, we have to add it to the local references table.
2063 */
2064#define CALL_VIRTUAL(_ctype, _jname, _retfail, _retok, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002065 static _ctype Call##_jname##Method(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002066 jmethodID methodID, ...) \
2067 { \
2068 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002069 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002070 const Method* meth; \
2071 va_list args; \
2072 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002073 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002074 if (meth == NULL) { \
2075 JNI_EXIT(); \
2076 return _retfail; \
2077 } \
2078 va_start(args, methodID); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002079 dvmCallMethodV(_self, meth, obj, &result, args); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002080 va_end(args); \
2081 if (_isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002082 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002083 JNI_EXIT(); \
2084 return _retok; \
2085 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002086 static _ctype Call##_jname##MethodV(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002087 jmethodID methodID, va_list args) \
2088 { \
2089 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002090 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002091 const Method* meth; \
2092 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002093 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002094 if (meth == NULL) { \
2095 JNI_EXIT(); \
2096 return _retfail; \
2097 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002098 dvmCallMethodV(_self, meth, obj, &result, args); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002099 if (_isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002100 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002101 JNI_EXIT(); \
2102 return _retok; \
2103 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002104 static _ctype Call##_jname##MethodA(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002105 jmethodID methodID, jvalue* args) \
2106 { \
2107 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002108 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002109 const Method* meth; \
2110 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002111 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002112 if (meth == NULL) { \
2113 JNI_EXIT(); \
2114 return _retfail; \
2115 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002116 dvmCallMethodA(_self, meth, obj, &result, args); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002117 if (_isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002118 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002119 JNI_EXIT(); \
2120 return _retok; \
2121 }
2122CALL_VIRTUAL(jobject, Object, NULL, result.l, true);
2123CALL_VIRTUAL(jboolean, Boolean, 0, result.z, false);
2124CALL_VIRTUAL(jbyte, Byte, 0, result.b, false);
2125CALL_VIRTUAL(jchar, Char, 0, result.c, false);
2126CALL_VIRTUAL(jshort, Short, 0, result.s, false);
2127CALL_VIRTUAL(jint, Int, 0, result.i, false);
2128CALL_VIRTUAL(jlong, Long, 0, result.j, false);
2129CALL_VIRTUAL(jfloat, Float, 0.0f, result.f, false);
2130CALL_VIRTUAL(jdouble, Double, 0.0, result.d, false);
2131CALL_VIRTUAL(void, Void, , , false);
2132
2133/*
2134 * Make a "non-virtual" method call. We're still calling a virtual method,
2135 * but this time we're not doing an indirection through the object's vtable.
2136 * The "clazz" parameter defines which implementation of a method we want.
2137 *
2138 * Three versions (..., va_list, jvalue[]) for each return type.
2139 */
2140#define CALL_NONVIRTUAL(_ctype, _jname, _retfail, _retok, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002141 static _ctype CallNonvirtual##_jname##Method(JNIEnv* env, jobject jobj, \
2142 jclass jclazz, jmethodID methodID, ...) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002143 { \
2144 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002145 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2146 ClassObject* clazz = \
2147 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002148 const Method* meth; \
2149 va_list args; \
2150 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002151 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002152 if (meth == NULL) { \
2153 JNI_EXIT(); \
2154 return _retfail; \
2155 } \
2156 va_start(args, methodID); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002157 dvmCallMethodV(_self, meth, obj, &result, args); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002158 if (_isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002159 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002160 va_end(args); \
2161 JNI_EXIT(); \
2162 return _retok; \
2163 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002164 static _ctype CallNonvirtual##_jname##MethodV(JNIEnv* env, jobject jobj,\
2165 jclass jclazz, jmethodID methodID, va_list args) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002166 { \
2167 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002168 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2169 ClassObject* clazz = \
2170 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002171 const Method* meth; \
2172 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002173 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002174 if (meth == NULL) { \
2175 JNI_EXIT(); \
2176 return _retfail; \
2177 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002178 dvmCallMethodV(_self, meth, obj, &result, args); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002179 if (_isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002180 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002181 JNI_EXIT(); \
2182 return _retok; \
2183 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002184 static _ctype CallNonvirtual##_jname##MethodA(JNIEnv* env, jobject jobj,\
2185 jclass jclazz, jmethodID methodID, jvalue* args) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002186 { \
2187 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002188 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2189 ClassObject* clazz = \
2190 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002191 const Method* meth; \
2192 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002193 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002194 if (meth == NULL) { \
2195 JNI_EXIT(); \
2196 return _retfail; \
2197 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002198 dvmCallMethodA(_self, meth, obj, &result, args); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002199 if (_isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002200 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002201 JNI_EXIT(); \
2202 return _retok; \
2203 }
2204CALL_NONVIRTUAL(jobject, Object, NULL, result.l, true);
2205CALL_NONVIRTUAL(jboolean, Boolean, 0, result.z, false);
2206CALL_NONVIRTUAL(jbyte, Byte, 0, result.b, false);
2207CALL_NONVIRTUAL(jchar, Char, 0, result.c, false);
2208CALL_NONVIRTUAL(jshort, Short, 0, result.s, false);
2209CALL_NONVIRTUAL(jint, Int, 0, result.i, false);
2210CALL_NONVIRTUAL(jlong, Long, 0, result.j, false);
2211CALL_NONVIRTUAL(jfloat, Float, 0.0f, result.f, false);
2212CALL_NONVIRTUAL(jdouble, Double, 0.0, result.d, false);
2213CALL_NONVIRTUAL(void, Void, , , false);
2214
2215
2216/*
2217 * Call a static method.
2218 */
2219#define CALL_STATIC(_ctype, _jname, _retfail, _retok, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002220 static _ctype CallStatic##_jname##Method(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002221 jmethodID methodID, ...) \
2222 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002223 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002224 JNI_ENTER(); \
2225 JValue result; \
2226 va_list args; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002227 va_start(args, methodID); \
2228 dvmCallMethodV(_self, (Method*) methodID, NULL, &result, args); \
2229 va_end(args); \
2230 if (_isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002231 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002232 JNI_EXIT(); \
2233 return _retok; \
2234 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002235 static _ctype CallStatic##_jname##MethodV(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002236 jmethodID methodID, va_list args) \
2237 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002238 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002239 JNI_ENTER(); \
2240 JValue result; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002241 dvmCallMethodV(_self, (Method*) methodID, NULL, &result, args); \
2242 if (_isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002243 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002244 JNI_EXIT(); \
2245 return _retok; \
2246 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002247 static _ctype CallStatic##_jname##MethodA(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002248 jmethodID methodID, jvalue* args) \
2249 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002250 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002251 JNI_ENTER(); \
2252 JValue result; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002253 dvmCallMethodA(_self, (Method*) methodID, NULL, &result, args); \
2254 if (_isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002255 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002256 JNI_EXIT(); \
2257 return _retok; \
2258 }
2259CALL_STATIC(jobject, Object, NULL, result.l, true);
2260CALL_STATIC(jboolean, Boolean, 0, result.z, false);
2261CALL_STATIC(jbyte, Byte, 0, result.b, false);
2262CALL_STATIC(jchar, Char, 0, result.c, false);
2263CALL_STATIC(jshort, Short, 0, result.s, false);
2264CALL_STATIC(jint, Int, 0, result.i, false);
2265CALL_STATIC(jlong, Long, 0, result.j, false);
2266CALL_STATIC(jfloat, Float, 0.0f, result.f, false);
2267CALL_STATIC(jdouble, Double, 0.0, result.d, false);
2268CALL_STATIC(void, Void, , , false);
2269
2270/*
2271 * Create a new String from Unicode data.
2272 *
2273 * If "len" is zero, we will return an empty string even if "unicodeChars"
2274 * is NULL. (The JNI spec is vague here.)
2275 */
2276static jstring NewString(JNIEnv* env, const jchar* unicodeChars, jsize len)
2277{
2278 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002279 jobject retval;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002280
Andy McFaddenab00d452009-08-19 07:21:41 -07002281 StringObject* jstr = dvmCreateStringFromUnicode(unicodeChars, len);
2282 if (jstr == NULL) {
2283 retval = NULL;
2284 } else {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002285 dvmReleaseTrackedAlloc((Object*) jstr, NULL);
Andy McFaddenab00d452009-08-19 07:21:41 -07002286 retval = addLocalReference(env, (Object*) jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002287 }
2288
2289 JNI_EXIT();
2290 return jstr;
2291}
2292
2293/*
2294 * Return the length of a String in Unicode character units.
2295 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002296static jsize GetStringLength(JNIEnv* env, jstring jstr)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002297{
2298 JNI_ENTER();
2299
Andy McFaddenab00d452009-08-19 07:21:41 -07002300 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2301 jsize len = dvmStringLen(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002302
2303 JNI_EXIT();
2304 return len;
2305}
2306
Andy McFaddenab00d452009-08-19 07:21:41 -07002307
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002308/*
Andy McFaddenab00d452009-08-19 07:21:41 -07002309 * Get a string's character data.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002310 *
2311 * The result is guaranteed to be valid until ReleaseStringChars is
Andy McFaddenab00d452009-08-19 07:21:41 -07002312 * called, which means we have to pin it or return a copy.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002313 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002314static const jchar* GetStringChars(JNIEnv* env, jstring jstr, jboolean* isCopy)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002315{
2316 JNI_ENTER();
2317
Andy McFaddenab00d452009-08-19 07:21:41 -07002318 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2319 ArrayObject* strChars = dvmStringCharArray(jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002320
Andy McFaddenab00d452009-08-19 07:21:41 -07002321 pinPrimitiveArray(strChars);
2322
2323 const u2* data = dvmStringChars(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002324 if (isCopy != NULL)
2325 *isCopy = JNI_FALSE;
2326
2327 JNI_EXIT();
2328 return (jchar*)data;
2329}
2330
2331/*
2332 * Release our grip on some characters from a string.
2333 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002334static void ReleaseStringChars(JNIEnv* env, jstring jstr, const jchar* chars)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002335{
2336 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002337 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2338 ArrayObject* strChars = dvmStringCharArray(jstr);
2339 unpinPrimitiveArray(strChars);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002340 JNI_EXIT();
2341}
2342
2343/*
2344 * Create a new java.lang.String object from chars in modified UTF-8 form.
2345 *
2346 * The spec doesn't say how to handle a NULL string. Popular desktop VMs
2347 * accept it and return a NULL pointer in response.
2348 */
2349static jstring NewStringUTF(JNIEnv* env, const char* bytes)
2350{
2351 JNI_ENTER();
2352
Andy McFaddenab00d452009-08-19 07:21:41 -07002353 jstring result;
2354
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002355 if (bytes == NULL) {
Andy McFaddenab00d452009-08-19 07:21:41 -07002356 result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002357 } else {
Andy McFaddenab00d452009-08-19 07:21:41 -07002358 /* note newStr could come back NULL on OOM */
2359 StringObject* newStr = dvmCreateStringFromCstr(bytes, ALLOC_DEFAULT);
2360 result = addLocalReference(env, (Object*) newStr);
2361 dvmReleaseTrackedAlloc((Object*)newStr, NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002362 }
2363
2364 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002365 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002366}
2367
2368/*
2369 * Return the length in bytes of the modified UTF-8 form of the string.
2370 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002371static jsize GetStringUTFLength(JNIEnv* env, jstring jstr)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002372{
2373 JNI_ENTER();
2374
Andy McFaddenab00d452009-08-19 07:21:41 -07002375 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2376 jsize len = dvmStringUtf8ByteLen(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002377
2378 JNI_EXIT();
2379 return len;
2380}
2381
2382/*
2383 * Convert "string" to modified UTF-8 and return a pointer. The returned
2384 * value must be released with ReleaseStringUTFChars.
2385 *
2386 * According to the JNI reference, "Returns a pointer to a UTF-8 string,
2387 * or NULL if the operation fails. Returns NULL if and only if an invocation
2388 * of this function has thrown an exception."
2389 *
2390 * The behavior here currently follows that of other open-source VMs, which
2391 * quietly return NULL if "string" is NULL. We should consider throwing an
2392 * NPE. (The CheckJNI code blows up if you try to pass in a NULL string,
2393 * which should catch this sort of thing during development.) Certain other
2394 * VMs will crash with a segmentation fault.
2395 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002396static const char* GetStringUTFChars(JNIEnv* env, jstring jstr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002397 jboolean* isCopy)
2398{
2399 JNI_ENTER();
2400 char* newStr;
2401
Andy McFaddenab00d452009-08-19 07:21:41 -07002402 if (jstr == NULL) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002403 /* this shouldn't happen; throw NPE? */
2404 newStr = NULL;
2405 } else {
2406 if (isCopy != NULL)
2407 *isCopy = JNI_TRUE;
2408
Andy McFaddenab00d452009-08-19 07:21:41 -07002409 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2410 newStr = dvmCreateCstrFromString(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002411 if (newStr == NULL) {
2412 /* assume memory failure */
2413 dvmThrowException("Ljava/lang/OutOfMemoryError;",
2414 "native heap string alloc failed");
2415 }
2416 }
2417
2418 JNI_EXIT();
2419 return newStr;
2420}
2421
2422/*
2423 * Release a string created by GetStringUTFChars().
2424 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002425static void ReleaseStringUTFChars(JNIEnv* env, jstring jstr, const char* utf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002426{
2427 JNI_ENTER();
2428 free((char*)utf);
2429 JNI_EXIT();
2430}
2431
2432/*
2433 * Return the capacity of the array.
2434 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002435static jsize GetArrayLength(JNIEnv* env, jarray jarr)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002436{
2437 JNI_ENTER();
2438
Andy McFaddenab00d452009-08-19 07:21:41 -07002439 ArrayObject* arrObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
2440 jsize length = arrObj->length;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002441
2442 JNI_EXIT();
2443 return length;
2444}
2445
2446/*
2447 * Construct a new array that holds objects from class "elementClass".
2448 */
2449static jobjectArray NewObjectArray(JNIEnv* env, jsize length,
Andy McFaddenab00d452009-08-19 07:21:41 -07002450 jclass jelementClass, jobject jinitialElement)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002451{
2452 JNI_ENTER();
2453
Andy McFaddenab00d452009-08-19 07:21:41 -07002454 jobjectArray newArray = NULL;
2455 ClassObject* elemClassObj =
2456 (ClassObject*) dvmDecodeIndirectRef(env, jelementClass);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002457
2458 if (elemClassObj == NULL) {
2459 dvmThrowException("Ljava/lang/NullPointerException;",
2460 "JNI NewObjectArray");
2461 goto bail;
2462 }
2463
Andy McFaddenab00d452009-08-19 07:21:41 -07002464 ArrayObject* newObj =
2465 dvmAllocObjectArray(elemClassObj, length, ALLOC_DEFAULT);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002466 if (newObj == NULL) {
2467 assert(dvmCheckException(_self));
2468 goto bail;
2469 }
Andy McFaddenab00d452009-08-19 07:21:41 -07002470 newArray = addLocalReference(env, (Object*) newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002471 dvmReleaseTrackedAlloc((Object*) newObj, NULL);
2472
2473 /*
2474 * Initialize the array. Trashes "length".
2475 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002476 if (jinitialElement != NULL) {
2477 Object* initialElement = dvmDecodeIndirectRef(env, jinitialElement);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002478 Object** arrayData = (Object**) newObj->contents;
2479
2480 while (length--)
Andy McFaddenab00d452009-08-19 07:21:41 -07002481 *arrayData++ = initialElement;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002482 }
2483
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002484
2485bail:
2486 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002487 return newArray;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002488}
2489
2490/*
2491 * Get one element of an Object array.
2492 *
2493 * Add the object to the local references table in case the array goes away.
2494 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002495static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray jarr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002496 jsize index)
2497{
2498 JNI_ENTER();
2499
Andy McFaddenab00d452009-08-19 07:21:41 -07002500 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
2501 jobject retval = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002502
Andy McFaddenab00d452009-08-19 07:21:41 -07002503 assert(arrayObj != NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002504
2505 /* check the array bounds */
2506 if (index < 0 || index >= (int) arrayObj->length) {
2507 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
2508 arrayObj->obj.clazz->descriptor);
2509 goto bail;
2510 }
2511
Andy McFaddenab00d452009-08-19 07:21:41 -07002512 Object* value = ((Object**) arrayObj->contents)[index];
2513 retval = addLocalReference(env, value);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002514
2515bail:
2516 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002517 return retval;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002518}
2519
2520/*
2521 * Set one element of an Object array.
2522 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002523static void SetObjectArrayElement(JNIEnv* env, jobjectArray jarr,
2524 jsize index, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002525{
2526 JNI_ENTER();
2527
Andy McFaddenab00d452009-08-19 07:21:41 -07002528 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002529
Andy McFaddenab00d452009-08-19 07:21:41 -07002530 assert(arrayObj != NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002531
2532 /* check the array bounds */
2533 if (index < 0 || index >= (int) arrayObj->length) {
2534 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
2535 arrayObj->obj.clazz->descriptor);
2536 goto bail;
2537 }
2538
2539 //LOGV("JNI: set element %d in array %p to %p\n", index, array, value);
2540
Andy McFaddenab00d452009-08-19 07:21:41 -07002541 Object* obj;
2542 if (jobj == NULL)
2543 obj = NULL;
2544 else
2545 obj = dvmDecodeIndirectRef(env, jobj);
2546 ((Object**) arrayObj->contents)[index] = obj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002547
2548bail:
2549 JNI_EXIT();
2550}
2551
2552/*
2553 * Create a new array of primitive elements.
2554 */
2555#define NEW_PRIMITIVE_ARRAY(_artype, _jname, _typechar) \
2556 static _artype New##_jname##Array(JNIEnv* env, jsize length) \
2557 { \
2558 JNI_ENTER(); \
2559 ArrayObject* arrayObj; \
2560 arrayObj = dvmAllocPrimitiveArray(_typechar, length, \
2561 ALLOC_DEFAULT); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002562 jarray jarr = NULL; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002563 if (arrayObj != NULL) { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002564 jarr = addLocalReference(env, (Object*) arrayObj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002565 dvmReleaseTrackedAlloc((Object*) arrayObj, NULL); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002566 } \
2567 JNI_EXIT(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002568 return (_artype)jarr; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002569 }
2570NEW_PRIMITIVE_ARRAY(jbooleanArray, Boolean, 'Z');
2571NEW_PRIMITIVE_ARRAY(jbyteArray, Byte, 'B');
2572NEW_PRIMITIVE_ARRAY(jcharArray, Char, 'C');
2573NEW_PRIMITIVE_ARRAY(jshortArray, Short, 'S');
2574NEW_PRIMITIVE_ARRAY(jintArray, Int, 'I');
2575NEW_PRIMITIVE_ARRAY(jlongArray, Long, 'J');
2576NEW_PRIMITIVE_ARRAY(jfloatArray, Float, 'F');
2577NEW_PRIMITIVE_ARRAY(jdoubleArray, Double, 'D');
2578
2579/*
2580 * Get a pointer to a C array of primitive elements from an array object
2581 * of the matching type.
2582 *
Andy McFaddenab00d452009-08-19 07:21:41 -07002583 * In a compacting GC, we either need to return a copy of the elements or
2584 * "pin" the memory. Otherwise we run the risk of native code using the
2585 * buffer as the destination of e.g. a blocking read() call that wakes up
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002586 * during a GC.
2587 */
2588#define GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
2589 static _ctype* Get##_jname##ArrayElements(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07002590 _ctype##Array jarr, jboolean* isCopy) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002591 { \
2592 JNI_ENTER(); \
2593 _ctype* data; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002594 ArrayObject* arrayObj = \
2595 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
2596 pinPrimitiveArray(arrayObj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002597 data = (_ctype*) arrayObj->contents; \
2598 if (isCopy != NULL) \
2599 *isCopy = JNI_FALSE; \
2600 JNI_EXIT(); \
2601 return data; \
2602 }
2603
2604/*
2605 * Release the storage locked down by the "get" function.
2606 *
Andy McFaddenab00d452009-08-19 07:21:41 -07002607 * The spec says, "'mode' has no effect if 'elems' is not a copy of the
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002608 * elements in 'array'." They apparently did not anticipate the need to
Andy McFaddenab00d452009-08-19 07:21:41 -07002609 * un-pin memory.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002610 */
2611#define RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
2612 static void Release##_jname##ArrayElements(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07002613 _ctype##Array jarr, _ctype* elems, jint mode) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002614 { \
2615 UNUSED_PARAMETER(elems); \
2616 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002617 if (mode != JNI_COMMIT) { \
2618 ArrayObject* arrayObj = \
2619 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
2620 unpinPrimitiveArray(arrayObj); \
2621 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002622 JNI_EXIT(); \
2623 }
2624
2625/*
2626 * Copy a section of a primitive array to a buffer.
2627 */
2628#define GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
2629 static void Get##_jname##ArrayRegion(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07002630 _ctype##Array jarr, jsize start, jsize len, _ctype* buf) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002631 { \
2632 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002633 ArrayObject* arrayObj = \
2634 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002635 _ctype* data = (_ctype*) arrayObj->contents; \
2636 if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
2637 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
2638 arrayObj->obj.clazz->descriptor); \
2639 } else { \
2640 memcpy(buf, data + start, len * sizeof(_ctype)); \
2641 } \
2642 JNI_EXIT(); \
2643 }
2644
2645/*
2646 * Copy a section of a primitive array to a buffer.
2647 */
2648#define SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
2649 static void Set##_jname##ArrayRegion(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07002650 _ctype##Array jarr, jsize start, jsize len, const _ctype* buf) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002651 { \
2652 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002653 ArrayObject* arrayObj = \
2654 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002655 _ctype* data = (_ctype*) arrayObj->contents; \
2656 if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
2657 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
2658 arrayObj->obj.clazz->descriptor); \
2659 } else { \
2660 memcpy(data + start, buf, len * sizeof(_ctype)); \
2661 } \
2662 JNI_EXIT(); \
2663 }
2664
2665/*
2666 * 4-in-1:
2667 * Get<Type>ArrayElements
2668 * Release<Type>ArrayElements
2669 * Get<Type>ArrayRegion
2670 * Set<Type>ArrayRegion
2671 */
2672#define PRIMITIVE_ARRAY_FUNCTIONS(_ctype, _jname) \
2673 GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
2674 RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
2675 GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname); \
2676 SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname);
2677
2678PRIMITIVE_ARRAY_FUNCTIONS(jboolean, Boolean);
2679PRIMITIVE_ARRAY_FUNCTIONS(jbyte, Byte);
2680PRIMITIVE_ARRAY_FUNCTIONS(jchar, Char);
2681PRIMITIVE_ARRAY_FUNCTIONS(jshort, Short);
2682PRIMITIVE_ARRAY_FUNCTIONS(jint, Int);
2683PRIMITIVE_ARRAY_FUNCTIONS(jlong, Long);
2684PRIMITIVE_ARRAY_FUNCTIONS(jfloat, Float);
2685PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double);
2686
2687/*
2688 * Register one or more native functions in one class.
2689 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002690static jint RegisterNatives(JNIEnv* env, jclass jclazz,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002691 const JNINativeMethod* methods, jint nMethods)
2692{
2693 JNI_ENTER();
2694
Andy McFaddenab00d452009-08-19 07:21:41 -07002695 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002696 jint retval;
2697 int i;
2698
2699 if (gDvm.verboseJni) {
2700 LOGI("[Registering JNI native methods for class %s]\n",
Andy McFaddenab00d452009-08-19 07:21:41 -07002701 clazz->descriptor);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002702 }
2703
2704 for (i = 0; i < nMethods; i++) {
Andy McFaddenab00d452009-08-19 07:21:41 -07002705 if (!dvmRegisterJNIMethod(clazz, methods[i].name,
2706 methods[i].signature, methods[i].fnPtr))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002707 {
2708 retval = JNI_ERR;
2709 goto bail;
2710 }
2711 }
2712 retval = JNI_OK;
2713
2714bail:
2715 JNI_EXIT();
2716 return retval;
2717}
2718
2719/*
2720 * Un-register a native function.
2721 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002722static jint UnregisterNatives(JNIEnv* env, jclass jclazz)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002723{
2724 JNI_ENTER();
2725 /*
2726 * The JNI docs refer to this as a way to reload/relink native libraries,
2727 * and say it "should not be used in normal native code".
2728 *
2729 * We can implement it if we decide we need it.
2730 */
2731 JNI_EXIT();
2732 return JNI_ERR;
2733}
2734
2735/*
2736 * Lock the monitor.
2737 *
2738 * We have to track all monitor enters and exits, so that we can undo any
2739 * outstanding synchronization before the thread exits.
2740 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002741static jint MonitorEnter(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002742{
2743 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002744 Object* obj = dvmDecodeIndirectRef(env, jobj);
2745 dvmLockObject(_self, obj);
2746 trackMonitorEnter(_self, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002747 JNI_EXIT();
2748 return JNI_OK;
2749}
2750
2751/*
2752 * Unlock the monitor.
2753 *
2754 * Throws an IllegalMonitorStateException if the current thread
Andy McFaddenab00d452009-08-19 07:21:41 -07002755 * doesn't own the monitor. (dvmUnlockObject() takes care of the throw.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002756 *
2757 * According to the 1.6 spec, it's legal to call here with an exception
2758 * pending. If this fails, we'll stomp the original exception.
2759 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002760static jint MonitorExit(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002761{
2762 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002763 Object* obj = dvmDecodeIndirectRef(env, jobj);
2764 bool success = dvmUnlockObject(_self, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002765 if (success)
Andy McFaddenab00d452009-08-19 07:21:41 -07002766 trackMonitorExit(_self, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002767 JNI_EXIT();
2768 return success ? JNI_OK : JNI_ERR;
2769}
2770
2771/*
2772 * Return the JavaVM interface associated with the current thread.
2773 */
2774static jint GetJavaVM(JNIEnv* env, JavaVM** vm)
2775{
2776 JNI_ENTER();
2777 //*vm = gDvm.vmList;
2778 *vm = (JavaVM*) ((JNIEnvExt*)env)->vm;
2779 JNI_EXIT();
2780 if (*vm == NULL)
2781 return JNI_ERR;
2782 else
2783 return JNI_OK;
2784}
2785
2786/*
2787 * Copies "len" Unicode characters, from offset "start".
2788 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002789static void GetStringRegion(JNIEnv* env, jstring jstr, jsize start, jsize len,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002790 jchar* buf)
2791{
2792 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002793 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002794 if (start + len > dvmStringLen(strObj))
2795 dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
2796 else
2797 memcpy(buf, dvmStringChars(strObj) + start, len * sizeof(u2));
2798 JNI_EXIT();
2799}
2800
2801/*
2802 * Translates "len" Unicode characters, from offset "start", into
2803 * modified UTF-8 encoding.
2804 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002805static void GetStringUTFRegion(JNIEnv* env, jstring jstr, jsize start,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002806 jsize len, char* buf)
2807{
2808 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002809 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002810 if (start + len > dvmStringLen(strObj))
2811 dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
2812 else
2813 dvmCreateCstrFromStringRegion(strObj, start, len, buf);
2814 JNI_EXIT();
2815}
2816
2817/*
2818 * Get a raw pointer to array data.
2819 *
2820 * The caller is expected to call "release" before doing any JNI calls
2821 * or blocking I/O operations.
2822 *
Andy McFaddenab00d452009-08-19 07:21:41 -07002823 * We need to pin the memory or block GC.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002824 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002825static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray jarr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002826 jboolean* isCopy)
2827{
2828 JNI_ENTER();
2829 void* data;
Andy McFaddenab00d452009-08-19 07:21:41 -07002830 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
2831 pinPrimitiveArray(arrayObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002832 data = arrayObj->contents;
2833 if (isCopy != NULL)
2834 *isCopy = JNI_FALSE;
2835 JNI_EXIT();
2836 return data;
2837}
2838
2839/*
2840 * Release an array obtained with GetPrimitiveArrayCritical.
2841 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002842static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray jarr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002843 void* carray, jint mode)
2844{
2845 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002846 if (mode != JNI_COMMIT) {
2847 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
2848 unpinPrimitiveArray(arrayObj);
2849 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002850 JNI_EXIT();
2851}
2852
2853/*
2854 * Like GetStringChars, but with restricted use.
2855 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002856static const jchar* GetStringCritical(JNIEnv* env, jstring jstr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002857 jboolean* isCopy)
2858{
2859 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002860 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2861 ArrayObject* strChars = dvmStringCharArray(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002862
Andy McFaddenab00d452009-08-19 07:21:41 -07002863 pinPrimitiveArray(strChars);
2864
2865 const u2* data = dvmStringChars(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002866 if (isCopy != NULL)
2867 *isCopy = JNI_FALSE;
2868
2869 JNI_EXIT();
2870 return (jchar*)data;
2871}
2872
2873/*
2874 * Like ReleaseStringChars, but with restricted use.
2875 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002876static void ReleaseStringCritical(JNIEnv* env, jstring jstr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002877 const jchar* carray)
2878{
2879 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002880 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2881 ArrayObject* strChars = dvmStringCharArray(jstr);
2882 unpinPrimitiveArray(strChars);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002883 JNI_EXIT();
2884}
2885
2886/*
2887 * Create a new weak global reference.
2888 */
2889static jweak NewWeakGlobalRef(JNIEnv* env, jobject obj)
2890{
2891 JNI_ENTER();
2892 // TODO - implement
2893 jobject gref = NULL;
2894 LOGE("JNI ERROR: NewWeakGlobalRef not implemented\n");
2895 dvmAbort();
2896 JNI_EXIT();
2897 return gref;
2898}
2899
2900/*
2901 * Delete the specified weak global reference.
2902 */
2903static void DeleteWeakGlobalRef(JNIEnv* env, jweak obj)
2904{
2905 JNI_ENTER();
2906 // TODO - implement
2907 LOGE("JNI ERROR: DeleteWeakGlobalRef not implemented\n");
2908 dvmAbort();
2909 JNI_EXIT();
2910}
2911
2912/*
2913 * Quick check for pending exceptions.
2914 *
2915 * TODO: we should be able to skip the enter/exit macros here.
2916 */
2917static jboolean ExceptionCheck(JNIEnv* env)
2918{
2919 JNI_ENTER();
2920 bool result = dvmCheckException(_self);
2921 JNI_EXIT();
2922 return result;
2923}
2924
2925/*
2926 * Returns the type of the object referred to by "obj". It can be local,
2927 * global, or weak global.
2928 *
2929 * In the current implementation, references can be global and local at
2930 * the same time, so while the return value is accurate it may not tell
2931 * the whole story.
2932 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002933static jobjectRefType GetObjectRefType(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002934{
2935 JNI_ENTER();
2936 jobjectRefType type;
The Android Open Source Project99409882009-03-18 22:20:24 -07002937
Andy McFaddenab00d452009-08-19 07:21:41 -07002938 if (jobj == NULL)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002939 type = JNIInvalidRefType;
2940 else
Andy McFaddenab00d452009-08-19 07:21:41 -07002941 type = dvmGetJNIRefType(env, jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002942 JNI_EXIT();
2943 return type;
2944}
2945
2946/*
2947 * Allocate and return a new java.nio.ByteBuffer for this block of memory.
2948 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07002949 * "address" may not be NULL, and "capacity" must be > 0. (These are only
2950 * verified when CheckJNI is enabled.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002951 */
Andy McFadden8e5c7842009-07-23 17:47:18 -07002952static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002953{
Andy McFadden8e5c7842009-07-23 17:47:18 -07002954 JNI_ENTER();
2955
2956 Thread* self = _self /*dvmThreadSelf()*/;
2957 Object* platformAddress = NULL;
2958 JValue callResult;
The Android Open Source Project99409882009-03-18 22:20:24 -07002959 jobject result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002960
Andy McFadden8e5c7842009-07-23 17:47:18 -07002961 /* get an instance of PlatformAddress that wraps the provided address */
2962 dvmCallMethod(self,
2963 gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on,
2964 NULL, &callResult, address);
2965 if (dvmGetException(self) != NULL || callResult.l == NULL)
The Android Open Source Project99409882009-03-18 22:20:24 -07002966 goto bail;
Andy McFadden8e5c7842009-07-23 17:47:18 -07002967
2968 /* don't let the GC discard it */
2969 platformAddress = (Object*) callResult.l;
2970 dvmAddTrackedAlloc(platformAddress, self);
2971 LOGV("tracking %p for address=%p\n", platformAddress, address);
2972
2973 /* create an instance of java.nio.ReadWriteDirectByteBuffer */
2974 ClassObject* clazz = gDvm.classJavaNioReadWriteDirectByteBuffer;
2975 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))
2976 goto bail;
2977 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
2978 if (newObj != NULL) {
2979 /* call the (PlatformAddress, int, int) constructor */
Andy McFaddenab00d452009-08-19 07:21:41 -07002980 result = addLocalReference(env, newObj);
Andy McFadden8e5c7842009-07-23 17:47:18 -07002981 dvmCallMethod(self, gDvm.methJavaNioReadWriteDirectByteBuffer_init,
2982 newObj, &callResult, platformAddress, (jint) capacity, (jint) 0);
Andy McFaddenab00d452009-08-19 07:21:41 -07002983 if (dvmGetException(self) != NULL) {
2984 deleteLocalReference(env, result);
2985 result = NULL;
Andy McFadden8e5c7842009-07-23 17:47:18 -07002986 goto bail;
Andy McFaddenab00d452009-08-19 07:21:41 -07002987 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002988 }
2989
The Android Open Source Project99409882009-03-18 22:20:24 -07002990bail:
Andy McFadden8e5c7842009-07-23 17:47:18 -07002991 if (platformAddress != NULL)
2992 dvmReleaseTrackedAlloc(platformAddress, self);
2993 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07002994 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002995}
2996
2997/*
2998 * Get the starting address of the buffer for the specified java.nio.Buffer.
2999 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003000 * If this is not a "direct" buffer, we return NULL.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003001 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003002static void* GetDirectBufferAddress(JNIEnv* env, jobject jbuf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003003{
Andy McFadden8e5c7842009-07-23 17:47:18 -07003004 JNI_ENTER();
3005
Andy McFaddenab00d452009-08-19 07:21:41 -07003006 Object* bufObj = dvmDecodeIndirectRef(env, jbuf);
Andy McFadden8e5c7842009-07-23 17:47:18 -07003007 Thread* self = _self /*dvmThreadSelf()*/;
Andy McFadden8e696dc2009-07-24 15:28:16 -07003008 void* result;
3009
3010 /*
3011 * All Buffer objects have an effectiveDirectAddress field. If it's
3012 * nonzero, we can just return that value. If not, we have to call
3013 * through DirectBuffer.getEffectiveAddress(), which as a side-effect
3014 * will set the effectiveDirectAddress field for direct buffers (and
3015 * things that wrap direct buffers).
3016 */
3017 result = (void*) dvmGetFieldInt(bufObj,
3018 gDvm.offJavaNioBuffer_effectiveDirectAddress);
3019 if (result != NULL) {
3020 //LOGI("fast path for %p\n", buf);
3021 goto bail;
3022 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003023
Andy McFadden72d61fb2009-06-24 16:56:06 -07003024 /*
3025 * Start by determining if the object supports the DirectBuffer
3026 * interfaces. Note this does not guarantee that it's a direct buffer.
3027 */
Andy McFadden8e5c7842009-07-23 17:47:18 -07003028 if (!dvmInstanceof(bufObj->clazz,
3029 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003030 {
The Android Open Source Project99409882009-03-18 22:20:24 -07003031 goto bail;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003032 }
3033
Andy McFadden72d61fb2009-06-24 16:56:06 -07003034 /*
Andy McFadden8e5c7842009-07-23 17:47:18 -07003035 * Get a PlatformAddress object with the effective address.
Andy McFadden5f612b82009-07-22 15:07:27 -07003036 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003037 * If this isn't a direct buffer, the result will be NULL and/or an
Andy McFadden72d61fb2009-06-24 16:56:06 -07003038 * exception will have been thrown.
3039 */
Andy McFadden8e696dc2009-07-24 15:28:16 -07003040 JValue callResult;
Andy McFadden8e5c7842009-07-23 17:47:18 -07003041 const Method* meth = dvmGetVirtualizedMethod(bufObj->clazz,
3042 gDvm.methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress);
3043 dvmCallMethodA(self, meth, bufObj, &callResult, NULL);
3044 if (dvmGetException(self) != NULL) {
3045 dvmClearException(self);
3046 callResult.l = NULL;
Andy McFadden72d61fb2009-06-24 16:56:06 -07003047 }
Andy McFadden8e5c7842009-07-23 17:47:18 -07003048
Andy McFadden8e696dc2009-07-24 15:28:16 -07003049 Object* platformAddr = callResult.l;
Andy McFadden72d61fb2009-06-24 16:56:06 -07003050 if (platformAddr == NULL) {
Andy McFadden8e696dc2009-07-24 15:28:16 -07003051 LOGV("Got request for address of non-direct buffer\n");
Andy McFadden72d61fb2009-06-24 16:56:06 -07003052 goto bail;
3053 }
3054
Andy McFadden8e5c7842009-07-23 17:47:18 -07003055 /*
3056 * Extract the address from the PlatformAddress object. Instead of
3057 * calling the toLong() method, just grab the field directly. This
3058 * is faster but more fragile.
3059 */
3060 result = (void*) dvmGetFieldInt(platformAddr,
3061 gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr);
The Android Open Source Project99409882009-03-18 22:20:24 -07003062
Andy McFadden8e696dc2009-07-24 15:28:16 -07003063 //LOGI("slow path for %p --> %p\n", buf, result);
3064
The Android Open Source Project99409882009-03-18 22:20:24 -07003065bail:
Andy McFadden8e5c7842009-07-23 17:47:18 -07003066 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07003067 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003068}
3069
3070/*
3071 * Get the capacity of the buffer for the specified java.nio.Buffer.
3072 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003073 * Returns -1 if the object is not a direct buffer. (We actually skip
3074 * this check, since it's expensive to determine, and just return the
3075 * capacity regardless.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003076 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003077static jlong GetDirectBufferCapacity(JNIEnv* env, jobject jbuf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003078{
Andy McFadden8e5c7842009-07-23 17:47:18 -07003079 JNI_ENTER();
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003080
Andy McFadden8e5c7842009-07-23 17:47:18 -07003081 /*
3082 * The capacity is always in the Buffer.capacity field.
3083 *
3084 * (The "check" version should verify that this is actually a Buffer,
3085 * but we're not required to do so here.)
3086 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003087 Object* buf = dvmDecodeIndirectRef(env, jbuf);
3088 jlong result = dvmGetFieldInt(buf, gDvm.offJavaNioBuffer_capacity);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003089
Andy McFadden8e5c7842009-07-23 17:47:18 -07003090 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07003091 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003092}
3093
3094
3095/*
3096 * ===========================================================================
3097 * JNI invocation functions
3098 * ===========================================================================
3099 */
3100
3101/*
3102 * Handle AttachCurrentThread{AsDaemon}.
3103 *
3104 * We need to make sure the VM is actually running. For example, if we start
3105 * up, issue an Attach, and the VM exits almost immediately, by the time the
3106 * attaching happens the VM could already be shutting down.
3107 *
3108 * It's hard to avoid a race condition here because we don't want to hold
3109 * a lock across the entire operation. What we can do is temporarily
3110 * increment the thread count to prevent a VM exit.
3111 *
3112 * This could potentially still have problems if a daemon thread calls here
3113 * while the VM is shutting down. dvmThreadSelf() will work, since it just
3114 * uses pthread TLS, but dereferencing "vm" could fail. Such is life when
3115 * you shut down a VM while threads are still running inside it.
3116 *
3117 * Remember that some code may call this as a way to find the per-thread
3118 * JNIEnv pointer. Don't do excess work for that case.
3119 */
3120static jint attachThread(JavaVM* vm, JNIEnv** p_env, void* thr_args,
3121 bool isDaemon)
3122{
3123 JavaVMAttachArgs* args = (JavaVMAttachArgs*) thr_args;
3124 Thread* self;
3125 bool result = false;
3126
3127 /*
3128 * Return immediately if we're already one with the VM.
3129 */
3130 self = dvmThreadSelf();
3131 if (self != NULL) {
3132 *p_env = self->jniEnv;
3133 return JNI_OK;
3134 }
3135
3136 /*
3137 * No threads allowed in zygote mode.
3138 */
3139 if (gDvm.zygote) {
3140 return JNI_ERR;
3141 }
3142
3143 /* increment the count to keep the VM from bailing while we run */
3144 dvmLockThreadList(NULL);
3145 if (gDvm.nonDaemonThreadCount == 0) {
3146 // dead or dying
3147 LOGV("Refusing to attach thread '%s' -- VM is shutting down\n",
3148 (thr_args == NULL) ? "(unknown)" : args->name);
3149 dvmUnlockThreadList();
3150 return JNI_ERR;
3151 }
3152 gDvm.nonDaemonThreadCount++;
3153 dvmUnlockThreadList();
3154
3155 /* tweak the JavaVMAttachArgs as needed */
3156 JavaVMAttachArgs argsCopy;
3157 if (args == NULL) {
3158 /* allow the v1.1 calling convention */
3159 argsCopy.version = JNI_VERSION_1_2;
3160 argsCopy.name = NULL;
3161 argsCopy.group = dvmGetMainThreadGroup();
3162 } else {
3163 assert(args->version >= JNI_VERSION_1_2);
3164
3165 argsCopy.version = args->version;
3166 argsCopy.name = args->name;
3167 if (args->group != NULL)
3168 argsCopy.group = args->group;
3169 else
3170 argsCopy.group = dvmGetMainThreadGroup();
3171 }
3172
3173 result = dvmAttachCurrentThread(&argsCopy, isDaemon);
3174
3175 /* restore the count */
3176 dvmLockThreadList(NULL);
3177 gDvm.nonDaemonThreadCount--;
3178 dvmUnlockThreadList();
3179
3180 /*
3181 * Change the status to indicate that we're out in native code. This
3182 * call is not guarded with state-change macros, so we have to do it
3183 * by hand.
3184 */
3185 if (result) {
3186 self = dvmThreadSelf();
3187 assert(self != NULL);
3188 dvmChangeStatus(self, THREAD_NATIVE);
3189 *p_env = self->jniEnv;
3190 return JNI_OK;
3191 } else {
3192 return JNI_ERR;
3193 }
3194}
3195
3196/*
3197 * Attach the current thread to the VM. If the thread is already attached,
3198 * this is a no-op.
3199 */
3200static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args)
3201{
3202 return attachThread(vm, p_env, thr_args, false);
3203}
3204
3205/*
3206 * Like AttachCurrentThread, but set the "daemon" flag.
3207 */
3208static jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env,
3209 void* thr_args)
3210{
3211 return attachThread(vm, p_env, thr_args, true);
3212}
3213
3214/*
3215 * Dissociate the current thread from the VM.
3216 */
3217static jint DetachCurrentThread(JavaVM* vm)
3218{
3219 Thread* self = dvmThreadSelf();
3220
3221 if (self == NULL) /* not attached, can't do anything */
3222 return JNI_ERR;
3223
3224 /* switch to "running" to check for suspension */
3225 dvmChangeStatus(self, THREAD_RUNNING);
3226
3227 /* detach the thread */
3228 dvmDetachCurrentThread();
3229
3230 /* (no need to change status back -- we have no status) */
3231 return JNI_OK;
3232}
3233
3234/*
3235 * If current thread is attached to VM, return the associated JNIEnv.
3236 * Otherwise, stuff NULL in and return JNI_EDETACHED.
3237 *
3238 * JVMTI overloads this by specifying a magic value for "version", so we
3239 * do want to check that here.
3240 */
3241static jint GetEnv(JavaVM* vm, void** env, jint version)
3242{
3243 Thread* self = dvmThreadSelf();
3244
3245 if (version < JNI_VERSION_1_1 || version > JNI_VERSION_1_6)
3246 return JNI_EVERSION;
3247
3248 if (self == NULL) {
3249 *env = NULL;
3250 } else {
3251 /* TODO: status change is probably unnecessary */
3252 dvmChangeStatus(self, THREAD_RUNNING);
3253 *env = (void*) dvmGetThreadJNIEnv(self);
3254 dvmChangeStatus(self, THREAD_NATIVE);
3255 }
3256 if (*env == NULL)
3257 return JNI_EDETACHED;
3258 else
3259 return JNI_OK;
3260}
3261
3262/*
3263 * Destroy the VM. This may be called from any thread.
3264 *
3265 * If the current thread is attached, wait until the current thread is
3266 * the only non-daemon user-level thread. If the current thread is not
3267 * attached, we attach it and do the processing as usual. (If the attach
3268 * fails, it's probably because all the non-daemon threads have already
3269 * exited and the VM doesn't want to let us back in.)
3270 *
3271 * TODO: we don't really deal with the situation where more than one thread
3272 * has called here. One thread wins, the other stays trapped waiting on
3273 * the condition variable forever. Not sure this situation is interesting
3274 * in real life.
3275 */
3276static jint DestroyJavaVM(JavaVM* vm)
3277{
3278 JavaVMExt* ext = (JavaVMExt*) vm;
3279 Thread* self;
3280
3281 if (ext == NULL)
3282 return JNI_ERR;
3283
3284 LOGD("DestroyJavaVM waiting for non-daemon threads to exit\n");
3285
3286 /*
3287 * Sleep on a condition variable until it's okay to exit.
3288 */
3289 self = dvmThreadSelf();
3290 if (self == NULL) {
3291 JNIEnv* tmpEnv;
3292 if (AttachCurrentThread(vm, &tmpEnv, NULL) != JNI_OK) {
3293 LOGV("Unable to reattach main for Destroy; assuming VM is "
3294 "shutting down (count=%d)\n",
3295 gDvm.nonDaemonThreadCount);
3296 goto shutdown;
3297 } else {
3298 LOGV("Attached to wait for shutdown in Destroy\n");
3299 }
3300 }
3301 dvmChangeStatus(self, THREAD_VMWAIT);
3302
3303 dvmLockThreadList(self);
3304 gDvm.nonDaemonThreadCount--; // remove current thread from count
3305
3306 while (gDvm.nonDaemonThreadCount > 0)
3307 pthread_cond_wait(&gDvm.vmExitCond, &gDvm.threadListLock);
3308
3309 dvmUnlockThreadList();
3310 self = NULL;
3311
3312shutdown:
3313 // TODO: call System.exit() to run any registered shutdown hooks
3314 // (this may not return -- figure out how this should work)
3315
3316 LOGD("DestroyJavaVM shutting VM down\n");
3317 dvmShutdown();
3318
3319 // TODO - free resources associated with JNI-attached daemon threads
3320 free(ext->envList);
3321 free(ext);
3322
3323 return JNI_OK;
3324}
3325
3326
3327/*
3328 * ===========================================================================
3329 * Function tables
3330 * ===========================================================================
3331 */
3332
3333static const struct JNINativeInterface gNativeInterface = {
3334 NULL,
3335 NULL,
3336 NULL,
3337 NULL,
3338
3339 GetVersion,
3340
3341 DefineClass,
3342 FindClass,
3343
3344 FromReflectedMethod,
3345 FromReflectedField,
3346 ToReflectedMethod,
3347
3348 GetSuperclass,
3349 IsAssignableFrom,
3350
3351 ToReflectedField,
3352
3353 Throw,
3354 ThrowNew,
3355 ExceptionOccurred,
3356 ExceptionDescribe,
3357 ExceptionClear,
3358 FatalError,
3359
3360 PushLocalFrame,
3361 PopLocalFrame,
3362
3363 NewGlobalRef,
3364 DeleteGlobalRef,
3365 DeleteLocalRef,
3366 IsSameObject,
3367 NewLocalRef,
3368 EnsureLocalCapacity,
3369
3370 AllocObject,
3371 NewObject,
3372 NewObjectV,
3373 NewObjectA,
3374
3375 GetObjectClass,
3376 IsInstanceOf,
3377
3378 GetMethodID,
3379
3380 CallObjectMethod,
3381 CallObjectMethodV,
3382 CallObjectMethodA,
3383 CallBooleanMethod,
3384 CallBooleanMethodV,
3385 CallBooleanMethodA,
3386 CallByteMethod,
3387 CallByteMethodV,
3388 CallByteMethodA,
3389 CallCharMethod,
3390 CallCharMethodV,
3391 CallCharMethodA,
3392 CallShortMethod,
3393 CallShortMethodV,
3394 CallShortMethodA,
3395 CallIntMethod,
3396 CallIntMethodV,
3397 CallIntMethodA,
3398 CallLongMethod,
3399 CallLongMethodV,
3400 CallLongMethodA,
3401 CallFloatMethod,
3402 CallFloatMethodV,
3403 CallFloatMethodA,
3404 CallDoubleMethod,
3405 CallDoubleMethodV,
3406 CallDoubleMethodA,
3407 CallVoidMethod,
3408 CallVoidMethodV,
3409 CallVoidMethodA,
3410
3411 CallNonvirtualObjectMethod,
3412 CallNonvirtualObjectMethodV,
3413 CallNonvirtualObjectMethodA,
3414 CallNonvirtualBooleanMethod,
3415 CallNonvirtualBooleanMethodV,
3416 CallNonvirtualBooleanMethodA,
3417 CallNonvirtualByteMethod,
3418 CallNonvirtualByteMethodV,
3419 CallNonvirtualByteMethodA,
3420 CallNonvirtualCharMethod,
3421 CallNonvirtualCharMethodV,
3422 CallNonvirtualCharMethodA,
3423 CallNonvirtualShortMethod,
3424 CallNonvirtualShortMethodV,
3425 CallNonvirtualShortMethodA,
3426 CallNonvirtualIntMethod,
3427 CallNonvirtualIntMethodV,
3428 CallNonvirtualIntMethodA,
3429 CallNonvirtualLongMethod,
3430 CallNonvirtualLongMethodV,
3431 CallNonvirtualLongMethodA,
3432 CallNonvirtualFloatMethod,
3433 CallNonvirtualFloatMethodV,
3434 CallNonvirtualFloatMethodA,
3435 CallNonvirtualDoubleMethod,
3436 CallNonvirtualDoubleMethodV,
3437 CallNonvirtualDoubleMethodA,
3438 CallNonvirtualVoidMethod,
3439 CallNonvirtualVoidMethodV,
3440 CallNonvirtualVoidMethodA,
3441
3442 GetFieldID,
3443
3444 GetObjectField,
3445 GetBooleanField,
3446 GetByteField,
3447 GetCharField,
3448 GetShortField,
3449 GetIntField,
3450 GetLongField,
3451 GetFloatField,
3452 GetDoubleField,
3453 SetObjectField,
3454 SetBooleanField,
3455 SetByteField,
3456 SetCharField,
3457 SetShortField,
3458 SetIntField,
3459 SetLongField,
3460 SetFloatField,
3461 SetDoubleField,
3462
3463 GetStaticMethodID,
3464
3465 CallStaticObjectMethod,
3466 CallStaticObjectMethodV,
3467 CallStaticObjectMethodA,
3468 CallStaticBooleanMethod,
3469 CallStaticBooleanMethodV,
3470 CallStaticBooleanMethodA,
3471 CallStaticByteMethod,
3472 CallStaticByteMethodV,
3473 CallStaticByteMethodA,
3474 CallStaticCharMethod,
3475 CallStaticCharMethodV,
3476 CallStaticCharMethodA,
3477 CallStaticShortMethod,
3478 CallStaticShortMethodV,
3479 CallStaticShortMethodA,
3480 CallStaticIntMethod,
3481 CallStaticIntMethodV,
3482 CallStaticIntMethodA,
3483 CallStaticLongMethod,
3484 CallStaticLongMethodV,
3485 CallStaticLongMethodA,
3486 CallStaticFloatMethod,
3487 CallStaticFloatMethodV,
3488 CallStaticFloatMethodA,
3489 CallStaticDoubleMethod,
3490 CallStaticDoubleMethodV,
3491 CallStaticDoubleMethodA,
3492 CallStaticVoidMethod,
3493 CallStaticVoidMethodV,
3494 CallStaticVoidMethodA,
3495
3496 GetStaticFieldID,
3497
3498 GetStaticObjectField,
3499 GetStaticBooleanField,
3500 GetStaticByteField,
3501 GetStaticCharField,
3502 GetStaticShortField,
3503 GetStaticIntField,
3504 GetStaticLongField,
3505 GetStaticFloatField,
3506 GetStaticDoubleField,
3507
3508 SetStaticObjectField,
3509 SetStaticBooleanField,
3510 SetStaticByteField,
3511 SetStaticCharField,
3512 SetStaticShortField,
3513 SetStaticIntField,
3514 SetStaticLongField,
3515 SetStaticFloatField,
3516 SetStaticDoubleField,
3517
3518 NewString,
3519
3520 GetStringLength,
3521 GetStringChars,
3522 ReleaseStringChars,
3523
3524 NewStringUTF,
3525 GetStringUTFLength,
3526 GetStringUTFChars,
3527 ReleaseStringUTFChars,
3528
3529 GetArrayLength,
3530 NewObjectArray,
3531 GetObjectArrayElement,
3532 SetObjectArrayElement,
3533
3534 NewBooleanArray,
3535 NewByteArray,
3536 NewCharArray,
3537 NewShortArray,
3538 NewIntArray,
3539 NewLongArray,
3540 NewFloatArray,
3541 NewDoubleArray,
3542
3543 GetBooleanArrayElements,
3544 GetByteArrayElements,
3545 GetCharArrayElements,
3546 GetShortArrayElements,
3547 GetIntArrayElements,
3548 GetLongArrayElements,
3549 GetFloatArrayElements,
3550 GetDoubleArrayElements,
3551
3552 ReleaseBooleanArrayElements,
3553 ReleaseByteArrayElements,
3554 ReleaseCharArrayElements,
3555 ReleaseShortArrayElements,
3556 ReleaseIntArrayElements,
3557 ReleaseLongArrayElements,
3558 ReleaseFloatArrayElements,
3559 ReleaseDoubleArrayElements,
3560
3561 GetBooleanArrayRegion,
3562 GetByteArrayRegion,
3563 GetCharArrayRegion,
3564 GetShortArrayRegion,
3565 GetIntArrayRegion,
3566 GetLongArrayRegion,
3567 GetFloatArrayRegion,
3568 GetDoubleArrayRegion,
3569 SetBooleanArrayRegion,
3570 SetByteArrayRegion,
3571 SetCharArrayRegion,
3572 SetShortArrayRegion,
3573 SetIntArrayRegion,
3574 SetLongArrayRegion,
3575 SetFloatArrayRegion,
3576 SetDoubleArrayRegion,
3577
3578 RegisterNatives,
3579 UnregisterNatives,
3580
3581 MonitorEnter,
3582 MonitorExit,
3583
3584 GetJavaVM,
3585
3586 GetStringRegion,
3587 GetStringUTFRegion,
3588
3589 GetPrimitiveArrayCritical,
3590 ReleasePrimitiveArrayCritical,
3591
3592 GetStringCritical,
3593 ReleaseStringCritical,
3594
3595 NewWeakGlobalRef,
3596 DeleteWeakGlobalRef,
3597
3598 ExceptionCheck,
3599
3600 NewDirectByteBuffer,
3601 GetDirectBufferAddress,
3602 GetDirectBufferCapacity,
3603
3604 GetObjectRefType
3605};
3606static const struct JNIInvokeInterface gInvokeInterface = {
3607 NULL,
3608 NULL,
3609 NULL,
3610
3611 DestroyJavaVM,
3612 AttachCurrentThread,
3613 DetachCurrentThread,
3614
3615 GetEnv,
3616
3617 AttachCurrentThreadAsDaemon,
3618};
3619
3620
3621/*
3622 * ===========================================================================
3623 * VM/Env creation
3624 * ===========================================================================
3625 */
3626
3627/*
3628 * Enable "checked JNI" after the VM has partially started. This must
3629 * only be called in "zygote" mode, when we have one thread running.
Andy McFadden59b61772009-05-13 16:44:34 -07003630 *
3631 * This doesn't attempt to rewrite the JNI call bridge associated with
3632 * native methods, so we won't get those checks for any methods that have
3633 * already been resolved.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003634 */
3635void dvmLateEnableCheckedJni(void)
3636{
3637 JNIEnvExt* extEnv;
3638 JavaVMExt* extVm;
Andy McFaddenab00d452009-08-19 07:21:41 -07003639
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003640 extEnv = dvmGetJNIEnvForThread();
3641 if (extEnv == NULL) {
3642 LOGE("dvmLateEnableCheckedJni: thread has no JNIEnv\n");
3643 return;
3644 }
3645 extVm = extEnv->vm;
3646 assert(extVm != NULL);
3647
3648 if (!extVm->useChecked) {
3649 LOGD("Late-enabling CheckJNI\n");
3650 dvmUseCheckedJniVm(extVm);
3651 extVm->useChecked = true;
3652 dvmUseCheckedJniEnv(extEnv);
3653
3654 /* currently no way to pick up jniopts features */
3655 } else {
3656 LOGD("Not late-enabling CheckJNI (already on)\n");
3657 }
3658}
3659
3660/*
3661 * Not supported.
3662 */
3663jint JNI_GetDefaultJavaVMInitArgs(void* vm_args)
3664{
3665 return JNI_ERR;
3666}
3667
3668/*
3669 * Return a buffer full of created VMs.
3670 *
3671 * We always have zero or one.
3672 */
3673jint JNI_GetCreatedJavaVMs(JavaVM** vmBuf, jsize bufLen, jsize* nVMs)
3674{
3675 if (gDvm.vmList != NULL) {
3676 *nVMs = 1;
3677
3678 if (bufLen > 0)
3679 *vmBuf++ = gDvm.vmList;
3680 } else {
3681 *nVMs = 0;
3682 }
3683
3684 return JNI_OK;
3685}
3686
3687
3688/*
3689 * Create a new VM instance.
3690 *
3691 * The current thread becomes the main VM thread. We return immediately,
3692 * which effectively means the caller is executing in a native method.
3693 */
3694jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args)
3695{
3696 const JavaVMInitArgs* args = (JavaVMInitArgs*) vm_args;
3697 JNIEnvExt* pEnv = NULL;
3698 JavaVMExt* pVM = NULL;
3699 const char** argv;
3700 int argc = 0;
3701 int i, curOpt;
3702 int result = JNI_ERR;
3703 bool checkJni = false;
3704 bool warnError = true;
3705 bool forceDataCopy = false;
3706
3707 if (args->version < JNI_VERSION_1_2)
3708 return JNI_EVERSION;
3709
3710 // TODO: don't allow creation of multiple VMs -- one per customer for now
3711
3712 /* zero globals; not strictly necessary the first time a VM is started */
3713 memset(&gDvm, 0, sizeof(gDvm));
3714
3715 /*
3716 * Set up structures for JNIEnv and VM.
3717 */
3718 //pEnv = (JNIEnvExt*) malloc(sizeof(JNIEnvExt));
3719 pVM = (JavaVMExt*) malloc(sizeof(JavaVMExt));
3720
3721 //memset(pEnv, 0, sizeof(JNIEnvExt));
3722 //pEnv->funcTable = &gNativeInterface;
3723 //pEnv->vm = pVM;
3724 memset(pVM, 0, sizeof(JavaVMExt));
3725 pVM->funcTable = &gInvokeInterface;
3726 pVM->envList = pEnv;
3727 dvmInitMutex(&pVM->envListLock);
3728
3729 argv = (const char**) malloc(sizeof(char*) * (args->nOptions));
3730 memset(argv, 0, sizeof(char*) * (args->nOptions));
3731
3732 curOpt = 0;
3733
3734 /*
3735 * Convert JNI args to argv.
3736 *
3737 * We have to pull out vfprintf/exit/abort, because they use the
3738 * "extraInfo" field to pass function pointer "hooks" in. We also
3739 * look for the -Xcheck:jni stuff here.
3740 */
3741 for (i = 0; i < args->nOptions; i++) {
3742 const char* optStr = args->options[i].optionString;
3743
3744 if (optStr == NULL) {
3745 fprintf(stderr, "ERROR: arg %d string was null\n", i);
3746 goto bail;
3747 } else if (strcmp(optStr, "vfprintf") == 0) {
3748 gDvm.vfprintfHook = args->options[i].extraInfo;
3749 } else if (strcmp(optStr, "exit") == 0) {
3750 gDvm.exitHook = args->options[i].extraInfo;
3751 } else if (strcmp(optStr, "abort") == 0) {
3752 gDvm.abortHook = args->options[i].extraInfo;
3753 } else if (strcmp(optStr, "-Xcheck:jni") == 0) {
3754 checkJni = true;
3755 } else if (strncmp(optStr, "-Xjniopts:", 10) == 0) {
3756 const char* jniOpts = optStr + 9;
3757 while (jniOpts != NULL) {
3758 jniOpts++; /* skip past ':' or ',' */
3759 if (strncmp(jniOpts, "warnonly", 8) == 0) {
3760 warnError = false;
3761 } else if (strncmp(jniOpts, "forcecopy", 9) == 0) {
3762 forceDataCopy = true;
3763 } else {
3764 LOGW("unknown jni opt starting at '%s'\n", jniOpts);
3765 }
3766 jniOpts = strchr(jniOpts, ',');
3767 }
3768 } else {
3769 /* regular option */
3770 argv[curOpt++] = optStr;
3771 }
3772 }
3773 argc = curOpt;
3774
3775 if (checkJni) {
3776 dvmUseCheckedJniVm(pVM);
3777 pVM->useChecked = true;
3778 }
3779 pVM->warnError = warnError;
3780 pVM->forceDataCopy = forceDataCopy;
3781
3782 /* set this up before initializing VM, so it can create some JNIEnvs */
3783 gDvm.vmList = (JavaVM*) pVM;
3784
3785 /*
3786 * Create an env for main thread. We need to have something set up
3787 * here because some of the class initialization we do when starting
3788 * up the VM will call into native code.
3789 */
3790 pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL);
3791
3792 /* initialize VM */
3793 gDvm.initializing = true;
3794 if (dvmStartup(argc, argv, args->ignoreUnrecognized, (JNIEnv*)pEnv) != 0) {
3795 free(pEnv);
3796 free(pVM);
3797 goto bail;
3798 }
3799
3800 /*
3801 * Success! Return stuff to caller.
3802 */
3803 dvmChangeStatus(NULL, THREAD_NATIVE);
3804 *p_env = (JNIEnv*) pEnv;
3805 *p_vm = (JavaVM*) pVM;
3806 result = JNI_OK;
3807
3808bail:
3809 gDvm.initializing = false;
3810 if (result == JNI_OK)
3811 LOGV("JNI_CreateJavaVM succeeded\n");
3812 else
3813 LOGW("JNI_CreateJavaVM failed\n");
3814 free(argv);
3815 return result;
3816}
3817