blob: a720648b45f77dea7b97d4b0813e3e642ff63c39 [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
Andy McFadden0083d372009-08-21 14:44:04 -070051JNI provides explicit control over natively-held references that the GC
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080052needs to know about. These can be local, in which case they're released
Andy McFadden0083d372009-08-21 14:44:04 -070053when the native method returns into the VM, or global, which are held
Andy McFaddend5ab7262009-08-25 07:19:34 -070054until explicitly released. (There are also weak-global references,
55which have the lifespan and visibility of global references, but the
56object they refer to may be collected.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080057
Andy McFadden0083d372009-08-21 14:44:04 -070058The references can be created with explicit JNI NewLocalRef / NewGlobalRef
59calls. The former is very unusual, the latter is reasonably common
60(e.g. for caching references to class objects).
61
62Local references are most often created as a side-effect of JNI functions.
63For example, the AllocObject/NewObject functions must create local
64references to the objects returned, because nothing else in the GC root
65set has a reference to the new objects.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080066
67The most common mode of operation is for a method to create zero or
68more local references and return. Explicit "local delete" operations
69are expected to be exceedingly rare, except when walking through an
70object array, and the Push/PopLocalFrame calls are expected to be used
71infrequently. For efficient operation, we want to add new local refs
72with a simple store/increment operation; to avoid infinite growth in
73pathological situations, we need to reclaim the space used by deleted
74entries.
75
Andy McFaddend5ab7262009-08-25 07:19:34 -070076If we just want to maintain a list for the GC root set, we can use an
77expanding append-only array that compacts when objects are deleted.
78In typical situations, e.g. running through an array of objects, we will
79be deleting one of the most recently added entries, so we can minimize
80the number of elements moved (or avoid having to move any).
81
82If we want to conceal the pointer values from native code, which is
83necessary to allow the GC to move JNI-referenced objects around, then we
84have to use a more complicated indirection mechanism.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080085
86The spec says, "Local references are only valid in the thread in which
87they are created. The native code must not pass local references from
Andy McFadden0083d372009-08-21 14:44:04 -070088one thread to another."
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080089
90
Andy McFaddend5ab7262009-08-25 07:19:34 -070091Pinned objects
92
93For some large chunks of data, notably primitive arrays and String data,
94JNI allows the VM to choose whether it wants to pin the array object or
95make a copy. We currently pin the memory for better execution performance.
96
97TODO: we're using simple root set references to pin primitive array data,
98because they have the property we need (i.e. the pointer we return is
99guaranteed valid until we explicitly release it). However, if we have a
100compacting GC and don't want to pin all memory held by all global refs,
101we need to treat these differently.
102
103
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800104Global reference tracking
105
106There should be a small "active" set centered around the most-recently
Andy McFaddend5ab7262009-08-25 07:19:34 -0700107added items.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800108
Andy McFaddend5ab7262009-08-25 07:19:34 -0700109Because it's global, access to it has to be synchronized. Additions and
110removals require grabbing a mutex. If the table serves as an indirection
111mechanism (i.e. it's not just a list for the benefit of the garbage
112collector), reference lookups may also require grabbing a mutex.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800113
114The JNI spec does not define any sort of limit, so the list must be able
Andy McFaddend5ab7262009-08-25 07:19:34 -0700115to expand to a reasonable size. It may be useful to log significant
116increases in usage to help identify resource leaks.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800117
Andy McFaddend5ab7262009-08-25 07:19:34 -0700118
119Weak-global reference tracking
120
121[TBD]
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800122
123
124Local reference tracking
125
126The table of local references can be stored on the interpreted stack or
127in a parallel data structure (one per thread).
128
129*** Approach #1: use the interpreted stack
130
131The easiest place to tuck it is between the frame ptr and the first saved
132register, which is always in0. (See the ASCII art in Stack.h.) We can
133shift the "VM-specific goop" and frame ptr down, effectively inserting
134the JNI local refs in the space normally occupied by local variables.
135
136(Three things are accessed from the frame pointer:
137 (1) framePtr[N] is register vN, used to get at "ins" and "locals".
138 (2) framePtr - sizeof(StackSaveArea) is the VM frame goop.
139 (3) framePtr - sizeof(StackSaveArea) - numOuts is where the "outs" go.
140The only thing that isn't determined by an offset from the current FP
141is the previous frame. However, tucking things below the previous frame
142can be problematic because the "outs" of the previous frame overlap with
143the "ins" of the current frame. If the "ins" are altered they must be
144restored before we return. For a native method call, the easiest and
145safest thing to disrupt is #1, because there are no locals and the "ins"
146are all copied to the native stack.)
147
148We can implement Push/PopLocalFrame with the existing stack frame calls,
149making sure we copy some goop from the previous frame (notably the method
150ptr, so that dvmGetCurrentJNIMethod() doesn't require extra effort).
151
152We can pre-allocate the storage at the time the stack frame is first
153set up, but we have to be careful. When calling from interpreted code
154the frame ptr points directly at the arguments we're passing, but we can
155offset the args pointer when calling the native bridge.
156
157To manage the local ref collection, we need to be able to find three
158things: (1) the start of the region, (2) the end of the region, and (3)
159the next available entry. The last is only required for quick adds.
160We currently have two easily-accessible pointers, the current FP and the
161previous frame's FP. (The "stack pointer" shown in the ASCII art doesn't
162actually exist in the interpreted world.)
163
164We can't use the current FP to find the first "in", because we want to
165insert the variable-sized local refs table between them. It's awkward
166to use the previous frame's FP because native methods invoked via
167dvmCallMethod() or dvmInvokeMethod() don't have "ins", but native methods
168invoked from interpreted code do. We can either track the local refs
169table size with a field in the stack frame, or insert unnecessary items
170so that all native stack frames have "ins".
171
172Assuming we can find the region bounds, we still need pointer #3
173for an efficient implementation. This can be stored in an otherwise
174unused-for-native field in the frame goop.
175
176When we run out of room we have to make more space. If we start allocating
177locals immediately below in0 and grow downward, we will detect end-of-space
178by running into the current frame's FP. We then memmove() the goop down
179(memcpy if we guarantee the additional size is larger than the frame).
180This is nice because we only have to move sizeof(StackSaveArea) bytes
181each time.
182
183Stack walking should be okay so long as nothing tries to access the
184"ins" by an offset from the FP. In theory the "ins" could be read by
185the debugger or SIGQUIT handler looking for "this" or other arguments,
186but in practice this behavior isn't expected to work for native methods,
187so we can simply disallow it.
188
189A conservative GC can just scan the entire stack from top to bottom to find
190all references. An exact GC will need to understand the actual layout.
191
192*** Approach #2: use a parallel stack
193
Andy McFaddend5ab7262009-08-25 07:19:34 -0700194Each Thread/JNIEnv points to a reference table. The struct has
195a system-heap-allocated array of references and a pointer to the
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800196next-available entry ("nextEntry").
197
Andy McFaddend5ab7262009-08-25 07:19:34 -0700198Each stack frame has a pointer to what it sees as the "bottom" element
199in the array (we can double-up the "currentPc" field). This is set to
200"nextEntry" when the frame is pushed on. As local references are added
201or removed, "nextEntry" is updated.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800202
203We implement Push/PopLocalFrame with actual stack frames. Before a JNI
204frame gets popped, we set "nextEntry" to the "top" pointer of the current
205frame, effectively releasing the references.
206
207The GC will scan all references from the start of the table to the
208"nextEntry" pointer.
209
210*** Comparison
211
212All approaches will return a failure result when they run out of local
213reference space. For #1 that means blowing out the stack, for #2 it's
214running out of room in the array.
215
216Compared to #1, approach #2:
217 - Needs only one pointer in the stack frame goop.
218 - Makes pre-allocating storage unnecessary.
219 - Doesn't contend with interpreted stack depth for space. In most
220 cases, if something blows out the local ref storage, it's because the
221 JNI code was misbehaving rather than called from way down.
222 - Allows the GC to do a linear scan per thread in a buffer that is 100%
223 references. The GC can be slightly less smart when scanning the stack.
224 - Will be easier to work with if we combine native and interpeted stacks.
225
226 - Isn't as clean, especially when popping frames, since we have to do
227 explicit work. Fortunately we only have to do it when popping native
228 method calls off, so it doesn't add overhead to interpreted code paths.
229 - Is awkward to expand dynamically. We'll want to pre-allocate the full
230 amount of space; this is fine, since something on the order of 1KB should
231 be plenty. The JNI spec allows us to limit this.
232 - Requires the GC to scan even more memory. With the references embedded
233 in the stack we get better locality of reference.
234
235*/
236
Andy McFadden5f612b82009-07-22 15:07:27 -0700237/* fwd */
238static const struct JNINativeInterface gNativeInterface;
Andy McFaddenab00d452009-08-19 07:21:41 -0700239static jobject addGlobalReference(Object* obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800240
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800241#ifdef WITH_JNI_STACK_CHECK
242# define COMPUTE_STACK_SUM(_self) computeStackSum(_self);
243# define CHECK_STACK_SUM(_self) checkStackSum(_self);
Andy McFaddend5ab7262009-08-25 07:19:34 -0700244//static void computeStackSum(Thread* self);
245//static void checkStackSum(Thread* self);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800246#else
247# define COMPUTE_STACK_SUM(_self) ((void)0)
248# define CHECK_STACK_SUM(_self) ((void)0)
249#endif
250
251
252/*
253 * ===========================================================================
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800254 * Utility functions
255 * ===========================================================================
256 */
257
258/*
259 * Entry/exit processing for all JNI calls.
260 *
261 * If TRUSTED_JNIENV is set, we get to skip the (curiously expensive)
262 * thread-local storage lookup on our Thread*. If the caller has passed
263 * the wrong JNIEnv in, we're going to be accessing unsynchronized
264 * structures from more than one thread, and things are going to fail
265 * in bizarre ways. This is only sensible if the native code has been
266 * fully exercised with CheckJNI enabled.
267 */
268#define TRUSTED_JNIENV
269#ifdef TRUSTED_JNIENV
270# define JNI_ENTER() \
271 Thread* _self = ((JNIEnvExt*)env)->self; \
272 CHECK_STACK_SUM(_self); \
273 dvmChangeStatus(_self, THREAD_RUNNING)
274#else
275# define JNI_ENTER() \
276 Thread* _self = dvmThreadSelf(); \
277 UNUSED_PARAMETER(env); \
278 CHECK_STACK_SUM(_self); \
279 dvmChangeStatus(_self, THREAD_RUNNING)
280#endif
281#define JNI_EXIT() \
282 dvmChangeStatus(_self, THREAD_NATIVE); \
283 COMPUTE_STACK_SUM(_self)
284
285#define kGlobalRefsTableInitialSize 512
Andy McFaddenab00d452009-08-19 07:21:41 -0700286#define kGlobalRefsTableMaxSize 51200 /* arbitrary, must be < 64K */
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800287#define kGrefWaterInterval 100
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800288#define kTrackGrefUsage true
289
Andy McFaddenc26bb632009-08-21 12:01:31 -0700290#define kPinTableInitialSize 16
291#define kPinTableMaxSize 1024
292#define kPinComplainThreshold 10
293
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800294/*
Andy McFadden5f612b82009-07-22 15:07:27 -0700295 * Allocate the global references table, and look up some classes for
296 * the benefit of direct buffer access.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800297 */
298bool dvmJniStartup(void)
299{
Andy McFaddend5ab7262009-08-25 07:19:34 -0700300#ifdef USE_INDIRECT_REF
301 if (!dvmInitIndirectRefTable(&gDvm.jniGlobalRefTable,
302 kGlobalRefsTableInitialSize, kGlobalRefsTableMaxSize,
303 kIndirectKindGlobal))
304 return false;
305#else
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800306 if (!dvmInitReferenceTable(&gDvm.jniGlobalRefTable,
307 kGlobalRefsTableInitialSize, kGlobalRefsTableMaxSize))
308 return false;
Andy McFaddend5ab7262009-08-25 07:19:34 -0700309#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800310
311 dvmInitMutex(&gDvm.jniGlobalRefLock);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800312 gDvm.jniGlobalRefLoMark = 0;
313 gDvm.jniGlobalRefHiMark = kGrefWaterInterval * 2;
314
Andy McFaddenc26bb632009-08-21 12:01:31 -0700315 if (!dvmInitReferenceTable(&gDvm.jniPinRefTable,
316 kPinTableInitialSize, kPinTableMaxSize))
317 return false;
318
319 dvmInitMutex(&gDvm.jniPinRefLock);
320
Andy McFadden8e5c7842009-07-23 17:47:18 -0700321 /*
322 * Look up and cache pointers to some direct buffer classes, fields,
323 * and methods.
324 */
325 Method* meth;
326
Andy McFadden5f612b82009-07-22 15:07:27 -0700327 ClassObject* platformAddressClass =
328 dvmFindSystemClassNoInit("Lorg/apache/harmony/luni/platform/PlatformAddress;");
Andy McFadden8e5c7842009-07-23 17:47:18 -0700329 ClassObject* platformAddressFactoryClass =
330 dvmFindSystemClassNoInit("Lorg/apache/harmony/luni/platform/PlatformAddressFactory;");
Andy McFadden5f612b82009-07-22 15:07:27 -0700331 ClassObject* directBufferClass =
332 dvmFindSystemClassNoInit("Lorg/apache/harmony/nio/internal/DirectBuffer;");
Andy McFadden8e5c7842009-07-23 17:47:18 -0700333 ClassObject* readWriteBufferClass =
334 dvmFindSystemClassNoInit("Ljava/nio/ReadWriteDirectByteBuffer;");
335 ClassObject* bufferClass =
336 dvmFindSystemClassNoInit("Ljava/nio/Buffer;");
337
338 if (platformAddressClass == NULL || platformAddressFactoryClass == NULL ||
339 directBufferClass == NULL || readWriteBufferClass == NULL ||
340 bufferClass == NULL)
341 {
Andy McFadden5f612b82009-07-22 15:07:27 -0700342 LOGE("Unable to find internal direct buffer classes\n");
343 return false;
344 }
Andy McFadden8e5c7842009-07-23 17:47:18 -0700345 gDvm.classJavaNioReadWriteDirectByteBuffer = readWriteBufferClass;
Andy McFaddend5ab7262009-08-25 07:19:34 -0700346 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer = directBufferClass;
347 /* need a global reference for extended CheckJNI tests */
348 gDvm.jclassOrgApacheHarmonyNioInternalDirectBuffer =
349 addGlobalReference((Object*) directBufferClass);
Andy McFadden5f612b82009-07-22 15:07:27 -0700350
Andy McFadden8e5c7842009-07-23 17:47:18 -0700351 /*
352 * We need a Method* here rather than a vtable offset, because
353 * DirectBuffer is an interface class.
354 */
Andy McFadden5f612b82009-07-22 15:07:27 -0700355 meth = dvmFindVirtualMethodByDescriptor(
356 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer,
357 "getEffectiveAddress",
358 "()Lorg/apache/harmony/luni/platform/PlatformAddress;");
359 if (meth == NULL) {
360 LOGE("Unable to find PlatformAddress.getEffectiveAddress\n");
361 return false;
362 }
363 gDvm.methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress = meth;
364
365 meth = dvmFindVirtualMethodByDescriptor(platformAddressClass,
366 "toLong", "()J");
367 if (meth == NULL) {
368 LOGE("Unable to find PlatformAddress.toLong\n");
369 return false;
370 }
Andy McFadden8e5c7842009-07-23 17:47:18 -0700371 gDvm.voffOrgApacheHarmonyLuniPlatformPlatformAddress_toLong =
372 meth->methodIndex;
373
374 meth = dvmFindDirectMethodByDescriptor(platformAddressFactoryClass,
375 "on",
376 "(I)Lorg/apache/harmony/luni/platform/PlatformAddress;");
377 if (meth == NULL) {
378 LOGE("Unable to find PlatformAddressFactory.on\n");
379 return false;
380 }
381 gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on = meth;
382
383 meth = dvmFindDirectMethodByDescriptor(readWriteBufferClass,
384 "<init>",
385 "(Lorg/apache/harmony/luni/platform/PlatformAddress;II)V");
386 if (meth == NULL) {
387 LOGE("Unable to find ReadWriteDirectByteBuffer.<init>\n");
388 return false;
389 }
390 gDvm.methJavaNioReadWriteDirectByteBuffer_init = meth;
391
392 gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr =
393 dvmFindFieldOffset(platformAddressClass, "osaddr", "I");
394 if (gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr < 0) {
395 LOGE("Unable to find PlatformAddress.osaddr\n");
396 return false;
397 }
398
399 gDvm.offJavaNioBuffer_capacity =
400 dvmFindFieldOffset(bufferClass, "capacity", "I");
401 if (gDvm.offJavaNioBuffer_capacity < 0) {
402 LOGE("Unable to find Buffer.capacity\n");
403 return false;
404 }
Andy McFadden5f612b82009-07-22 15:07:27 -0700405
Andy McFadden8e696dc2009-07-24 15:28:16 -0700406 gDvm.offJavaNioBuffer_effectiveDirectAddress =
407 dvmFindFieldOffset(bufferClass, "effectiveDirectAddress", "I");
408 if (gDvm.offJavaNioBuffer_effectiveDirectAddress < 0) {
409 LOGE("Unable to find Buffer.effectiveDirectAddress\n");
410 return false;
411 }
412
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800413 return true;
414}
415
416/*
417 * Free the global references table.
418 */
419void dvmJniShutdown(void)
420{
Andy McFaddend5ab7262009-08-25 07:19:34 -0700421#ifdef USE_INDIRECT_REF
422 dvmClearIndirectRefTable(&gDvm.jniGlobalRefTable);
423#else
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800424 dvmClearReferenceTable(&gDvm.jniGlobalRefTable);
Andy McFaddend5ab7262009-08-25 07:19:34 -0700425#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800426}
427
428
429/*
430 * Find the JNIEnv associated with the current thread.
431 *
432 * Currently stored in the Thread struct. Could also just drop this into
433 * thread-local storage.
434 */
435JNIEnvExt* dvmGetJNIEnvForThread(void)
436{
437 Thread* self = dvmThreadSelf();
438 if (self == NULL)
439 return NULL;
440 return (JNIEnvExt*) dvmGetThreadJNIEnv(self);
441}
442
443/*
444 * Create a new JNIEnv struct and add it to the VM's list.
445 *
446 * "self" will be NULL for the main thread, since the VM hasn't started
447 * yet; the value will be filled in later.
448 */
449JNIEnv* dvmCreateJNIEnv(Thread* self)
450{
451 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
452 JNIEnvExt* newEnv;
453
454 //if (self != NULL)
455 // LOGI("Ent CreateJNIEnv: threadid=%d %p\n", self->threadId, self);
456
457 assert(vm != NULL);
458
459 newEnv = (JNIEnvExt*) calloc(1, sizeof(JNIEnvExt));
460 newEnv->funcTable = &gNativeInterface;
461 newEnv->vm = vm;
462 newEnv->forceDataCopy = vm->forceDataCopy;
463 if (self != NULL) {
464 dvmSetJniEnvThreadId((JNIEnv*) newEnv, self);
465 assert(newEnv->envThreadId != 0);
466 } else {
467 /* make it obvious if we fail to initialize these later */
468 newEnv->envThreadId = 0x77777775;
469 newEnv->self = (Thread*) 0x77777779;
470 }
471 if (vm->useChecked)
472 dvmUseCheckedJniEnv(newEnv);
473
474 dvmLockMutex(&vm->envListLock);
475
476 /* insert at head of list */
477 newEnv->next = vm->envList;
478 assert(newEnv->prev == NULL);
479 if (vm->envList == NULL) // rare, but possible
480 vm->envList = newEnv;
481 else
482 vm->envList->prev = newEnv;
483 vm->envList = newEnv;
484
485 dvmUnlockMutex(&vm->envListLock);
486
487 //if (self != NULL)
488 // LOGI("Xit CreateJNIEnv: threadid=%d %p\n", self->threadId, self);
489 return (JNIEnv*) newEnv;
490}
491
492/*
493 * Remove a JNIEnv struct from the list and free it.
494 */
495void dvmDestroyJNIEnv(JNIEnv* env)
496{
497 JNIEnvExt* extEnv = (JNIEnvExt*) env;
498 JavaVMExt* vm = extEnv->vm;
499 Thread* self;
500
501 if (env == NULL)
502 return;
503
504 self = dvmThreadSelf();
505 assert(self != NULL);
506
507 //LOGI("Ent DestroyJNIEnv: threadid=%d %p\n", self->threadId, self);
508
509 dvmLockMutex(&vm->envListLock);
510
511 if (extEnv == vm->envList) {
512 assert(extEnv->prev == NULL);
513 vm->envList = extEnv->next;
514 } else {
515 assert(extEnv->prev != NULL);
516 extEnv->prev->next = extEnv->next;
517 }
518 if (extEnv->next != NULL)
519 extEnv->next->prev = extEnv->prev;
520
521 dvmUnlockMutex(&extEnv->vm->envListLock);
522
523 free(env);
524 //LOGI("Xit DestroyJNIEnv: threadid=%d %p\n", self->threadId, self);
525}
526
527
528/*
529 * Retrieve the ReferenceTable struct for the current thread.
530 *
Andy McFaddenab00d452009-08-19 07:21:41 -0700531 * Going through "env" rather than dvmThreadSelf() is faster but will
532 * get weird if the JNI code is passing the wrong JNIEnv around.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800533 */
Andy McFaddend5ab7262009-08-25 07:19:34 -0700534#ifdef USE_INDIRECT_REF
535static inline IndirectRefTable* getLocalRefTable(JNIEnv* env)
536{
537 return &((JNIEnvExt*)env)->self->jniLocalRefTable;
538}
539#else
Andy McFaddenab00d452009-08-19 07:21:41 -0700540static inline ReferenceTable* getLocalRefTable(JNIEnv* env)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800541{
Andy McFaddenab00d452009-08-19 07:21:41 -0700542 //return &dvmThreadSelf()->jniLocalRefTable;
543 return &((JNIEnvExt*)env)->self->jniLocalRefTable;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800544}
Andy McFaddend5ab7262009-08-25 07:19:34 -0700545#endif
546
547/*
548 * Convert an indirect reference to an Object reference. The indirect
549 * reference may be local, global, or weak-global.
550 *
551 * If "jobj" is NULL or an invalid indirect reference, this returns NULL.
552 */
553Object* dvmDecodeIndirectRef(JNIEnv* env, jobject jobj)
554{
555#ifdef USE_INDIRECT_REF
556 if (jobj == NULL)
557 return NULL;
558
559 Object* result;
560
561 switch (dvmGetIndirectRefType(jobj)) {
562 case kIndirectKindLocal:
563 {
564 IndirectRefTable* pRefTable = getLocalRefTable(env);
565 result = dvmGetFromIndirectRefTable(pRefTable, jobj);
566 }
567 break;
568 case kIndirectKindGlobal:
569 {
570 // TODO: find a way to avoid the mutex activity here
571 IndirectRefTable* pRefTable = &gDvm.jniGlobalRefTable;
572 dvmLockMutex(&gDvm.jniGlobalRefLock);
573 result = dvmGetFromIndirectRefTable(pRefTable, jobj);
574 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
575 }
576 break;
577 case kIndirectKindWeakGlobal:
578 {
579 LOGE("weak-global not yet supported\n");
580 result = NULL;
581 dvmAbort();
582 }
583 break;
584 case kIndirectKindInvalid:
585 default:
586 LOGW("Invalid indirect reference %p in decodeIndirectRef\n", jobj);
587 dvmAbort();
588 result = NULL;
589 break;
590 }
591
592 return result;
593#else
594 return (Object*) jobj;
595#endif
596}
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800597
598/*
599 * Add a local reference for an object to the current stack frame. When
600 * the native function returns, the reference will be discarded.
601 *
602 * We need to allow the same reference to be added multiple times.
603 *
604 * This will be called on otherwise unreferenced objects. We cannot do
605 * GC allocations here, and it's best if we don't grab a mutex.
606 *
607 * Returns the local reference (currently just the same pointer that was
608 * passed in), or NULL on failure.
609 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700610static jobject addLocalReference(JNIEnv* env, Object* obj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800611{
612 if (obj == NULL)
613 return NULL;
614
Andy McFaddend5ab7262009-08-25 07:19:34 -0700615 jobject jobj;
616
617#ifdef USE_INDIRECT_REF
618 IndirectRefTable* pRefTable = getLocalRefTable(env);
619 void* curFrame = ((JNIEnvExt*)env)->self->curFrame;
620 u4 cookie = SAVEAREA_FROM_FP(curFrame)->xtra.localRefCookie;
621
622 jobj = (jobject) dvmAddToIndirectRefTable(pRefTable, cookie, obj);
623 if (jobj == NULL) {
624 dvmDumpIndirectRefTable(pRefTable, "JNI local");
625 LOGE("Failed adding to JNI local ref table (has %d entries)\n",
626 (int) dvmIndirectRefTableEntries(pRefTable));
627 dvmDumpThread(dvmThreadSelf(), false);
628 dvmAbort(); // spec says call FatalError; this is equivalent
629 } else {
630 LOGVV("LREF add %p (%s.%s) (ent=%d)\n", obj,
631 dvmGetCurrentJNIMethod()->clazz->descriptor,
632 dvmGetCurrentJNIMethod()->name,
633 (int) dvmReferenceTableEntries(pRefTable));
634 }
635#else
Andy McFaddenab00d452009-08-19 07:21:41 -0700636 ReferenceTable* pRefTable = getLocalRefTable(env);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800637
Andy McFaddenab00d452009-08-19 07:21:41 -0700638 if (!dvmAddToReferenceTable(pRefTable, obj)) {
639 dvmDumpReferenceTable(pRefTable, "JNI local");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800640 LOGE("Failed adding to JNI local ref table (has %d entries)\n",
Andy McFaddenab00d452009-08-19 07:21:41 -0700641 (int) dvmReferenceTableEntries(pRefTable));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800642 dvmDumpThread(dvmThreadSelf(), false);
643 dvmAbort(); // spec says call FatalError; this is equivalent
644 } else {
Andy McFadden0083d372009-08-21 14:44:04 -0700645 LOGVV("LREF add %p (%s.%s) (ent=%d)\n", obj,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800646 dvmGetCurrentJNIMethod()->clazz->descriptor,
Andy McFadden0083d372009-08-21 14:44:04 -0700647 dvmGetCurrentJNIMethod()->name,
648 (int) dvmReferenceTableEntries(pRefTable));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800649 }
650
Andy McFaddend5ab7262009-08-25 07:19:34 -0700651 jobj = (jobject) obj;
652#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800653
Andy McFaddend5ab7262009-08-25 07:19:34 -0700654 return jobj;
Andy McFaddenab00d452009-08-19 07:21:41 -0700655}
656
657/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800658 * Ensure that at least "capacity" references can be held in the local
659 * refs table of the current thread.
660 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700661static bool ensureLocalCapacity(JNIEnv* env, int capacity)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800662{
Andy McFaddend5ab7262009-08-25 07:19:34 -0700663#ifdef USE_INDIRECT_REF
664 IndirectRefTable* pRefTable = getLocalRefTable(env);
665 int numEntries = dvmIndirectRefTableEntries(pRefTable);
666 // TODO: this isn't quite right, since "numEntries" includes holes
667 return ((kJniLocalRefMax - numEntries) >= capacity);
668#else
Andy McFaddenab00d452009-08-19 07:21:41 -0700669 ReferenceTable* pRefTable = getLocalRefTable(env);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800670
Andy McFaddenab00d452009-08-19 07:21:41 -0700671 return (kJniLocalRefMax - (pRefTable->nextEntry - pRefTable->table) >= capacity);
Andy McFaddend5ab7262009-08-25 07:19:34 -0700672#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800673}
674
675/*
676 * Explicitly delete a reference from the local list.
677 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700678static void deleteLocalReference(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800679{
Andy McFaddenab00d452009-08-19 07:21:41 -0700680 if (jobj == NULL)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800681 return;
682
Andy McFaddend5ab7262009-08-25 07:19:34 -0700683#ifdef USE_INDIRECT_REF
684 IndirectRefTable* pRefTable = getLocalRefTable(env);
685 Thread* self = ((JNIEnvExt*)env)->self;
686 u4 cookie = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefCookie;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800687
Andy McFaddend5ab7262009-08-25 07:19:34 -0700688 if (!dvmRemoveFromIndirectRefTable(pRefTable, cookie, jobj)) {
689 /*
690 * Attempting to delete a local reference that is not in the
691 * topmost local reference frame is a no-op. DeleteLocalRef returns
692 * void and doesn't throw any exceptions, but we should probably
693 * complain about it so the user will notice that things aren't
694 * going quite the way they expect.
695 */
696 LOGW("JNI WARNING: DeleteLocalRef(%p) failed to find entry\n", jobj);
697 }
698#else
699 ReferenceTable* pRefTable = getLocalRefTable(env);
700 Thread* self = ((JNIEnvExt*)env)->self;
701 Object** bottom = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefCookie;
702
703 if (!dvmRemoveFromReferenceTable(pRefTable, bottom, (Object*) jobj)) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800704 /*
705 * Attempting to delete a local reference that is not in the
706 * topmost local reference frame is a no-op. DeleteLocalRef returns
707 * void and doesn't throw any exceptions, but we should probably
708 * complain about it so the user will notice that things aren't
709 * going quite the way they expect.
710 */
711 LOGW("JNI WARNING: DeleteLocalRef(%p) failed to find entry (valid=%d)\n",
Andy McFaddenab00d452009-08-19 07:21:41 -0700712 jobj, dvmIsValidObject((Object*) jobj));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800713 }
Andy McFaddend5ab7262009-08-25 07:19:34 -0700714#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800715}
716
717/*
718 * Add a global reference for an object.
719 *
720 * We may add the same object more than once. Add/remove calls are paired,
721 * so it needs to appear on the list multiple times.
722 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700723static jobject addGlobalReference(Object* obj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800724{
725 if (obj == NULL)
726 return NULL;
727
728 //LOGI("adding obj=%p\n", obj);
729 //dvmDumpThread(dvmThreadSelf(), false);
730
731 if (false && ((Object*)obj)->clazz == gDvm.classJavaLangClass) {
732 ClassObject* clazz = (ClassObject*) obj;
733 LOGI("-------\n");
734 LOGI("Adding global ref on class %s\n", clazz->descriptor);
735 dvmDumpThread(dvmThreadSelf(), false);
736 }
737 if (false && ((Object*)obj)->clazz == gDvm.classJavaLangString) {
738 StringObject* strObj = (StringObject*) obj;
739 char* str = dvmCreateCstrFromString(strObj);
740 if (strcmp(str, "sync-response") == 0) {
741 LOGI("-------\n");
742 LOGI("Adding global ref on string '%s'\n", str);
743 dvmDumpThread(dvmThreadSelf(), false);
744 //dvmAbort();
745 }
746 free(str);
747 }
748 if (false && ((Object*)obj)->clazz == gDvm.classArrayByte) {
749 ArrayObject* arrayObj = (ArrayObject*) obj;
Andy McFaddend5ab7262009-08-25 07:19:34 -0700750 if (arrayObj->length == 8192 /*&&
751 dvmReferenceTableEntries(&gDvm.jniGlobalRefTable) > 400*/)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800752 {
753 LOGI("Adding global ref on byte array %p (len=%d)\n",
754 arrayObj, arrayObj->length);
755 dvmDumpThread(dvmThreadSelf(), false);
756 }
757 }
758
Andy McFaddend5ab7262009-08-25 07:19:34 -0700759 jobject jobj;
760
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800761 dvmLockMutex(&gDvm.jniGlobalRefLock);
762
763 /*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800764 * Throwing an exception on failure is problematic, because JNI code
765 * may not be expecting an exception, and things sort of cascade. We
766 * want to have a hard limit to catch leaks during debugging, but this
767 * otherwise needs to expand until memory is consumed. As a practical
768 * matter, if we have many thousands of global references, chances are
769 * we're either leaking global ref table entries or we're going to
770 * run out of space in the GC heap.
771 */
Andy McFaddend5ab7262009-08-25 07:19:34 -0700772#ifdef USE_INDIRECT_REF
773 jobj = dvmAddToIndirectRefTable(&gDvm.jniGlobalRefTable, IRT_FIRST_SEGMENT,
774 obj);
775 if (jobj == NULL) {
776 dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable, "JNI global");
777 LOGE("Failed adding to JNI global ref table (%d entries)\n",
778 (int) dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable));
779 dvmAbort();
780 }
781
782 LOGVV("GREF add %p (%s.%s)\n", obj,
783 dvmGetCurrentJNIMethod()->clazz->descriptor,
784 dvmGetCurrentJNIMethod()->name);
785
786 /* GREF usage tracking; should probably be disabled for production env */
787 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
788 int count = dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable);
789 // TODO: adjust for "holes"
790 if (count > gDvm.jniGlobalRefHiMark) {
791 LOGD("GREF has increased to %d\n", count);
792 gDvm.jniGlobalRefHiMark += kGrefWaterInterval;
793 gDvm.jniGlobalRefLoMark += kGrefWaterInterval;
794
795 /* watch for "excessive" use; not generally appropriate */
796 if (count >= gDvm.jniGrefLimit) {
797 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
798 if (vm->warnError) {
799 dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable,
800 "JNI global");
801 LOGE("Excessive JNI global references (%d)\n", count);
802 dvmAbort();
803 } else {
804 LOGW("Excessive JNI global references (%d)\n", count);
805 }
806 }
807 }
808 }
809#else
810 if (!dvmAddToReferenceTable(&gDvm.jniGlobalRefTable, obj)) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800811 dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global");
812 LOGE("Failed adding to JNI global ref table (%d entries)\n",
813 (int) dvmReferenceTableEntries(&gDvm.jniGlobalRefTable));
814 dvmAbort();
815 }
Andy McFaddend5ab7262009-08-25 07:19:34 -0700816 jobj = (jobject) obj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800817
818 LOGVV("GREF add %p (%s.%s)\n", obj,
819 dvmGetCurrentJNIMethod()->clazz->descriptor,
820 dvmGetCurrentJNIMethod()->name);
821
822 /* GREF usage tracking; should probably be disabled for production env */
823 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
824 int count = dvmReferenceTableEntries(&gDvm.jniGlobalRefTable);
825 if (count > gDvm.jniGlobalRefHiMark) {
826 LOGD("GREF has increased to %d\n", count);
827 gDvm.jniGlobalRefHiMark += kGrefWaterInterval;
828 gDvm.jniGlobalRefLoMark += kGrefWaterInterval;
829
830 /* watch for "excessive" use; not generally appropriate */
831 if (count >= gDvm.jniGrefLimit) {
832 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
833 if (vm->warnError) {
834 dvmDumpReferenceTable(&gDvm.jniGlobalRefTable,"JNI global");
835 LOGE("Excessive JNI global references (%d)\n", count);
836 dvmAbort();
837 } else {
838 LOGW("Excessive JNI global references (%d)\n", count);
839 }
840 }
841 }
842 }
Andy McFaddend5ab7262009-08-25 07:19:34 -0700843#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800844
845bail:
846 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
Andy McFaddend5ab7262009-08-25 07:19:34 -0700847 return jobj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800848}
849
850/*
851 * Remove a global reference. In most cases it's the entry most recently
852 * added, which makes this pretty quick.
853 *
854 * Thought: if it's not the most recent entry, just null it out. When we
855 * fill up, do a compaction pass before we expand the list.
856 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700857static void deleteGlobalReference(jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800858{
Andy McFaddenab00d452009-08-19 07:21:41 -0700859 if (jobj == NULL)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800860 return;
861
862 dvmLockMutex(&gDvm.jniGlobalRefLock);
863
Andy McFaddend5ab7262009-08-25 07:19:34 -0700864#ifdef USE_INDIRECT_REF
865 if (!dvmRemoveFromIndirectRefTable(&gDvm.jniGlobalRefTable,
866 IRT_FIRST_SEGMENT, jobj))
867 {
868 LOGW("JNI: DeleteGlobalRef(%p) failed to find entry\n", jobj);
869 goto bail;
870 }
871
872 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
873 int count = dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable);
874 // TODO: not quite right, need to subtract holes
875 if (count < gDvm.jniGlobalRefLoMark) {
876 LOGD("GREF has decreased to %d\n", count);
877 gDvm.jniGlobalRefHiMark -= kGrefWaterInterval;
878 gDvm.jniGlobalRefLoMark -= kGrefWaterInterval;
879 }
880 }
881#else
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800882 if (!dvmRemoveFromReferenceTable(&gDvm.jniGlobalRefTable,
Andy McFaddenab00d452009-08-19 07:21:41 -0700883 gDvm.jniGlobalRefTable.table, jobj))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800884 {
885 LOGW("JNI: DeleteGlobalRef(%p) failed to find entry (valid=%d)\n",
Andy McFaddenab00d452009-08-19 07:21:41 -0700886 jobj, dvmIsValidObject((Object*) jobj));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800887 goto bail;
888 }
889
890 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
891 int count = dvmReferenceTableEntries(&gDvm.jniGlobalRefTable);
892 if (count < gDvm.jniGlobalRefLoMark) {
893 LOGD("GREF has decreased to %d\n", count);
894 gDvm.jniGlobalRefHiMark -= kGrefWaterInterval;
895 gDvm.jniGlobalRefLoMark -= kGrefWaterInterval;
896 }
897 }
Andy McFaddend5ab7262009-08-25 07:19:34 -0700898#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800899
900bail:
901 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
902}
903
904/*
Andy McFaddenab00d452009-08-19 07:21:41 -0700905 * Objects don't currently move, so we just need to create a reference
906 * that will ensure the array object isn't collected.
907 *
Andy McFaddenc26bb632009-08-21 12:01:31 -0700908 * We use a separate reference table, which is part of the GC root set.
Andy McFaddenab00d452009-08-19 07:21:41 -0700909 */
910static void pinPrimitiveArray(ArrayObject* arrayObj)
911{
Andy McFaddenc26bb632009-08-21 12:01:31 -0700912 if (arrayObj == NULL)
913 return;
914
915 dvmLockMutex(&gDvm.jniPinRefLock);
916 if (!dvmAddToReferenceTable(&gDvm.jniPinRefTable, (Object*)arrayObj)) {
917 dvmDumpReferenceTable(&gDvm.jniPinRefTable, "JNI pinned array");
918 LOGE("Failed adding to JNI pinned array ref table (%d entries)\n",
919 (int) dvmReferenceTableEntries(&gDvm.jniPinRefTable));
920 dvmDumpThread(dvmThreadSelf(), false);
921 dvmAbort();
922 }
923
924 /*
925 * If we're watching global ref usage, also keep an eye on these.
926 *
927 * The total number of pinned primitive arrays should be pretty small.
928 * A single array should not be pinned more than once or twice; any
929 * more than that is a strong indicator that a Release function is
930 * not being called.
931 */
932 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
933 int count = 0;
934 Object** ppObj = gDvm.jniPinRefTable.table;
935 while (ppObj < gDvm.jniPinRefTable.nextEntry) {
936 if (*ppObj++ == (Object*) arrayObj)
937 count++;
938 }
939
940 if (count > kPinComplainThreshold) {
941 LOGW("JNI: pin count on array %p (%s) is now %d\n",
942 arrayObj, arrayObj->obj.clazz->descriptor, count);
943 /* keep going */
944 }
945 }
946
947 dvmUnlockMutex(&gDvm.jniPinRefLock);
Andy McFaddenab00d452009-08-19 07:21:41 -0700948}
949
950/*
951 * Un-pin the array object. If an object was pinned twice, it must be
952 * unpinned twice before it's free to move.
Andy McFaddenab00d452009-08-19 07:21:41 -0700953 */
954static void unpinPrimitiveArray(ArrayObject* arrayObj)
955{
Andy McFaddenc26bb632009-08-21 12:01:31 -0700956 if (arrayObj == NULL)
957 return;
958
959 dvmLockMutex(&gDvm.jniPinRefLock);
960 if (!dvmRemoveFromReferenceTable(&gDvm.jniPinRefTable,
961 gDvm.jniPinRefTable.table, (Object*) arrayObj))
962 {
963 LOGW("JNI: unpinPrimitiveArray(%p) failed to find entry (valid=%d)\n",
964 arrayObj, dvmIsValidObject((Object*) arrayObj));
965 goto bail;
966 }
967
968bail:
969 dvmUnlockMutex(&gDvm.jniPinRefLock);
Andy McFaddenab00d452009-08-19 07:21:41 -0700970}
971
972/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800973 * GC helper function to mark all JNI global references.
Andy McFaddenc26bb632009-08-21 12:01:31 -0700974 *
975 * We're currently handling the "pin" table here too.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800976 */
977void dvmGcMarkJniGlobalRefs()
978{
Andy McFaddend5ab7262009-08-25 07:19:34 -0700979 Object** op;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800980
981 dvmLockMutex(&gDvm.jniGlobalRefLock);
982
Andy McFaddend5ab7262009-08-25 07:19:34 -0700983#ifdef USE_INDIRECT_REF
984 IndirectRefTable* pRefTable = &gDvm.jniGlobalRefTable;
985 op = pRefTable->table;
986 int numEntries = dvmIndirectRefTableEntries(pRefTable);
987 int i;
988
989 for (i = 0; i < numEntries; i++) {
990 Object* obj = *op;
991 if (obj != NULL)
992 dvmMarkObjectNonNull(obj);
993 op++;
994 }
995#else
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800996 op = gDvm.jniGlobalRefTable.table;
997 while ((uintptr_t)op < (uintptr_t)gDvm.jniGlobalRefTable.nextEntry) {
998 dvmMarkObjectNonNull(*(op++));
999 }
Andy McFaddend5ab7262009-08-25 07:19:34 -07001000#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001001
1002 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
Andy McFaddenc26bb632009-08-21 12:01:31 -07001003
1004
1005 dvmLockMutex(&gDvm.jniPinRefLock);
1006
1007 op = gDvm.jniPinRefTable.table;
1008 while ((uintptr_t)op < (uintptr_t)gDvm.jniPinRefTable.nextEntry) {
1009 dvmMarkObjectNonNull(*(op++));
1010 }
1011
1012 dvmUnlockMutex(&gDvm.jniPinRefLock);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001013}
1014
1015
Andy McFadden0083d372009-08-21 14:44:04 -07001016#if 0
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001017/*
1018 * Determine if "obj" appears in the argument list for the native method.
1019 *
1020 * We use the "shorty" signature to determine which argument slots hold
1021 * reference types.
1022 */
1023static bool findInArgList(Thread* self, Object* obj)
1024{
1025 const Method* meth;
1026 u4* fp;
1027 int i;
1028
1029 fp = self->curFrame;
1030 while (1) {
1031 /*
1032 * Back up over JNI PushLocalFrame frames. This works because the
1033 * previous frame on the interpreted stack is either a break frame
1034 * (if we called here via native code) or an interpreted method (if
1035 * we called here via the interpreter). In both cases the method
1036 * pointer won't match.
1037 */
1038 StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
1039 meth = saveArea->method;
1040 if (meth != SAVEAREA_FROM_FP(saveArea->prevFrame)->method)
1041 break;
1042 fp = saveArea->prevFrame;
1043 }
1044
1045 LOGVV("+++ scanning %d args in %s (%s)\n",
1046 meth->insSize, meth->name, meth->shorty);
1047 const char* shorty = meth->shorty +1; /* skip return type char */
1048 for (i = 0; i < meth->insSize; i++) {
1049 if (i == 0 && !dvmIsStaticMethod(meth)) {
1050 /* first arg is "this" ref, not represented in "shorty" */
1051 if (fp[i] == (u4) obj)
1052 return true;
1053 } else {
1054 /* if this is a reference type, see if it matches */
1055 switch (*shorty) {
1056 case 'L':
1057 if (fp[i] == (u4) obj)
1058 return true;
1059 break;
1060 case 'D':
1061 case 'J':
1062 i++;
1063 break;
1064 case '\0':
1065 LOGE("Whoops! ran off the end of %s (%d)\n",
1066 meth->shorty, meth->insSize);
1067 break;
1068 default:
1069 if (fp[i] == (u4) obj)
1070 LOGI("NOTE: ref %p match on arg type %c\n", obj, *shorty);
1071 break;
1072 }
1073 shorty++;
1074 }
1075 }
1076
1077 /*
1078 * For static methods, we also pass a class pointer in.
1079 */
1080 if (dvmIsStaticMethod(meth)) {
1081 //LOGI("+++ checking class pointer in %s\n", meth->name);
1082 if ((void*)obj == (void*)meth->clazz)
1083 return true;
1084 }
1085 return false;
1086}
Andy McFadden0083d372009-08-21 14:44:04 -07001087#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001088
1089/*
1090 * Verify that a reference passed in from native code is one that the
1091 * code is allowed to have.
1092 *
1093 * It's okay for native code to pass us a reference that:
Andy McFadden0083d372009-08-21 14:44:04 -07001094 * - was passed in as an argument when invoked by native code (and hence
1095 * is in the JNI local refs table)
1096 * - was returned to it from JNI (and is now in the local refs table)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001097 * - is present in the JNI global refs table
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001098 *
1099 * Used by -Xcheck:jni and GetObjectRefType.
1100 *
1101 * NOTE: in the current VM, global and local references are identical. If
1102 * something is both global and local, we can't tell them apart, and always
1103 * return "local".
1104 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001105jobjectRefType dvmGetJNIRefType(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001106{
Andy McFaddend5ab7262009-08-25 07:19:34 -07001107#ifdef USE_INDIRECT_REF
1108 /*
1109 * IndirectRefKind is currently defined as an exact match of
Andy McFadden0423f0e2009-08-26 07:21:53 -07001110 * jobjectRefType, so this is easy. We have to decode it to determine
1111 * if it's a valid reference and not merely valid-looking.
Andy McFaddend5ab7262009-08-25 07:19:34 -07001112 */
Andy McFadden0423f0e2009-08-26 07:21:53 -07001113 Object* obj = dvmDecodeIndirectRef(env, jobj);
1114
1115 if (obj == NULL) {
1116 /* invalid ref, or jobj was NULL */
1117 return JNIInvalidRefType;
1118 } else {
1119 return (jobjectRefType) dvmGetIndirectRefType(jobj);
1120 }
Andy McFaddend5ab7262009-08-25 07:19:34 -07001121#else
Andy McFaddenab00d452009-08-19 07:21:41 -07001122 ReferenceTable* pRefTable = getLocalRefTable(env);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001123 Thread* self = dvmThreadSelf();
1124 //Object** top;
1125 Object** ptr;
1126
Andy McFadden0083d372009-08-21 14:44:04 -07001127#if 0
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001128 /* check args */
Andy McFaddenab00d452009-08-19 07:21:41 -07001129 if (findInArgList(self, jobj)) {
1130 //LOGI("--- REF found %p on stack\n", jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001131 return JNILocalRefType;
1132 }
Andy McFadden0083d372009-08-21 14:44:04 -07001133#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001134
1135 /* check locals */
Andy McFaddenab00d452009-08-19 07:21:41 -07001136 if (dvmFindInReferenceTable(pRefTable, pRefTable->table, jobj) != NULL) {
1137 //LOGI("--- REF found %p in locals\n", jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001138 return JNILocalRefType;
1139 }
1140
1141 /* check globals */
1142 dvmLockMutex(&gDvm.jniGlobalRefLock);
1143 if (dvmFindInReferenceTable(&gDvm.jniGlobalRefTable,
Andy McFaddenab00d452009-08-19 07:21:41 -07001144 gDvm.jniGlobalRefTable.table, jobj))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001145 {
Andy McFaddenab00d452009-08-19 07:21:41 -07001146 //LOGI("--- REF found %p in globals\n", jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001147 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
1148 return JNIGlobalRefType;
1149 }
1150 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
1151
1152 /* not found! */
1153 return JNIInvalidRefType;
Andy McFaddend5ab7262009-08-25 07:19:34 -07001154#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001155}
1156
1157/*
1158 * Register a method that uses JNI calling conventions.
1159 */
1160static bool dvmRegisterJNIMethod(ClassObject* clazz, const char* methodName,
1161 const char* signature, void* fnPtr)
1162{
1163 Method* method;
1164 bool result = false;
1165
1166 if (fnPtr == NULL)
1167 goto bail;
1168
1169 method = dvmFindDirectMethodByDescriptor(clazz, methodName, signature);
1170 if (method == NULL)
1171 method = dvmFindVirtualMethodByDescriptor(clazz, methodName, signature);
1172 if (method == NULL) {
1173 LOGW("ERROR: Unable to find decl for native %s.%s %s\n",
1174 clazz->descriptor, methodName, signature);
1175 goto bail;
1176 }
1177
1178 if (!dvmIsNativeMethod(method)) {
1179 LOGW("Unable to register: not native: %s.%s %s\n",
1180 clazz->descriptor, methodName, signature);
1181 goto bail;
1182 }
1183
1184 if (method->nativeFunc != dvmResolveNativeMethod) {
1185 LOGW("Warning: %s.%s %s was already registered/resolved?\n",
1186 clazz->descriptor, methodName, signature);
1187 /* keep going, I guess */
1188 }
1189
Andy McFadden59b61772009-05-13 16:44:34 -07001190 dvmUseJNIBridge(method, fnPtr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001191
1192 LOGV("JNI-registered %s.%s %s\n", clazz->descriptor, methodName,
1193 signature);
1194 result = true;
1195
1196bail:
1197 return result;
1198}
1199
1200/*
Andy McFadden59b61772009-05-13 16:44:34 -07001201 * Returns "true" if CheckJNI is enabled in the VM.
1202 */
1203static bool dvmIsCheckJNIEnabled(void)
1204{
1205 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
1206 return vm->useChecked;
1207}
1208
1209/*
1210 * Point "method->nativeFunc" at the JNI bridge, and overload "method->insns"
1211 * to point at the actual function.
1212 */
1213void dvmUseJNIBridge(Method* method, void* func)
1214{
Andy McFadden0083d372009-08-21 14:44:04 -07001215 enum {
1216 kJNIGeneral = 0,
1217 kJNISync = 1,
1218 kJNIVirtualNoRef = 2,
1219 kJNIStaticNoRef = 3,
1220 } kind;
1221 static const DalvikBridgeFunc stdFunc[] = {
1222 dvmCallJNIMethod_general,
1223 dvmCallJNIMethod_synchronized,
1224 dvmCallJNIMethod_virtualNoRef,
1225 dvmCallJNIMethod_staticNoRef
1226 };
1227 static const DalvikBridgeFunc checkFunc[] = {
1228 dvmCheckCallJNIMethod_general,
1229 dvmCheckCallJNIMethod_synchronized,
1230 dvmCheckCallJNIMethod_virtualNoRef,
1231 dvmCheckCallJNIMethod_staticNoRef
1232 };
1233
1234 bool hasRefArg = false;
1235
1236 if (dvmIsSynchronizedMethod(method)) {
1237 /* use version with synchronization; calls into general handler */
1238 kind = kJNISync;
Andy McFadden59b61772009-05-13 16:44:34 -07001239 } else {
Andy McFadden0083d372009-08-21 14:44:04 -07001240 /*
1241 * Do a quick scan through the "shorty" signature to see if the method
1242 * takes any reference arguments.
1243 */
1244 const char* cp = method->shorty;
1245 while (*++cp != '\0') { /* pre-incr to skip return type */
1246 if (*cp == 'L') {
1247 /* 'L' used for both object and array references */
1248 hasRefArg = true;
1249 break;
1250 }
1251 }
1252
1253 if (hasRefArg) {
1254 /* use general handler to slurp up reference args */
1255 kind = kJNIGeneral;
1256 } else {
1257 /* virtual methods have a ref in args[0] (not in signature) */
1258 if (dvmIsStaticMethod(method))
1259 kind = kJNIStaticNoRef;
1260 else
1261 kind = kJNIVirtualNoRef;
1262 }
1263 }
1264
1265 if (dvmIsCheckJNIEnabled()) {
1266 dvmSetNativeFunc(method, checkFunc[kind], func);
1267 } else {
1268 dvmSetNativeFunc(method, stdFunc[kind], func);
Andy McFadden59b61772009-05-13 16:44:34 -07001269 }
1270}
1271
1272/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001273 * Get the method currently being executed by examining the interp stack.
1274 */
1275const Method* dvmGetCurrentJNIMethod(void)
1276{
1277 assert(dvmThreadSelf() != NULL);
1278
1279 void* fp = dvmThreadSelf()->curFrame;
1280 const Method* meth = SAVEAREA_FROM_FP(fp)->method;
1281
1282 assert(meth != NULL);
1283 assert(dvmIsNativeMethod(meth));
1284 return meth;
1285}
1286
1287
1288/*
1289 * Track a JNI MonitorEnter in the current thread.
1290 *
1291 * The goal is to be able to "implicitly" release all JNI-held monitors
1292 * when the thread detaches.
1293 *
1294 * Monitors may be entered multiple times, so we add a new entry for each
1295 * enter call. It would be more efficient to keep a counter. At present
1296 * there's no real motivation to improve this however.
1297 */
1298static void trackMonitorEnter(Thread* self, Object* obj)
1299{
1300 static const int kInitialSize = 16;
1301 ReferenceTable* refTable = &self->jniMonitorRefTable;
1302
1303 /* init table on first use */
1304 if (refTable->table == NULL) {
1305 assert(refTable->maxEntries == 0);
1306
1307 if (!dvmInitReferenceTable(refTable, kInitialSize, INT_MAX)) {
1308 LOGE("Unable to initialize monitor tracking table\n");
1309 dvmAbort();
1310 }
1311 }
1312
1313 if (!dvmAddToReferenceTable(refTable, obj)) {
1314 /* ran out of memory? could throw exception instead */
1315 LOGE("Unable to add entry to monitor tracking table\n");
1316 dvmAbort();
1317 } else {
1318 LOGVV("--- added monitor %p\n", obj);
1319 }
1320}
1321
Andy McFaddenab00d452009-08-19 07:21:41 -07001322
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001323/*
1324 * Track a JNI MonitorExit in the current thread.
1325 */
1326static void trackMonitorExit(Thread* self, Object* obj)
1327{
Andy McFaddenab00d452009-08-19 07:21:41 -07001328 ReferenceTable* pRefTable = &self->jniMonitorRefTable;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001329
Andy McFaddenab00d452009-08-19 07:21:41 -07001330 if (!dvmRemoveFromReferenceTable(pRefTable, pRefTable->table, obj)) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001331 LOGE("JNI monitor %p not found in tracking list\n", obj);
1332 /* keep going? */
1333 } else {
1334 LOGVV("--- removed monitor %p\n", obj);
1335 }
1336}
1337
1338/*
1339 * Release all monitors held by the jniMonitorRefTable list.
1340 */
1341void dvmReleaseJniMonitors(Thread* self)
1342{
Andy McFaddenab00d452009-08-19 07:21:41 -07001343 ReferenceTable* pRefTable = &self->jniMonitorRefTable;
1344 Object** top = pRefTable->table;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001345
1346 if (top == NULL)
1347 return;
1348
Andy McFaddenab00d452009-08-19 07:21:41 -07001349 Object** ptr = pRefTable->nextEntry;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001350 while (--ptr >= top) {
1351 if (!dvmUnlockObject(self, *ptr)) {
1352 LOGW("Unable to unlock monitor %p at thread detach\n", *ptr);
1353 } else {
1354 LOGVV("--- detach-releasing monitor %p\n", *ptr);
1355 }
1356 }
1357
1358 /* zap it */
Andy McFaddenab00d452009-08-19 07:21:41 -07001359 pRefTable->nextEntry = pRefTable->table;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001360}
1361
1362#ifdef WITH_JNI_STACK_CHECK
1363/*
1364 * Compute a CRC on the entire interpreted stack.
1365 *
1366 * Would be nice to compute it on "self" as well, but there are parts of
1367 * the Thread that can be altered by other threads (e.g. prev/next pointers).
1368 */
1369static void computeStackSum(Thread* self)
1370{
1371 const u1* low = (const u1*)SAVEAREA_FROM_FP(self->curFrame);
1372 u4 crc = dvmInitCrc32();
1373 self->stackCrc = 0;
1374 crc = dvmComputeCrc32(crc, low, self->interpStackStart - low);
1375 self->stackCrc = crc;
1376}
1377
1378/*
1379 * Compute a CRC on the entire interpreted stack, and compare it to what
1380 * we previously computed.
1381 *
1382 * We can execute JNI directly from native code without calling in from
1383 * interpreted code during VM initialization and immediately after JNI
1384 * thread attachment. Another opportunity exists during JNI_OnLoad. Rather
1385 * than catching these cases we just ignore them here, which is marginally
1386 * less accurate but reduces the amount of code we have to touch with #ifdefs.
1387 */
1388static void checkStackSum(Thread* self)
1389{
1390 const u1* low = (const u1*)SAVEAREA_FROM_FP(self->curFrame);
1391 u4 stackCrc, crc;
1392
1393 stackCrc = self->stackCrc;
1394 self->stackCrc = 0;
1395 crc = dvmInitCrc32();
1396 crc = dvmComputeCrc32(crc, low, self->interpStackStart - low);
1397 if (crc != stackCrc) {
1398 const Method* meth = dvmGetCurrentJNIMethod();
1399 if (dvmComputeExactFrameDepth(self->curFrame) == 1) {
1400 LOGD("JNI: bad stack CRC (0x%08x) -- okay during init\n",
1401 stackCrc);
1402 } else if (strcmp(meth->name, "nativeLoad") == 0 &&
1403 (strcmp(meth->clazz->descriptor, "Ljava/lang/Runtime;") == 0))
1404 {
1405 LOGD("JNI: bad stack CRC (0x%08x) -- okay during JNI_OnLoad\n",
1406 stackCrc);
1407 } else {
1408 LOGW("JNI: bad stack CRC (%08x vs %08x)\n", crc, stackCrc);
1409 dvmAbort();
1410 }
1411 }
1412 self->stackCrc = (u4) -1; /* make logic errors more noticeable */
1413}
1414#endif
1415
1416
1417/*
1418 * ===========================================================================
Andy McFaddend5ab7262009-08-25 07:19:34 -07001419 * JNI call bridge
1420 * ===========================================================================
1421 */
1422
1423/*
1424 * The functions here form a bridge between interpreted code and JNI native
1425 * functions. The basic task is to convert an array of primitives and
1426 * references into C-style function arguments. This is architecture-specific
1427 * and usually requires help from assembly code.
1428 *
1429 * The bridge takes four arguments: the array of parameters, a place to
1430 * store the function result (if any), the method to call, and a pointer
1431 * to the current thread.
1432 *
1433 * These functions aren't called directly from elsewhere in the VM.
1434 * A pointer in the Method struct points to one of these, and when a native
1435 * method is invoked the interpreter jumps to it.
1436 *
1437 * (The "internal native" methods are invoked the same way, but instead
1438 * of calling through a bridge, the target method is called directly.)
1439 *
1440 * The "args" array should not be modified, but we do so anyway for
1441 * performance reasons. We know that it points to the "outs" area on
1442 * the current method's interpreted stack. This area is ignored by the
1443 * precise GC, because there is no register map for a native method (for
1444 * an interpreted method the args would be listed in the argument set).
1445 * We know all of the values exist elsewhere on the interpreted stack,
1446 * because the method call setup copies them right before making the call,
1447 * so we don't have to worry about concealing stuff from the GC.
1448 *
1449 * If we don't want to modify "args", we either have to create a local
1450 * copy and modify it before calling dvmPlatformInvoke, or we have to do
1451 * the local reference replacement within dvmPlatformInvoke. The latter
1452 * has some performance advantages, though if we can inline the local
1453 * reference adds we may win when there's a lot of reference args (unless
1454 * we want to code up some local ref table manipulation in assembly.
1455 */
1456
1457/*
Andy McFadden0423f0e2009-08-26 07:21:53 -07001458 * If necessary, convert the value in pResult from a local/global reference
1459 * to an object pointer.
1460 */
1461static inline void convertReferenceResult(JNIEnv* env, JValue* pResult,
1462 const Method* method, Thread* self)
1463{
1464 if (method->shorty[0] == 'L' && !dvmCheckException(self) &&
1465 pResult->l != NULL)
1466 {
1467 pResult->l = dvmDecodeIndirectRef(env, pResult->l);
1468 }
1469}
1470
1471/*
Andy McFaddend5ab7262009-08-25 07:19:34 -07001472 * General form, handles all cases.
1473 */
1474void dvmCallJNIMethod_general(const u4* args, JValue* pResult,
1475 const Method* method, Thread* self)
1476{
1477 int oldStatus;
1478 u4* modArgs = (u4*) args;
1479
1480 assert(method->insns != NULL);
1481
1482 //LOGI("JNI calling %p (%s.%s:%s):\n", method->insns,
1483 // method->clazz->descriptor, method->name, method->shorty);
1484
1485 /*
1486 * Walk the argument list, creating local references for appropriate
1487 * arguments.
1488 */
1489 JNIEnv* env = self->jniEnv;
1490 jclass staticMethodClass;
1491 int idx = 0;
1492 if (dvmIsStaticMethod(method)) {
1493 /* add the class object we pass in */
1494 staticMethodClass = addLocalReference(env, (Object*) method->clazz);
1495 if (staticMethodClass == NULL) {
1496 assert(dvmCheckException(self));
1497 return;
1498 }
1499 } else {
1500 /* add "this" */
1501 staticMethodClass = NULL;
1502 jobject thisObj = addLocalReference(env, (Object*) modArgs[0]);
1503 if (thisObj == NULL) {
1504 assert(dvmCheckException(self));
1505 return;
1506 }
1507 modArgs[idx] = (u4) thisObj;
1508 idx = 1;
1509 }
1510
1511 const char* shorty = &method->shorty[1]; /* skip return type */
1512 while (*shorty != '\0') {
1513 switch (*shorty++) {
1514 case 'L':
1515 //LOGI(" local %d: 0x%08x\n", idx, modArgs[idx]);
1516 if (modArgs[idx] != 0) {
1517 //if (!dvmIsValidObject((Object*) modArgs[idx]))
1518 // dvmAbort();
1519 jobject argObj = addLocalReference(env, (Object*) modArgs[idx]);
1520 if (argObj == NULL) {
1521 assert(dvmCheckException(self));
1522 return;
1523 }
1524 modArgs[idx] = (u4) argObj;
1525 }
1526 break;
1527 case 'D':
1528 case 'J':
1529 idx++;
1530 break;
1531 default:
1532 /* Z B C S I -- do nothing */
1533 break;
1534 }
1535
1536 idx++;
1537 }
1538
1539 oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
1540
1541 COMPUTE_STACK_SUM(self);
1542 dvmPlatformInvoke(self->jniEnv, staticMethodClass,
1543 method->jniArgInfo, method->insSize, modArgs, method->shorty,
1544 (void*)method->insns, pResult);
1545 CHECK_STACK_SUM(self);
1546
1547 dvmChangeStatus(self, oldStatus);
Andy McFadden0423f0e2009-08-26 07:21:53 -07001548
1549 convertReferenceResult(env, pResult, method, self);
Andy McFaddend5ab7262009-08-25 07:19:34 -07001550}
1551
1552/*
1553 * Handler for the unusual case of a synchronized native method.
1554 *
1555 * Lock the object, then call through the general function.
1556 */
1557void dvmCallJNIMethod_synchronized(const u4* args, JValue* pResult,
1558 const Method* method, Thread* self)
1559{
1560 Object* lockObj;
1561
1562 assert(dvmIsSynchronizedMethod(method));
1563
1564 if (dvmIsStaticMethod(method))
1565 lockObj = (Object*) method->clazz;
1566 else
1567 lockObj = (Object*) args[0];
1568
1569 LOGVV("Calling %s.%s: locking %p (%s)\n",
1570 method->clazz->descriptor, method->name,
1571 lockObj, lockObj->clazz->descriptor);
1572
1573 dvmLockObject(self, lockObj);
1574 dvmCallJNIMethod_general(args, pResult, method, self);
1575 dvmUnlockObject(self, lockObj);
1576}
1577
1578/*
1579 * Virtual method call, no reference arguments.
1580 *
1581 * We need to local-ref the "this" argument, found in args[0].
1582 */
1583void dvmCallJNIMethod_virtualNoRef(const u4* args, JValue* pResult,
1584 const Method* method, Thread* self)
1585{
1586 u4* modArgs = (u4*) args;
1587 int oldStatus;
1588
1589 jobject thisObj = addLocalReference(self->jniEnv, (Object*) args[0]);
1590 if (thisObj == NULL) {
1591 assert(dvmCheckException(self));
1592 return;
1593 }
1594 modArgs[0] = (u4) thisObj;
1595
1596 oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
1597
1598 COMPUTE_STACK_SUM(self);
1599 dvmPlatformInvoke(self->jniEnv, NULL,
1600 method->jniArgInfo, method->insSize, modArgs, method->shorty,
1601 (void*)method->insns, pResult);
1602 CHECK_STACK_SUM(self);
1603
1604 dvmChangeStatus(self, oldStatus);
Andy McFadden0423f0e2009-08-26 07:21:53 -07001605
1606 convertReferenceResult(self->jniEnv, pResult, method, self);
Andy McFaddend5ab7262009-08-25 07:19:34 -07001607}
1608
1609/*
1610 * Static method call, no reference arguments.
1611 *
1612 * We need to local-ref the class reference.
1613 */
1614void dvmCallJNIMethod_staticNoRef(const u4* args, JValue* pResult,
1615 const Method* method, Thread* self)
1616{
1617 jclass staticMethodClass;
1618 int oldStatus;
1619
1620 staticMethodClass = addLocalReference(self->jniEnv, (Object*)method->clazz);
1621 if (staticMethodClass == NULL) {
1622 assert(dvmCheckException(self));
1623 return;
1624 }
1625
1626 oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
1627
1628 COMPUTE_STACK_SUM(self);
1629 dvmPlatformInvoke(self->jniEnv, staticMethodClass,
1630 method->jniArgInfo, method->insSize, args, method->shorty,
1631 (void*)method->insns, pResult);
1632 CHECK_STACK_SUM(self);
1633
1634 dvmChangeStatus(self, oldStatus);
Andy McFadden0423f0e2009-08-26 07:21:53 -07001635
1636 convertReferenceResult(self->jniEnv, pResult, method, self);
Andy McFaddend5ab7262009-08-25 07:19:34 -07001637}
1638
1639/*
1640 * Extract the return type enum from the "jniArgInfo" field.
1641 */
1642DalvikJniReturnType dvmGetArgInfoReturnType(int jniArgInfo)
1643{
1644 return (jniArgInfo & DALVIK_JNI_RETURN_MASK) >> DALVIK_JNI_RETURN_SHIFT;
1645}
1646
1647
1648/*
1649 * ===========================================================================
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001650 * JNI implementation
1651 * ===========================================================================
1652 */
1653
1654/*
1655 * Return the version of the native method interface.
1656 */
1657static jint GetVersion(JNIEnv* env)
1658{
1659 JNI_ENTER();
1660 /*
1661 * There is absolutely no need to toggle the mode for correct behavior.
1662 * However, it does provide native code with a simple "suspend self
1663 * if necessary" call.
1664 */
1665 JNI_EXIT();
1666 return JNI_VERSION_1_6;
1667}
1668
1669/*
1670 * Create a new class from a bag of bytes.
1671 *
1672 * This is not currently supported within Dalvik.
1673 */
1674static jclass DefineClass(JNIEnv* env, const char *name, jobject loader,
1675 const jbyte* buf, jsize bufLen)
1676{
1677 UNUSED_PARAMETER(name);
1678 UNUSED_PARAMETER(loader);
1679 UNUSED_PARAMETER(buf);
1680 UNUSED_PARAMETER(bufLen);
1681
1682 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001683 LOGW("JNI DefineClass is not supported\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001684 JNI_EXIT();
1685 return NULL;
1686}
1687
1688/*
1689 * Find a class by name.
1690 *
1691 * We have to use the "no init" version of FindClass here, because we might
1692 * be getting the class prior to registering native methods that will be
1693 * used in <clinit>.
1694 *
1695 * We need to get the class loader associated with the current native
1696 * method. If there is no native method, e.g. we're calling this from native
1697 * code right after creating the VM, the spec says we need to use the class
1698 * loader returned by "ClassLoader.getBaseClassLoader". There is no such
1699 * method, but it's likely they meant ClassLoader.getSystemClassLoader.
1700 * We can't get that until after the VM has initialized though.
1701 */
1702static jclass FindClass(JNIEnv* env, const char* name)
1703{
1704 JNI_ENTER();
1705
1706 const Method* thisMethod;
1707 ClassObject* clazz;
Andy McFaddenab00d452009-08-19 07:21:41 -07001708 jclass jclazz = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001709 Object* loader;
1710 char* descriptor = NULL;
1711
1712 thisMethod = dvmGetCurrentJNIMethod();
1713 assert(thisMethod != NULL);
1714
1715 descriptor = dvmNameToDescriptor(name);
1716 if (descriptor == NULL) {
1717 clazz = NULL;
1718 goto bail;
1719 }
1720
1721 //Thread* self = dvmThreadSelf();
1722 if (_self->classLoaderOverride != NULL) {
1723 /* hack for JNI_OnLoad */
1724 assert(strcmp(thisMethod->name, "nativeLoad") == 0);
1725 loader = _self->classLoaderOverride;
1726 } else if (thisMethod == gDvm.methFakeNativeEntry) {
1727 /* start point of invocation interface */
1728 if (!gDvm.initializing)
1729 loader = dvmGetSystemClassLoader();
1730 else
1731 loader = NULL;
1732 } else {
1733 loader = thisMethod->clazz->classLoader;
1734 }
1735
1736 clazz = dvmFindClassNoInit(descriptor, loader);
Andy McFaddenab00d452009-08-19 07:21:41 -07001737 jclazz = addLocalReference(env, (Object*) clazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001738
1739bail:
1740 free(descriptor);
Andy McFaddenab00d452009-08-19 07:21:41 -07001741
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001742 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001743 return jclazz;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001744}
1745
1746/*
1747 * Return the superclass of a class.
1748 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001749static jclass GetSuperclass(JNIEnv* env, jclass jclazz)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001750{
1751 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001752 jclass jsuper;
1753
1754 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1755 if (clazz == NULL)
1756 jsuper = NULL;
1757 else
1758 jsuper = addLocalReference(env, (Object*)clazz->super);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001759 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001760 return jsuper;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001761}
1762
1763/*
1764 * Determine whether an object of clazz1 can be safely cast to clazz2.
1765 *
1766 * Like IsInstanceOf, but with a pair of class objects instead of obj+class.
1767 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001768static jboolean IsAssignableFrom(JNIEnv* env, jclass jclazz1, jclass jclazz2)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001769{
1770 JNI_ENTER();
1771
Andy McFaddenab00d452009-08-19 07:21:41 -07001772 ClassObject* clazz1 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz1);
1773 ClassObject* clazz2 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz2);
1774
1775 jboolean result = dvmInstanceof(clazz1, clazz2);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001776
1777 JNI_EXIT();
1778 return result;
1779}
1780
1781/*
1782 * Given a java.lang.reflect.Method or .Constructor, return a methodID.
1783 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001784static jmethodID FromReflectedMethod(JNIEnv* env, jobject jmethod)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001785{
1786 JNI_ENTER();
1787 jmethodID methodID;
Andy McFaddenab00d452009-08-19 07:21:41 -07001788 Object* method = dvmDecodeIndirectRef(env, jmethod);
1789 methodID = (jmethodID) dvmGetMethodFromReflectObj(method);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001790 JNI_EXIT();
1791 return methodID;
1792}
1793
1794/*
1795 * Given a java.lang.reflect.Field, return a fieldID.
1796 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001797static jfieldID FromReflectedField(JNIEnv* env, jobject jfield)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001798{
1799 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001800 jfieldID fieldID;
1801 Object* field = dvmDecodeIndirectRef(env, jfield);
1802 fieldID = (jfieldID) dvmGetFieldFromReflectObj(field);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001803 JNI_EXIT();
1804 return fieldID;
1805}
1806
1807/*
1808 * Convert a methodID to a java.lang.reflect.Method or .Constructor.
1809 *
1810 * (The "isStatic" field does not appear in the spec.)
1811 *
1812 * Throws OutOfMemory and returns NULL on failure.
1813 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001814static jobject ToReflectedMethod(JNIEnv* env, jclass jcls, jmethodID methodID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001815 jboolean isStatic)
1816{
1817 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001818 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls);
1819 Object* obj = dvmCreateReflectObjForMethod(clazz, (Method*) methodID);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001820 dvmReleaseTrackedAlloc(obj, NULL);
Andy McFaddenab00d452009-08-19 07:21:41 -07001821 jobject jobj = addLocalReference(env, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001822 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001823 return jobj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001824}
1825
1826/*
1827 * Convert a fieldID to a java.lang.reflect.Field.
1828 *
1829 * (The "isStatic" field does not appear in the spec.)
1830 *
1831 * Throws OutOfMemory and returns NULL on failure.
1832 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001833static jobject ToReflectedField(JNIEnv* env, jclass jcls, jfieldID fieldID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001834 jboolean isStatic)
1835{
1836 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001837 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls);
1838 Object* obj = dvmCreateReflectObjForField(jcls, (Field*) fieldID);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001839 dvmReleaseTrackedAlloc(obj, NULL);
Andy McFaddenab00d452009-08-19 07:21:41 -07001840 jobject jobj = addLocalReference(env, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001841 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001842 return jobj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001843}
1844
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001845/*
1846 * Take this exception and throw it.
1847 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001848static jint Throw(JNIEnv* env, jthrowable jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001849{
1850 JNI_ENTER();
1851
1852 jint retval;
1853
Andy McFaddenab00d452009-08-19 07:21:41 -07001854 if (jobj != NULL) {
1855 Object* obj = dvmDecodeIndirectRef(env, jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001856 dvmSetException(_self, obj);
1857 retval = JNI_OK;
Andy McFaddenab00d452009-08-19 07:21:41 -07001858 } else {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001859 retval = JNI_ERR;
Andy McFaddenab00d452009-08-19 07:21:41 -07001860 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001861
1862 JNI_EXIT();
1863 return retval;
1864}
1865
1866/*
Andy McFaddenab00d452009-08-19 07:21:41 -07001867 * Constructs an exception object from the specified class with the message
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001868 * specified by "message", and throws it.
1869 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001870static jint ThrowNew(JNIEnv* env, jclass jclazz, const char* message)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001871{
1872 JNI_ENTER();
1873
Andy McFaddenab00d452009-08-19 07:21:41 -07001874 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1875 dvmThrowExceptionByClass(clazz, message);
1876 // TODO: should return failure if this didn't work (e.g. OOM)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001877
1878 JNI_EXIT();
1879 return JNI_OK;
1880}
1881
1882/*
1883 * If an exception is being thrown, return the exception object. Otherwise,
1884 * return NULL.
1885 *
1886 * TODO: if there is no pending exception, we should be able to skip the
1887 * enter/exit checks. If we find one, we need to enter and then re-fetch
1888 * the exception (in case it got moved by a compacting GC).
1889 */
1890static jthrowable ExceptionOccurred(JNIEnv* env)
1891{
1892 JNI_ENTER();
1893
1894 Object* exception;
Andy McFaddenab00d452009-08-19 07:21:41 -07001895 jobject localException;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001896
Andy McFaddenab00d452009-08-19 07:21:41 -07001897 exception = dvmGetException(_self);
1898 localException = addLocalReference(env, exception);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001899 if (localException == NULL && exception != NULL) {
1900 /*
1901 * We were unable to add a new local reference, and threw a new
1902 * exception. We can't return "exception", because it's not a
1903 * local reference. So we have to return NULL, indicating that
1904 * there was no exception, even though it's pretty much raining
1905 * exceptions in here.
1906 */
1907 LOGW("JNI WARNING: addLocal/exception combo\n");
1908 }
1909
1910 JNI_EXIT();
1911 return localException;
1912}
1913
1914/*
1915 * Print an exception and stack trace to stderr.
1916 */
1917static void ExceptionDescribe(JNIEnv* env)
1918{
1919 JNI_ENTER();
1920
1921 Object* exception = dvmGetException(_self);
1922 if (exception != NULL) {
1923 dvmPrintExceptionStackTrace();
1924 } else {
1925 LOGI("Odd: ExceptionDescribe called, but no exception pending\n");
1926 }
1927
1928 JNI_EXIT();
1929}
1930
1931/*
1932 * Clear the exception currently being thrown.
1933 *
1934 * TODO: we should be able to skip the enter/exit stuff.
1935 */
1936static void ExceptionClear(JNIEnv* env)
1937{
1938 JNI_ENTER();
1939 dvmClearException(_self);
1940 JNI_EXIT();
1941}
1942
1943/*
1944 * Kill the VM. This function does not return.
1945 */
1946static void FatalError(JNIEnv* env, const char* msg)
1947{
1948 //dvmChangeStatus(NULL, THREAD_RUNNING);
1949 LOGE("JNI posting fatal error: %s\n", msg);
1950 dvmAbort();
1951}
1952
1953/*
1954 * Push a new JNI frame on the stack, with a new set of locals.
1955 *
1956 * The new frame must have the same method pointer. (If for no other
1957 * reason than FindClass needs it to get the appropriate class loader.)
1958 */
1959static jint PushLocalFrame(JNIEnv* env, jint capacity)
1960{
1961 JNI_ENTER();
1962 int result = JNI_OK;
Andy McFaddenab00d452009-08-19 07:21:41 -07001963 if (!ensureLocalCapacity(env, capacity) ||
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001964 !dvmPushLocalFrame(_self /*dvmThreadSelf()*/, dvmGetCurrentJNIMethod()))
1965 {
1966 /* yes, OutOfMemoryError, not StackOverflowError */
1967 dvmClearException(_self);
1968 dvmThrowException("Ljava/lang/OutOfMemoryError;",
1969 "out of stack in JNI PushLocalFrame");
1970 result = JNI_ERR;
1971 }
1972 JNI_EXIT();
1973 return result;
1974}
1975
1976/*
1977 * Pop the local frame off. If "result" is not null, add it as a
1978 * local reference on the now-current frame.
1979 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001980static jobject PopLocalFrame(JNIEnv* env, jobject jresult)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001981{
1982 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001983 Object* result = dvmDecodeIndirectRef(env, jresult);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001984 if (!dvmPopLocalFrame(_self /*dvmThreadSelf()*/)) {
1985 LOGW("JNI WARNING: too many PopLocalFrame calls\n");
1986 dvmClearException(_self);
1987 dvmThrowException("Ljava/lang/RuntimeException;",
1988 "too many PopLocalFrame calls");
1989 }
Andy McFaddenab00d452009-08-19 07:21:41 -07001990 jresult = addLocalReference(env, result);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001991 JNI_EXIT();
1992 return result;
1993}
1994
1995/*
1996 * Add a reference to the global list.
1997 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001998static jobject NewGlobalRef(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001999{
2000 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002001 Object* obj = dvmDecodeIndirectRef(env, jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002002 jobject retval = addGlobalReference(obj);
2003 JNI_EXIT();
2004 return retval;
2005}
2006
2007/*
2008 * Delete a reference from the global list.
2009 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002010static void DeleteGlobalRef(JNIEnv* env, jobject jglobalRef)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002011{
2012 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002013 deleteGlobalReference(jglobalRef);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002014 JNI_EXIT();
2015}
2016
2017
2018/*
2019 * Add a reference to the local list.
2020 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002021static jobject NewLocalRef(JNIEnv* env, jobject jref)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002022{
2023 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002024 Object* obj = dvmDecodeIndirectRef(env, jref);
2025 jobject retval = addLocalReference(env, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002026 JNI_EXIT();
2027 return retval;
2028}
2029
2030/*
2031 * Delete a reference from the local list.
2032 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002033static void DeleteLocalRef(JNIEnv* env, jobject jlocalRef)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002034{
2035 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002036 deleteLocalReference(env, jlocalRef);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002037 JNI_EXIT();
2038}
2039
2040/*
2041 * Ensure that the local references table can hold at least this many
2042 * references.
2043 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002044static jint EnsureLocalCapacity(JNIEnv* env, jint capacity)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002045{
2046 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002047 bool okay = ensureLocalCapacity(env, capacity);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002048 if (!okay) {
2049 dvmThrowException("Ljava/lang/OutOfMemoryError;",
2050 "can't ensure local reference capacity");
2051 }
2052 JNI_EXIT();
2053 if (okay)
2054 return 0;
2055 else
2056 return -1;
2057}
2058
2059
2060/*
2061 * Determine whether two Object references refer to the same underlying object.
2062 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002063static jboolean IsSameObject(JNIEnv* env, jobject jref1, jobject jref2)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002064{
2065 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002066 Object* obj1 = dvmDecodeIndirectRef(env, jref1);
2067 Object* obj2 = dvmDecodeIndirectRef(env, jref2);
2068 jboolean result = (obj1 == obj2);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002069 JNI_EXIT();
2070 return result;
2071}
2072
2073/*
2074 * Allocate a new object without invoking any constructors.
2075 */
2076static jobject AllocObject(JNIEnv* env, jclass jclazz)
2077{
2078 JNI_ENTER();
2079
Andy McFaddenab00d452009-08-19 07:21:41 -07002080 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2081 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002082
2083 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2084 assert(dvmCheckException(_self));
Andy McFaddenab00d452009-08-19 07:21:41 -07002085 result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002086 } else {
Andy McFaddenab00d452009-08-19 07:21:41 -07002087 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
2088 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002089 }
2090
2091 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002092 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002093}
2094
2095/*
Andy McFaddenab00d452009-08-19 07:21:41 -07002096 * Allocate a new object and invoke the supplied constructor.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002097 */
2098static jobject NewObject(JNIEnv* env, jclass jclazz, jmethodID methodID, ...)
2099{
2100 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002101 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2102 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002103
2104 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2105 assert(dvmCheckException(_self));
Andy McFaddenab00d452009-08-19 07:21:41 -07002106 result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002107 } else {
Andy McFaddenab00d452009-08-19 07:21:41 -07002108 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
2109 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002110 if (newObj != NULL) {
2111 JValue unused;
2112 va_list args;
2113 va_start(args, methodID);
Andy McFaddend5ab7262009-08-25 07:19:34 -07002114 dvmCallMethodV(_self, (Method*) methodID, newObj, true, &unused,
2115 args);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002116 va_end(args);
2117 }
2118 }
2119
2120 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002121 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002122}
Andy McFaddenab00d452009-08-19 07:21:41 -07002123static jobject NewObjectV(JNIEnv* env, jclass jclazz, jmethodID methodID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002124 va_list args)
2125{
2126 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002127 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2128 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002129
Andy McFaddenab00d452009-08-19 07:21:41 -07002130 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
2131 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002132 if (newObj != NULL) {
2133 JValue unused;
Andy McFaddend5ab7262009-08-25 07:19:34 -07002134 dvmCallMethodV(_self, (Method*) methodID, newObj, true, &unused, args);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002135 }
2136
2137 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002138 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002139}
Andy McFaddenab00d452009-08-19 07:21:41 -07002140static jobject NewObjectA(JNIEnv* env, jclass jclazz, jmethodID methodID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002141 jvalue* args)
2142{
2143 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002144 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2145 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002146
Andy McFaddenab00d452009-08-19 07:21:41 -07002147 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
2148 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002149 if (newObj != NULL) {
2150 JValue unused;
Andy McFaddend5ab7262009-08-25 07:19:34 -07002151 dvmCallMethodA(_self, (Method*) methodID, newObj, true, &unused, args);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002152 }
2153
2154 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002155 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002156}
2157
2158/*
2159 * Returns the class of an object.
2160 *
2161 * JNI spec says: obj must not be NULL.
2162 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002163static jclass GetObjectClass(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002164{
2165 JNI_ENTER();
2166
Andy McFaddenab00d452009-08-19 07:21:41 -07002167 assert(jobj != NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002168
Andy McFaddenab00d452009-08-19 07:21:41 -07002169 Object* obj = dvmDecodeIndirectRef(env, jobj);
2170 jclass jclazz = addLocalReference(env, (Object*) obj->clazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002171
2172 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002173 return jclazz;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002174}
2175
2176/*
2177 * Determine whether "obj" is an instance of "clazz".
2178 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002179static jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass jclazz)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002180{
2181 JNI_ENTER();
2182
Andy McFaddenab00d452009-08-19 07:21:41 -07002183 assert(jclazz != NULL);
2184
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002185 jboolean result;
2186
Andy McFaddenab00d452009-08-19 07:21:41 -07002187 if (jobj == NULL) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002188 result = true;
Andy McFaddenab00d452009-08-19 07:21:41 -07002189 } else {
2190 Object* obj = dvmDecodeIndirectRef(env, jobj);
2191 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2192 result = dvmInstanceof(obj->clazz, clazz);
2193 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002194
2195 JNI_EXIT();
2196 return result;
2197}
2198
2199/*
2200 * Get a method ID for an instance method.
2201 *
2202 * JNI defines <init> as an instance method, but Dalvik considers it a
2203 * "direct" method, so we have to special-case it here.
2204 *
2205 * Dalvik also puts all private methods into the "direct" list, so we
2206 * really need to just search both lists.
2207 */
2208static jmethodID GetMethodID(JNIEnv* env, jclass jclazz, const char* name,
2209 const char* sig)
2210{
2211 JNI_ENTER();
2212
Andy McFaddenab00d452009-08-19 07:21:41 -07002213 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002214 jmethodID id = NULL;
2215
2216 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2217 assert(dvmCheckException(_self));
2218 } else {
2219 Method* meth;
2220
2221 meth = dvmFindVirtualMethodHierByDescriptor(clazz, name, sig);
2222 if (meth == NULL) {
2223 /* search private methods and constructors; non-hierarchical */
2224 meth = dvmFindDirectMethodByDescriptor(clazz, name, sig);
2225 }
2226 if (meth != NULL && dvmIsStaticMethod(meth)) {
2227 IF_LOGD() {
2228 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
2229 LOGD("GetMethodID: not returning static method %s.%s %s\n",
2230 clazz->descriptor, meth->name, desc);
2231 free(desc);
2232 }
2233 meth = NULL;
2234 }
2235 if (meth == NULL) {
Andy McFadden03bd0d52009-08-18 15:32:27 -07002236 LOGD("GetMethodID: method not found: %s.%s:%s\n",
2237 clazz->descriptor, name, sig);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002238 dvmThrowException("Ljava/lang/NoSuchMethodError;", name);
2239 }
2240
2241 /*
2242 * The method's class may not be the same as clazz, but if
2243 * it isn't this must be a virtual method and the class must
2244 * be a superclass (and, hence, already initialized).
2245 */
2246 if (meth != NULL) {
2247 assert(dvmIsClassInitialized(meth->clazz) ||
2248 dvmIsClassInitializing(meth->clazz));
2249 }
2250 id = (jmethodID) meth;
2251 }
2252 JNI_EXIT();
2253 return id;
2254}
2255
2256/*
2257 * Get a field ID (instance fields).
2258 */
2259static jfieldID GetFieldID(JNIEnv* env, jclass jclazz,
2260 const char* name, const char* sig)
2261{
2262 JNI_ENTER();
2263
Andy McFaddenab00d452009-08-19 07:21:41 -07002264 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002265 jfieldID id;
2266
2267 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2268 assert(dvmCheckException(_self));
2269 id = NULL;
2270 } else {
2271 id = (jfieldID) dvmFindInstanceFieldHier(clazz, name, sig);
Andy McFadden03bd0d52009-08-18 15:32:27 -07002272 if (id == NULL) {
2273 LOGD("GetFieldID: unable to find field %s.%s:%s\n",
2274 clazz->descriptor, name, sig);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002275 dvmThrowException("Ljava/lang/NoSuchFieldError;", name);
Andy McFadden03bd0d52009-08-18 15:32:27 -07002276 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002277 }
2278 JNI_EXIT();
2279 return id;
2280}
2281
2282/*
2283 * Get the method ID for a static method in a class.
2284 */
2285static jmethodID GetStaticMethodID(JNIEnv* env, jclass jclazz,
2286 const char* name, const char* sig)
2287{
2288 JNI_ENTER();
2289
Andy McFaddenab00d452009-08-19 07:21:41 -07002290 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002291 jmethodID id;
2292
2293 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2294 assert(dvmCheckException(_self));
2295 id = NULL;
2296 } else {
2297 Method* meth;
2298
2299 meth = dvmFindDirectMethodHierByDescriptor(clazz, name, sig);
2300
2301 /* make sure it's static, not virtual+private */
2302 if (meth != NULL && !dvmIsStaticMethod(meth)) {
2303 IF_LOGD() {
2304 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
2305 LOGD("GetStaticMethodID: "
2306 "not returning nonstatic method %s.%s %s\n",
2307 clazz->descriptor, meth->name, desc);
2308 free(desc);
2309 }
2310 meth = NULL;
2311 }
2312
2313 id = (jmethodID) meth;
2314 if (id == NULL)
2315 dvmThrowException("Ljava/lang/NoSuchMethodError;", name);
2316 }
2317
2318 JNI_EXIT();
2319 return id;
2320}
2321
2322/*
2323 * Get a field ID (static fields).
2324 */
2325static jfieldID GetStaticFieldID(JNIEnv* env, jclass jclazz,
2326 const char* name, const char* sig)
2327{
2328 JNI_ENTER();
2329
Andy McFaddenab00d452009-08-19 07:21:41 -07002330 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002331 jfieldID id;
2332
2333 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2334 assert(dvmCheckException(_self));
2335 id = NULL;
2336 } else {
2337 id = (jfieldID) dvmFindStaticField(clazz, name, sig);
2338 if (id == NULL)
2339 dvmThrowException("Ljava/lang/NoSuchFieldError;", name);
2340 }
2341 JNI_EXIT();
2342 return id;
2343}
2344
2345/*
2346 * Get a static field.
2347 *
2348 * If we get an object reference, add it to the local refs list.
2349 */
2350#define GET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002351 static _ctype GetStatic##_jname##Field(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002352 jfieldID fieldID) \
2353 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002354 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002355 JNI_ENTER(); \
2356 StaticField* sfield = (StaticField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002357 _ctype value; \
2358 if (_isref) { /* only when _ctype==jobject */ \
2359 Object* obj = dvmGetStaticFieldObject(sfield); \
2360 value = (_ctype)(u4)addLocalReference(env, obj); \
2361 } else { \
2362 value = dvmGetStaticField##_jname(sfield); \
2363 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002364 JNI_EXIT(); \
2365 return value; \
2366 }
2367GET_STATIC_TYPE_FIELD(jobject, Object, true);
2368GET_STATIC_TYPE_FIELD(jboolean, Boolean, false);
2369GET_STATIC_TYPE_FIELD(jbyte, Byte, false);
2370GET_STATIC_TYPE_FIELD(jchar, Char, false);
2371GET_STATIC_TYPE_FIELD(jshort, Short, false);
2372GET_STATIC_TYPE_FIELD(jint, Int, false);
2373GET_STATIC_TYPE_FIELD(jlong, Long, false);
2374GET_STATIC_TYPE_FIELD(jfloat, Float, false);
2375GET_STATIC_TYPE_FIELD(jdouble, Double, false);
2376
2377/*
2378 * Set a static field.
2379 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002380#define SET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) \
2381 static void SetStatic##_jname##Field(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002382 jfieldID fieldID, _ctype value) \
2383 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002384 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002385 JNI_ENTER(); \
2386 StaticField* sfield = (StaticField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002387 if (_isref) { /* only when _ctype==jobject */ \
2388 Object* valObj = dvmDecodeIndirectRef(env, (jobject)(u4)value); \
2389 dvmSetStaticFieldObject(sfield, valObj); \
2390 } else { \
2391 dvmSetStaticField##_jname(sfield, value); \
2392 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002393 JNI_EXIT(); \
2394 }
Andy McFaddenab00d452009-08-19 07:21:41 -07002395SET_STATIC_TYPE_FIELD(jobject, Object, true);
2396SET_STATIC_TYPE_FIELD(jboolean, Boolean, false);
2397SET_STATIC_TYPE_FIELD(jbyte, Byte, false);
2398SET_STATIC_TYPE_FIELD(jchar, Char, false);
2399SET_STATIC_TYPE_FIELD(jshort, Short, false);
2400SET_STATIC_TYPE_FIELD(jint, Int, false);
2401SET_STATIC_TYPE_FIELD(jlong, Long, false);
2402SET_STATIC_TYPE_FIELD(jfloat, Float, false);
2403SET_STATIC_TYPE_FIELD(jdouble, Double, false);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002404
2405/*
2406 * Get an instance field.
2407 *
2408 * If we get an object reference, add it to the local refs list.
2409 */
2410#define GET_TYPE_FIELD(_ctype, _jname, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002411 static _ctype Get##_jname##Field(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002412 jfieldID fieldID) \
2413 { \
2414 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002415 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002416 InstField* field = (InstField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002417 _ctype value; \
2418 if (_isref) { /* only when _ctype==jobject */ \
2419 Object* valObj = dvmGetFieldObject(obj, field->byteOffset); \
2420 value = (_ctype)(u4)addLocalReference(env, valObj); \
2421 } else { \
2422 value = dvmGetField##_jname(obj, field->byteOffset); \
2423 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002424 JNI_EXIT(); \
2425 return value; \
2426 }
2427GET_TYPE_FIELD(jobject, Object, true);
2428GET_TYPE_FIELD(jboolean, Boolean, false);
2429GET_TYPE_FIELD(jbyte, Byte, false);
2430GET_TYPE_FIELD(jchar, Char, false);
2431GET_TYPE_FIELD(jshort, Short, false);
2432GET_TYPE_FIELD(jint, Int, false);
2433GET_TYPE_FIELD(jlong, Long, false);
2434GET_TYPE_FIELD(jfloat, Float, false);
2435GET_TYPE_FIELD(jdouble, Double, false);
2436
2437/*
2438 * Set an instance field.
2439 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002440#define SET_TYPE_FIELD(_ctype, _jname, _isref) \
2441 static void Set##_jname##Field(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002442 jfieldID fieldID, _ctype value) \
2443 { \
2444 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002445 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002446 InstField* field = (InstField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002447 if (_isref) { /* only when _ctype==jobject */ \
2448 Object* valObj = dvmDecodeIndirectRef(env, (jobject)(u4)value); \
2449 dvmSetFieldObject(obj, field->byteOffset, valObj); \
2450 } else { \
2451 dvmSetField##_jname(obj, field->byteOffset, value); \
2452 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002453 JNI_EXIT(); \
2454 }
Andy McFaddenab00d452009-08-19 07:21:41 -07002455SET_TYPE_FIELD(jobject, Object, true);
2456SET_TYPE_FIELD(jboolean, Boolean, false);
2457SET_TYPE_FIELD(jbyte, Byte, false);
2458SET_TYPE_FIELD(jchar, Char, false);
2459SET_TYPE_FIELD(jshort, Short, false);
2460SET_TYPE_FIELD(jint, Int, false);
2461SET_TYPE_FIELD(jlong, Long, false);
2462SET_TYPE_FIELD(jfloat, Float, false);
2463SET_TYPE_FIELD(jdouble, Double, false);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002464
2465/*
2466 * Make a virtual method call.
2467 *
2468 * Three versions (..., va_list, jvalue[]) for each return type. If we're
2469 * returning an Object, we have to add it to the local references table.
2470 */
2471#define CALL_VIRTUAL(_ctype, _jname, _retfail, _retok, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002472 static _ctype Call##_jname##Method(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002473 jmethodID methodID, ...) \
2474 { \
2475 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002476 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002477 const Method* meth; \
2478 va_list args; \
2479 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002480 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002481 if (meth == NULL) { \
2482 JNI_EXIT(); \
2483 return _retfail; \
2484 } \
2485 va_start(args, methodID); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002486 dvmCallMethodV(_self, meth, obj, true, &result, args); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002487 va_end(args); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002488 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002489 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002490 JNI_EXIT(); \
2491 return _retok; \
2492 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002493 static _ctype Call##_jname##MethodV(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002494 jmethodID methodID, va_list args) \
2495 { \
2496 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002497 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002498 const Method* meth; \
2499 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002500 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002501 if (meth == NULL) { \
2502 JNI_EXIT(); \
2503 return _retfail; \
2504 } \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002505 dvmCallMethodV(_self, meth, obj, true, &result, args); \
2506 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002507 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002508 JNI_EXIT(); \
2509 return _retok; \
2510 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002511 static _ctype Call##_jname##MethodA(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002512 jmethodID methodID, jvalue* args) \
2513 { \
2514 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002515 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002516 const Method* meth; \
2517 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002518 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002519 if (meth == NULL) { \
2520 JNI_EXIT(); \
2521 return _retfail; \
2522 } \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002523 dvmCallMethodA(_self, meth, obj, true, &result, args); \
2524 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002525 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002526 JNI_EXIT(); \
2527 return _retok; \
2528 }
2529CALL_VIRTUAL(jobject, Object, NULL, result.l, true);
2530CALL_VIRTUAL(jboolean, Boolean, 0, result.z, false);
2531CALL_VIRTUAL(jbyte, Byte, 0, result.b, false);
2532CALL_VIRTUAL(jchar, Char, 0, result.c, false);
2533CALL_VIRTUAL(jshort, Short, 0, result.s, false);
2534CALL_VIRTUAL(jint, Int, 0, result.i, false);
2535CALL_VIRTUAL(jlong, Long, 0, result.j, false);
2536CALL_VIRTUAL(jfloat, Float, 0.0f, result.f, false);
2537CALL_VIRTUAL(jdouble, Double, 0.0, result.d, false);
2538CALL_VIRTUAL(void, Void, , , false);
2539
2540/*
2541 * Make a "non-virtual" method call. We're still calling a virtual method,
2542 * but this time we're not doing an indirection through the object's vtable.
2543 * The "clazz" parameter defines which implementation of a method we want.
2544 *
2545 * Three versions (..., va_list, jvalue[]) for each return type.
2546 */
2547#define CALL_NONVIRTUAL(_ctype, _jname, _retfail, _retok, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002548 static _ctype CallNonvirtual##_jname##Method(JNIEnv* env, jobject jobj, \
2549 jclass jclazz, jmethodID methodID, ...) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002550 { \
2551 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002552 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2553 ClassObject* clazz = \
2554 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002555 const Method* meth; \
2556 va_list args; \
2557 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002558 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002559 if (meth == NULL) { \
2560 JNI_EXIT(); \
2561 return _retfail; \
2562 } \
2563 va_start(args, methodID); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002564 dvmCallMethodV(_self, meth, obj, true, &result, args); \
2565 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002566 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002567 va_end(args); \
2568 JNI_EXIT(); \
2569 return _retok; \
2570 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002571 static _ctype CallNonvirtual##_jname##MethodV(JNIEnv* env, jobject jobj,\
2572 jclass jclazz, jmethodID methodID, va_list args) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002573 { \
2574 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002575 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2576 ClassObject* clazz = \
2577 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002578 const Method* meth; \
2579 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002580 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002581 if (meth == NULL) { \
2582 JNI_EXIT(); \
2583 return _retfail; \
2584 } \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002585 dvmCallMethodV(_self, meth, obj, true, &result, args); \
2586 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002587 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002588 JNI_EXIT(); \
2589 return _retok; \
2590 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002591 static _ctype CallNonvirtual##_jname##MethodA(JNIEnv* env, jobject jobj,\
2592 jclass jclazz, jmethodID methodID, jvalue* args) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002593 { \
2594 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002595 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2596 ClassObject* clazz = \
2597 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002598 const Method* meth; \
2599 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002600 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002601 if (meth == NULL) { \
2602 JNI_EXIT(); \
2603 return _retfail; \
2604 } \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002605 dvmCallMethodA(_self, meth, obj, true, &result, args); \
2606 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002607 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002608 JNI_EXIT(); \
2609 return _retok; \
2610 }
2611CALL_NONVIRTUAL(jobject, Object, NULL, result.l, true);
2612CALL_NONVIRTUAL(jboolean, Boolean, 0, result.z, false);
2613CALL_NONVIRTUAL(jbyte, Byte, 0, result.b, false);
2614CALL_NONVIRTUAL(jchar, Char, 0, result.c, false);
2615CALL_NONVIRTUAL(jshort, Short, 0, result.s, false);
2616CALL_NONVIRTUAL(jint, Int, 0, result.i, false);
2617CALL_NONVIRTUAL(jlong, Long, 0, result.j, false);
2618CALL_NONVIRTUAL(jfloat, Float, 0.0f, result.f, false);
2619CALL_NONVIRTUAL(jdouble, Double, 0.0, result.d, false);
2620CALL_NONVIRTUAL(void, Void, , , false);
2621
2622
2623/*
2624 * Call a static method.
2625 */
2626#define CALL_STATIC(_ctype, _jname, _retfail, _retok, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002627 static _ctype CallStatic##_jname##Method(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002628 jmethodID methodID, ...) \
2629 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002630 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002631 JNI_ENTER(); \
2632 JValue result; \
2633 va_list args; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002634 va_start(args, methodID); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002635 dvmCallMethodV(_self, (Method*)methodID, NULL, true, &result, args);\
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002636 va_end(args); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002637 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002638 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002639 JNI_EXIT(); \
2640 return _retok; \
2641 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002642 static _ctype CallStatic##_jname##MethodV(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002643 jmethodID methodID, va_list args) \
2644 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002645 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002646 JNI_ENTER(); \
2647 JValue result; \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002648 dvmCallMethodV(_self, (Method*)methodID, NULL, true, &result, args);\
2649 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002650 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002651 JNI_EXIT(); \
2652 return _retok; \
2653 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002654 static _ctype CallStatic##_jname##MethodA(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002655 jmethodID methodID, jvalue* args) \
2656 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002657 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002658 JNI_ENTER(); \
2659 JValue result; \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002660 dvmCallMethodA(_self, (Method*)methodID, NULL, true, &result, args);\
2661 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002662 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002663 JNI_EXIT(); \
2664 return _retok; \
2665 }
2666CALL_STATIC(jobject, Object, NULL, result.l, true);
2667CALL_STATIC(jboolean, Boolean, 0, result.z, false);
2668CALL_STATIC(jbyte, Byte, 0, result.b, false);
2669CALL_STATIC(jchar, Char, 0, result.c, false);
2670CALL_STATIC(jshort, Short, 0, result.s, false);
2671CALL_STATIC(jint, Int, 0, result.i, false);
2672CALL_STATIC(jlong, Long, 0, result.j, false);
2673CALL_STATIC(jfloat, Float, 0.0f, result.f, false);
2674CALL_STATIC(jdouble, Double, 0.0, result.d, false);
2675CALL_STATIC(void, Void, , , false);
2676
2677/*
2678 * Create a new String from Unicode data.
2679 *
2680 * If "len" is zero, we will return an empty string even if "unicodeChars"
2681 * is NULL. (The JNI spec is vague here.)
2682 */
2683static jstring NewString(JNIEnv* env, const jchar* unicodeChars, jsize len)
2684{
2685 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002686 jobject retval;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002687
Andy McFaddenab00d452009-08-19 07:21:41 -07002688 StringObject* jstr = dvmCreateStringFromUnicode(unicodeChars, len);
2689 if (jstr == NULL) {
2690 retval = NULL;
2691 } else {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002692 dvmReleaseTrackedAlloc((Object*) jstr, NULL);
Andy McFaddenab00d452009-08-19 07:21:41 -07002693 retval = addLocalReference(env, (Object*) jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002694 }
2695
2696 JNI_EXIT();
Andy McFadden0423f0e2009-08-26 07:21:53 -07002697 return retval;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002698}
2699
2700/*
2701 * Return the length of a String in Unicode character units.
2702 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002703static jsize GetStringLength(JNIEnv* env, jstring jstr)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002704{
2705 JNI_ENTER();
2706
Andy McFaddenab00d452009-08-19 07:21:41 -07002707 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2708 jsize len = dvmStringLen(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002709
2710 JNI_EXIT();
2711 return len;
2712}
2713
Andy McFaddenab00d452009-08-19 07:21:41 -07002714
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002715/*
Andy McFaddenab00d452009-08-19 07:21:41 -07002716 * Get a string's character data.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002717 *
2718 * The result is guaranteed to be valid until ReleaseStringChars is
Andy McFaddenab00d452009-08-19 07:21:41 -07002719 * called, which means we have to pin it or return a copy.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002720 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002721static const jchar* GetStringChars(JNIEnv* env, jstring jstr, jboolean* isCopy)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002722{
2723 JNI_ENTER();
2724
Andy McFaddenab00d452009-08-19 07:21:41 -07002725 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
Andy McFaddend5ab7262009-08-25 07:19:34 -07002726 ArrayObject* strChars = dvmStringCharArray(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002727
Andy McFaddenab00d452009-08-19 07:21:41 -07002728 pinPrimitiveArray(strChars);
2729
2730 const u2* data = dvmStringChars(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002731 if (isCopy != NULL)
2732 *isCopy = JNI_FALSE;
2733
2734 JNI_EXIT();
2735 return (jchar*)data;
2736}
2737
2738/*
2739 * Release our grip on some characters from a string.
2740 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002741static void ReleaseStringChars(JNIEnv* env, jstring jstr, const jchar* chars)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002742{
2743 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002744 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
Andy McFaddend5ab7262009-08-25 07:19:34 -07002745 ArrayObject* strChars = dvmStringCharArray(strObj);
Andy McFaddenab00d452009-08-19 07:21:41 -07002746 unpinPrimitiveArray(strChars);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002747 JNI_EXIT();
2748}
2749
2750/*
2751 * Create a new java.lang.String object from chars in modified UTF-8 form.
2752 *
2753 * The spec doesn't say how to handle a NULL string. Popular desktop VMs
2754 * accept it and return a NULL pointer in response.
2755 */
2756static jstring NewStringUTF(JNIEnv* env, const char* bytes)
2757{
2758 JNI_ENTER();
2759
Andy McFaddenab00d452009-08-19 07:21:41 -07002760 jstring result;
2761
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002762 if (bytes == NULL) {
Andy McFaddenab00d452009-08-19 07:21:41 -07002763 result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002764 } else {
Andy McFaddenab00d452009-08-19 07:21:41 -07002765 /* note newStr could come back NULL on OOM */
2766 StringObject* newStr = dvmCreateStringFromCstr(bytes, ALLOC_DEFAULT);
2767 result = addLocalReference(env, (Object*) newStr);
2768 dvmReleaseTrackedAlloc((Object*)newStr, NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002769 }
2770
2771 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002772 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002773}
2774
2775/*
2776 * Return the length in bytes of the modified UTF-8 form of the string.
2777 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002778static jsize GetStringUTFLength(JNIEnv* env, jstring jstr)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002779{
2780 JNI_ENTER();
2781
Andy McFaddenab00d452009-08-19 07:21:41 -07002782 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2783 jsize len = dvmStringUtf8ByteLen(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002784
2785 JNI_EXIT();
2786 return len;
2787}
2788
2789/*
2790 * Convert "string" to modified UTF-8 and return a pointer. The returned
2791 * value must be released with ReleaseStringUTFChars.
2792 *
2793 * According to the JNI reference, "Returns a pointer to a UTF-8 string,
2794 * or NULL if the operation fails. Returns NULL if and only if an invocation
2795 * of this function has thrown an exception."
2796 *
2797 * The behavior here currently follows that of other open-source VMs, which
2798 * quietly return NULL if "string" is NULL. We should consider throwing an
2799 * NPE. (The CheckJNI code blows up if you try to pass in a NULL string,
2800 * which should catch this sort of thing during development.) Certain other
2801 * VMs will crash with a segmentation fault.
2802 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002803static const char* GetStringUTFChars(JNIEnv* env, jstring jstr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002804 jboolean* isCopy)
2805{
2806 JNI_ENTER();
2807 char* newStr;
2808
Andy McFaddenab00d452009-08-19 07:21:41 -07002809 if (jstr == NULL) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002810 /* this shouldn't happen; throw NPE? */
2811 newStr = NULL;
2812 } else {
2813 if (isCopy != NULL)
2814 *isCopy = JNI_TRUE;
2815
Andy McFaddenab00d452009-08-19 07:21:41 -07002816 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2817 newStr = dvmCreateCstrFromString(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002818 if (newStr == NULL) {
2819 /* assume memory failure */
2820 dvmThrowException("Ljava/lang/OutOfMemoryError;",
2821 "native heap string alloc failed");
2822 }
2823 }
2824
2825 JNI_EXIT();
2826 return newStr;
2827}
2828
2829/*
2830 * Release a string created by GetStringUTFChars().
2831 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002832static void ReleaseStringUTFChars(JNIEnv* env, jstring jstr, const char* utf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002833{
2834 JNI_ENTER();
2835 free((char*)utf);
2836 JNI_EXIT();
2837}
2838
2839/*
2840 * Return the capacity of the array.
2841 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002842static jsize GetArrayLength(JNIEnv* env, jarray jarr)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002843{
2844 JNI_ENTER();
2845
Andy McFaddenab00d452009-08-19 07:21:41 -07002846 ArrayObject* arrObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
2847 jsize length = arrObj->length;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002848
2849 JNI_EXIT();
2850 return length;
2851}
2852
2853/*
2854 * Construct a new array that holds objects from class "elementClass".
2855 */
2856static jobjectArray NewObjectArray(JNIEnv* env, jsize length,
Andy McFaddenab00d452009-08-19 07:21:41 -07002857 jclass jelementClass, jobject jinitialElement)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002858{
2859 JNI_ENTER();
2860
Andy McFaddenab00d452009-08-19 07:21:41 -07002861 jobjectArray newArray = NULL;
2862 ClassObject* elemClassObj =
2863 (ClassObject*) dvmDecodeIndirectRef(env, jelementClass);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002864
2865 if (elemClassObj == NULL) {
2866 dvmThrowException("Ljava/lang/NullPointerException;",
2867 "JNI NewObjectArray");
2868 goto bail;
2869 }
2870
Andy McFaddenab00d452009-08-19 07:21:41 -07002871 ArrayObject* newObj =
2872 dvmAllocObjectArray(elemClassObj, length, ALLOC_DEFAULT);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002873 if (newObj == NULL) {
2874 assert(dvmCheckException(_self));
2875 goto bail;
2876 }
Andy McFaddenab00d452009-08-19 07:21:41 -07002877 newArray = addLocalReference(env, (Object*) newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002878 dvmReleaseTrackedAlloc((Object*) newObj, NULL);
2879
2880 /*
2881 * Initialize the array. Trashes "length".
2882 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002883 if (jinitialElement != NULL) {
2884 Object* initialElement = dvmDecodeIndirectRef(env, jinitialElement);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002885 Object** arrayData = (Object**) newObj->contents;
2886
2887 while (length--)
Andy McFaddenab00d452009-08-19 07:21:41 -07002888 *arrayData++ = initialElement;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002889 }
2890
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002891
2892bail:
2893 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002894 return newArray;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002895}
2896
2897/*
2898 * Get one element of an Object array.
2899 *
2900 * Add the object to the local references table in case the array goes away.
2901 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002902static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray jarr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002903 jsize index)
2904{
2905 JNI_ENTER();
2906
Andy McFaddenab00d452009-08-19 07:21:41 -07002907 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
2908 jobject retval = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002909
Andy McFaddenab00d452009-08-19 07:21:41 -07002910 assert(arrayObj != NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002911
2912 /* check the array bounds */
2913 if (index < 0 || index >= (int) arrayObj->length) {
2914 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
2915 arrayObj->obj.clazz->descriptor);
2916 goto bail;
2917 }
2918
Andy McFaddenab00d452009-08-19 07:21:41 -07002919 Object* value = ((Object**) arrayObj->contents)[index];
2920 retval = addLocalReference(env, value);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002921
2922bail:
2923 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002924 return retval;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002925}
2926
2927/*
2928 * Set one element of an Object array.
2929 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002930static void SetObjectArrayElement(JNIEnv* env, jobjectArray jarr,
2931 jsize index, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002932{
2933 JNI_ENTER();
2934
Andy McFaddenab00d452009-08-19 07:21:41 -07002935 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002936
Andy McFaddenab00d452009-08-19 07:21:41 -07002937 assert(arrayObj != NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002938
2939 /* check the array bounds */
2940 if (index < 0 || index >= (int) arrayObj->length) {
2941 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
2942 arrayObj->obj.clazz->descriptor);
2943 goto bail;
2944 }
2945
2946 //LOGV("JNI: set element %d in array %p to %p\n", index, array, value);
2947
Andy McFadden0423f0e2009-08-26 07:21:53 -07002948 Object* obj = dvmDecodeIndirectRef(env, jobj);
Andy McFaddenab00d452009-08-19 07:21:41 -07002949 ((Object**) arrayObj->contents)[index] = obj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002950
2951bail:
2952 JNI_EXIT();
2953}
2954
2955/*
2956 * Create a new array of primitive elements.
2957 */
2958#define NEW_PRIMITIVE_ARRAY(_artype, _jname, _typechar) \
2959 static _artype New##_jname##Array(JNIEnv* env, jsize length) \
2960 { \
2961 JNI_ENTER(); \
2962 ArrayObject* arrayObj; \
2963 arrayObj = dvmAllocPrimitiveArray(_typechar, length, \
2964 ALLOC_DEFAULT); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002965 jarray jarr = NULL; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002966 if (arrayObj != NULL) { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002967 jarr = addLocalReference(env, (Object*) arrayObj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002968 dvmReleaseTrackedAlloc((Object*) arrayObj, NULL); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002969 } \
2970 JNI_EXIT(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002971 return (_artype)jarr; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002972 }
2973NEW_PRIMITIVE_ARRAY(jbooleanArray, Boolean, 'Z');
2974NEW_PRIMITIVE_ARRAY(jbyteArray, Byte, 'B');
2975NEW_PRIMITIVE_ARRAY(jcharArray, Char, 'C');
2976NEW_PRIMITIVE_ARRAY(jshortArray, Short, 'S');
2977NEW_PRIMITIVE_ARRAY(jintArray, Int, 'I');
2978NEW_PRIMITIVE_ARRAY(jlongArray, Long, 'J');
2979NEW_PRIMITIVE_ARRAY(jfloatArray, Float, 'F');
2980NEW_PRIMITIVE_ARRAY(jdoubleArray, Double, 'D');
2981
2982/*
2983 * Get a pointer to a C array of primitive elements from an array object
2984 * of the matching type.
2985 *
Andy McFaddenab00d452009-08-19 07:21:41 -07002986 * In a compacting GC, we either need to return a copy of the elements or
2987 * "pin" the memory. Otherwise we run the risk of native code using the
2988 * buffer as the destination of e.g. a blocking read() call that wakes up
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002989 * during a GC.
2990 */
2991#define GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
2992 static _ctype* Get##_jname##ArrayElements(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07002993 _ctype##Array jarr, jboolean* isCopy) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002994 { \
2995 JNI_ENTER(); \
2996 _ctype* data; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002997 ArrayObject* arrayObj = \
2998 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
2999 pinPrimitiveArray(arrayObj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003000 data = (_ctype*) arrayObj->contents; \
3001 if (isCopy != NULL) \
3002 *isCopy = JNI_FALSE; \
3003 JNI_EXIT(); \
3004 return data; \
3005 }
3006
3007/*
3008 * Release the storage locked down by the "get" function.
3009 *
Andy McFaddenab00d452009-08-19 07:21:41 -07003010 * 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 -08003011 * elements in 'array'." They apparently did not anticipate the need to
Andy McFaddenab00d452009-08-19 07:21:41 -07003012 * un-pin memory.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003013 */
3014#define RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
3015 static void Release##_jname##ArrayElements(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07003016 _ctype##Array jarr, _ctype* elems, jint mode) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003017 { \
3018 UNUSED_PARAMETER(elems); \
3019 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07003020 if (mode != JNI_COMMIT) { \
3021 ArrayObject* arrayObj = \
3022 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
3023 unpinPrimitiveArray(arrayObj); \
3024 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003025 JNI_EXIT(); \
3026 }
3027
3028/*
3029 * Copy a section of a primitive array to a buffer.
3030 */
3031#define GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
3032 static void Get##_jname##ArrayRegion(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07003033 _ctype##Array jarr, jsize start, jsize len, _ctype* buf) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003034 { \
3035 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07003036 ArrayObject* arrayObj = \
3037 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003038 _ctype* data = (_ctype*) arrayObj->contents; \
3039 if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
3040 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
3041 arrayObj->obj.clazz->descriptor); \
3042 } else { \
3043 memcpy(buf, data + start, len * sizeof(_ctype)); \
3044 } \
3045 JNI_EXIT(); \
3046 }
3047
3048/*
3049 * Copy a section of a primitive array to a buffer.
3050 */
3051#define SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
3052 static void Set##_jname##ArrayRegion(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07003053 _ctype##Array jarr, jsize start, jsize len, const _ctype* buf) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003054 { \
3055 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07003056 ArrayObject* arrayObj = \
3057 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003058 _ctype* data = (_ctype*) arrayObj->contents; \
3059 if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
3060 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
3061 arrayObj->obj.clazz->descriptor); \
3062 } else { \
3063 memcpy(data + start, buf, len * sizeof(_ctype)); \
3064 } \
3065 JNI_EXIT(); \
3066 }
3067
3068/*
3069 * 4-in-1:
3070 * Get<Type>ArrayElements
3071 * Release<Type>ArrayElements
3072 * Get<Type>ArrayRegion
3073 * Set<Type>ArrayRegion
3074 */
3075#define PRIMITIVE_ARRAY_FUNCTIONS(_ctype, _jname) \
3076 GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
3077 RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
3078 GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname); \
3079 SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname);
3080
3081PRIMITIVE_ARRAY_FUNCTIONS(jboolean, Boolean);
3082PRIMITIVE_ARRAY_FUNCTIONS(jbyte, Byte);
3083PRIMITIVE_ARRAY_FUNCTIONS(jchar, Char);
3084PRIMITIVE_ARRAY_FUNCTIONS(jshort, Short);
3085PRIMITIVE_ARRAY_FUNCTIONS(jint, Int);
3086PRIMITIVE_ARRAY_FUNCTIONS(jlong, Long);
3087PRIMITIVE_ARRAY_FUNCTIONS(jfloat, Float);
3088PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double);
3089
3090/*
3091 * Register one or more native functions in one class.
3092 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003093static jint RegisterNatives(JNIEnv* env, jclass jclazz,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003094 const JNINativeMethod* methods, jint nMethods)
3095{
3096 JNI_ENTER();
3097
Andy McFaddenab00d452009-08-19 07:21:41 -07003098 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003099 jint retval;
3100 int i;
3101
3102 if (gDvm.verboseJni) {
3103 LOGI("[Registering JNI native methods for class %s]\n",
Andy McFaddenab00d452009-08-19 07:21:41 -07003104 clazz->descriptor);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003105 }
3106
3107 for (i = 0; i < nMethods; i++) {
Andy McFaddenab00d452009-08-19 07:21:41 -07003108 if (!dvmRegisterJNIMethod(clazz, methods[i].name,
3109 methods[i].signature, methods[i].fnPtr))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003110 {
3111 retval = JNI_ERR;
3112 goto bail;
3113 }
3114 }
3115 retval = JNI_OK;
3116
3117bail:
3118 JNI_EXIT();
3119 return retval;
3120}
3121
3122/*
3123 * Un-register a native function.
3124 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003125static jint UnregisterNatives(JNIEnv* env, jclass jclazz)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003126{
3127 JNI_ENTER();
3128 /*
3129 * The JNI docs refer to this as a way to reload/relink native libraries,
3130 * and say it "should not be used in normal native code".
3131 *
3132 * We can implement it if we decide we need it.
3133 */
3134 JNI_EXIT();
3135 return JNI_ERR;
3136}
3137
3138/*
3139 * Lock the monitor.
3140 *
3141 * We have to track all monitor enters and exits, so that we can undo any
3142 * outstanding synchronization before the thread exits.
3143 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003144static jint MonitorEnter(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003145{
3146 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003147 Object* obj = dvmDecodeIndirectRef(env, jobj);
3148 dvmLockObject(_self, obj);
3149 trackMonitorEnter(_self, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003150 JNI_EXIT();
3151 return JNI_OK;
3152}
3153
3154/*
3155 * Unlock the monitor.
3156 *
3157 * Throws an IllegalMonitorStateException if the current thread
Andy McFaddenab00d452009-08-19 07:21:41 -07003158 * doesn't own the monitor. (dvmUnlockObject() takes care of the throw.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003159 *
3160 * According to the 1.6 spec, it's legal to call here with an exception
3161 * pending. If this fails, we'll stomp the original exception.
3162 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003163static jint MonitorExit(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003164{
3165 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003166 Object* obj = dvmDecodeIndirectRef(env, jobj);
3167 bool success = dvmUnlockObject(_self, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003168 if (success)
Andy McFaddenab00d452009-08-19 07:21:41 -07003169 trackMonitorExit(_self, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003170 JNI_EXIT();
3171 return success ? JNI_OK : JNI_ERR;
3172}
3173
3174/*
3175 * Return the JavaVM interface associated with the current thread.
3176 */
3177static jint GetJavaVM(JNIEnv* env, JavaVM** vm)
3178{
3179 JNI_ENTER();
3180 //*vm = gDvm.vmList;
3181 *vm = (JavaVM*) ((JNIEnvExt*)env)->vm;
3182 JNI_EXIT();
3183 if (*vm == NULL)
3184 return JNI_ERR;
3185 else
3186 return JNI_OK;
3187}
3188
3189/*
3190 * Copies "len" Unicode characters, from offset "start".
3191 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003192static void GetStringRegion(JNIEnv* env, jstring jstr, jsize start, jsize len,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003193 jchar* buf)
3194{
3195 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003196 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003197 if (start + len > dvmStringLen(strObj))
3198 dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
3199 else
3200 memcpy(buf, dvmStringChars(strObj) + start, len * sizeof(u2));
3201 JNI_EXIT();
3202}
3203
3204/*
3205 * Translates "len" Unicode characters, from offset "start", into
3206 * modified UTF-8 encoding.
3207 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003208static void GetStringUTFRegion(JNIEnv* env, jstring jstr, jsize start,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003209 jsize len, char* buf)
3210{
3211 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003212 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003213 if (start + len > dvmStringLen(strObj))
3214 dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
3215 else
3216 dvmCreateCstrFromStringRegion(strObj, start, len, buf);
3217 JNI_EXIT();
3218}
3219
3220/*
3221 * Get a raw pointer to array data.
3222 *
3223 * The caller is expected to call "release" before doing any JNI calls
3224 * or blocking I/O operations.
3225 *
Andy McFaddenab00d452009-08-19 07:21:41 -07003226 * We need to pin the memory or block GC.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003227 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003228static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray jarr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003229 jboolean* isCopy)
3230{
3231 JNI_ENTER();
3232 void* data;
Andy McFaddenab00d452009-08-19 07:21:41 -07003233 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
3234 pinPrimitiveArray(arrayObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003235 data = arrayObj->contents;
3236 if (isCopy != NULL)
3237 *isCopy = JNI_FALSE;
3238 JNI_EXIT();
3239 return data;
3240}
3241
3242/*
3243 * Release an array obtained with GetPrimitiveArrayCritical.
3244 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003245static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray jarr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003246 void* carray, jint mode)
3247{
3248 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003249 if (mode != JNI_COMMIT) {
3250 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
3251 unpinPrimitiveArray(arrayObj);
3252 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003253 JNI_EXIT();
3254}
3255
3256/*
3257 * Like GetStringChars, but with restricted use.
3258 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003259static const jchar* GetStringCritical(JNIEnv* env, jstring jstr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003260 jboolean* isCopy)
3261{
3262 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003263 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
3264 ArrayObject* strChars = dvmStringCharArray(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003265
Andy McFaddenab00d452009-08-19 07:21:41 -07003266 pinPrimitiveArray(strChars);
3267
3268 const u2* data = dvmStringChars(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003269 if (isCopy != NULL)
3270 *isCopy = JNI_FALSE;
3271
3272 JNI_EXIT();
3273 return (jchar*)data;
3274}
3275
3276/*
3277 * Like ReleaseStringChars, but with restricted use.
3278 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003279static void ReleaseStringCritical(JNIEnv* env, jstring jstr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003280 const jchar* carray)
3281{
3282 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003283 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
Andy McFadden0423f0e2009-08-26 07:21:53 -07003284 ArrayObject* strChars = dvmStringCharArray(strObj);
Andy McFaddenab00d452009-08-19 07:21:41 -07003285 unpinPrimitiveArray(strChars);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003286 JNI_EXIT();
3287}
3288
3289/*
3290 * Create a new weak global reference.
3291 */
3292static jweak NewWeakGlobalRef(JNIEnv* env, jobject obj)
3293{
3294 JNI_ENTER();
3295 // TODO - implement
3296 jobject gref = NULL;
3297 LOGE("JNI ERROR: NewWeakGlobalRef not implemented\n");
3298 dvmAbort();
3299 JNI_EXIT();
3300 return gref;
3301}
3302
3303/*
3304 * Delete the specified weak global reference.
3305 */
3306static void DeleteWeakGlobalRef(JNIEnv* env, jweak obj)
3307{
3308 JNI_ENTER();
3309 // TODO - implement
3310 LOGE("JNI ERROR: DeleteWeakGlobalRef not implemented\n");
3311 dvmAbort();
3312 JNI_EXIT();
3313}
3314
3315/*
3316 * Quick check for pending exceptions.
3317 *
3318 * TODO: we should be able to skip the enter/exit macros here.
3319 */
3320static jboolean ExceptionCheck(JNIEnv* env)
3321{
3322 JNI_ENTER();
3323 bool result = dvmCheckException(_self);
3324 JNI_EXIT();
3325 return result;
3326}
3327
3328/*
3329 * Returns the type of the object referred to by "obj". It can be local,
3330 * global, or weak global.
3331 *
3332 * In the current implementation, references can be global and local at
3333 * the same time, so while the return value is accurate it may not tell
3334 * the whole story.
3335 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003336static jobjectRefType GetObjectRefType(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003337{
3338 JNI_ENTER();
Andy McFadden0423f0e2009-08-26 07:21:53 -07003339 jobjectRefType type = dvmGetJNIRefType(env, jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003340 JNI_EXIT();
3341 return type;
3342}
3343
3344/*
3345 * Allocate and return a new java.nio.ByteBuffer for this block of memory.
3346 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003347 * "address" may not be NULL, and "capacity" must be > 0. (These are only
3348 * verified when CheckJNI is enabled.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003349 */
Andy McFadden8e5c7842009-07-23 17:47:18 -07003350static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003351{
Andy McFadden8e5c7842009-07-23 17:47:18 -07003352 JNI_ENTER();
3353
3354 Thread* self = _self /*dvmThreadSelf()*/;
3355 Object* platformAddress = NULL;
3356 JValue callResult;
The Android Open Source Project99409882009-03-18 22:20:24 -07003357 jobject result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003358
Andy McFadden8e5c7842009-07-23 17:47:18 -07003359 /* get an instance of PlatformAddress that wraps the provided address */
3360 dvmCallMethod(self,
3361 gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on,
3362 NULL, &callResult, address);
3363 if (dvmGetException(self) != NULL || callResult.l == NULL)
The Android Open Source Project99409882009-03-18 22:20:24 -07003364 goto bail;
Andy McFadden8e5c7842009-07-23 17:47:18 -07003365
3366 /* don't let the GC discard it */
3367 platformAddress = (Object*) callResult.l;
3368 dvmAddTrackedAlloc(platformAddress, self);
3369 LOGV("tracking %p for address=%p\n", platformAddress, address);
3370
3371 /* create an instance of java.nio.ReadWriteDirectByteBuffer */
3372 ClassObject* clazz = gDvm.classJavaNioReadWriteDirectByteBuffer;
3373 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))
3374 goto bail;
3375 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
3376 if (newObj != NULL) {
3377 /* call the (PlatformAddress, int, int) constructor */
Andy McFaddenab00d452009-08-19 07:21:41 -07003378 result = addLocalReference(env, newObj);
Andy McFadden8e5c7842009-07-23 17:47:18 -07003379 dvmCallMethod(self, gDvm.methJavaNioReadWriteDirectByteBuffer_init,
3380 newObj, &callResult, platformAddress, (jint) capacity, (jint) 0);
Andy McFaddenab00d452009-08-19 07:21:41 -07003381 if (dvmGetException(self) != NULL) {
3382 deleteLocalReference(env, result);
3383 result = NULL;
Andy McFadden8e5c7842009-07-23 17:47:18 -07003384 goto bail;
Andy McFaddenab00d452009-08-19 07:21:41 -07003385 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003386 }
3387
The Android Open Source Project99409882009-03-18 22:20:24 -07003388bail:
Andy McFadden8e5c7842009-07-23 17:47:18 -07003389 if (platformAddress != NULL)
3390 dvmReleaseTrackedAlloc(platformAddress, self);
3391 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07003392 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003393}
3394
3395/*
3396 * Get the starting address of the buffer for the specified java.nio.Buffer.
3397 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003398 * If this is not a "direct" buffer, we return NULL.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003399 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003400static void* GetDirectBufferAddress(JNIEnv* env, jobject jbuf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003401{
Andy McFadden8e5c7842009-07-23 17:47:18 -07003402 JNI_ENTER();
3403
Andy McFaddenab00d452009-08-19 07:21:41 -07003404 Object* bufObj = dvmDecodeIndirectRef(env, jbuf);
Andy McFadden8e5c7842009-07-23 17:47:18 -07003405 Thread* self = _self /*dvmThreadSelf()*/;
Andy McFadden8e696dc2009-07-24 15:28:16 -07003406 void* result;
3407
3408 /*
3409 * All Buffer objects have an effectiveDirectAddress field. If it's
3410 * nonzero, we can just return that value. If not, we have to call
3411 * through DirectBuffer.getEffectiveAddress(), which as a side-effect
3412 * will set the effectiveDirectAddress field for direct buffers (and
3413 * things that wrap direct buffers).
3414 */
3415 result = (void*) dvmGetFieldInt(bufObj,
3416 gDvm.offJavaNioBuffer_effectiveDirectAddress);
3417 if (result != NULL) {
3418 //LOGI("fast path for %p\n", buf);
3419 goto bail;
3420 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003421
Andy McFadden72d61fb2009-06-24 16:56:06 -07003422 /*
3423 * Start by determining if the object supports the DirectBuffer
3424 * interfaces. Note this does not guarantee that it's a direct buffer.
3425 */
Andy McFadden8e5c7842009-07-23 17:47:18 -07003426 if (!dvmInstanceof(bufObj->clazz,
3427 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003428 {
The Android Open Source Project99409882009-03-18 22:20:24 -07003429 goto bail;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003430 }
3431
Andy McFadden72d61fb2009-06-24 16:56:06 -07003432 /*
Andy McFadden8e5c7842009-07-23 17:47:18 -07003433 * Get a PlatformAddress object with the effective address.
Andy McFadden5f612b82009-07-22 15:07:27 -07003434 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003435 * If this isn't a direct buffer, the result will be NULL and/or an
Andy McFadden72d61fb2009-06-24 16:56:06 -07003436 * exception will have been thrown.
3437 */
Andy McFadden8e696dc2009-07-24 15:28:16 -07003438 JValue callResult;
Andy McFadden8e5c7842009-07-23 17:47:18 -07003439 const Method* meth = dvmGetVirtualizedMethod(bufObj->clazz,
3440 gDvm.methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress);
Andy McFaddend5ab7262009-08-25 07:19:34 -07003441 dvmCallMethodA(self, meth, bufObj, false, &callResult, NULL);
Andy McFadden8e5c7842009-07-23 17:47:18 -07003442 if (dvmGetException(self) != NULL) {
3443 dvmClearException(self);
3444 callResult.l = NULL;
Andy McFadden72d61fb2009-06-24 16:56:06 -07003445 }
Andy McFadden8e5c7842009-07-23 17:47:18 -07003446
Andy McFadden8e696dc2009-07-24 15:28:16 -07003447 Object* platformAddr = callResult.l;
Andy McFadden72d61fb2009-06-24 16:56:06 -07003448 if (platformAddr == NULL) {
Andy McFadden8e696dc2009-07-24 15:28:16 -07003449 LOGV("Got request for address of non-direct buffer\n");
Andy McFadden72d61fb2009-06-24 16:56:06 -07003450 goto bail;
3451 }
3452
Andy McFadden8e5c7842009-07-23 17:47:18 -07003453 /*
3454 * Extract the address from the PlatformAddress object. Instead of
3455 * calling the toLong() method, just grab the field directly. This
3456 * is faster but more fragile.
3457 */
3458 result = (void*) dvmGetFieldInt(platformAddr,
3459 gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr);
The Android Open Source Project99409882009-03-18 22:20:24 -07003460
Andy McFadden8e696dc2009-07-24 15:28:16 -07003461 //LOGI("slow path for %p --> %p\n", buf, result);
3462
The Android Open Source Project99409882009-03-18 22:20:24 -07003463bail:
Andy McFadden8e5c7842009-07-23 17:47:18 -07003464 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07003465 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003466}
3467
3468/*
3469 * Get the capacity of the buffer for the specified java.nio.Buffer.
3470 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003471 * Returns -1 if the object is not a direct buffer. (We actually skip
3472 * this check, since it's expensive to determine, and just return the
3473 * capacity regardless.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003474 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003475static jlong GetDirectBufferCapacity(JNIEnv* env, jobject jbuf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003476{
Andy McFadden8e5c7842009-07-23 17:47:18 -07003477 JNI_ENTER();
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003478
Andy McFadden8e5c7842009-07-23 17:47:18 -07003479 /*
3480 * The capacity is always in the Buffer.capacity field.
3481 *
3482 * (The "check" version should verify that this is actually a Buffer,
3483 * but we're not required to do so here.)
3484 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003485 Object* buf = dvmDecodeIndirectRef(env, jbuf);
3486 jlong result = dvmGetFieldInt(buf, gDvm.offJavaNioBuffer_capacity);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003487
Andy McFadden8e5c7842009-07-23 17:47:18 -07003488 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07003489 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003490}
3491
3492
3493/*
3494 * ===========================================================================
3495 * JNI invocation functions
3496 * ===========================================================================
3497 */
3498
3499/*
3500 * Handle AttachCurrentThread{AsDaemon}.
3501 *
3502 * We need to make sure the VM is actually running. For example, if we start
3503 * up, issue an Attach, and the VM exits almost immediately, by the time the
3504 * attaching happens the VM could already be shutting down.
3505 *
3506 * It's hard to avoid a race condition here because we don't want to hold
3507 * a lock across the entire operation. What we can do is temporarily
3508 * increment the thread count to prevent a VM exit.
3509 *
3510 * This could potentially still have problems if a daemon thread calls here
3511 * while the VM is shutting down. dvmThreadSelf() will work, since it just
3512 * uses pthread TLS, but dereferencing "vm" could fail. Such is life when
3513 * you shut down a VM while threads are still running inside it.
3514 *
3515 * Remember that some code may call this as a way to find the per-thread
3516 * JNIEnv pointer. Don't do excess work for that case.
3517 */
3518static jint attachThread(JavaVM* vm, JNIEnv** p_env, void* thr_args,
3519 bool isDaemon)
3520{
3521 JavaVMAttachArgs* args = (JavaVMAttachArgs*) thr_args;
3522 Thread* self;
3523 bool result = false;
3524
3525 /*
3526 * Return immediately if we're already one with the VM.
3527 */
3528 self = dvmThreadSelf();
3529 if (self != NULL) {
3530 *p_env = self->jniEnv;
3531 return JNI_OK;
3532 }
3533
3534 /*
3535 * No threads allowed in zygote mode.
3536 */
3537 if (gDvm.zygote) {
3538 return JNI_ERR;
3539 }
3540
3541 /* increment the count to keep the VM from bailing while we run */
3542 dvmLockThreadList(NULL);
3543 if (gDvm.nonDaemonThreadCount == 0) {
3544 // dead or dying
3545 LOGV("Refusing to attach thread '%s' -- VM is shutting down\n",
3546 (thr_args == NULL) ? "(unknown)" : args->name);
3547 dvmUnlockThreadList();
3548 return JNI_ERR;
3549 }
3550 gDvm.nonDaemonThreadCount++;
3551 dvmUnlockThreadList();
3552
3553 /* tweak the JavaVMAttachArgs as needed */
3554 JavaVMAttachArgs argsCopy;
3555 if (args == NULL) {
3556 /* allow the v1.1 calling convention */
3557 argsCopy.version = JNI_VERSION_1_2;
3558 argsCopy.name = NULL;
3559 argsCopy.group = dvmGetMainThreadGroup();
3560 } else {
3561 assert(args->version >= JNI_VERSION_1_2);
3562
3563 argsCopy.version = args->version;
3564 argsCopy.name = args->name;
3565 if (args->group != NULL)
3566 argsCopy.group = args->group;
3567 else
3568 argsCopy.group = dvmGetMainThreadGroup();
3569 }
3570
3571 result = dvmAttachCurrentThread(&argsCopy, isDaemon);
3572
3573 /* restore the count */
3574 dvmLockThreadList(NULL);
3575 gDvm.nonDaemonThreadCount--;
3576 dvmUnlockThreadList();
3577
3578 /*
3579 * Change the status to indicate that we're out in native code. This
3580 * call is not guarded with state-change macros, so we have to do it
3581 * by hand.
3582 */
3583 if (result) {
3584 self = dvmThreadSelf();
3585 assert(self != NULL);
3586 dvmChangeStatus(self, THREAD_NATIVE);
3587 *p_env = self->jniEnv;
3588 return JNI_OK;
3589 } else {
3590 return JNI_ERR;
3591 }
3592}
3593
3594/*
3595 * Attach the current thread to the VM. If the thread is already attached,
3596 * this is a no-op.
3597 */
3598static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args)
3599{
3600 return attachThread(vm, p_env, thr_args, false);
3601}
3602
3603/*
3604 * Like AttachCurrentThread, but set the "daemon" flag.
3605 */
3606static jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env,
3607 void* thr_args)
3608{
3609 return attachThread(vm, p_env, thr_args, true);
3610}
3611
3612/*
3613 * Dissociate the current thread from the VM.
3614 */
3615static jint DetachCurrentThread(JavaVM* vm)
3616{
3617 Thread* self = dvmThreadSelf();
3618
3619 if (self == NULL) /* not attached, can't do anything */
3620 return JNI_ERR;
3621
3622 /* switch to "running" to check for suspension */
3623 dvmChangeStatus(self, THREAD_RUNNING);
3624
3625 /* detach the thread */
3626 dvmDetachCurrentThread();
3627
3628 /* (no need to change status back -- we have no status) */
3629 return JNI_OK;
3630}
3631
3632/*
3633 * If current thread is attached to VM, return the associated JNIEnv.
3634 * Otherwise, stuff NULL in and return JNI_EDETACHED.
3635 *
3636 * JVMTI overloads this by specifying a magic value for "version", so we
3637 * do want to check that here.
3638 */
3639static jint GetEnv(JavaVM* vm, void** env, jint version)
3640{
3641 Thread* self = dvmThreadSelf();
3642
3643 if (version < JNI_VERSION_1_1 || version > JNI_VERSION_1_6)
3644 return JNI_EVERSION;
3645
3646 if (self == NULL) {
3647 *env = NULL;
3648 } else {
3649 /* TODO: status change is probably unnecessary */
3650 dvmChangeStatus(self, THREAD_RUNNING);
3651 *env = (void*) dvmGetThreadJNIEnv(self);
3652 dvmChangeStatus(self, THREAD_NATIVE);
3653 }
3654 if (*env == NULL)
3655 return JNI_EDETACHED;
3656 else
3657 return JNI_OK;
3658}
3659
3660/*
3661 * Destroy the VM. This may be called from any thread.
3662 *
3663 * If the current thread is attached, wait until the current thread is
3664 * the only non-daemon user-level thread. If the current thread is not
3665 * attached, we attach it and do the processing as usual. (If the attach
3666 * fails, it's probably because all the non-daemon threads have already
3667 * exited and the VM doesn't want to let us back in.)
3668 *
3669 * TODO: we don't really deal with the situation where more than one thread
3670 * has called here. One thread wins, the other stays trapped waiting on
3671 * the condition variable forever. Not sure this situation is interesting
3672 * in real life.
3673 */
3674static jint DestroyJavaVM(JavaVM* vm)
3675{
3676 JavaVMExt* ext = (JavaVMExt*) vm;
3677 Thread* self;
3678
3679 if (ext == NULL)
3680 return JNI_ERR;
3681
3682 LOGD("DestroyJavaVM waiting for non-daemon threads to exit\n");
3683
3684 /*
3685 * Sleep on a condition variable until it's okay to exit.
3686 */
3687 self = dvmThreadSelf();
3688 if (self == NULL) {
3689 JNIEnv* tmpEnv;
3690 if (AttachCurrentThread(vm, &tmpEnv, NULL) != JNI_OK) {
3691 LOGV("Unable to reattach main for Destroy; assuming VM is "
3692 "shutting down (count=%d)\n",
3693 gDvm.nonDaemonThreadCount);
3694 goto shutdown;
3695 } else {
3696 LOGV("Attached to wait for shutdown in Destroy\n");
3697 }
3698 }
3699 dvmChangeStatus(self, THREAD_VMWAIT);
3700
3701 dvmLockThreadList(self);
3702 gDvm.nonDaemonThreadCount--; // remove current thread from count
3703
3704 while (gDvm.nonDaemonThreadCount > 0)
3705 pthread_cond_wait(&gDvm.vmExitCond, &gDvm.threadListLock);
3706
3707 dvmUnlockThreadList();
3708 self = NULL;
3709
3710shutdown:
3711 // TODO: call System.exit() to run any registered shutdown hooks
3712 // (this may not return -- figure out how this should work)
3713
3714 LOGD("DestroyJavaVM shutting VM down\n");
3715 dvmShutdown();
3716
3717 // TODO - free resources associated with JNI-attached daemon threads
3718 free(ext->envList);
3719 free(ext);
3720
3721 return JNI_OK;
3722}
3723
3724
3725/*
3726 * ===========================================================================
3727 * Function tables
3728 * ===========================================================================
3729 */
3730
3731static const struct JNINativeInterface gNativeInterface = {
3732 NULL,
3733 NULL,
3734 NULL,
3735 NULL,
3736
3737 GetVersion,
3738
3739 DefineClass,
3740 FindClass,
3741
3742 FromReflectedMethod,
3743 FromReflectedField,
3744 ToReflectedMethod,
3745
3746 GetSuperclass,
3747 IsAssignableFrom,
3748
3749 ToReflectedField,
3750
3751 Throw,
3752 ThrowNew,
3753 ExceptionOccurred,
3754 ExceptionDescribe,
3755 ExceptionClear,
3756 FatalError,
3757
3758 PushLocalFrame,
3759 PopLocalFrame,
3760
3761 NewGlobalRef,
3762 DeleteGlobalRef,
3763 DeleteLocalRef,
3764 IsSameObject,
3765 NewLocalRef,
3766 EnsureLocalCapacity,
3767
3768 AllocObject,
3769 NewObject,
3770 NewObjectV,
3771 NewObjectA,
3772
3773 GetObjectClass,
3774 IsInstanceOf,
3775
3776 GetMethodID,
3777
3778 CallObjectMethod,
3779 CallObjectMethodV,
3780 CallObjectMethodA,
3781 CallBooleanMethod,
3782 CallBooleanMethodV,
3783 CallBooleanMethodA,
3784 CallByteMethod,
3785 CallByteMethodV,
3786 CallByteMethodA,
3787 CallCharMethod,
3788 CallCharMethodV,
3789 CallCharMethodA,
3790 CallShortMethod,
3791 CallShortMethodV,
3792 CallShortMethodA,
3793 CallIntMethod,
3794 CallIntMethodV,
3795 CallIntMethodA,
3796 CallLongMethod,
3797 CallLongMethodV,
3798 CallLongMethodA,
3799 CallFloatMethod,
3800 CallFloatMethodV,
3801 CallFloatMethodA,
3802 CallDoubleMethod,
3803 CallDoubleMethodV,
3804 CallDoubleMethodA,
3805 CallVoidMethod,
3806 CallVoidMethodV,
3807 CallVoidMethodA,
3808
3809 CallNonvirtualObjectMethod,
3810 CallNonvirtualObjectMethodV,
3811 CallNonvirtualObjectMethodA,
3812 CallNonvirtualBooleanMethod,
3813 CallNonvirtualBooleanMethodV,
3814 CallNonvirtualBooleanMethodA,
3815 CallNonvirtualByteMethod,
3816 CallNonvirtualByteMethodV,
3817 CallNonvirtualByteMethodA,
3818 CallNonvirtualCharMethod,
3819 CallNonvirtualCharMethodV,
3820 CallNonvirtualCharMethodA,
3821 CallNonvirtualShortMethod,
3822 CallNonvirtualShortMethodV,
3823 CallNonvirtualShortMethodA,
3824 CallNonvirtualIntMethod,
3825 CallNonvirtualIntMethodV,
3826 CallNonvirtualIntMethodA,
3827 CallNonvirtualLongMethod,
3828 CallNonvirtualLongMethodV,
3829 CallNonvirtualLongMethodA,
3830 CallNonvirtualFloatMethod,
3831 CallNonvirtualFloatMethodV,
3832 CallNonvirtualFloatMethodA,
3833 CallNonvirtualDoubleMethod,
3834 CallNonvirtualDoubleMethodV,
3835 CallNonvirtualDoubleMethodA,
3836 CallNonvirtualVoidMethod,
3837 CallNonvirtualVoidMethodV,
3838 CallNonvirtualVoidMethodA,
3839
3840 GetFieldID,
3841
3842 GetObjectField,
3843 GetBooleanField,
3844 GetByteField,
3845 GetCharField,
3846 GetShortField,
3847 GetIntField,
3848 GetLongField,
3849 GetFloatField,
3850 GetDoubleField,
3851 SetObjectField,
3852 SetBooleanField,
3853 SetByteField,
3854 SetCharField,
3855 SetShortField,
3856 SetIntField,
3857 SetLongField,
3858 SetFloatField,
3859 SetDoubleField,
3860
3861 GetStaticMethodID,
3862
3863 CallStaticObjectMethod,
3864 CallStaticObjectMethodV,
3865 CallStaticObjectMethodA,
3866 CallStaticBooleanMethod,
3867 CallStaticBooleanMethodV,
3868 CallStaticBooleanMethodA,
3869 CallStaticByteMethod,
3870 CallStaticByteMethodV,
3871 CallStaticByteMethodA,
3872 CallStaticCharMethod,
3873 CallStaticCharMethodV,
3874 CallStaticCharMethodA,
3875 CallStaticShortMethod,
3876 CallStaticShortMethodV,
3877 CallStaticShortMethodA,
3878 CallStaticIntMethod,
3879 CallStaticIntMethodV,
3880 CallStaticIntMethodA,
3881 CallStaticLongMethod,
3882 CallStaticLongMethodV,
3883 CallStaticLongMethodA,
3884 CallStaticFloatMethod,
3885 CallStaticFloatMethodV,
3886 CallStaticFloatMethodA,
3887 CallStaticDoubleMethod,
3888 CallStaticDoubleMethodV,
3889 CallStaticDoubleMethodA,
3890 CallStaticVoidMethod,
3891 CallStaticVoidMethodV,
3892 CallStaticVoidMethodA,
3893
3894 GetStaticFieldID,
3895
3896 GetStaticObjectField,
3897 GetStaticBooleanField,
3898 GetStaticByteField,
3899 GetStaticCharField,
3900 GetStaticShortField,
3901 GetStaticIntField,
3902 GetStaticLongField,
3903 GetStaticFloatField,
3904 GetStaticDoubleField,
3905
3906 SetStaticObjectField,
3907 SetStaticBooleanField,
3908 SetStaticByteField,
3909 SetStaticCharField,
3910 SetStaticShortField,
3911 SetStaticIntField,
3912 SetStaticLongField,
3913 SetStaticFloatField,
3914 SetStaticDoubleField,
3915
3916 NewString,
3917
3918 GetStringLength,
3919 GetStringChars,
3920 ReleaseStringChars,
3921
3922 NewStringUTF,
3923 GetStringUTFLength,
3924 GetStringUTFChars,
3925 ReleaseStringUTFChars,
3926
3927 GetArrayLength,
3928 NewObjectArray,
3929 GetObjectArrayElement,
3930 SetObjectArrayElement,
3931
3932 NewBooleanArray,
3933 NewByteArray,
3934 NewCharArray,
3935 NewShortArray,
3936 NewIntArray,
3937 NewLongArray,
3938 NewFloatArray,
3939 NewDoubleArray,
3940
3941 GetBooleanArrayElements,
3942 GetByteArrayElements,
3943 GetCharArrayElements,
3944 GetShortArrayElements,
3945 GetIntArrayElements,
3946 GetLongArrayElements,
3947 GetFloatArrayElements,
3948 GetDoubleArrayElements,
3949
3950 ReleaseBooleanArrayElements,
3951 ReleaseByteArrayElements,
3952 ReleaseCharArrayElements,
3953 ReleaseShortArrayElements,
3954 ReleaseIntArrayElements,
3955 ReleaseLongArrayElements,
3956 ReleaseFloatArrayElements,
3957 ReleaseDoubleArrayElements,
3958
3959 GetBooleanArrayRegion,
3960 GetByteArrayRegion,
3961 GetCharArrayRegion,
3962 GetShortArrayRegion,
3963 GetIntArrayRegion,
3964 GetLongArrayRegion,
3965 GetFloatArrayRegion,
3966 GetDoubleArrayRegion,
3967 SetBooleanArrayRegion,
3968 SetByteArrayRegion,
3969 SetCharArrayRegion,
3970 SetShortArrayRegion,
3971 SetIntArrayRegion,
3972 SetLongArrayRegion,
3973 SetFloatArrayRegion,
3974 SetDoubleArrayRegion,
3975
3976 RegisterNatives,
3977 UnregisterNatives,
3978
3979 MonitorEnter,
3980 MonitorExit,
3981
3982 GetJavaVM,
3983
3984 GetStringRegion,
3985 GetStringUTFRegion,
3986
3987 GetPrimitiveArrayCritical,
3988 ReleasePrimitiveArrayCritical,
3989
3990 GetStringCritical,
3991 ReleaseStringCritical,
3992
3993 NewWeakGlobalRef,
3994 DeleteWeakGlobalRef,
3995
3996 ExceptionCheck,
3997
3998 NewDirectByteBuffer,
3999 GetDirectBufferAddress,
4000 GetDirectBufferCapacity,
4001
4002 GetObjectRefType
4003};
4004static const struct JNIInvokeInterface gInvokeInterface = {
4005 NULL,
4006 NULL,
4007 NULL,
4008
4009 DestroyJavaVM,
4010 AttachCurrentThread,
4011 DetachCurrentThread,
4012
4013 GetEnv,
4014
4015 AttachCurrentThreadAsDaemon,
4016};
4017
4018
4019/*
4020 * ===========================================================================
4021 * VM/Env creation
4022 * ===========================================================================
4023 */
4024
4025/*
4026 * Enable "checked JNI" after the VM has partially started. This must
4027 * only be called in "zygote" mode, when we have one thread running.
Andy McFadden59b61772009-05-13 16:44:34 -07004028 *
4029 * This doesn't attempt to rewrite the JNI call bridge associated with
4030 * native methods, so we won't get those checks for any methods that have
4031 * already been resolved.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08004032 */
4033void dvmLateEnableCheckedJni(void)
4034{
4035 JNIEnvExt* extEnv;
4036 JavaVMExt* extVm;
Andy McFaddenab00d452009-08-19 07:21:41 -07004037
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08004038 extEnv = dvmGetJNIEnvForThread();
4039 if (extEnv == NULL) {
4040 LOGE("dvmLateEnableCheckedJni: thread has no JNIEnv\n");
4041 return;
4042 }
4043 extVm = extEnv->vm;
4044 assert(extVm != NULL);
4045
4046 if (!extVm->useChecked) {
4047 LOGD("Late-enabling CheckJNI\n");
4048 dvmUseCheckedJniVm(extVm);
4049 extVm->useChecked = true;
4050 dvmUseCheckedJniEnv(extEnv);
4051
4052 /* currently no way to pick up jniopts features */
4053 } else {
4054 LOGD("Not late-enabling CheckJNI (already on)\n");
4055 }
4056}
4057
4058/*
4059 * Not supported.
4060 */
4061jint JNI_GetDefaultJavaVMInitArgs(void* vm_args)
4062{
4063 return JNI_ERR;
4064}
4065
4066/*
4067 * Return a buffer full of created VMs.
4068 *
4069 * We always have zero or one.
4070 */
4071jint JNI_GetCreatedJavaVMs(JavaVM** vmBuf, jsize bufLen, jsize* nVMs)
4072{
4073 if (gDvm.vmList != NULL) {
4074 *nVMs = 1;
4075
4076 if (bufLen > 0)
4077 *vmBuf++ = gDvm.vmList;
4078 } else {
4079 *nVMs = 0;
4080 }
4081
4082 return JNI_OK;
4083}
4084
4085
4086/*
4087 * Create a new VM instance.
4088 *
4089 * The current thread becomes the main VM thread. We return immediately,
4090 * which effectively means the caller is executing in a native method.
4091 */
4092jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args)
4093{
4094 const JavaVMInitArgs* args = (JavaVMInitArgs*) vm_args;
4095 JNIEnvExt* pEnv = NULL;
4096 JavaVMExt* pVM = NULL;
4097 const char** argv;
4098 int argc = 0;
4099 int i, curOpt;
4100 int result = JNI_ERR;
4101 bool checkJni = false;
4102 bool warnError = true;
4103 bool forceDataCopy = false;
4104
4105 if (args->version < JNI_VERSION_1_2)
4106 return JNI_EVERSION;
4107
4108 // TODO: don't allow creation of multiple VMs -- one per customer for now
4109
4110 /* zero globals; not strictly necessary the first time a VM is started */
4111 memset(&gDvm, 0, sizeof(gDvm));
4112
4113 /*
4114 * Set up structures for JNIEnv and VM.
4115 */
4116 //pEnv = (JNIEnvExt*) malloc(sizeof(JNIEnvExt));
4117 pVM = (JavaVMExt*) malloc(sizeof(JavaVMExt));
4118
4119 //memset(pEnv, 0, sizeof(JNIEnvExt));
4120 //pEnv->funcTable = &gNativeInterface;
4121 //pEnv->vm = pVM;
4122 memset(pVM, 0, sizeof(JavaVMExt));
4123 pVM->funcTable = &gInvokeInterface;
4124 pVM->envList = pEnv;
4125 dvmInitMutex(&pVM->envListLock);
4126
4127 argv = (const char**) malloc(sizeof(char*) * (args->nOptions));
4128 memset(argv, 0, sizeof(char*) * (args->nOptions));
4129
4130 curOpt = 0;
4131
4132 /*
4133 * Convert JNI args to argv.
4134 *
4135 * We have to pull out vfprintf/exit/abort, because they use the
4136 * "extraInfo" field to pass function pointer "hooks" in. We also
4137 * look for the -Xcheck:jni stuff here.
4138 */
4139 for (i = 0; i < args->nOptions; i++) {
4140 const char* optStr = args->options[i].optionString;
4141
4142 if (optStr == NULL) {
4143 fprintf(stderr, "ERROR: arg %d string was null\n", i);
4144 goto bail;
4145 } else if (strcmp(optStr, "vfprintf") == 0) {
4146 gDvm.vfprintfHook = args->options[i].extraInfo;
4147 } else if (strcmp(optStr, "exit") == 0) {
4148 gDvm.exitHook = args->options[i].extraInfo;
4149 } else if (strcmp(optStr, "abort") == 0) {
4150 gDvm.abortHook = args->options[i].extraInfo;
4151 } else if (strcmp(optStr, "-Xcheck:jni") == 0) {
4152 checkJni = true;
4153 } else if (strncmp(optStr, "-Xjniopts:", 10) == 0) {
4154 const char* jniOpts = optStr + 9;
4155 while (jniOpts != NULL) {
4156 jniOpts++; /* skip past ':' or ',' */
4157 if (strncmp(jniOpts, "warnonly", 8) == 0) {
4158 warnError = false;
4159 } else if (strncmp(jniOpts, "forcecopy", 9) == 0) {
4160 forceDataCopy = true;
4161 } else {
4162 LOGW("unknown jni opt starting at '%s'\n", jniOpts);
4163 }
4164 jniOpts = strchr(jniOpts, ',');
4165 }
4166 } else {
4167 /* regular option */
4168 argv[curOpt++] = optStr;
4169 }
4170 }
4171 argc = curOpt;
4172
4173 if (checkJni) {
4174 dvmUseCheckedJniVm(pVM);
4175 pVM->useChecked = true;
4176 }
4177 pVM->warnError = warnError;
4178 pVM->forceDataCopy = forceDataCopy;
4179
4180 /* set this up before initializing VM, so it can create some JNIEnvs */
4181 gDvm.vmList = (JavaVM*) pVM;
4182
4183 /*
4184 * Create an env for main thread. We need to have something set up
4185 * here because some of the class initialization we do when starting
4186 * up the VM will call into native code.
4187 */
4188 pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL);
4189
4190 /* initialize VM */
4191 gDvm.initializing = true;
4192 if (dvmStartup(argc, argv, args->ignoreUnrecognized, (JNIEnv*)pEnv) != 0) {
4193 free(pEnv);
4194 free(pVM);
4195 goto bail;
4196 }
4197
4198 /*
4199 * Success! Return stuff to caller.
4200 */
4201 dvmChangeStatus(NULL, THREAD_NATIVE);
4202 *p_env = (JNIEnv*) pEnv;
4203 *p_vm = (JavaVM*) pVM;
4204 result = JNI_OK;
4205
4206bail:
4207 gDvm.initializing = false;
4208 if (result == JNI_OK)
4209 LOGV("JNI_CreateJavaVM succeeded\n");
4210 else
4211 LOGW("JNI_CreateJavaVM failed\n");
4212 free(argv);
4213 return result;
4214}
4215