blob: 0a8103b890006a2d48ec9558b0aa33091cb9860e [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
352
353#define kTrackGrefUsage true
354
355/*
Andy McFadden5f612b82009-07-22 15:07:27 -0700356 * Allocate the global references table, and look up some classes for
357 * the benefit of direct buffer access.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800358 */
359bool dvmJniStartup(void)
360{
361 if (!dvmInitReferenceTable(&gDvm.jniGlobalRefTable,
362 kGlobalRefsTableInitialSize, kGlobalRefsTableMaxSize))
363 return false;
364
365 dvmInitMutex(&gDvm.jniGlobalRefLock);
366
367 gDvm.jniGlobalRefLoMark = 0;
368 gDvm.jniGlobalRefHiMark = kGrefWaterInterval * 2;
369
Andy McFadden8e5c7842009-07-23 17:47:18 -0700370 /*
371 * Look up and cache pointers to some direct buffer classes, fields,
372 * and methods.
373 */
374 Method* meth;
375
Andy McFadden5f612b82009-07-22 15:07:27 -0700376 ClassObject* platformAddressClass =
377 dvmFindSystemClassNoInit("Lorg/apache/harmony/luni/platform/PlatformAddress;");
Andy McFadden8e5c7842009-07-23 17:47:18 -0700378 ClassObject* platformAddressFactoryClass =
379 dvmFindSystemClassNoInit("Lorg/apache/harmony/luni/platform/PlatformAddressFactory;");
Andy McFadden5f612b82009-07-22 15:07:27 -0700380 ClassObject* directBufferClass =
381 dvmFindSystemClassNoInit("Lorg/apache/harmony/nio/internal/DirectBuffer;");
Andy McFadden8e5c7842009-07-23 17:47:18 -0700382 ClassObject* readWriteBufferClass =
383 dvmFindSystemClassNoInit("Ljava/nio/ReadWriteDirectByteBuffer;");
384 ClassObject* bufferClass =
385 dvmFindSystemClassNoInit("Ljava/nio/Buffer;");
386
387 if (platformAddressClass == NULL || platformAddressFactoryClass == NULL ||
388 directBufferClass == NULL || readWriteBufferClass == NULL ||
389 bufferClass == NULL)
390 {
Andy McFadden5f612b82009-07-22 15:07:27 -0700391 LOGE("Unable to find internal direct buffer classes\n");
392 return false;
393 }
394 /* needs to be a global ref so CheckJNI thinks we're allowed to see it */
395 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer =
396 addGlobalReference((Object*) directBufferClass);
Andy McFadden8e5c7842009-07-23 17:47:18 -0700397 gDvm.classJavaNioReadWriteDirectByteBuffer = readWriteBufferClass;
Andy McFadden5f612b82009-07-22 15:07:27 -0700398
Andy McFadden8e5c7842009-07-23 17:47:18 -0700399 /*
400 * We need a Method* here rather than a vtable offset, because
401 * DirectBuffer is an interface class.
402 */
Andy McFadden5f612b82009-07-22 15:07:27 -0700403 meth = dvmFindVirtualMethodByDescriptor(
404 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer,
405 "getEffectiveAddress",
406 "()Lorg/apache/harmony/luni/platform/PlatformAddress;");
407 if (meth == NULL) {
408 LOGE("Unable to find PlatformAddress.getEffectiveAddress\n");
409 return false;
410 }
411 gDvm.methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress = meth;
412
413 meth = dvmFindVirtualMethodByDescriptor(platformAddressClass,
414 "toLong", "()J");
415 if (meth == NULL) {
416 LOGE("Unable to find PlatformAddress.toLong\n");
417 return false;
418 }
Andy McFadden8e5c7842009-07-23 17:47:18 -0700419 gDvm.voffOrgApacheHarmonyLuniPlatformPlatformAddress_toLong =
420 meth->methodIndex;
421
422 meth = dvmFindDirectMethodByDescriptor(platformAddressFactoryClass,
423 "on",
424 "(I)Lorg/apache/harmony/luni/platform/PlatformAddress;");
425 if (meth == NULL) {
426 LOGE("Unable to find PlatformAddressFactory.on\n");
427 return false;
428 }
429 gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on = meth;
430
431 meth = dvmFindDirectMethodByDescriptor(readWriteBufferClass,
432 "<init>",
433 "(Lorg/apache/harmony/luni/platform/PlatformAddress;II)V");
434 if (meth == NULL) {
435 LOGE("Unable to find ReadWriteDirectByteBuffer.<init>\n");
436 return false;
437 }
438 gDvm.methJavaNioReadWriteDirectByteBuffer_init = meth;
439
440 gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr =
441 dvmFindFieldOffset(platformAddressClass, "osaddr", "I");
442 if (gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr < 0) {
443 LOGE("Unable to find PlatformAddress.osaddr\n");
444 return false;
445 }
446
447 gDvm.offJavaNioBuffer_capacity =
448 dvmFindFieldOffset(bufferClass, "capacity", "I");
449 if (gDvm.offJavaNioBuffer_capacity < 0) {
450 LOGE("Unable to find Buffer.capacity\n");
451 return false;
452 }
Andy McFadden5f612b82009-07-22 15:07:27 -0700453
Andy McFadden8e696dc2009-07-24 15:28:16 -0700454 gDvm.offJavaNioBuffer_effectiveDirectAddress =
455 dvmFindFieldOffset(bufferClass, "effectiveDirectAddress", "I");
456 if (gDvm.offJavaNioBuffer_effectiveDirectAddress < 0) {
457 LOGE("Unable to find Buffer.effectiveDirectAddress\n");
458 return false;
459 }
460
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800461 return true;
462}
463
464/*
465 * Free the global references table.
466 */
467void dvmJniShutdown(void)
468{
469 dvmClearReferenceTable(&gDvm.jniGlobalRefTable);
470}
471
472
473/*
474 * Find the JNIEnv associated with the current thread.
475 *
476 * Currently stored in the Thread struct. Could also just drop this into
477 * thread-local storage.
478 */
479JNIEnvExt* dvmGetJNIEnvForThread(void)
480{
481 Thread* self = dvmThreadSelf();
482 if (self == NULL)
483 return NULL;
484 return (JNIEnvExt*) dvmGetThreadJNIEnv(self);
485}
486
487/*
488 * Create a new JNIEnv struct and add it to the VM's list.
489 *
490 * "self" will be NULL for the main thread, since the VM hasn't started
491 * yet; the value will be filled in later.
492 */
493JNIEnv* dvmCreateJNIEnv(Thread* self)
494{
495 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
496 JNIEnvExt* newEnv;
497
498 //if (self != NULL)
499 // LOGI("Ent CreateJNIEnv: threadid=%d %p\n", self->threadId, self);
500
501 assert(vm != NULL);
502
503 newEnv = (JNIEnvExt*) calloc(1, sizeof(JNIEnvExt));
504 newEnv->funcTable = &gNativeInterface;
505 newEnv->vm = vm;
506 newEnv->forceDataCopy = vm->forceDataCopy;
507 if (self != NULL) {
508 dvmSetJniEnvThreadId((JNIEnv*) newEnv, self);
509 assert(newEnv->envThreadId != 0);
510 } else {
511 /* make it obvious if we fail to initialize these later */
512 newEnv->envThreadId = 0x77777775;
513 newEnv->self = (Thread*) 0x77777779;
514 }
515 if (vm->useChecked)
516 dvmUseCheckedJniEnv(newEnv);
517
518 dvmLockMutex(&vm->envListLock);
519
520 /* insert at head of list */
521 newEnv->next = vm->envList;
522 assert(newEnv->prev == NULL);
523 if (vm->envList == NULL) // rare, but possible
524 vm->envList = newEnv;
525 else
526 vm->envList->prev = newEnv;
527 vm->envList = newEnv;
528
529 dvmUnlockMutex(&vm->envListLock);
530
531 //if (self != NULL)
532 // LOGI("Xit CreateJNIEnv: threadid=%d %p\n", self->threadId, self);
533 return (JNIEnv*) newEnv;
534}
535
536/*
537 * Remove a JNIEnv struct from the list and free it.
538 */
539void dvmDestroyJNIEnv(JNIEnv* env)
540{
541 JNIEnvExt* extEnv = (JNIEnvExt*) env;
542 JavaVMExt* vm = extEnv->vm;
543 Thread* self;
544
545 if (env == NULL)
546 return;
547
548 self = dvmThreadSelf();
549 assert(self != NULL);
550
551 //LOGI("Ent DestroyJNIEnv: threadid=%d %p\n", self->threadId, self);
552
553 dvmLockMutex(&vm->envListLock);
554
555 if (extEnv == vm->envList) {
556 assert(extEnv->prev == NULL);
557 vm->envList = extEnv->next;
558 } else {
559 assert(extEnv->prev != NULL);
560 extEnv->prev->next = extEnv->next;
561 }
562 if (extEnv->next != NULL)
563 extEnv->next->prev = extEnv->prev;
564
565 dvmUnlockMutex(&extEnv->vm->envListLock);
566
567 free(env);
568 //LOGI("Xit DestroyJNIEnv: threadid=%d %p\n", self->threadId, self);
569}
570
571
572/*
573 * Retrieve the ReferenceTable struct for the current thread.
574 *
Andy McFaddenab00d452009-08-19 07:21:41 -0700575 * Going through "env" rather than dvmThreadSelf() is faster but will
576 * get weird if the JNI code is passing the wrong JNIEnv around.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800577 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700578static inline ReferenceTable* getLocalRefTable(JNIEnv* env)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800579{
Andy McFaddenab00d452009-08-19 07:21:41 -0700580 //return &dvmThreadSelf()->jniLocalRefTable;
581 return &((JNIEnvExt*)env)->self->jniLocalRefTable;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800582}
583
584/*
585 * Add a local reference for an object to the current stack frame. When
586 * the native function returns, the reference will be discarded.
587 *
588 * We need to allow the same reference to be added multiple times.
589 *
590 * This will be called on otherwise unreferenced objects. We cannot do
591 * GC allocations here, and it's best if we don't grab a mutex.
592 *
593 * Returns the local reference (currently just the same pointer that was
594 * passed in), or NULL on failure.
595 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700596static jobject addLocalReference(JNIEnv* env, Object* obj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800597{
598 if (obj == NULL)
599 return NULL;
600
Andy McFaddenab00d452009-08-19 07:21:41 -0700601 ReferenceTable* pRefTable = getLocalRefTable(env);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800602
Andy McFaddenab00d452009-08-19 07:21:41 -0700603 if (!dvmAddToReferenceTable(pRefTable, obj)) {
604 dvmDumpReferenceTable(pRefTable, "JNI local");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800605 LOGE("Failed adding to JNI local ref table (has %d entries)\n",
Andy McFaddenab00d452009-08-19 07:21:41 -0700606 (int) dvmReferenceTableEntries(pRefTable));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800607 dvmDumpThread(dvmThreadSelf(), false);
608 dvmAbort(); // spec says call FatalError; this is equivalent
609 } else {
610 LOGVV("LREF add %p (%s.%s)\n", obj,
611 dvmGetCurrentJNIMethod()->clazz->descriptor,
612 dvmGetCurrentJNIMethod()->name);
613 }
614
615 return obj;
616}
617
618/*
Andy McFaddenab00d452009-08-19 07:21:41 -0700619 * Convert an indirect reference to an Object reference. The indirect
620 * reference may be local, global, or weak-global.
621 *
622 * If "jobj" is NULL or an invalid indirect reference, this returns NULL.
623 *
624 * [ this is currently a no-op ]
625 */
626Object* dvmDecodeIndirectRef(JNIEnv* env, jobject jobj)
627{
628 return (Object*) jobj;
629}
630
631/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800632 * Ensure that at least "capacity" references can be held in the local
633 * refs table of the current thread.
634 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700635static bool ensureLocalCapacity(JNIEnv* env, int capacity)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800636{
Andy McFaddenab00d452009-08-19 07:21:41 -0700637 ReferenceTable* pRefTable = getLocalRefTable(env);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800638
Andy McFaddenab00d452009-08-19 07:21:41 -0700639 return (kJniLocalRefMax - (pRefTable->nextEntry - pRefTable->table) >= capacity);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800640}
641
642/*
643 * Explicitly delete a reference from the local list.
644 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700645static void deleteLocalReference(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800646{
Andy McFaddenab00d452009-08-19 07:21:41 -0700647 if (jobj == NULL)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800648 return;
649
Andy McFaddenab00d452009-08-19 07:21:41 -0700650 ReferenceTable* pRefTable = getLocalRefTable(env);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800651 Thread* self = dvmThreadSelf();
652 Object** top = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefTop;
653
Andy McFaddenab00d452009-08-19 07:21:41 -0700654 if (!dvmRemoveFromReferenceTable(pRefTable, top, (Object*) jobj)) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800655 /*
656 * Attempting to delete a local reference that is not in the
657 * topmost local reference frame is a no-op. DeleteLocalRef returns
658 * void and doesn't throw any exceptions, but we should probably
659 * complain about it so the user will notice that things aren't
660 * going quite the way they expect.
661 */
662 LOGW("JNI WARNING: DeleteLocalRef(%p) failed to find entry (valid=%d)\n",
Andy McFaddenab00d452009-08-19 07:21:41 -0700663 jobj, dvmIsValidObject((Object*) jobj));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800664 }
665}
666
667/*
668 * Add a global reference for an object.
669 *
670 * We may add the same object more than once. Add/remove calls are paired,
671 * so it needs to appear on the list multiple times.
672 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700673static jobject addGlobalReference(Object* obj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800674{
675 if (obj == NULL)
676 return NULL;
677
678 //LOGI("adding obj=%p\n", obj);
679 //dvmDumpThread(dvmThreadSelf(), false);
680
681 if (false && ((Object*)obj)->clazz == gDvm.classJavaLangClass) {
682 ClassObject* clazz = (ClassObject*) obj;
683 LOGI("-------\n");
684 LOGI("Adding global ref on class %s\n", clazz->descriptor);
685 dvmDumpThread(dvmThreadSelf(), false);
686 }
687 if (false && ((Object*)obj)->clazz == gDvm.classJavaLangString) {
688 StringObject* strObj = (StringObject*) obj;
689 char* str = dvmCreateCstrFromString(strObj);
690 if (strcmp(str, "sync-response") == 0) {
691 LOGI("-------\n");
692 LOGI("Adding global ref on string '%s'\n", str);
693 dvmDumpThread(dvmThreadSelf(), false);
694 //dvmAbort();
695 }
696 free(str);
697 }
698 if (false && ((Object*)obj)->clazz == gDvm.classArrayByte) {
699 ArrayObject* arrayObj = (ArrayObject*) obj;
700 if (arrayObj->length == 8192 &&
701 dvmReferenceTableEntries(&gDvm.jniGlobalRefTable) > 400)
702 {
703 LOGI("Adding global ref on byte array %p (len=%d)\n",
704 arrayObj, arrayObj->length);
705 dvmDumpThread(dvmThreadSelf(), false);
706 }
707 }
708
709 dvmLockMutex(&gDvm.jniGlobalRefLock);
710
711 /*
712 * Expanding the table should happen rarely, so I'm not overly
713 * concerned about the performance impact of copying the old list
714 * over. We shouldn't see one-time activity spikes, so freeing
715 * up storage shouldn't be required.
716 *
717 * Throwing an exception on failure is problematic, because JNI code
718 * may not be expecting an exception, and things sort of cascade. We
719 * want to have a hard limit to catch leaks during debugging, but this
720 * otherwise needs to expand until memory is consumed. As a practical
721 * matter, if we have many thousands of global references, chances are
722 * we're either leaking global ref table entries or we're going to
723 * run out of space in the GC heap.
724 */
725 if (!dvmAddToReferenceTable(&gDvm.jniGlobalRefTable, (Object*)obj)) {
726 dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global");
727 LOGE("Failed adding to JNI global ref table (%d entries)\n",
728 (int) dvmReferenceTableEntries(&gDvm.jniGlobalRefTable));
729 dvmAbort();
730 }
731
732 LOGVV("GREF add %p (%s.%s)\n", obj,
733 dvmGetCurrentJNIMethod()->clazz->descriptor,
734 dvmGetCurrentJNIMethod()->name);
735
736 /* GREF usage tracking; should probably be disabled for production env */
737 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
738 int count = dvmReferenceTableEntries(&gDvm.jniGlobalRefTable);
739 if (count > gDvm.jniGlobalRefHiMark) {
740 LOGD("GREF has increased to %d\n", count);
741 gDvm.jniGlobalRefHiMark += kGrefWaterInterval;
742 gDvm.jniGlobalRefLoMark += kGrefWaterInterval;
743
744 /* watch for "excessive" use; not generally appropriate */
745 if (count >= gDvm.jniGrefLimit) {
746 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
747 if (vm->warnError) {
748 dvmDumpReferenceTable(&gDvm.jniGlobalRefTable,"JNI global");
749 LOGE("Excessive JNI global references (%d)\n", count);
750 dvmAbort();
751 } else {
752 LOGW("Excessive JNI global references (%d)\n", count);
753 }
754 }
755 }
756 }
757
758bail:
759 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
Andy McFaddenab00d452009-08-19 07:21:41 -0700760 return (jobject) obj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800761}
762
763/*
764 * Remove a global reference. In most cases it's the entry most recently
765 * added, which makes this pretty quick.
766 *
767 * Thought: if it's not the most recent entry, just null it out. When we
768 * fill up, do a compaction pass before we expand the list.
769 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700770static void deleteGlobalReference(jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800771{
Andy McFaddenab00d452009-08-19 07:21:41 -0700772 if (jobj == NULL)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800773 return;
774
775 dvmLockMutex(&gDvm.jniGlobalRefLock);
776
777 if (!dvmRemoveFromReferenceTable(&gDvm.jniGlobalRefTable,
Andy McFaddenab00d452009-08-19 07:21:41 -0700778 gDvm.jniGlobalRefTable.table, jobj))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800779 {
780 LOGW("JNI: DeleteGlobalRef(%p) failed to find entry (valid=%d)\n",
Andy McFaddenab00d452009-08-19 07:21:41 -0700781 jobj, dvmIsValidObject((Object*) jobj));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800782 goto bail;
783 }
784
785 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
786 int count = dvmReferenceTableEntries(&gDvm.jniGlobalRefTable);
787 if (count < gDvm.jniGlobalRefLoMark) {
788 LOGD("GREF has decreased to %d\n", count);
789 gDvm.jniGlobalRefHiMark -= kGrefWaterInterval;
790 gDvm.jniGlobalRefLoMark -= kGrefWaterInterval;
791 }
792 }
793
794bail:
795 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
796}
797
798/*
Andy McFaddenab00d452009-08-19 07:21:41 -0700799 * Objects don't currently move, so we just need to create a reference
800 * that will ensure the array object isn't collected.
801 *
802 * Currently just using global references.
803 *
804 * TODO: if the same array gets pinned more than N times, print a warning
805 * (but only if CheckJNI is enabled)
806 */
807static void pinPrimitiveArray(ArrayObject* arrayObj)
808{
809 (void) addGlobalReference((Object*) arrayObj);
810}
811
812/*
813 * Un-pin the array object. If an object was pinned twice, it must be
814 * unpinned twice before it's free to move.
815 *
816 * Currently just removing a global reference.
817 */
818static void unpinPrimitiveArray(ArrayObject* arrayObj)
819{
820 (void) deleteGlobalReference((jobject) arrayObj);
821}
822
823/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800824 * GC helper function to mark all JNI global references.
825 */
826void dvmGcMarkJniGlobalRefs()
827{
828 Object **op;
829
830 dvmLockMutex(&gDvm.jniGlobalRefLock);
831
832 op = gDvm.jniGlobalRefTable.table;
833 while ((uintptr_t)op < (uintptr_t)gDvm.jniGlobalRefTable.nextEntry) {
834 dvmMarkObjectNonNull(*(op++));
835 }
836
837 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
838}
839
840
841/*
842 * Determine if "obj" appears in the argument list for the native method.
843 *
844 * We use the "shorty" signature to determine which argument slots hold
845 * reference types.
846 */
847static bool findInArgList(Thread* self, Object* obj)
848{
849 const Method* meth;
850 u4* fp;
851 int i;
852
853 fp = self->curFrame;
854 while (1) {
855 /*
856 * Back up over JNI PushLocalFrame frames. This works because the
857 * previous frame on the interpreted stack is either a break frame
858 * (if we called here via native code) or an interpreted method (if
859 * we called here via the interpreter). In both cases the method
860 * pointer won't match.
861 */
862 StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
863 meth = saveArea->method;
864 if (meth != SAVEAREA_FROM_FP(saveArea->prevFrame)->method)
865 break;
866 fp = saveArea->prevFrame;
867 }
868
869 LOGVV("+++ scanning %d args in %s (%s)\n",
870 meth->insSize, meth->name, meth->shorty);
871 const char* shorty = meth->shorty +1; /* skip return type char */
872 for (i = 0; i < meth->insSize; i++) {
873 if (i == 0 && !dvmIsStaticMethod(meth)) {
874 /* first arg is "this" ref, not represented in "shorty" */
875 if (fp[i] == (u4) obj)
876 return true;
877 } else {
878 /* if this is a reference type, see if it matches */
879 switch (*shorty) {
880 case 'L':
881 if (fp[i] == (u4) obj)
882 return true;
883 break;
884 case 'D':
885 case 'J':
886 i++;
887 break;
888 case '\0':
889 LOGE("Whoops! ran off the end of %s (%d)\n",
890 meth->shorty, meth->insSize);
891 break;
892 default:
893 if (fp[i] == (u4) obj)
894 LOGI("NOTE: ref %p match on arg type %c\n", obj, *shorty);
895 break;
896 }
897 shorty++;
898 }
899 }
900
901 /*
902 * For static methods, we also pass a class pointer in.
903 */
904 if (dvmIsStaticMethod(meth)) {
905 //LOGI("+++ checking class pointer in %s\n", meth->name);
906 if ((void*)obj == (void*)meth->clazz)
907 return true;
908 }
909 return false;
910}
911
912/*
913 * Verify that a reference passed in from native code is one that the
914 * code is allowed to have.
915 *
916 * It's okay for native code to pass us a reference that:
917 * - was just passed in as an argument when invoked by native code
918 * - was returned to it from JNI (and is now in the JNI local refs table)
919 * - is present in the JNI global refs table
920 * The first one is a little awkward. The latter two are just table lookups.
921 *
922 * Used by -Xcheck:jni and GetObjectRefType.
923 *
924 * NOTE: in the current VM, global and local references are identical. If
925 * something is both global and local, we can't tell them apart, and always
926 * return "local".
927 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700928jobjectRefType dvmGetJNIRefType(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800929{
Andy McFaddenab00d452009-08-19 07:21:41 -0700930 ReferenceTable* pRefTable = getLocalRefTable(env);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800931 Thread* self = dvmThreadSelf();
932 //Object** top;
933 Object** ptr;
934
935 /* check args */
Andy McFaddenab00d452009-08-19 07:21:41 -0700936 if (findInArgList(self, jobj)) {
937 //LOGI("--- REF found %p on stack\n", jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800938 return JNILocalRefType;
939 }
940
941 /* check locals */
942 //top = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefTop;
Andy McFaddenab00d452009-08-19 07:21:41 -0700943 if (dvmFindInReferenceTable(pRefTable, pRefTable->table, jobj) != NULL) {
944 //LOGI("--- REF found %p in locals\n", jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800945 return JNILocalRefType;
946 }
947
948 /* check globals */
949 dvmLockMutex(&gDvm.jniGlobalRefLock);
950 if (dvmFindInReferenceTable(&gDvm.jniGlobalRefTable,
Andy McFaddenab00d452009-08-19 07:21:41 -0700951 gDvm.jniGlobalRefTable.table, jobj))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800952 {
Andy McFaddenab00d452009-08-19 07:21:41 -0700953 //LOGI("--- REF found %p in globals\n", jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800954 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
955 return JNIGlobalRefType;
956 }
957 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
958
959 /* not found! */
960 return JNIInvalidRefType;
961}
962
963/*
964 * Register a method that uses JNI calling conventions.
965 */
966static bool dvmRegisterJNIMethod(ClassObject* clazz, const char* methodName,
967 const char* signature, void* fnPtr)
968{
969 Method* method;
970 bool result = false;
971
972 if (fnPtr == NULL)
973 goto bail;
974
975 method = dvmFindDirectMethodByDescriptor(clazz, methodName, signature);
976 if (method == NULL)
977 method = dvmFindVirtualMethodByDescriptor(clazz, methodName, signature);
978 if (method == NULL) {
979 LOGW("ERROR: Unable to find decl for native %s.%s %s\n",
980 clazz->descriptor, methodName, signature);
981 goto bail;
982 }
983
984 if (!dvmIsNativeMethod(method)) {
985 LOGW("Unable to register: not native: %s.%s %s\n",
986 clazz->descriptor, methodName, signature);
987 goto bail;
988 }
989
990 if (method->nativeFunc != dvmResolveNativeMethod) {
991 LOGW("Warning: %s.%s %s was already registered/resolved?\n",
992 clazz->descriptor, methodName, signature);
993 /* keep going, I guess */
994 }
995
Andy McFadden59b61772009-05-13 16:44:34 -0700996 dvmUseJNIBridge(method, fnPtr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800997
998 LOGV("JNI-registered %s.%s %s\n", clazz->descriptor, methodName,
999 signature);
1000 result = true;
1001
1002bail:
1003 return result;
1004}
1005
1006/*
Andy McFadden59b61772009-05-13 16:44:34 -07001007 * Returns "true" if CheckJNI is enabled in the VM.
1008 */
1009static bool dvmIsCheckJNIEnabled(void)
1010{
1011 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
1012 return vm->useChecked;
1013}
1014
1015/*
1016 * Point "method->nativeFunc" at the JNI bridge, and overload "method->insns"
1017 * to point at the actual function.
1018 */
1019void dvmUseJNIBridge(Method* method, void* func)
1020{
1021 if (dvmIsCheckJNIEnabled()) {
1022 if (dvmIsSynchronizedMethod(method))
1023 dvmSetNativeFunc(method, dvmCheckCallSynchronizedJNIMethod, func);
1024 else
1025 dvmSetNativeFunc(method, dvmCheckCallJNIMethod, func);
1026 } else {
1027 if (dvmIsSynchronizedMethod(method))
1028 dvmSetNativeFunc(method, dvmCallSynchronizedJNIMethod, func);
1029 else
1030 dvmSetNativeFunc(method, dvmCallJNIMethod, func);
1031 }
1032}
1033
1034/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001035 * Get the method currently being executed by examining the interp stack.
1036 */
1037const Method* dvmGetCurrentJNIMethod(void)
1038{
1039 assert(dvmThreadSelf() != NULL);
1040
1041 void* fp = dvmThreadSelf()->curFrame;
1042 const Method* meth = SAVEAREA_FROM_FP(fp)->method;
1043
1044 assert(meth != NULL);
1045 assert(dvmIsNativeMethod(meth));
1046 return meth;
1047}
1048
1049
1050/*
1051 * Track a JNI MonitorEnter in the current thread.
1052 *
1053 * The goal is to be able to "implicitly" release all JNI-held monitors
1054 * when the thread detaches.
1055 *
1056 * Monitors may be entered multiple times, so we add a new entry for each
1057 * enter call. It would be more efficient to keep a counter. At present
1058 * there's no real motivation to improve this however.
1059 */
1060static void trackMonitorEnter(Thread* self, Object* obj)
1061{
1062 static const int kInitialSize = 16;
1063 ReferenceTable* refTable = &self->jniMonitorRefTable;
1064
1065 /* init table on first use */
1066 if (refTable->table == NULL) {
1067 assert(refTable->maxEntries == 0);
1068
1069 if (!dvmInitReferenceTable(refTable, kInitialSize, INT_MAX)) {
1070 LOGE("Unable to initialize monitor tracking table\n");
1071 dvmAbort();
1072 }
1073 }
1074
1075 if (!dvmAddToReferenceTable(refTable, obj)) {
1076 /* ran out of memory? could throw exception instead */
1077 LOGE("Unable to add entry to monitor tracking table\n");
1078 dvmAbort();
1079 } else {
1080 LOGVV("--- added monitor %p\n", obj);
1081 }
1082}
1083
Andy McFaddenab00d452009-08-19 07:21:41 -07001084
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001085/*
1086 * Track a JNI MonitorExit in the current thread.
1087 */
1088static void trackMonitorExit(Thread* self, Object* obj)
1089{
Andy McFaddenab00d452009-08-19 07:21:41 -07001090 ReferenceTable* pRefTable = &self->jniMonitorRefTable;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001091
Andy McFaddenab00d452009-08-19 07:21:41 -07001092 if (!dvmRemoveFromReferenceTable(pRefTable, pRefTable->table, obj)) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001093 LOGE("JNI monitor %p not found in tracking list\n", obj);
1094 /* keep going? */
1095 } else {
1096 LOGVV("--- removed monitor %p\n", obj);
1097 }
1098}
1099
1100/*
1101 * Release all monitors held by the jniMonitorRefTable list.
1102 */
1103void dvmReleaseJniMonitors(Thread* self)
1104{
Andy McFaddenab00d452009-08-19 07:21:41 -07001105 ReferenceTable* pRefTable = &self->jniMonitorRefTable;
1106 Object** top = pRefTable->table;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001107
1108 if (top == NULL)
1109 return;
1110
Andy McFaddenab00d452009-08-19 07:21:41 -07001111 Object** ptr = pRefTable->nextEntry;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001112 while (--ptr >= top) {
1113 if (!dvmUnlockObject(self, *ptr)) {
1114 LOGW("Unable to unlock monitor %p at thread detach\n", *ptr);
1115 } else {
1116 LOGVV("--- detach-releasing monitor %p\n", *ptr);
1117 }
1118 }
1119
1120 /* zap it */
Andy McFaddenab00d452009-08-19 07:21:41 -07001121 pRefTable->nextEntry = pRefTable->table;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001122}
1123
1124#ifdef WITH_JNI_STACK_CHECK
1125/*
1126 * Compute a CRC on the entire interpreted stack.
1127 *
1128 * Would be nice to compute it on "self" as well, but there are parts of
1129 * the Thread that can be altered by other threads (e.g. prev/next pointers).
1130 */
1131static void computeStackSum(Thread* self)
1132{
1133 const u1* low = (const u1*)SAVEAREA_FROM_FP(self->curFrame);
1134 u4 crc = dvmInitCrc32();
1135 self->stackCrc = 0;
1136 crc = dvmComputeCrc32(crc, low, self->interpStackStart - low);
1137 self->stackCrc = crc;
1138}
1139
1140/*
1141 * Compute a CRC on the entire interpreted stack, and compare it to what
1142 * we previously computed.
1143 *
1144 * We can execute JNI directly from native code without calling in from
1145 * interpreted code during VM initialization and immediately after JNI
1146 * thread attachment. Another opportunity exists during JNI_OnLoad. Rather
1147 * than catching these cases we just ignore them here, which is marginally
1148 * less accurate but reduces the amount of code we have to touch with #ifdefs.
1149 */
1150static void checkStackSum(Thread* self)
1151{
1152 const u1* low = (const u1*)SAVEAREA_FROM_FP(self->curFrame);
1153 u4 stackCrc, crc;
1154
1155 stackCrc = self->stackCrc;
1156 self->stackCrc = 0;
1157 crc = dvmInitCrc32();
1158 crc = dvmComputeCrc32(crc, low, self->interpStackStart - low);
1159 if (crc != stackCrc) {
1160 const Method* meth = dvmGetCurrentJNIMethod();
1161 if (dvmComputeExactFrameDepth(self->curFrame) == 1) {
1162 LOGD("JNI: bad stack CRC (0x%08x) -- okay during init\n",
1163 stackCrc);
1164 } else if (strcmp(meth->name, "nativeLoad") == 0 &&
1165 (strcmp(meth->clazz->descriptor, "Ljava/lang/Runtime;") == 0))
1166 {
1167 LOGD("JNI: bad stack CRC (0x%08x) -- okay during JNI_OnLoad\n",
1168 stackCrc);
1169 } else {
1170 LOGW("JNI: bad stack CRC (%08x vs %08x)\n", crc, stackCrc);
1171 dvmAbort();
1172 }
1173 }
1174 self->stackCrc = (u4) -1; /* make logic errors more noticeable */
1175}
1176#endif
1177
1178
1179/*
1180 * ===========================================================================
1181 * JNI implementation
1182 * ===========================================================================
1183 */
1184
1185/*
1186 * Return the version of the native method interface.
1187 */
1188static jint GetVersion(JNIEnv* env)
1189{
1190 JNI_ENTER();
1191 /*
1192 * There is absolutely no need to toggle the mode for correct behavior.
1193 * However, it does provide native code with a simple "suspend self
1194 * if necessary" call.
1195 */
1196 JNI_EXIT();
1197 return JNI_VERSION_1_6;
1198}
1199
1200/*
1201 * Create a new class from a bag of bytes.
1202 *
1203 * This is not currently supported within Dalvik.
1204 */
1205static jclass DefineClass(JNIEnv* env, const char *name, jobject loader,
1206 const jbyte* buf, jsize bufLen)
1207{
1208 UNUSED_PARAMETER(name);
1209 UNUSED_PARAMETER(loader);
1210 UNUSED_PARAMETER(buf);
1211 UNUSED_PARAMETER(bufLen);
1212
1213 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001214 LOGW("JNI DefineClass is not supported\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001215 JNI_EXIT();
1216 return NULL;
1217}
1218
1219/*
1220 * Find a class by name.
1221 *
1222 * We have to use the "no init" version of FindClass here, because we might
1223 * be getting the class prior to registering native methods that will be
1224 * used in <clinit>.
1225 *
1226 * We need to get the class loader associated with the current native
1227 * method. If there is no native method, e.g. we're calling this from native
1228 * code right after creating the VM, the spec says we need to use the class
1229 * loader returned by "ClassLoader.getBaseClassLoader". There is no such
1230 * method, but it's likely they meant ClassLoader.getSystemClassLoader.
1231 * We can't get that until after the VM has initialized though.
1232 */
1233static jclass FindClass(JNIEnv* env, const char* name)
1234{
1235 JNI_ENTER();
1236
1237 const Method* thisMethod;
1238 ClassObject* clazz;
Andy McFaddenab00d452009-08-19 07:21:41 -07001239 jclass jclazz = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001240 Object* loader;
1241 char* descriptor = NULL;
1242
1243 thisMethod = dvmGetCurrentJNIMethod();
1244 assert(thisMethod != NULL);
1245
1246 descriptor = dvmNameToDescriptor(name);
1247 if (descriptor == NULL) {
1248 clazz = NULL;
1249 goto bail;
1250 }
1251
1252 //Thread* self = dvmThreadSelf();
1253 if (_self->classLoaderOverride != NULL) {
1254 /* hack for JNI_OnLoad */
1255 assert(strcmp(thisMethod->name, "nativeLoad") == 0);
1256 loader = _self->classLoaderOverride;
1257 } else if (thisMethod == gDvm.methFakeNativeEntry) {
1258 /* start point of invocation interface */
1259 if (!gDvm.initializing)
1260 loader = dvmGetSystemClassLoader();
1261 else
1262 loader = NULL;
1263 } else {
1264 loader = thisMethod->clazz->classLoader;
1265 }
1266
1267 clazz = dvmFindClassNoInit(descriptor, loader);
Andy McFaddenab00d452009-08-19 07:21:41 -07001268 jclazz = addLocalReference(env, (Object*) clazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001269
1270bail:
1271 free(descriptor);
Andy McFaddenab00d452009-08-19 07:21:41 -07001272
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001273 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001274 return jclazz;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001275}
1276
1277/*
1278 * Return the superclass of a class.
1279 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001280static jclass GetSuperclass(JNIEnv* env, jclass jclazz)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001281{
1282 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001283 jclass jsuper;
1284
1285 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1286 if (clazz == NULL)
1287 jsuper = NULL;
1288 else
1289 jsuper = addLocalReference(env, (Object*)clazz->super);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001290 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001291 return jsuper;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001292}
1293
1294/*
1295 * Determine whether an object of clazz1 can be safely cast to clazz2.
1296 *
1297 * Like IsInstanceOf, but with a pair of class objects instead of obj+class.
1298 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001299static jboolean IsAssignableFrom(JNIEnv* env, jclass jclazz1, jclass jclazz2)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001300{
1301 JNI_ENTER();
1302
Andy McFaddenab00d452009-08-19 07:21:41 -07001303 ClassObject* clazz1 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz1);
1304 ClassObject* clazz2 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz2);
1305
1306 jboolean result = dvmInstanceof(clazz1, clazz2);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001307
1308 JNI_EXIT();
1309 return result;
1310}
1311
1312/*
1313 * Given a java.lang.reflect.Method or .Constructor, return a methodID.
1314 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001315static jmethodID FromReflectedMethod(JNIEnv* env, jobject jmethod)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001316{
1317 JNI_ENTER();
1318 jmethodID methodID;
Andy McFaddenab00d452009-08-19 07:21:41 -07001319 Object* method = dvmDecodeIndirectRef(env, jmethod);
1320 methodID = (jmethodID) dvmGetMethodFromReflectObj(method);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001321 JNI_EXIT();
1322 return methodID;
1323}
1324
1325/*
1326 * Given a java.lang.reflect.Field, return a fieldID.
1327 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001328static jfieldID FromReflectedField(JNIEnv* env, jobject jfield)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001329{
1330 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001331 jfieldID fieldID;
1332 Object* field = dvmDecodeIndirectRef(env, jfield);
1333 fieldID = (jfieldID) dvmGetFieldFromReflectObj(field);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001334 JNI_EXIT();
1335 return fieldID;
1336}
1337
1338/*
1339 * Convert a methodID to a java.lang.reflect.Method or .Constructor.
1340 *
1341 * (The "isStatic" field does not appear in the spec.)
1342 *
1343 * Throws OutOfMemory and returns NULL on failure.
1344 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001345static jobject ToReflectedMethod(JNIEnv* env, jclass jcls, jmethodID methodID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001346 jboolean isStatic)
1347{
1348 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001349 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls);
1350 Object* obj = dvmCreateReflectObjForMethod(clazz, (Method*) methodID);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001351 dvmReleaseTrackedAlloc(obj, NULL);
Andy McFaddenab00d452009-08-19 07:21:41 -07001352 jobject jobj = addLocalReference(env, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001353 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001354 return jobj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001355}
1356
1357/*
1358 * Convert a fieldID to a java.lang.reflect.Field.
1359 *
1360 * (The "isStatic" field does not appear in the spec.)
1361 *
1362 * Throws OutOfMemory and returns NULL on failure.
1363 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001364static jobject ToReflectedField(JNIEnv* env, jclass jcls, jfieldID fieldID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001365 jboolean isStatic)
1366{
1367 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001368 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls);
1369 Object* obj = dvmCreateReflectObjForField(jcls, (Field*) fieldID);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001370 dvmReleaseTrackedAlloc(obj, NULL);
Andy McFaddenab00d452009-08-19 07:21:41 -07001371 jobject jobj = addLocalReference(env, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001372 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001373 return jobj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001374}
1375
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001376/*
1377 * Take this exception and throw it.
1378 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001379static jint Throw(JNIEnv* env, jthrowable jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001380{
1381 JNI_ENTER();
1382
1383 jint retval;
1384
Andy McFaddenab00d452009-08-19 07:21:41 -07001385 if (jobj != NULL) {
1386 Object* obj = dvmDecodeIndirectRef(env, jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001387 dvmSetException(_self, obj);
1388 retval = JNI_OK;
Andy McFaddenab00d452009-08-19 07:21:41 -07001389 } else {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001390 retval = JNI_ERR;
Andy McFaddenab00d452009-08-19 07:21:41 -07001391 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001392
1393 JNI_EXIT();
1394 return retval;
1395}
1396
1397/*
Andy McFaddenab00d452009-08-19 07:21:41 -07001398 * Constructs an exception object from the specified class with the message
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001399 * specified by "message", and throws it.
1400 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001401static jint ThrowNew(JNIEnv* env, jclass jclazz, const char* message)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001402{
1403 JNI_ENTER();
1404
Andy McFaddenab00d452009-08-19 07:21:41 -07001405 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1406 dvmThrowExceptionByClass(clazz, message);
1407 // TODO: should return failure if this didn't work (e.g. OOM)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001408
1409 JNI_EXIT();
1410 return JNI_OK;
1411}
1412
1413/*
1414 * If an exception is being thrown, return the exception object. Otherwise,
1415 * return NULL.
1416 *
1417 * TODO: if there is no pending exception, we should be able to skip the
1418 * enter/exit checks. If we find one, we need to enter and then re-fetch
1419 * the exception (in case it got moved by a compacting GC).
1420 */
1421static jthrowable ExceptionOccurred(JNIEnv* env)
1422{
1423 JNI_ENTER();
1424
1425 Object* exception;
Andy McFaddenab00d452009-08-19 07:21:41 -07001426 jobject localException;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001427
Andy McFaddenab00d452009-08-19 07:21:41 -07001428 exception = dvmGetException(_self);
1429 localException = addLocalReference(env, exception);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001430 if (localException == NULL && exception != NULL) {
1431 /*
1432 * We were unable to add a new local reference, and threw a new
1433 * exception. We can't return "exception", because it's not a
1434 * local reference. So we have to return NULL, indicating that
1435 * there was no exception, even though it's pretty much raining
1436 * exceptions in here.
1437 */
1438 LOGW("JNI WARNING: addLocal/exception combo\n");
1439 }
1440
1441 JNI_EXIT();
1442 return localException;
1443}
1444
1445/*
1446 * Print an exception and stack trace to stderr.
1447 */
1448static void ExceptionDescribe(JNIEnv* env)
1449{
1450 JNI_ENTER();
1451
1452 Object* exception = dvmGetException(_self);
1453 if (exception != NULL) {
1454 dvmPrintExceptionStackTrace();
1455 } else {
1456 LOGI("Odd: ExceptionDescribe called, but no exception pending\n");
1457 }
1458
1459 JNI_EXIT();
1460}
1461
1462/*
1463 * Clear the exception currently being thrown.
1464 *
1465 * TODO: we should be able to skip the enter/exit stuff.
1466 */
1467static void ExceptionClear(JNIEnv* env)
1468{
1469 JNI_ENTER();
1470 dvmClearException(_self);
1471 JNI_EXIT();
1472}
1473
1474/*
1475 * Kill the VM. This function does not return.
1476 */
1477static void FatalError(JNIEnv* env, const char* msg)
1478{
1479 //dvmChangeStatus(NULL, THREAD_RUNNING);
1480 LOGE("JNI posting fatal error: %s\n", msg);
1481 dvmAbort();
1482}
1483
1484/*
1485 * Push a new JNI frame on the stack, with a new set of locals.
1486 *
1487 * The new frame must have the same method pointer. (If for no other
1488 * reason than FindClass needs it to get the appropriate class loader.)
1489 */
1490static jint PushLocalFrame(JNIEnv* env, jint capacity)
1491{
1492 JNI_ENTER();
1493 int result = JNI_OK;
Andy McFaddenab00d452009-08-19 07:21:41 -07001494 if (!ensureLocalCapacity(env, capacity) ||
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001495 !dvmPushLocalFrame(_self /*dvmThreadSelf()*/, dvmGetCurrentJNIMethod()))
1496 {
1497 /* yes, OutOfMemoryError, not StackOverflowError */
1498 dvmClearException(_self);
1499 dvmThrowException("Ljava/lang/OutOfMemoryError;",
1500 "out of stack in JNI PushLocalFrame");
1501 result = JNI_ERR;
1502 }
1503 JNI_EXIT();
1504 return result;
1505}
1506
1507/*
1508 * Pop the local frame off. If "result" is not null, add it as a
1509 * local reference on the now-current frame.
1510 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001511static jobject PopLocalFrame(JNIEnv* env, jobject jresult)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001512{
1513 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001514 Object* result = dvmDecodeIndirectRef(env, jresult);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001515 if (!dvmPopLocalFrame(_self /*dvmThreadSelf()*/)) {
1516 LOGW("JNI WARNING: too many PopLocalFrame calls\n");
1517 dvmClearException(_self);
1518 dvmThrowException("Ljava/lang/RuntimeException;",
1519 "too many PopLocalFrame calls");
1520 }
Andy McFaddenab00d452009-08-19 07:21:41 -07001521 jresult = addLocalReference(env, result);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001522 JNI_EXIT();
1523 return result;
1524}
1525
1526/*
1527 * Add a reference to the global list.
1528 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001529static jobject NewGlobalRef(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001530{
1531 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001532 Object* obj = dvmDecodeIndirectRef(env, jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001533 jobject retval = addGlobalReference(obj);
1534 JNI_EXIT();
1535 return retval;
1536}
1537
1538/*
1539 * Delete a reference from the global list.
1540 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001541static void DeleteGlobalRef(JNIEnv* env, jobject jglobalRef)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001542{
1543 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001544 deleteGlobalReference(jglobalRef);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001545 JNI_EXIT();
1546}
1547
1548
1549/*
1550 * Add a reference to the local list.
1551 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001552static jobject NewLocalRef(JNIEnv* env, jobject jref)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001553{
1554 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001555 Object* obj = dvmDecodeIndirectRef(env, jref);
1556 jobject retval = addLocalReference(env, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001557 JNI_EXIT();
1558 return retval;
1559}
1560
1561/*
1562 * Delete a reference from the local list.
1563 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001564static void DeleteLocalRef(JNIEnv* env, jobject jlocalRef)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001565{
1566 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001567 deleteLocalReference(env, jlocalRef);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001568 JNI_EXIT();
1569}
1570
1571/*
1572 * Ensure that the local references table can hold at least this many
1573 * references.
1574 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001575static jint EnsureLocalCapacity(JNIEnv* env, jint capacity)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001576{
1577 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001578 bool okay = ensureLocalCapacity(env, capacity);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001579 if (!okay) {
1580 dvmThrowException("Ljava/lang/OutOfMemoryError;",
1581 "can't ensure local reference capacity");
1582 }
1583 JNI_EXIT();
1584 if (okay)
1585 return 0;
1586 else
1587 return -1;
1588}
1589
1590
1591/*
1592 * Determine whether two Object references refer to the same underlying object.
1593 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001594static jboolean IsSameObject(JNIEnv* env, jobject jref1, jobject jref2)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001595{
1596 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001597 Object* obj1 = dvmDecodeIndirectRef(env, jref1);
1598 Object* obj2 = dvmDecodeIndirectRef(env, jref2);
1599 jboolean result = (obj1 == obj2);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001600 JNI_EXIT();
1601 return result;
1602}
1603
1604/*
1605 * Allocate a new object without invoking any constructors.
1606 */
1607static jobject AllocObject(JNIEnv* env, jclass jclazz)
1608{
1609 JNI_ENTER();
1610
Andy McFaddenab00d452009-08-19 07:21:41 -07001611 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1612 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001613
1614 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
1615 assert(dvmCheckException(_self));
Andy McFaddenab00d452009-08-19 07:21:41 -07001616 result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001617 } else {
Andy McFaddenab00d452009-08-19 07:21:41 -07001618 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
1619 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001620 }
1621
1622 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001623 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001624}
1625
1626/*
Andy McFaddenab00d452009-08-19 07:21:41 -07001627 * Allocate a new object and invoke the supplied constructor.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001628 */
1629static jobject NewObject(JNIEnv* env, jclass jclazz, jmethodID methodID, ...)
1630{
1631 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001632 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1633 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001634
1635 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
1636 assert(dvmCheckException(_self));
Andy McFaddenab00d452009-08-19 07:21:41 -07001637 result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001638 } else {
Andy McFaddenab00d452009-08-19 07:21:41 -07001639 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
1640 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001641 if (newObj != NULL) {
1642 JValue unused;
1643 va_list args;
1644 va_start(args, methodID);
Andy McFaddenab00d452009-08-19 07:21:41 -07001645 dvmCallMethodV(_self, (Method*) methodID, newObj, &unused, args);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001646 va_end(args);
1647 }
1648 }
1649
1650 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001651 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001652}
Andy McFaddenab00d452009-08-19 07:21:41 -07001653static jobject NewObjectV(JNIEnv* env, jclass jclazz, jmethodID methodID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001654 va_list args)
1655{
1656 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001657 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1658 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001659
Andy McFaddenab00d452009-08-19 07:21:41 -07001660 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
1661 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001662 if (newObj != NULL) {
1663 JValue unused;
Andy McFaddenab00d452009-08-19 07:21:41 -07001664 dvmCallMethodV(_self, (Method*) methodID, newObj, &unused, args);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001665 }
1666
1667 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001668 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001669}
Andy McFaddenab00d452009-08-19 07:21:41 -07001670static jobject NewObjectA(JNIEnv* env, jclass jclazz, jmethodID methodID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001671 jvalue* args)
1672{
1673 JNI_ENTER();
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
Andy McFaddenab00d452009-08-19 07:21:41 -07001677 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
1678 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001679 if (newObj != NULL) {
1680 JValue unused;
Andy McFaddenab00d452009-08-19 07:21:41 -07001681 dvmCallMethodA(_self, (Method*) methodID, newObj, &unused, args);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001682 }
1683
1684 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001685 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001686}
1687
1688/*
1689 * Returns the class of an object.
1690 *
1691 * JNI spec says: obj must not be NULL.
1692 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001693static jclass GetObjectClass(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001694{
1695 JNI_ENTER();
1696
Andy McFaddenab00d452009-08-19 07:21:41 -07001697 assert(jobj != NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001698
Andy McFaddenab00d452009-08-19 07:21:41 -07001699 Object* obj = dvmDecodeIndirectRef(env, jobj);
1700 jclass jclazz = addLocalReference(env, (Object*) obj->clazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001701
1702 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001703 return jclazz;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001704}
1705
1706/*
1707 * Determine whether "obj" is an instance of "clazz".
1708 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001709static jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass jclazz)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001710{
1711 JNI_ENTER();
1712
Andy McFaddenab00d452009-08-19 07:21:41 -07001713 assert(jclazz != NULL);
1714
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001715 jboolean result;
1716
Andy McFaddenab00d452009-08-19 07:21:41 -07001717 if (jobj == NULL) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001718 result = true;
Andy McFaddenab00d452009-08-19 07:21:41 -07001719 } else {
1720 Object* obj = dvmDecodeIndirectRef(env, jobj);
1721 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1722 result = dvmInstanceof(obj->clazz, clazz);
1723 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001724
1725 JNI_EXIT();
1726 return result;
1727}
1728
1729/*
1730 * Get a method ID for an instance method.
1731 *
1732 * JNI defines <init> as an instance method, but Dalvik considers it a
1733 * "direct" method, so we have to special-case it here.
1734 *
1735 * Dalvik also puts all private methods into the "direct" list, so we
1736 * really need to just search both lists.
1737 */
1738static jmethodID GetMethodID(JNIEnv* env, jclass jclazz, const char* name,
1739 const char* sig)
1740{
1741 JNI_ENTER();
1742
Andy McFaddenab00d452009-08-19 07:21:41 -07001743 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001744 jmethodID id = NULL;
1745
1746 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
1747 assert(dvmCheckException(_self));
1748 } else {
1749 Method* meth;
1750
1751 meth = dvmFindVirtualMethodHierByDescriptor(clazz, name, sig);
1752 if (meth == NULL) {
1753 /* search private methods and constructors; non-hierarchical */
1754 meth = dvmFindDirectMethodByDescriptor(clazz, name, sig);
1755 }
1756 if (meth != NULL && dvmIsStaticMethod(meth)) {
1757 IF_LOGD() {
1758 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
1759 LOGD("GetMethodID: not returning static method %s.%s %s\n",
1760 clazz->descriptor, meth->name, desc);
1761 free(desc);
1762 }
1763 meth = NULL;
1764 }
1765 if (meth == NULL) {
Andy McFadden03bd0d52009-08-18 15:32:27 -07001766 LOGD("GetMethodID: method not found: %s.%s:%s\n",
1767 clazz->descriptor, name, sig);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001768 dvmThrowException("Ljava/lang/NoSuchMethodError;", name);
1769 }
1770
1771 /*
1772 * The method's class may not be the same as clazz, but if
1773 * it isn't this must be a virtual method and the class must
1774 * be a superclass (and, hence, already initialized).
1775 */
1776 if (meth != NULL) {
1777 assert(dvmIsClassInitialized(meth->clazz) ||
1778 dvmIsClassInitializing(meth->clazz));
1779 }
1780 id = (jmethodID) meth;
1781 }
1782 JNI_EXIT();
1783 return id;
1784}
1785
1786/*
1787 * Get a field ID (instance fields).
1788 */
1789static jfieldID GetFieldID(JNIEnv* env, jclass jclazz,
1790 const char* name, const char* sig)
1791{
1792 JNI_ENTER();
1793
Andy McFaddenab00d452009-08-19 07:21:41 -07001794 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001795 jfieldID id;
1796
1797 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
1798 assert(dvmCheckException(_self));
1799 id = NULL;
1800 } else {
1801 id = (jfieldID) dvmFindInstanceFieldHier(clazz, name, sig);
Andy McFadden03bd0d52009-08-18 15:32:27 -07001802 if (id == NULL) {
1803 LOGD("GetFieldID: unable to find field %s.%s:%s\n",
1804 clazz->descriptor, name, sig);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001805 dvmThrowException("Ljava/lang/NoSuchFieldError;", name);
Andy McFadden03bd0d52009-08-18 15:32:27 -07001806 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001807 }
1808 JNI_EXIT();
1809 return id;
1810}
1811
1812/*
1813 * Get the method ID for a static method in a class.
1814 */
1815static jmethodID GetStaticMethodID(JNIEnv* env, jclass jclazz,
1816 const char* name, const char* sig)
1817{
1818 JNI_ENTER();
1819
Andy McFaddenab00d452009-08-19 07:21:41 -07001820 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001821 jmethodID id;
1822
1823 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
1824 assert(dvmCheckException(_self));
1825 id = NULL;
1826 } else {
1827 Method* meth;
1828
1829 meth = dvmFindDirectMethodHierByDescriptor(clazz, name, sig);
1830
1831 /* make sure it's static, not virtual+private */
1832 if (meth != NULL && !dvmIsStaticMethod(meth)) {
1833 IF_LOGD() {
1834 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
1835 LOGD("GetStaticMethodID: "
1836 "not returning nonstatic method %s.%s %s\n",
1837 clazz->descriptor, meth->name, desc);
1838 free(desc);
1839 }
1840 meth = NULL;
1841 }
1842
1843 id = (jmethodID) meth;
1844 if (id == NULL)
1845 dvmThrowException("Ljava/lang/NoSuchMethodError;", name);
1846 }
1847
1848 JNI_EXIT();
1849 return id;
1850}
1851
1852/*
1853 * Get a field ID (static fields).
1854 */
1855static jfieldID GetStaticFieldID(JNIEnv* env, jclass jclazz,
1856 const char* name, const char* sig)
1857{
1858 JNI_ENTER();
1859
Andy McFaddenab00d452009-08-19 07:21:41 -07001860 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001861 jfieldID id;
1862
1863 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
1864 assert(dvmCheckException(_self));
1865 id = NULL;
1866 } else {
1867 id = (jfieldID) dvmFindStaticField(clazz, name, sig);
1868 if (id == NULL)
1869 dvmThrowException("Ljava/lang/NoSuchFieldError;", name);
1870 }
1871 JNI_EXIT();
1872 return id;
1873}
1874
1875/*
1876 * Get a static field.
1877 *
1878 * If we get an object reference, add it to the local refs list.
1879 */
1880#define GET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07001881 static _ctype GetStatic##_jname##Field(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001882 jfieldID fieldID) \
1883 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07001884 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001885 JNI_ENTER(); \
1886 StaticField* sfield = (StaticField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07001887 _ctype value; \
1888 if (_isref) { /* only when _ctype==jobject */ \
1889 Object* obj = dvmGetStaticFieldObject(sfield); \
1890 value = (_ctype)(u4)addLocalReference(env, obj); \
1891 } else { \
1892 value = dvmGetStaticField##_jname(sfield); \
1893 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001894 JNI_EXIT(); \
1895 return value; \
1896 }
1897GET_STATIC_TYPE_FIELD(jobject, Object, true);
1898GET_STATIC_TYPE_FIELD(jboolean, Boolean, false);
1899GET_STATIC_TYPE_FIELD(jbyte, Byte, false);
1900GET_STATIC_TYPE_FIELD(jchar, Char, false);
1901GET_STATIC_TYPE_FIELD(jshort, Short, false);
1902GET_STATIC_TYPE_FIELD(jint, Int, false);
1903GET_STATIC_TYPE_FIELD(jlong, Long, false);
1904GET_STATIC_TYPE_FIELD(jfloat, Float, false);
1905GET_STATIC_TYPE_FIELD(jdouble, Double, false);
1906
1907/*
1908 * Set a static field.
1909 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001910#define SET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) \
1911 static void SetStatic##_jname##Field(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001912 jfieldID fieldID, _ctype value) \
1913 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07001914 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001915 JNI_ENTER(); \
1916 StaticField* sfield = (StaticField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07001917 if (_isref) { /* only when _ctype==jobject */ \
1918 Object* valObj = dvmDecodeIndirectRef(env, (jobject)(u4)value); \
1919 dvmSetStaticFieldObject(sfield, valObj); \
1920 } else { \
1921 dvmSetStaticField##_jname(sfield, value); \
1922 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001923 JNI_EXIT(); \
1924 }
Andy McFaddenab00d452009-08-19 07:21:41 -07001925SET_STATIC_TYPE_FIELD(jobject, Object, true);
1926SET_STATIC_TYPE_FIELD(jboolean, Boolean, false);
1927SET_STATIC_TYPE_FIELD(jbyte, Byte, false);
1928SET_STATIC_TYPE_FIELD(jchar, Char, false);
1929SET_STATIC_TYPE_FIELD(jshort, Short, false);
1930SET_STATIC_TYPE_FIELD(jint, Int, false);
1931SET_STATIC_TYPE_FIELD(jlong, Long, false);
1932SET_STATIC_TYPE_FIELD(jfloat, Float, false);
1933SET_STATIC_TYPE_FIELD(jdouble, Double, false);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001934
1935/*
1936 * Get an instance field.
1937 *
1938 * If we get an object reference, add it to the local refs list.
1939 */
1940#define GET_TYPE_FIELD(_ctype, _jname, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07001941 static _ctype Get##_jname##Field(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001942 jfieldID fieldID) \
1943 { \
1944 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07001945 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001946 InstField* field = (InstField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07001947 _ctype value; \
1948 if (_isref) { /* only when _ctype==jobject */ \
1949 Object* valObj = dvmGetFieldObject(obj, field->byteOffset); \
1950 value = (_ctype)(u4)addLocalReference(env, valObj); \
1951 } else { \
1952 value = dvmGetField##_jname(obj, field->byteOffset); \
1953 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001954 JNI_EXIT(); \
1955 return value; \
1956 }
1957GET_TYPE_FIELD(jobject, Object, true);
1958GET_TYPE_FIELD(jboolean, Boolean, false);
1959GET_TYPE_FIELD(jbyte, Byte, false);
1960GET_TYPE_FIELD(jchar, Char, false);
1961GET_TYPE_FIELD(jshort, Short, false);
1962GET_TYPE_FIELD(jint, Int, false);
1963GET_TYPE_FIELD(jlong, Long, false);
1964GET_TYPE_FIELD(jfloat, Float, false);
1965GET_TYPE_FIELD(jdouble, Double, false);
1966
1967/*
1968 * Set an instance field.
1969 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001970#define SET_TYPE_FIELD(_ctype, _jname, _isref) \
1971 static void Set##_jname##Field(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001972 jfieldID fieldID, _ctype value) \
1973 { \
1974 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07001975 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001976 InstField* field = (InstField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07001977 if (_isref) { /* only when _ctype==jobject */ \
1978 Object* valObj = dvmDecodeIndirectRef(env, (jobject)(u4)value); \
1979 dvmSetFieldObject(obj, field->byteOffset, valObj); \
1980 } else { \
1981 dvmSetField##_jname(obj, field->byteOffset, value); \
1982 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001983 JNI_EXIT(); \
1984 }
Andy McFaddenab00d452009-08-19 07:21:41 -07001985SET_TYPE_FIELD(jobject, Object, true);
1986SET_TYPE_FIELD(jboolean, Boolean, false);
1987SET_TYPE_FIELD(jbyte, Byte, false);
1988SET_TYPE_FIELD(jchar, Char, false);
1989SET_TYPE_FIELD(jshort, Short, false);
1990SET_TYPE_FIELD(jint, Int, false);
1991SET_TYPE_FIELD(jlong, Long, false);
1992SET_TYPE_FIELD(jfloat, Float, false);
1993SET_TYPE_FIELD(jdouble, Double, false);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001994
1995/*
1996 * Make a virtual method call.
1997 *
1998 * Three versions (..., va_list, jvalue[]) for each return type. If we're
1999 * returning an Object, we have to add it to the local references table.
2000 */
2001#define CALL_VIRTUAL(_ctype, _jname, _retfail, _retok, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002002 static _ctype Call##_jname##Method(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002003 jmethodID methodID, ...) \
2004 { \
2005 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002006 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002007 const Method* meth; \
2008 va_list args; \
2009 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002010 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002011 if (meth == NULL) { \
2012 JNI_EXIT(); \
2013 return _retfail; \
2014 } \
2015 va_start(args, methodID); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002016 dvmCallMethodV(_self, meth, obj, &result, args); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002017 va_end(args); \
2018 if (_isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002019 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002020 JNI_EXIT(); \
2021 return _retok; \
2022 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002023 static _ctype Call##_jname##MethodV(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002024 jmethodID methodID, va_list args) \
2025 { \
2026 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002027 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002028 const Method* meth; \
2029 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002030 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002031 if (meth == NULL) { \
2032 JNI_EXIT(); \
2033 return _retfail; \
2034 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002035 dvmCallMethodV(_self, meth, obj, &result, args); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002036 if (_isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002037 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002038 JNI_EXIT(); \
2039 return _retok; \
2040 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002041 static _ctype Call##_jname##MethodA(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002042 jmethodID methodID, jvalue* args) \
2043 { \
2044 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002045 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002046 const Method* meth; \
2047 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002048 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002049 if (meth == NULL) { \
2050 JNI_EXIT(); \
2051 return _retfail; \
2052 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002053 dvmCallMethodA(_self, meth, obj, &result, args); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002054 if (_isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002055 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002056 JNI_EXIT(); \
2057 return _retok; \
2058 }
2059CALL_VIRTUAL(jobject, Object, NULL, result.l, true);
2060CALL_VIRTUAL(jboolean, Boolean, 0, result.z, false);
2061CALL_VIRTUAL(jbyte, Byte, 0, result.b, false);
2062CALL_VIRTUAL(jchar, Char, 0, result.c, false);
2063CALL_VIRTUAL(jshort, Short, 0, result.s, false);
2064CALL_VIRTUAL(jint, Int, 0, result.i, false);
2065CALL_VIRTUAL(jlong, Long, 0, result.j, false);
2066CALL_VIRTUAL(jfloat, Float, 0.0f, result.f, false);
2067CALL_VIRTUAL(jdouble, Double, 0.0, result.d, false);
2068CALL_VIRTUAL(void, Void, , , false);
2069
2070/*
2071 * Make a "non-virtual" method call. We're still calling a virtual method,
2072 * but this time we're not doing an indirection through the object's vtable.
2073 * The "clazz" parameter defines which implementation of a method we want.
2074 *
2075 * Three versions (..., va_list, jvalue[]) for each return type.
2076 */
2077#define CALL_NONVIRTUAL(_ctype, _jname, _retfail, _retok, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002078 static _ctype CallNonvirtual##_jname##Method(JNIEnv* env, jobject jobj, \
2079 jclass jclazz, jmethodID methodID, ...) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002080 { \
2081 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002082 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2083 ClassObject* clazz = \
2084 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002085 const Method* meth; \
2086 va_list args; \
2087 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002088 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002089 if (meth == NULL) { \
2090 JNI_EXIT(); \
2091 return _retfail; \
2092 } \
2093 va_start(args, methodID); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002094 dvmCallMethodV(_self, meth, obj, &result, args); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002095 if (_isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002096 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002097 va_end(args); \
2098 JNI_EXIT(); \
2099 return _retok; \
2100 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002101 static _ctype CallNonvirtual##_jname##MethodV(JNIEnv* env, jobject jobj,\
2102 jclass jclazz, jmethodID methodID, va_list args) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002103 { \
2104 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002105 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2106 ClassObject* clazz = \
2107 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002108 const Method* meth; \
2109 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002110 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002111 if (meth == NULL) { \
2112 JNI_EXIT(); \
2113 return _retfail; \
2114 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002115 dvmCallMethodV(_self, meth, obj, &result, args); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002116 if (_isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002117 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002118 JNI_EXIT(); \
2119 return _retok; \
2120 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002121 static _ctype CallNonvirtual##_jname##MethodA(JNIEnv* env, jobject jobj,\
2122 jclass jclazz, jmethodID methodID, jvalue* args) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002123 { \
2124 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002125 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2126 ClassObject* clazz = \
2127 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002128 const Method* meth; \
2129 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002130 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002131 if (meth == NULL) { \
2132 JNI_EXIT(); \
2133 return _retfail; \
2134 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002135 dvmCallMethodA(_self, meth, obj, &result, args); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002136 if (_isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002137 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002138 JNI_EXIT(); \
2139 return _retok; \
2140 }
2141CALL_NONVIRTUAL(jobject, Object, NULL, result.l, true);
2142CALL_NONVIRTUAL(jboolean, Boolean, 0, result.z, false);
2143CALL_NONVIRTUAL(jbyte, Byte, 0, result.b, false);
2144CALL_NONVIRTUAL(jchar, Char, 0, result.c, false);
2145CALL_NONVIRTUAL(jshort, Short, 0, result.s, false);
2146CALL_NONVIRTUAL(jint, Int, 0, result.i, false);
2147CALL_NONVIRTUAL(jlong, Long, 0, result.j, false);
2148CALL_NONVIRTUAL(jfloat, Float, 0.0f, result.f, false);
2149CALL_NONVIRTUAL(jdouble, Double, 0.0, result.d, false);
2150CALL_NONVIRTUAL(void, Void, , , false);
2151
2152
2153/*
2154 * Call a static method.
2155 */
2156#define CALL_STATIC(_ctype, _jname, _retfail, _retok, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002157 static _ctype CallStatic##_jname##Method(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002158 jmethodID methodID, ...) \
2159 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002160 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002161 JNI_ENTER(); \
2162 JValue result; \
2163 va_list args; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002164 va_start(args, methodID); \
2165 dvmCallMethodV(_self, (Method*) methodID, NULL, &result, args); \
2166 va_end(args); \
2167 if (_isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002168 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002169 JNI_EXIT(); \
2170 return _retok; \
2171 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002172 static _ctype CallStatic##_jname##MethodV(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002173 jmethodID methodID, va_list args) \
2174 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002175 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002176 JNI_ENTER(); \
2177 JValue result; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002178 dvmCallMethodV(_self, (Method*) methodID, NULL, &result, args); \
2179 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 CallStatic##_jname##MethodA(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002185 jmethodID methodID, jvalue* args) \
2186 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002187 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002188 JNI_ENTER(); \
2189 JValue result; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002190 dvmCallMethodA(_self, (Method*) methodID, NULL, &result, args); \
2191 if (_isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002192 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002193 JNI_EXIT(); \
2194 return _retok; \
2195 }
2196CALL_STATIC(jobject, Object, NULL, result.l, true);
2197CALL_STATIC(jboolean, Boolean, 0, result.z, false);
2198CALL_STATIC(jbyte, Byte, 0, result.b, false);
2199CALL_STATIC(jchar, Char, 0, result.c, false);
2200CALL_STATIC(jshort, Short, 0, result.s, false);
2201CALL_STATIC(jint, Int, 0, result.i, false);
2202CALL_STATIC(jlong, Long, 0, result.j, false);
2203CALL_STATIC(jfloat, Float, 0.0f, result.f, false);
2204CALL_STATIC(jdouble, Double, 0.0, result.d, false);
2205CALL_STATIC(void, Void, , , false);
2206
2207/*
2208 * Create a new String from Unicode data.
2209 *
2210 * If "len" is zero, we will return an empty string even if "unicodeChars"
2211 * is NULL. (The JNI spec is vague here.)
2212 */
2213static jstring NewString(JNIEnv* env, const jchar* unicodeChars, jsize len)
2214{
2215 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002216 jobject retval;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002217
Andy McFaddenab00d452009-08-19 07:21:41 -07002218 StringObject* jstr = dvmCreateStringFromUnicode(unicodeChars, len);
2219 if (jstr == NULL) {
2220 retval = NULL;
2221 } else {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002222 dvmReleaseTrackedAlloc((Object*) jstr, NULL);
Andy McFaddenab00d452009-08-19 07:21:41 -07002223 retval = addLocalReference(env, (Object*) jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002224 }
2225
2226 JNI_EXIT();
2227 return jstr;
2228}
2229
2230/*
2231 * Return the length of a String in Unicode character units.
2232 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002233static jsize GetStringLength(JNIEnv* env, jstring jstr)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002234{
2235 JNI_ENTER();
2236
Andy McFaddenab00d452009-08-19 07:21:41 -07002237 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2238 jsize len = dvmStringLen(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002239
2240 JNI_EXIT();
2241 return len;
2242}
2243
Andy McFaddenab00d452009-08-19 07:21:41 -07002244
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002245/*
Andy McFaddenab00d452009-08-19 07:21:41 -07002246 * Get a string's character data.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002247 *
2248 * The result is guaranteed to be valid until ReleaseStringChars is
Andy McFaddenab00d452009-08-19 07:21:41 -07002249 * called, which means we have to pin it or return a copy.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002250 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002251static const jchar* GetStringChars(JNIEnv* env, jstring jstr, jboolean* isCopy)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002252{
2253 JNI_ENTER();
2254
Andy McFaddenab00d452009-08-19 07:21:41 -07002255 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2256 ArrayObject* strChars = dvmStringCharArray(jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002257
Andy McFaddenab00d452009-08-19 07:21:41 -07002258 pinPrimitiveArray(strChars);
2259
2260 const u2* data = dvmStringChars(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002261 if (isCopy != NULL)
2262 *isCopy = JNI_FALSE;
2263
2264 JNI_EXIT();
2265 return (jchar*)data;
2266}
2267
2268/*
2269 * Release our grip on some characters from a string.
2270 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002271static void ReleaseStringChars(JNIEnv* env, jstring jstr, const jchar* chars)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002272{
2273 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002274 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2275 ArrayObject* strChars = dvmStringCharArray(jstr);
2276 unpinPrimitiveArray(strChars);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002277 JNI_EXIT();
2278}
2279
2280/*
2281 * Create a new java.lang.String object from chars in modified UTF-8 form.
2282 *
2283 * The spec doesn't say how to handle a NULL string. Popular desktop VMs
2284 * accept it and return a NULL pointer in response.
2285 */
2286static jstring NewStringUTF(JNIEnv* env, const char* bytes)
2287{
2288 JNI_ENTER();
2289
Andy McFaddenab00d452009-08-19 07:21:41 -07002290 jstring result;
2291
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002292 if (bytes == NULL) {
Andy McFaddenab00d452009-08-19 07:21:41 -07002293 result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002294 } else {
Andy McFaddenab00d452009-08-19 07:21:41 -07002295 /* note newStr could come back NULL on OOM */
2296 StringObject* newStr = dvmCreateStringFromCstr(bytes, ALLOC_DEFAULT);
2297 result = addLocalReference(env, (Object*) newStr);
2298 dvmReleaseTrackedAlloc((Object*)newStr, NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002299 }
2300
2301 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002302 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002303}
2304
2305/*
2306 * Return the length in bytes of the modified UTF-8 form of the string.
2307 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002308static jsize GetStringUTFLength(JNIEnv* env, jstring jstr)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002309{
2310 JNI_ENTER();
2311
Andy McFaddenab00d452009-08-19 07:21:41 -07002312 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2313 jsize len = dvmStringUtf8ByteLen(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002314
2315 JNI_EXIT();
2316 return len;
2317}
2318
2319/*
2320 * Convert "string" to modified UTF-8 and return a pointer. The returned
2321 * value must be released with ReleaseStringUTFChars.
2322 *
2323 * According to the JNI reference, "Returns a pointer to a UTF-8 string,
2324 * or NULL if the operation fails. Returns NULL if and only if an invocation
2325 * of this function has thrown an exception."
2326 *
2327 * The behavior here currently follows that of other open-source VMs, which
2328 * quietly return NULL if "string" is NULL. We should consider throwing an
2329 * NPE. (The CheckJNI code blows up if you try to pass in a NULL string,
2330 * which should catch this sort of thing during development.) Certain other
2331 * VMs will crash with a segmentation fault.
2332 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002333static const char* GetStringUTFChars(JNIEnv* env, jstring jstr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002334 jboolean* isCopy)
2335{
2336 JNI_ENTER();
2337 char* newStr;
2338
Andy McFaddenab00d452009-08-19 07:21:41 -07002339 if (jstr == NULL) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002340 /* this shouldn't happen; throw NPE? */
2341 newStr = NULL;
2342 } else {
2343 if (isCopy != NULL)
2344 *isCopy = JNI_TRUE;
2345
Andy McFaddenab00d452009-08-19 07:21:41 -07002346 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2347 newStr = dvmCreateCstrFromString(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002348 if (newStr == NULL) {
2349 /* assume memory failure */
2350 dvmThrowException("Ljava/lang/OutOfMemoryError;",
2351 "native heap string alloc failed");
2352 }
2353 }
2354
2355 JNI_EXIT();
2356 return newStr;
2357}
2358
2359/*
2360 * Release a string created by GetStringUTFChars().
2361 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002362static void ReleaseStringUTFChars(JNIEnv* env, jstring jstr, const char* utf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002363{
2364 JNI_ENTER();
2365 free((char*)utf);
2366 JNI_EXIT();
2367}
2368
2369/*
2370 * Return the capacity of the array.
2371 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002372static jsize GetArrayLength(JNIEnv* env, jarray jarr)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002373{
2374 JNI_ENTER();
2375
Andy McFaddenab00d452009-08-19 07:21:41 -07002376 ArrayObject* arrObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
2377 jsize length = arrObj->length;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002378
2379 JNI_EXIT();
2380 return length;
2381}
2382
2383/*
2384 * Construct a new array that holds objects from class "elementClass".
2385 */
2386static jobjectArray NewObjectArray(JNIEnv* env, jsize length,
Andy McFaddenab00d452009-08-19 07:21:41 -07002387 jclass jelementClass, jobject jinitialElement)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002388{
2389 JNI_ENTER();
2390
Andy McFaddenab00d452009-08-19 07:21:41 -07002391 jobjectArray newArray = NULL;
2392 ClassObject* elemClassObj =
2393 (ClassObject*) dvmDecodeIndirectRef(env, jelementClass);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002394
2395 if (elemClassObj == NULL) {
2396 dvmThrowException("Ljava/lang/NullPointerException;",
2397 "JNI NewObjectArray");
2398 goto bail;
2399 }
2400
Andy McFaddenab00d452009-08-19 07:21:41 -07002401 ArrayObject* newObj =
2402 dvmAllocObjectArray(elemClassObj, length, ALLOC_DEFAULT);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002403 if (newObj == NULL) {
2404 assert(dvmCheckException(_self));
2405 goto bail;
2406 }
Andy McFaddenab00d452009-08-19 07:21:41 -07002407 newArray = addLocalReference(env, (Object*) newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002408 dvmReleaseTrackedAlloc((Object*) newObj, NULL);
2409
2410 /*
2411 * Initialize the array. Trashes "length".
2412 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002413 if (jinitialElement != NULL) {
2414 Object* initialElement = dvmDecodeIndirectRef(env, jinitialElement);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002415 Object** arrayData = (Object**) newObj->contents;
2416
2417 while (length--)
Andy McFaddenab00d452009-08-19 07:21:41 -07002418 *arrayData++ = initialElement;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002419 }
2420
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002421
2422bail:
2423 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002424 return newArray;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002425}
2426
2427/*
2428 * Get one element of an Object array.
2429 *
2430 * Add the object to the local references table in case the array goes away.
2431 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002432static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray jarr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002433 jsize index)
2434{
2435 JNI_ENTER();
2436
Andy McFaddenab00d452009-08-19 07:21:41 -07002437 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
2438 jobject retval = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002439
Andy McFaddenab00d452009-08-19 07:21:41 -07002440 assert(arrayObj != NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002441
2442 /* check the array bounds */
2443 if (index < 0 || index >= (int) arrayObj->length) {
2444 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
2445 arrayObj->obj.clazz->descriptor);
2446 goto bail;
2447 }
2448
Andy McFaddenab00d452009-08-19 07:21:41 -07002449 Object* value = ((Object**) arrayObj->contents)[index];
2450 retval = addLocalReference(env, value);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002451
2452bail:
2453 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002454 return retval;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002455}
2456
2457/*
2458 * Set one element of an Object array.
2459 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002460static void SetObjectArrayElement(JNIEnv* env, jobjectArray jarr,
2461 jsize index, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002462{
2463 JNI_ENTER();
2464
Andy McFaddenab00d452009-08-19 07:21:41 -07002465 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002466
Andy McFaddenab00d452009-08-19 07:21:41 -07002467 assert(arrayObj != NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002468
2469 /* check the array bounds */
2470 if (index < 0 || index >= (int) arrayObj->length) {
2471 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
2472 arrayObj->obj.clazz->descriptor);
2473 goto bail;
2474 }
2475
2476 //LOGV("JNI: set element %d in array %p to %p\n", index, array, value);
2477
Andy McFaddenab00d452009-08-19 07:21:41 -07002478 Object* obj;
2479 if (jobj == NULL)
2480 obj = NULL;
2481 else
2482 obj = dvmDecodeIndirectRef(env, jobj);
2483 ((Object**) arrayObj->contents)[index] = obj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002484
2485bail:
2486 JNI_EXIT();
2487}
2488
2489/*
2490 * Create a new array of primitive elements.
2491 */
2492#define NEW_PRIMITIVE_ARRAY(_artype, _jname, _typechar) \
2493 static _artype New##_jname##Array(JNIEnv* env, jsize length) \
2494 { \
2495 JNI_ENTER(); \
2496 ArrayObject* arrayObj; \
2497 arrayObj = dvmAllocPrimitiveArray(_typechar, length, \
2498 ALLOC_DEFAULT); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002499 jarray jarr = NULL; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002500 if (arrayObj != NULL) { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002501 jarr = addLocalReference(env, (Object*) arrayObj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002502 dvmReleaseTrackedAlloc((Object*) arrayObj, NULL); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002503 } \
2504 JNI_EXIT(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002505 return (_artype)jarr; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002506 }
2507NEW_PRIMITIVE_ARRAY(jbooleanArray, Boolean, 'Z');
2508NEW_PRIMITIVE_ARRAY(jbyteArray, Byte, 'B');
2509NEW_PRIMITIVE_ARRAY(jcharArray, Char, 'C');
2510NEW_PRIMITIVE_ARRAY(jshortArray, Short, 'S');
2511NEW_PRIMITIVE_ARRAY(jintArray, Int, 'I');
2512NEW_PRIMITIVE_ARRAY(jlongArray, Long, 'J');
2513NEW_PRIMITIVE_ARRAY(jfloatArray, Float, 'F');
2514NEW_PRIMITIVE_ARRAY(jdoubleArray, Double, 'D');
2515
2516/*
2517 * Get a pointer to a C array of primitive elements from an array object
2518 * of the matching type.
2519 *
Andy McFaddenab00d452009-08-19 07:21:41 -07002520 * In a compacting GC, we either need to return a copy of the elements or
2521 * "pin" the memory. Otherwise we run the risk of native code using the
2522 * buffer as the destination of e.g. a blocking read() call that wakes up
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002523 * during a GC.
2524 */
2525#define GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
2526 static _ctype* Get##_jname##ArrayElements(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07002527 _ctype##Array jarr, jboolean* isCopy) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002528 { \
2529 JNI_ENTER(); \
2530 _ctype* data; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002531 ArrayObject* arrayObj = \
2532 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
2533 pinPrimitiveArray(arrayObj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002534 data = (_ctype*) arrayObj->contents; \
2535 if (isCopy != NULL) \
2536 *isCopy = JNI_FALSE; \
2537 JNI_EXIT(); \
2538 return data; \
2539 }
2540
2541/*
2542 * Release the storage locked down by the "get" function.
2543 *
Andy McFaddenab00d452009-08-19 07:21:41 -07002544 * 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 -08002545 * elements in 'array'." They apparently did not anticipate the need to
Andy McFaddenab00d452009-08-19 07:21:41 -07002546 * un-pin memory.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002547 */
2548#define RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
2549 static void Release##_jname##ArrayElements(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07002550 _ctype##Array jarr, _ctype* elems, jint mode) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002551 { \
2552 UNUSED_PARAMETER(elems); \
2553 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002554 if (mode != JNI_COMMIT) { \
2555 ArrayObject* arrayObj = \
2556 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
2557 unpinPrimitiveArray(arrayObj); \
2558 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002559 JNI_EXIT(); \
2560 }
2561
2562/*
2563 * Copy a section of a primitive array to a buffer.
2564 */
2565#define GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
2566 static void Get##_jname##ArrayRegion(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07002567 _ctype##Array jarr, jsize start, jsize len, _ctype* buf) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002568 { \
2569 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002570 ArrayObject* arrayObj = \
2571 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002572 _ctype* data = (_ctype*) arrayObj->contents; \
2573 if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
2574 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
2575 arrayObj->obj.clazz->descriptor); \
2576 } else { \
2577 memcpy(buf, data + start, len * sizeof(_ctype)); \
2578 } \
2579 JNI_EXIT(); \
2580 }
2581
2582/*
2583 * Copy a section of a primitive array to a buffer.
2584 */
2585#define SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
2586 static void Set##_jname##ArrayRegion(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07002587 _ctype##Array jarr, jsize start, jsize len, const _ctype* buf) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002588 { \
2589 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002590 ArrayObject* arrayObj = \
2591 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002592 _ctype* data = (_ctype*) arrayObj->contents; \
2593 if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
2594 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
2595 arrayObj->obj.clazz->descriptor); \
2596 } else { \
2597 memcpy(data + start, buf, len * sizeof(_ctype)); \
2598 } \
2599 JNI_EXIT(); \
2600 }
2601
2602/*
2603 * 4-in-1:
2604 * Get<Type>ArrayElements
2605 * Release<Type>ArrayElements
2606 * Get<Type>ArrayRegion
2607 * Set<Type>ArrayRegion
2608 */
2609#define PRIMITIVE_ARRAY_FUNCTIONS(_ctype, _jname) \
2610 GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
2611 RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
2612 GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname); \
2613 SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname);
2614
2615PRIMITIVE_ARRAY_FUNCTIONS(jboolean, Boolean);
2616PRIMITIVE_ARRAY_FUNCTIONS(jbyte, Byte);
2617PRIMITIVE_ARRAY_FUNCTIONS(jchar, Char);
2618PRIMITIVE_ARRAY_FUNCTIONS(jshort, Short);
2619PRIMITIVE_ARRAY_FUNCTIONS(jint, Int);
2620PRIMITIVE_ARRAY_FUNCTIONS(jlong, Long);
2621PRIMITIVE_ARRAY_FUNCTIONS(jfloat, Float);
2622PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double);
2623
2624/*
2625 * Register one or more native functions in one class.
2626 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002627static jint RegisterNatives(JNIEnv* env, jclass jclazz,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002628 const JNINativeMethod* methods, jint nMethods)
2629{
2630 JNI_ENTER();
2631
Andy McFaddenab00d452009-08-19 07:21:41 -07002632 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002633 jint retval;
2634 int i;
2635
2636 if (gDvm.verboseJni) {
2637 LOGI("[Registering JNI native methods for class %s]\n",
Andy McFaddenab00d452009-08-19 07:21:41 -07002638 clazz->descriptor);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002639 }
2640
2641 for (i = 0; i < nMethods; i++) {
Andy McFaddenab00d452009-08-19 07:21:41 -07002642 if (!dvmRegisterJNIMethod(clazz, methods[i].name,
2643 methods[i].signature, methods[i].fnPtr))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002644 {
2645 retval = JNI_ERR;
2646 goto bail;
2647 }
2648 }
2649 retval = JNI_OK;
2650
2651bail:
2652 JNI_EXIT();
2653 return retval;
2654}
2655
2656/*
2657 * Un-register a native function.
2658 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002659static jint UnregisterNatives(JNIEnv* env, jclass jclazz)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002660{
2661 JNI_ENTER();
2662 /*
2663 * The JNI docs refer to this as a way to reload/relink native libraries,
2664 * and say it "should not be used in normal native code".
2665 *
2666 * We can implement it if we decide we need it.
2667 */
2668 JNI_EXIT();
2669 return JNI_ERR;
2670}
2671
2672/*
2673 * Lock the monitor.
2674 *
2675 * We have to track all monitor enters and exits, so that we can undo any
2676 * outstanding synchronization before the thread exits.
2677 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002678static jint MonitorEnter(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002679{
2680 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002681 Object* obj = dvmDecodeIndirectRef(env, jobj);
2682 dvmLockObject(_self, obj);
2683 trackMonitorEnter(_self, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002684 JNI_EXIT();
2685 return JNI_OK;
2686}
2687
2688/*
2689 * Unlock the monitor.
2690 *
2691 * Throws an IllegalMonitorStateException if the current thread
Andy McFaddenab00d452009-08-19 07:21:41 -07002692 * doesn't own the monitor. (dvmUnlockObject() takes care of the throw.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002693 *
2694 * According to the 1.6 spec, it's legal to call here with an exception
2695 * pending. If this fails, we'll stomp the original exception.
2696 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002697static jint MonitorExit(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002698{
2699 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002700 Object* obj = dvmDecodeIndirectRef(env, jobj);
2701 bool success = dvmUnlockObject(_self, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002702 if (success)
Andy McFaddenab00d452009-08-19 07:21:41 -07002703 trackMonitorExit(_self, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002704 JNI_EXIT();
2705 return success ? JNI_OK : JNI_ERR;
2706}
2707
2708/*
2709 * Return the JavaVM interface associated with the current thread.
2710 */
2711static jint GetJavaVM(JNIEnv* env, JavaVM** vm)
2712{
2713 JNI_ENTER();
2714 //*vm = gDvm.vmList;
2715 *vm = (JavaVM*) ((JNIEnvExt*)env)->vm;
2716 JNI_EXIT();
2717 if (*vm == NULL)
2718 return JNI_ERR;
2719 else
2720 return JNI_OK;
2721}
2722
2723/*
2724 * Copies "len" Unicode characters, from offset "start".
2725 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002726static void GetStringRegion(JNIEnv* env, jstring jstr, jsize start, jsize len,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002727 jchar* buf)
2728{
2729 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002730 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002731 if (start + len > dvmStringLen(strObj))
2732 dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
2733 else
2734 memcpy(buf, dvmStringChars(strObj) + start, len * sizeof(u2));
2735 JNI_EXIT();
2736}
2737
2738/*
2739 * Translates "len" Unicode characters, from offset "start", into
2740 * modified UTF-8 encoding.
2741 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002742static void GetStringUTFRegion(JNIEnv* env, jstring jstr, jsize start,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002743 jsize len, char* buf)
2744{
2745 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002746 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002747 if (start + len > dvmStringLen(strObj))
2748 dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
2749 else
2750 dvmCreateCstrFromStringRegion(strObj, start, len, buf);
2751 JNI_EXIT();
2752}
2753
2754/*
2755 * Get a raw pointer to array data.
2756 *
2757 * The caller is expected to call "release" before doing any JNI calls
2758 * or blocking I/O operations.
2759 *
Andy McFaddenab00d452009-08-19 07:21:41 -07002760 * We need to pin the memory or block GC.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002761 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002762static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray jarr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002763 jboolean* isCopy)
2764{
2765 JNI_ENTER();
2766 void* data;
Andy McFaddenab00d452009-08-19 07:21:41 -07002767 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
2768 pinPrimitiveArray(arrayObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002769 data = arrayObj->contents;
2770 if (isCopy != NULL)
2771 *isCopy = JNI_FALSE;
2772 JNI_EXIT();
2773 return data;
2774}
2775
2776/*
2777 * Release an array obtained with GetPrimitiveArrayCritical.
2778 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002779static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray jarr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002780 void* carray, jint mode)
2781{
2782 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002783 if (mode != JNI_COMMIT) {
2784 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
2785 unpinPrimitiveArray(arrayObj);
2786 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002787 JNI_EXIT();
2788}
2789
2790/*
2791 * Like GetStringChars, but with restricted use.
2792 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002793static const jchar* GetStringCritical(JNIEnv* env, jstring jstr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002794 jboolean* isCopy)
2795{
2796 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002797 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2798 ArrayObject* strChars = dvmStringCharArray(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002799
Andy McFaddenab00d452009-08-19 07:21:41 -07002800 pinPrimitiveArray(strChars);
2801
2802 const u2* data = dvmStringChars(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002803 if (isCopy != NULL)
2804 *isCopy = JNI_FALSE;
2805
2806 JNI_EXIT();
2807 return (jchar*)data;
2808}
2809
2810/*
2811 * Like ReleaseStringChars, but with restricted use.
2812 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002813static void ReleaseStringCritical(JNIEnv* env, jstring jstr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002814 const jchar* carray)
2815{
2816 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002817 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2818 ArrayObject* strChars = dvmStringCharArray(jstr);
2819 unpinPrimitiveArray(strChars);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002820 JNI_EXIT();
2821}
2822
2823/*
2824 * Create a new weak global reference.
2825 */
2826static jweak NewWeakGlobalRef(JNIEnv* env, jobject obj)
2827{
2828 JNI_ENTER();
2829 // TODO - implement
2830 jobject gref = NULL;
2831 LOGE("JNI ERROR: NewWeakGlobalRef not implemented\n");
2832 dvmAbort();
2833 JNI_EXIT();
2834 return gref;
2835}
2836
2837/*
2838 * Delete the specified weak global reference.
2839 */
2840static void DeleteWeakGlobalRef(JNIEnv* env, jweak obj)
2841{
2842 JNI_ENTER();
2843 // TODO - implement
2844 LOGE("JNI ERROR: DeleteWeakGlobalRef not implemented\n");
2845 dvmAbort();
2846 JNI_EXIT();
2847}
2848
2849/*
2850 * Quick check for pending exceptions.
2851 *
2852 * TODO: we should be able to skip the enter/exit macros here.
2853 */
2854static jboolean ExceptionCheck(JNIEnv* env)
2855{
2856 JNI_ENTER();
2857 bool result = dvmCheckException(_self);
2858 JNI_EXIT();
2859 return result;
2860}
2861
2862/*
2863 * Returns the type of the object referred to by "obj". It can be local,
2864 * global, or weak global.
2865 *
2866 * In the current implementation, references can be global and local at
2867 * the same time, so while the return value is accurate it may not tell
2868 * the whole story.
2869 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002870static jobjectRefType GetObjectRefType(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002871{
2872 JNI_ENTER();
2873 jobjectRefType type;
The Android Open Source Project99409882009-03-18 22:20:24 -07002874
Andy McFaddenab00d452009-08-19 07:21:41 -07002875 if (jobj == NULL)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002876 type = JNIInvalidRefType;
2877 else
Andy McFaddenab00d452009-08-19 07:21:41 -07002878 type = dvmGetJNIRefType(env, jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002879 JNI_EXIT();
2880 return type;
2881}
2882
2883/*
2884 * Allocate and return a new java.nio.ByteBuffer for this block of memory.
2885 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07002886 * "address" may not be NULL, and "capacity" must be > 0. (These are only
2887 * verified when CheckJNI is enabled.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002888 */
Andy McFadden8e5c7842009-07-23 17:47:18 -07002889static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002890{
Andy McFadden8e5c7842009-07-23 17:47:18 -07002891 JNI_ENTER();
2892
2893 Thread* self = _self /*dvmThreadSelf()*/;
2894 Object* platformAddress = NULL;
2895 JValue callResult;
The Android Open Source Project99409882009-03-18 22:20:24 -07002896 jobject result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002897
Andy McFadden8e5c7842009-07-23 17:47:18 -07002898 /* get an instance of PlatformAddress that wraps the provided address */
2899 dvmCallMethod(self,
2900 gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on,
2901 NULL, &callResult, address);
2902 if (dvmGetException(self) != NULL || callResult.l == NULL)
The Android Open Source Project99409882009-03-18 22:20:24 -07002903 goto bail;
Andy McFadden8e5c7842009-07-23 17:47:18 -07002904
2905 /* don't let the GC discard it */
2906 platformAddress = (Object*) callResult.l;
2907 dvmAddTrackedAlloc(platformAddress, self);
2908 LOGV("tracking %p for address=%p\n", platformAddress, address);
2909
2910 /* create an instance of java.nio.ReadWriteDirectByteBuffer */
2911 ClassObject* clazz = gDvm.classJavaNioReadWriteDirectByteBuffer;
2912 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))
2913 goto bail;
2914 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
2915 if (newObj != NULL) {
2916 /* call the (PlatformAddress, int, int) constructor */
Andy McFaddenab00d452009-08-19 07:21:41 -07002917 result = addLocalReference(env, newObj);
Andy McFadden8e5c7842009-07-23 17:47:18 -07002918 dvmCallMethod(self, gDvm.methJavaNioReadWriteDirectByteBuffer_init,
2919 newObj, &callResult, platformAddress, (jint) capacity, (jint) 0);
Andy McFaddenab00d452009-08-19 07:21:41 -07002920 if (dvmGetException(self) != NULL) {
2921 deleteLocalReference(env, result);
2922 result = NULL;
Andy McFadden8e5c7842009-07-23 17:47:18 -07002923 goto bail;
Andy McFaddenab00d452009-08-19 07:21:41 -07002924 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002925 }
2926
The Android Open Source Project99409882009-03-18 22:20:24 -07002927bail:
Andy McFadden8e5c7842009-07-23 17:47:18 -07002928 if (platformAddress != NULL)
2929 dvmReleaseTrackedAlloc(platformAddress, self);
2930 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07002931 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002932}
2933
2934/*
2935 * Get the starting address of the buffer for the specified java.nio.Buffer.
2936 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07002937 * If this is not a "direct" buffer, we return NULL.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002938 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002939static void* GetDirectBufferAddress(JNIEnv* env, jobject jbuf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002940{
Andy McFadden8e5c7842009-07-23 17:47:18 -07002941 JNI_ENTER();
2942
Andy McFaddenab00d452009-08-19 07:21:41 -07002943 Object* bufObj = dvmDecodeIndirectRef(env, jbuf);
Andy McFadden8e5c7842009-07-23 17:47:18 -07002944 Thread* self = _self /*dvmThreadSelf()*/;
Andy McFadden8e696dc2009-07-24 15:28:16 -07002945 void* result;
2946
2947 /*
2948 * All Buffer objects have an effectiveDirectAddress field. If it's
2949 * nonzero, we can just return that value. If not, we have to call
2950 * through DirectBuffer.getEffectiveAddress(), which as a side-effect
2951 * will set the effectiveDirectAddress field for direct buffers (and
2952 * things that wrap direct buffers).
2953 */
2954 result = (void*) dvmGetFieldInt(bufObj,
2955 gDvm.offJavaNioBuffer_effectiveDirectAddress);
2956 if (result != NULL) {
2957 //LOGI("fast path for %p\n", buf);
2958 goto bail;
2959 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002960
Andy McFadden72d61fb2009-06-24 16:56:06 -07002961 /*
2962 * Start by determining if the object supports the DirectBuffer
2963 * interfaces. Note this does not guarantee that it's a direct buffer.
2964 */
Andy McFadden8e5c7842009-07-23 17:47:18 -07002965 if (!dvmInstanceof(bufObj->clazz,
2966 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002967 {
The Android Open Source Project99409882009-03-18 22:20:24 -07002968 goto bail;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002969 }
2970
Andy McFadden72d61fb2009-06-24 16:56:06 -07002971 /*
Andy McFadden8e5c7842009-07-23 17:47:18 -07002972 * Get a PlatformAddress object with the effective address.
Andy McFadden5f612b82009-07-22 15:07:27 -07002973 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07002974 * If this isn't a direct buffer, the result will be NULL and/or an
Andy McFadden72d61fb2009-06-24 16:56:06 -07002975 * exception will have been thrown.
2976 */
Andy McFadden8e696dc2009-07-24 15:28:16 -07002977 JValue callResult;
Andy McFadden8e5c7842009-07-23 17:47:18 -07002978 const Method* meth = dvmGetVirtualizedMethod(bufObj->clazz,
2979 gDvm.methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress);
2980 dvmCallMethodA(self, meth, bufObj, &callResult, NULL);
2981 if (dvmGetException(self) != NULL) {
2982 dvmClearException(self);
2983 callResult.l = NULL;
Andy McFadden72d61fb2009-06-24 16:56:06 -07002984 }
Andy McFadden8e5c7842009-07-23 17:47:18 -07002985
Andy McFadden8e696dc2009-07-24 15:28:16 -07002986 Object* platformAddr = callResult.l;
Andy McFadden72d61fb2009-06-24 16:56:06 -07002987 if (platformAddr == NULL) {
Andy McFadden8e696dc2009-07-24 15:28:16 -07002988 LOGV("Got request for address of non-direct buffer\n");
Andy McFadden72d61fb2009-06-24 16:56:06 -07002989 goto bail;
2990 }
2991
Andy McFadden8e5c7842009-07-23 17:47:18 -07002992 /*
2993 * Extract the address from the PlatformAddress object. Instead of
2994 * calling the toLong() method, just grab the field directly. This
2995 * is faster but more fragile.
2996 */
2997 result = (void*) dvmGetFieldInt(platformAddr,
2998 gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr);
The Android Open Source Project99409882009-03-18 22:20:24 -07002999
Andy McFadden8e696dc2009-07-24 15:28:16 -07003000 //LOGI("slow path for %p --> %p\n", buf, result);
3001
The Android Open Source Project99409882009-03-18 22:20:24 -07003002bail:
Andy McFadden8e5c7842009-07-23 17:47:18 -07003003 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07003004 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003005}
3006
3007/*
3008 * Get the capacity of the buffer for the specified java.nio.Buffer.
3009 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003010 * Returns -1 if the object is not a direct buffer. (We actually skip
3011 * this check, since it's expensive to determine, and just return the
3012 * capacity regardless.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003013 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003014static jlong GetDirectBufferCapacity(JNIEnv* env, jobject jbuf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003015{
Andy McFadden8e5c7842009-07-23 17:47:18 -07003016 JNI_ENTER();
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003017
Andy McFadden8e5c7842009-07-23 17:47:18 -07003018 /*
3019 * The capacity is always in the Buffer.capacity field.
3020 *
3021 * (The "check" version should verify that this is actually a Buffer,
3022 * but we're not required to do so here.)
3023 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003024 Object* buf = dvmDecodeIndirectRef(env, jbuf);
3025 jlong result = dvmGetFieldInt(buf, gDvm.offJavaNioBuffer_capacity);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003026
Andy McFadden8e5c7842009-07-23 17:47:18 -07003027 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07003028 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003029}
3030
3031
3032/*
3033 * ===========================================================================
3034 * JNI invocation functions
3035 * ===========================================================================
3036 */
3037
3038/*
3039 * Handle AttachCurrentThread{AsDaemon}.
3040 *
3041 * We need to make sure the VM is actually running. For example, if we start
3042 * up, issue an Attach, and the VM exits almost immediately, by the time the
3043 * attaching happens the VM could already be shutting down.
3044 *
3045 * It's hard to avoid a race condition here because we don't want to hold
3046 * a lock across the entire operation. What we can do is temporarily
3047 * increment the thread count to prevent a VM exit.
3048 *
3049 * This could potentially still have problems if a daemon thread calls here
3050 * while the VM is shutting down. dvmThreadSelf() will work, since it just
3051 * uses pthread TLS, but dereferencing "vm" could fail. Such is life when
3052 * you shut down a VM while threads are still running inside it.
3053 *
3054 * Remember that some code may call this as a way to find the per-thread
3055 * JNIEnv pointer. Don't do excess work for that case.
3056 */
3057static jint attachThread(JavaVM* vm, JNIEnv** p_env, void* thr_args,
3058 bool isDaemon)
3059{
3060 JavaVMAttachArgs* args = (JavaVMAttachArgs*) thr_args;
3061 Thread* self;
3062 bool result = false;
3063
3064 /*
3065 * Return immediately if we're already one with the VM.
3066 */
3067 self = dvmThreadSelf();
3068 if (self != NULL) {
3069 *p_env = self->jniEnv;
3070 return JNI_OK;
3071 }
3072
3073 /*
3074 * No threads allowed in zygote mode.
3075 */
3076 if (gDvm.zygote) {
3077 return JNI_ERR;
3078 }
3079
3080 /* increment the count to keep the VM from bailing while we run */
3081 dvmLockThreadList(NULL);
3082 if (gDvm.nonDaemonThreadCount == 0) {
3083 // dead or dying
3084 LOGV("Refusing to attach thread '%s' -- VM is shutting down\n",
3085 (thr_args == NULL) ? "(unknown)" : args->name);
3086 dvmUnlockThreadList();
3087 return JNI_ERR;
3088 }
3089 gDvm.nonDaemonThreadCount++;
3090 dvmUnlockThreadList();
3091
3092 /* tweak the JavaVMAttachArgs as needed */
3093 JavaVMAttachArgs argsCopy;
3094 if (args == NULL) {
3095 /* allow the v1.1 calling convention */
3096 argsCopy.version = JNI_VERSION_1_2;
3097 argsCopy.name = NULL;
3098 argsCopy.group = dvmGetMainThreadGroup();
3099 } else {
3100 assert(args->version >= JNI_VERSION_1_2);
3101
3102 argsCopy.version = args->version;
3103 argsCopy.name = args->name;
3104 if (args->group != NULL)
3105 argsCopy.group = args->group;
3106 else
3107 argsCopy.group = dvmGetMainThreadGroup();
3108 }
3109
3110 result = dvmAttachCurrentThread(&argsCopy, isDaemon);
3111
3112 /* restore the count */
3113 dvmLockThreadList(NULL);
3114 gDvm.nonDaemonThreadCount--;
3115 dvmUnlockThreadList();
3116
3117 /*
3118 * Change the status to indicate that we're out in native code. This
3119 * call is not guarded with state-change macros, so we have to do it
3120 * by hand.
3121 */
3122 if (result) {
3123 self = dvmThreadSelf();
3124 assert(self != NULL);
3125 dvmChangeStatus(self, THREAD_NATIVE);
3126 *p_env = self->jniEnv;
3127 return JNI_OK;
3128 } else {
3129 return JNI_ERR;
3130 }
3131}
3132
3133/*
3134 * Attach the current thread to the VM. If the thread is already attached,
3135 * this is a no-op.
3136 */
3137static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args)
3138{
3139 return attachThread(vm, p_env, thr_args, false);
3140}
3141
3142/*
3143 * Like AttachCurrentThread, but set the "daemon" flag.
3144 */
3145static jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env,
3146 void* thr_args)
3147{
3148 return attachThread(vm, p_env, thr_args, true);
3149}
3150
3151/*
3152 * Dissociate the current thread from the VM.
3153 */
3154static jint DetachCurrentThread(JavaVM* vm)
3155{
3156 Thread* self = dvmThreadSelf();
3157
3158 if (self == NULL) /* not attached, can't do anything */
3159 return JNI_ERR;
3160
3161 /* switch to "running" to check for suspension */
3162 dvmChangeStatus(self, THREAD_RUNNING);
3163
3164 /* detach the thread */
3165 dvmDetachCurrentThread();
3166
3167 /* (no need to change status back -- we have no status) */
3168 return JNI_OK;
3169}
3170
3171/*
3172 * If current thread is attached to VM, return the associated JNIEnv.
3173 * Otherwise, stuff NULL in and return JNI_EDETACHED.
3174 *
3175 * JVMTI overloads this by specifying a magic value for "version", so we
3176 * do want to check that here.
3177 */
3178static jint GetEnv(JavaVM* vm, void** env, jint version)
3179{
3180 Thread* self = dvmThreadSelf();
3181
3182 if (version < JNI_VERSION_1_1 || version > JNI_VERSION_1_6)
3183 return JNI_EVERSION;
3184
3185 if (self == NULL) {
3186 *env = NULL;
3187 } else {
3188 /* TODO: status change is probably unnecessary */
3189 dvmChangeStatus(self, THREAD_RUNNING);
3190 *env = (void*) dvmGetThreadJNIEnv(self);
3191 dvmChangeStatus(self, THREAD_NATIVE);
3192 }
3193 if (*env == NULL)
3194 return JNI_EDETACHED;
3195 else
3196 return JNI_OK;
3197}
3198
3199/*
3200 * Destroy the VM. This may be called from any thread.
3201 *
3202 * If the current thread is attached, wait until the current thread is
3203 * the only non-daemon user-level thread. If the current thread is not
3204 * attached, we attach it and do the processing as usual. (If the attach
3205 * fails, it's probably because all the non-daemon threads have already
3206 * exited and the VM doesn't want to let us back in.)
3207 *
3208 * TODO: we don't really deal with the situation where more than one thread
3209 * has called here. One thread wins, the other stays trapped waiting on
3210 * the condition variable forever. Not sure this situation is interesting
3211 * in real life.
3212 */
3213static jint DestroyJavaVM(JavaVM* vm)
3214{
3215 JavaVMExt* ext = (JavaVMExt*) vm;
3216 Thread* self;
3217
3218 if (ext == NULL)
3219 return JNI_ERR;
3220
3221 LOGD("DestroyJavaVM waiting for non-daemon threads to exit\n");
3222
3223 /*
3224 * Sleep on a condition variable until it's okay to exit.
3225 */
3226 self = dvmThreadSelf();
3227 if (self == NULL) {
3228 JNIEnv* tmpEnv;
3229 if (AttachCurrentThread(vm, &tmpEnv, NULL) != JNI_OK) {
3230 LOGV("Unable to reattach main for Destroy; assuming VM is "
3231 "shutting down (count=%d)\n",
3232 gDvm.nonDaemonThreadCount);
3233 goto shutdown;
3234 } else {
3235 LOGV("Attached to wait for shutdown in Destroy\n");
3236 }
3237 }
3238 dvmChangeStatus(self, THREAD_VMWAIT);
3239
3240 dvmLockThreadList(self);
3241 gDvm.nonDaemonThreadCount--; // remove current thread from count
3242
3243 while (gDvm.nonDaemonThreadCount > 0)
3244 pthread_cond_wait(&gDvm.vmExitCond, &gDvm.threadListLock);
3245
3246 dvmUnlockThreadList();
3247 self = NULL;
3248
3249shutdown:
3250 // TODO: call System.exit() to run any registered shutdown hooks
3251 // (this may not return -- figure out how this should work)
3252
3253 LOGD("DestroyJavaVM shutting VM down\n");
3254 dvmShutdown();
3255
3256 // TODO - free resources associated with JNI-attached daemon threads
3257 free(ext->envList);
3258 free(ext);
3259
3260 return JNI_OK;
3261}
3262
3263
3264/*
3265 * ===========================================================================
3266 * Function tables
3267 * ===========================================================================
3268 */
3269
3270static const struct JNINativeInterface gNativeInterface = {
3271 NULL,
3272 NULL,
3273 NULL,
3274 NULL,
3275
3276 GetVersion,
3277
3278 DefineClass,
3279 FindClass,
3280
3281 FromReflectedMethod,
3282 FromReflectedField,
3283 ToReflectedMethod,
3284
3285 GetSuperclass,
3286 IsAssignableFrom,
3287
3288 ToReflectedField,
3289
3290 Throw,
3291 ThrowNew,
3292 ExceptionOccurred,
3293 ExceptionDescribe,
3294 ExceptionClear,
3295 FatalError,
3296
3297 PushLocalFrame,
3298 PopLocalFrame,
3299
3300 NewGlobalRef,
3301 DeleteGlobalRef,
3302 DeleteLocalRef,
3303 IsSameObject,
3304 NewLocalRef,
3305 EnsureLocalCapacity,
3306
3307 AllocObject,
3308 NewObject,
3309 NewObjectV,
3310 NewObjectA,
3311
3312 GetObjectClass,
3313 IsInstanceOf,
3314
3315 GetMethodID,
3316
3317 CallObjectMethod,
3318 CallObjectMethodV,
3319 CallObjectMethodA,
3320 CallBooleanMethod,
3321 CallBooleanMethodV,
3322 CallBooleanMethodA,
3323 CallByteMethod,
3324 CallByteMethodV,
3325 CallByteMethodA,
3326 CallCharMethod,
3327 CallCharMethodV,
3328 CallCharMethodA,
3329 CallShortMethod,
3330 CallShortMethodV,
3331 CallShortMethodA,
3332 CallIntMethod,
3333 CallIntMethodV,
3334 CallIntMethodA,
3335 CallLongMethod,
3336 CallLongMethodV,
3337 CallLongMethodA,
3338 CallFloatMethod,
3339 CallFloatMethodV,
3340 CallFloatMethodA,
3341 CallDoubleMethod,
3342 CallDoubleMethodV,
3343 CallDoubleMethodA,
3344 CallVoidMethod,
3345 CallVoidMethodV,
3346 CallVoidMethodA,
3347
3348 CallNonvirtualObjectMethod,
3349 CallNonvirtualObjectMethodV,
3350 CallNonvirtualObjectMethodA,
3351 CallNonvirtualBooleanMethod,
3352 CallNonvirtualBooleanMethodV,
3353 CallNonvirtualBooleanMethodA,
3354 CallNonvirtualByteMethod,
3355 CallNonvirtualByteMethodV,
3356 CallNonvirtualByteMethodA,
3357 CallNonvirtualCharMethod,
3358 CallNonvirtualCharMethodV,
3359 CallNonvirtualCharMethodA,
3360 CallNonvirtualShortMethod,
3361 CallNonvirtualShortMethodV,
3362 CallNonvirtualShortMethodA,
3363 CallNonvirtualIntMethod,
3364 CallNonvirtualIntMethodV,
3365 CallNonvirtualIntMethodA,
3366 CallNonvirtualLongMethod,
3367 CallNonvirtualLongMethodV,
3368 CallNonvirtualLongMethodA,
3369 CallNonvirtualFloatMethod,
3370 CallNonvirtualFloatMethodV,
3371 CallNonvirtualFloatMethodA,
3372 CallNonvirtualDoubleMethod,
3373 CallNonvirtualDoubleMethodV,
3374 CallNonvirtualDoubleMethodA,
3375 CallNonvirtualVoidMethod,
3376 CallNonvirtualVoidMethodV,
3377 CallNonvirtualVoidMethodA,
3378
3379 GetFieldID,
3380
3381 GetObjectField,
3382 GetBooleanField,
3383 GetByteField,
3384 GetCharField,
3385 GetShortField,
3386 GetIntField,
3387 GetLongField,
3388 GetFloatField,
3389 GetDoubleField,
3390 SetObjectField,
3391 SetBooleanField,
3392 SetByteField,
3393 SetCharField,
3394 SetShortField,
3395 SetIntField,
3396 SetLongField,
3397 SetFloatField,
3398 SetDoubleField,
3399
3400 GetStaticMethodID,
3401
3402 CallStaticObjectMethod,
3403 CallStaticObjectMethodV,
3404 CallStaticObjectMethodA,
3405 CallStaticBooleanMethod,
3406 CallStaticBooleanMethodV,
3407 CallStaticBooleanMethodA,
3408 CallStaticByteMethod,
3409 CallStaticByteMethodV,
3410 CallStaticByteMethodA,
3411 CallStaticCharMethod,
3412 CallStaticCharMethodV,
3413 CallStaticCharMethodA,
3414 CallStaticShortMethod,
3415 CallStaticShortMethodV,
3416 CallStaticShortMethodA,
3417 CallStaticIntMethod,
3418 CallStaticIntMethodV,
3419 CallStaticIntMethodA,
3420 CallStaticLongMethod,
3421 CallStaticLongMethodV,
3422 CallStaticLongMethodA,
3423 CallStaticFloatMethod,
3424 CallStaticFloatMethodV,
3425 CallStaticFloatMethodA,
3426 CallStaticDoubleMethod,
3427 CallStaticDoubleMethodV,
3428 CallStaticDoubleMethodA,
3429 CallStaticVoidMethod,
3430 CallStaticVoidMethodV,
3431 CallStaticVoidMethodA,
3432
3433 GetStaticFieldID,
3434
3435 GetStaticObjectField,
3436 GetStaticBooleanField,
3437 GetStaticByteField,
3438 GetStaticCharField,
3439 GetStaticShortField,
3440 GetStaticIntField,
3441 GetStaticLongField,
3442 GetStaticFloatField,
3443 GetStaticDoubleField,
3444
3445 SetStaticObjectField,
3446 SetStaticBooleanField,
3447 SetStaticByteField,
3448 SetStaticCharField,
3449 SetStaticShortField,
3450 SetStaticIntField,
3451 SetStaticLongField,
3452 SetStaticFloatField,
3453 SetStaticDoubleField,
3454
3455 NewString,
3456
3457 GetStringLength,
3458 GetStringChars,
3459 ReleaseStringChars,
3460
3461 NewStringUTF,
3462 GetStringUTFLength,
3463 GetStringUTFChars,
3464 ReleaseStringUTFChars,
3465
3466 GetArrayLength,
3467 NewObjectArray,
3468 GetObjectArrayElement,
3469 SetObjectArrayElement,
3470
3471 NewBooleanArray,
3472 NewByteArray,
3473 NewCharArray,
3474 NewShortArray,
3475 NewIntArray,
3476 NewLongArray,
3477 NewFloatArray,
3478 NewDoubleArray,
3479
3480 GetBooleanArrayElements,
3481 GetByteArrayElements,
3482 GetCharArrayElements,
3483 GetShortArrayElements,
3484 GetIntArrayElements,
3485 GetLongArrayElements,
3486 GetFloatArrayElements,
3487 GetDoubleArrayElements,
3488
3489 ReleaseBooleanArrayElements,
3490 ReleaseByteArrayElements,
3491 ReleaseCharArrayElements,
3492 ReleaseShortArrayElements,
3493 ReleaseIntArrayElements,
3494 ReleaseLongArrayElements,
3495 ReleaseFloatArrayElements,
3496 ReleaseDoubleArrayElements,
3497
3498 GetBooleanArrayRegion,
3499 GetByteArrayRegion,
3500 GetCharArrayRegion,
3501 GetShortArrayRegion,
3502 GetIntArrayRegion,
3503 GetLongArrayRegion,
3504 GetFloatArrayRegion,
3505 GetDoubleArrayRegion,
3506 SetBooleanArrayRegion,
3507 SetByteArrayRegion,
3508 SetCharArrayRegion,
3509 SetShortArrayRegion,
3510 SetIntArrayRegion,
3511 SetLongArrayRegion,
3512 SetFloatArrayRegion,
3513 SetDoubleArrayRegion,
3514
3515 RegisterNatives,
3516 UnregisterNatives,
3517
3518 MonitorEnter,
3519 MonitorExit,
3520
3521 GetJavaVM,
3522
3523 GetStringRegion,
3524 GetStringUTFRegion,
3525
3526 GetPrimitiveArrayCritical,
3527 ReleasePrimitiveArrayCritical,
3528
3529 GetStringCritical,
3530 ReleaseStringCritical,
3531
3532 NewWeakGlobalRef,
3533 DeleteWeakGlobalRef,
3534
3535 ExceptionCheck,
3536
3537 NewDirectByteBuffer,
3538 GetDirectBufferAddress,
3539 GetDirectBufferCapacity,
3540
3541 GetObjectRefType
3542};
3543static const struct JNIInvokeInterface gInvokeInterface = {
3544 NULL,
3545 NULL,
3546 NULL,
3547
3548 DestroyJavaVM,
3549 AttachCurrentThread,
3550 DetachCurrentThread,
3551
3552 GetEnv,
3553
3554 AttachCurrentThreadAsDaemon,
3555};
3556
3557
3558/*
3559 * ===========================================================================
3560 * VM/Env creation
3561 * ===========================================================================
3562 */
3563
3564/*
3565 * Enable "checked JNI" after the VM has partially started. This must
3566 * only be called in "zygote" mode, when we have one thread running.
Andy McFadden59b61772009-05-13 16:44:34 -07003567 *
3568 * This doesn't attempt to rewrite the JNI call bridge associated with
3569 * native methods, so we won't get those checks for any methods that have
3570 * already been resolved.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003571 */
3572void dvmLateEnableCheckedJni(void)
3573{
3574 JNIEnvExt* extEnv;
3575 JavaVMExt* extVm;
Andy McFaddenab00d452009-08-19 07:21:41 -07003576
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003577 extEnv = dvmGetJNIEnvForThread();
3578 if (extEnv == NULL) {
3579 LOGE("dvmLateEnableCheckedJni: thread has no JNIEnv\n");
3580 return;
3581 }
3582 extVm = extEnv->vm;
3583 assert(extVm != NULL);
3584
3585 if (!extVm->useChecked) {
3586 LOGD("Late-enabling CheckJNI\n");
3587 dvmUseCheckedJniVm(extVm);
3588 extVm->useChecked = true;
3589 dvmUseCheckedJniEnv(extEnv);
3590
3591 /* currently no way to pick up jniopts features */
3592 } else {
3593 LOGD("Not late-enabling CheckJNI (already on)\n");
3594 }
3595}
3596
3597/*
3598 * Not supported.
3599 */
3600jint JNI_GetDefaultJavaVMInitArgs(void* vm_args)
3601{
3602 return JNI_ERR;
3603}
3604
3605/*
3606 * Return a buffer full of created VMs.
3607 *
3608 * We always have zero or one.
3609 */
3610jint JNI_GetCreatedJavaVMs(JavaVM** vmBuf, jsize bufLen, jsize* nVMs)
3611{
3612 if (gDvm.vmList != NULL) {
3613 *nVMs = 1;
3614
3615 if (bufLen > 0)
3616 *vmBuf++ = gDvm.vmList;
3617 } else {
3618 *nVMs = 0;
3619 }
3620
3621 return JNI_OK;
3622}
3623
3624
3625/*
3626 * Create a new VM instance.
3627 *
3628 * The current thread becomes the main VM thread. We return immediately,
3629 * which effectively means the caller is executing in a native method.
3630 */
3631jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args)
3632{
3633 const JavaVMInitArgs* args = (JavaVMInitArgs*) vm_args;
3634 JNIEnvExt* pEnv = NULL;
3635 JavaVMExt* pVM = NULL;
3636 const char** argv;
3637 int argc = 0;
3638 int i, curOpt;
3639 int result = JNI_ERR;
3640 bool checkJni = false;
3641 bool warnError = true;
3642 bool forceDataCopy = false;
3643
3644 if (args->version < JNI_VERSION_1_2)
3645 return JNI_EVERSION;
3646
3647 // TODO: don't allow creation of multiple VMs -- one per customer for now
3648
3649 /* zero globals; not strictly necessary the first time a VM is started */
3650 memset(&gDvm, 0, sizeof(gDvm));
3651
3652 /*
3653 * Set up structures for JNIEnv and VM.
3654 */
3655 //pEnv = (JNIEnvExt*) malloc(sizeof(JNIEnvExt));
3656 pVM = (JavaVMExt*) malloc(sizeof(JavaVMExt));
3657
3658 //memset(pEnv, 0, sizeof(JNIEnvExt));
3659 //pEnv->funcTable = &gNativeInterface;
3660 //pEnv->vm = pVM;
3661 memset(pVM, 0, sizeof(JavaVMExt));
3662 pVM->funcTable = &gInvokeInterface;
3663 pVM->envList = pEnv;
3664 dvmInitMutex(&pVM->envListLock);
3665
3666 argv = (const char**) malloc(sizeof(char*) * (args->nOptions));
3667 memset(argv, 0, sizeof(char*) * (args->nOptions));
3668
3669 curOpt = 0;
3670
3671 /*
3672 * Convert JNI args to argv.
3673 *
3674 * We have to pull out vfprintf/exit/abort, because they use the
3675 * "extraInfo" field to pass function pointer "hooks" in. We also
3676 * look for the -Xcheck:jni stuff here.
3677 */
3678 for (i = 0; i < args->nOptions; i++) {
3679 const char* optStr = args->options[i].optionString;
3680
3681 if (optStr == NULL) {
3682 fprintf(stderr, "ERROR: arg %d string was null\n", i);
3683 goto bail;
3684 } else if (strcmp(optStr, "vfprintf") == 0) {
3685 gDvm.vfprintfHook = args->options[i].extraInfo;
3686 } else if (strcmp(optStr, "exit") == 0) {
3687 gDvm.exitHook = args->options[i].extraInfo;
3688 } else if (strcmp(optStr, "abort") == 0) {
3689 gDvm.abortHook = args->options[i].extraInfo;
3690 } else if (strcmp(optStr, "-Xcheck:jni") == 0) {
3691 checkJni = true;
3692 } else if (strncmp(optStr, "-Xjniopts:", 10) == 0) {
3693 const char* jniOpts = optStr + 9;
3694 while (jniOpts != NULL) {
3695 jniOpts++; /* skip past ':' or ',' */
3696 if (strncmp(jniOpts, "warnonly", 8) == 0) {
3697 warnError = false;
3698 } else if (strncmp(jniOpts, "forcecopy", 9) == 0) {
3699 forceDataCopy = true;
3700 } else {
3701 LOGW("unknown jni opt starting at '%s'\n", jniOpts);
3702 }
3703 jniOpts = strchr(jniOpts, ',');
3704 }
3705 } else {
3706 /* regular option */
3707 argv[curOpt++] = optStr;
3708 }
3709 }
3710 argc = curOpt;
3711
3712 if (checkJni) {
3713 dvmUseCheckedJniVm(pVM);
3714 pVM->useChecked = true;
3715 }
3716 pVM->warnError = warnError;
3717 pVM->forceDataCopy = forceDataCopy;
3718
3719 /* set this up before initializing VM, so it can create some JNIEnvs */
3720 gDvm.vmList = (JavaVM*) pVM;
3721
3722 /*
3723 * Create an env for main thread. We need to have something set up
3724 * here because some of the class initialization we do when starting
3725 * up the VM will call into native code.
3726 */
3727 pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL);
3728
3729 /* initialize VM */
3730 gDvm.initializing = true;
3731 if (dvmStartup(argc, argv, args->ignoreUnrecognized, (JNIEnv*)pEnv) != 0) {
3732 free(pEnv);
3733 free(pVM);
3734 goto bail;
3735 }
3736
3737 /*
3738 * Success! Return stuff to caller.
3739 */
3740 dvmChangeStatus(NULL, THREAD_NATIVE);
3741 *p_env = (JNIEnv*) pEnv;
3742 *p_vm = (JavaVM*) pVM;
3743 result = JNI_OK;
3744
3745bail:
3746 gDvm.initializing = false;
3747 if (result == JNI_OK)
3748 LOGV("JNI_CreateJavaVM succeeded\n");
3749 else
3750 LOGW("JNI_CreateJavaVM failed\n");
3751 free(argv);
3752 return result;
3753}
3754