blob: 6692f3bb5b98e717d8f2e127712e02a1d0755d00 [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 McFaddenb18992f2009-09-25 10:42:15 -0700321 Method* meth;
322
323 /*
324 * Grab the PhantomReference constructor.
325 */
326 gDvm.classJavaLangRefPhantomReference =
327 dvmFindSystemClassNoInit("Ljava/lang/ref/PhantomReference;");
328 if (gDvm.classJavaLangRefPhantomReference == NULL) {
329 LOGE("Unable to find PhantomReference class\n");
330 return false;
331 }
332 meth= dvmFindDirectMethodByDescriptor(gDvm.classJavaLangRefPhantomReference,
333 "<init>", "(Ljava/lang/Object;Ljava/lang/ref/ReferenceQueue;)V");
334 if (meth == NULL) {
335 LOGE("Unable to find constructor for PhantomReference\n");
336 return false;
337 }
338 gDvm.methJavaLangRefPhantomReference_init = meth;
339
340
Andy McFadden8e5c7842009-07-23 17:47:18 -0700341 /*
342 * Look up and cache pointers to some direct buffer classes, fields,
343 * and methods.
344 */
Andy McFadden5f612b82009-07-22 15:07:27 -0700345 ClassObject* platformAddressClass =
346 dvmFindSystemClassNoInit("Lorg/apache/harmony/luni/platform/PlatformAddress;");
Andy McFadden8e5c7842009-07-23 17:47:18 -0700347 ClassObject* platformAddressFactoryClass =
348 dvmFindSystemClassNoInit("Lorg/apache/harmony/luni/platform/PlatformAddressFactory;");
Andy McFadden5f612b82009-07-22 15:07:27 -0700349 ClassObject* directBufferClass =
350 dvmFindSystemClassNoInit("Lorg/apache/harmony/nio/internal/DirectBuffer;");
Andy McFadden8e5c7842009-07-23 17:47:18 -0700351 ClassObject* readWriteBufferClass =
352 dvmFindSystemClassNoInit("Ljava/nio/ReadWriteDirectByteBuffer;");
353 ClassObject* bufferClass =
354 dvmFindSystemClassNoInit("Ljava/nio/Buffer;");
355
356 if (platformAddressClass == NULL || platformAddressFactoryClass == NULL ||
357 directBufferClass == NULL || readWriteBufferClass == NULL ||
358 bufferClass == NULL)
359 {
Andy McFadden5f612b82009-07-22 15:07:27 -0700360 LOGE("Unable to find internal direct buffer classes\n");
361 return false;
362 }
Andy McFadden8e5c7842009-07-23 17:47:18 -0700363 gDvm.classJavaNioReadWriteDirectByteBuffer = readWriteBufferClass;
Andy McFaddend5ab7262009-08-25 07:19:34 -0700364 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer = directBufferClass;
365 /* need a global reference for extended CheckJNI tests */
366 gDvm.jclassOrgApacheHarmonyNioInternalDirectBuffer =
367 addGlobalReference((Object*) directBufferClass);
Andy McFadden5f612b82009-07-22 15:07:27 -0700368
Andy McFadden8e5c7842009-07-23 17:47:18 -0700369 /*
370 * We need a Method* here rather than a vtable offset, because
371 * DirectBuffer is an interface class.
372 */
Andy McFadden5f612b82009-07-22 15:07:27 -0700373 meth = dvmFindVirtualMethodByDescriptor(
374 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer,
375 "getEffectiveAddress",
376 "()Lorg/apache/harmony/luni/platform/PlatformAddress;");
377 if (meth == NULL) {
378 LOGE("Unable to find PlatformAddress.getEffectiveAddress\n");
379 return false;
380 }
381 gDvm.methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress = meth;
382
383 meth = dvmFindVirtualMethodByDescriptor(platformAddressClass,
384 "toLong", "()J");
385 if (meth == NULL) {
386 LOGE("Unable to find PlatformAddress.toLong\n");
387 return false;
388 }
Andy McFadden8e5c7842009-07-23 17:47:18 -0700389 gDvm.voffOrgApacheHarmonyLuniPlatformPlatformAddress_toLong =
390 meth->methodIndex;
391
392 meth = dvmFindDirectMethodByDescriptor(platformAddressFactoryClass,
393 "on",
394 "(I)Lorg/apache/harmony/luni/platform/PlatformAddress;");
395 if (meth == NULL) {
396 LOGE("Unable to find PlatformAddressFactory.on\n");
397 return false;
398 }
399 gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on = meth;
400
401 meth = dvmFindDirectMethodByDescriptor(readWriteBufferClass,
402 "<init>",
403 "(Lorg/apache/harmony/luni/platform/PlatformAddress;II)V");
404 if (meth == NULL) {
405 LOGE("Unable to find ReadWriteDirectByteBuffer.<init>\n");
406 return false;
407 }
408 gDvm.methJavaNioReadWriteDirectByteBuffer_init = meth;
409
410 gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr =
411 dvmFindFieldOffset(platformAddressClass, "osaddr", "I");
412 if (gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr < 0) {
413 LOGE("Unable to find PlatformAddress.osaddr\n");
414 return false;
415 }
416
417 gDvm.offJavaNioBuffer_capacity =
418 dvmFindFieldOffset(bufferClass, "capacity", "I");
419 if (gDvm.offJavaNioBuffer_capacity < 0) {
420 LOGE("Unable to find Buffer.capacity\n");
421 return false;
422 }
Andy McFadden5f612b82009-07-22 15:07:27 -0700423
Andy McFadden8e696dc2009-07-24 15:28:16 -0700424 gDvm.offJavaNioBuffer_effectiveDirectAddress =
425 dvmFindFieldOffset(bufferClass, "effectiveDirectAddress", "I");
426 if (gDvm.offJavaNioBuffer_effectiveDirectAddress < 0) {
427 LOGE("Unable to find Buffer.effectiveDirectAddress\n");
428 return false;
429 }
430
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800431 return true;
432}
433
434/*
435 * Free the global references table.
436 */
437void dvmJniShutdown(void)
438{
Andy McFaddend5ab7262009-08-25 07:19:34 -0700439#ifdef USE_INDIRECT_REF
440 dvmClearIndirectRefTable(&gDvm.jniGlobalRefTable);
441#else
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800442 dvmClearReferenceTable(&gDvm.jniGlobalRefTable);
Andy McFaddend5ab7262009-08-25 07:19:34 -0700443#endif
Andy McFadden96516932009-10-28 17:39:02 -0700444 dvmClearReferenceTable(&gDvm.jniPinRefTable);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800445}
446
447
448/*
449 * Find the JNIEnv associated with the current thread.
450 *
451 * Currently stored in the Thread struct. Could also just drop this into
452 * thread-local storage.
453 */
454JNIEnvExt* dvmGetJNIEnvForThread(void)
455{
456 Thread* self = dvmThreadSelf();
457 if (self == NULL)
458 return NULL;
459 return (JNIEnvExt*) dvmGetThreadJNIEnv(self);
460}
461
462/*
463 * Create a new JNIEnv struct and add it to the VM's list.
464 *
465 * "self" will be NULL for the main thread, since the VM hasn't started
466 * yet; the value will be filled in later.
467 */
468JNIEnv* dvmCreateJNIEnv(Thread* self)
469{
470 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
471 JNIEnvExt* newEnv;
472
473 //if (self != NULL)
474 // LOGI("Ent CreateJNIEnv: threadid=%d %p\n", self->threadId, self);
475
476 assert(vm != NULL);
477
478 newEnv = (JNIEnvExt*) calloc(1, sizeof(JNIEnvExt));
479 newEnv->funcTable = &gNativeInterface;
480 newEnv->vm = vm;
481 newEnv->forceDataCopy = vm->forceDataCopy;
482 if (self != NULL) {
483 dvmSetJniEnvThreadId((JNIEnv*) newEnv, self);
484 assert(newEnv->envThreadId != 0);
485 } else {
486 /* make it obvious if we fail to initialize these later */
487 newEnv->envThreadId = 0x77777775;
488 newEnv->self = (Thread*) 0x77777779;
489 }
490 if (vm->useChecked)
491 dvmUseCheckedJniEnv(newEnv);
492
493 dvmLockMutex(&vm->envListLock);
494
495 /* insert at head of list */
496 newEnv->next = vm->envList;
497 assert(newEnv->prev == NULL);
498 if (vm->envList == NULL) // rare, but possible
499 vm->envList = newEnv;
500 else
501 vm->envList->prev = newEnv;
502 vm->envList = newEnv;
503
504 dvmUnlockMutex(&vm->envListLock);
505
506 //if (self != NULL)
507 // LOGI("Xit CreateJNIEnv: threadid=%d %p\n", self->threadId, self);
508 return (JNIEnv*) newEnv;
509}
510
511/*
512 * Remove a JNIEnv struct from the list and free it.
513 */
514void dvmDestroyJNIEnv(JNIEnv* env)
515{
516 JNIEnvExt* extEnv = (JNIEnvExt*) env;
517 JavaVMExt* vm = extEnv->vm;
518 Thread* self;
519
520 if (env == NULL)
521 return;
522
523 self = dvmThreadSelf();
524 assert(self != NULL);
525
526 //LOGI("Ent DestroyJNIEnv: threadid=%d %p\n", self->threadId, self);
527
528 dvmLockMutex(&vm->envListLock);
529
530 if (extEnv == vm->envList) {
531 assert(extEnv->prev == NULL);
532 vm->envList = extEnv->next;
533 } else {
534 assert(extEnv->prev != NULL);
535 extEnv->prev->next = extEnv->next;
536 }
537 if (extEnv->next != NULL)
538 extEnv->next->prev = extEnv->prev;
539
540 dvmUnlockMutex(&extEnv->vm->envListLock);
541
542 free(env);
543 //LOGI("Xit DestroyJNIEnv: threadid=%d %p\n", self->threadId, self);
544}
545
546
547/*
548 * Retrieve the ReferenceTable struct for the current thread.
549 *
Andy McFaddenab00d452009-08-19 07:21:41 -0700550 * Going through "env" rather than dvmThreadSelf() is faster but will
551 * get weird if the JNI code is passing the wrong JNIEnv around.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800552 */
Andy McFaddend5ab7262009-08-25 07:19:34 -0700553#ifdef USE_INDIRECT_REF
554static inline IndirectRefTable* getLocalRefTable(JNIEnv* env)
Andy McFaddend5ab7262009-08-25 07:19:34 -0700555#else
Andy McFaddenab00d452009-08-19 07:21:41 -0700556static inline ReferenceTable* getLocalRefTable(JNIEnv* env)
Andy McFaddeneb9cbc32009-08-28 14:45:12 -0700557#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800558{
Andy McFaddenab00d452009-08-19 07:21:41 -0700559 //return &dvmThreadSelf()->jniLocalRefTable;
560 return &((JNIEnvExt*)env)->self->jniLocalRefTable;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800561}
Andy McFaddend5ab7262009-08-25 07:19:34 -0700562
Andy McFaddeneb9cbc32009-08-28 14:45:12 -0700563#ifdef USE_INDIRECT_REF
Andy McFaddend5ab7262009-08-25 07:19:34 -0700564/*
565 * Convert an indirect reference to an Object reference. The indirect
566 * reference may be local, global, or weak-global.
567 *
568 * If "jobj" is NULL or an invalid indirect reference, this returns NULL.
569 */
570Object* dvmDecodeIndirectRef(JNIEnv* env, jobject jobj)
571{
Andy McFaddend5ab7262009-08-25 07:19:34 -0700572 if (jobj == NULL)
573 return NULL;
574
575 Object* result;
576
577 switch (dvmGetIndirectRefType(jobj)) {
578 case kIndirectKindLocal:
579 {
580 IndirectRefTable* pRefTable = getLocalRefTable(env);
581 result = dvmGetFromIndirectRefTable(pRefTable, jobj);
582 }
583 break;
584 case kIndirectKindGlobal:
585 {
586 // TODO: find a way to avoid the mutex activity here
587 IndirectRefTable* pRefTable = &gDvm.jniGlobalRefTable;
588 dvmLockMutex(&gDvm.jniGlobalRefLock);
589 result = dvmGetFromIndirectRefTable(pRefTable, jobj);
590 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
591 }
592 break;
593 case kIndirectKindWeakGlobal:
594 {
Andy McFaddenb18992f2009-09-25 10:42:15 -0700595 // TODO: implement
Andy McFaddend5ab7262009-08-25 07:19:34 -0700596 LOGE("weak-global not yet supported\n");
597 result = NULL;
598 dvmAbort();
599 }
600 break;
601 case kIndirectKindInvalid:
602 default:
603 LOGW("Invalid indirect reference %p in decodeIndirectRef\n", jobj);
604 dvmAbort();
605 result = NULL;
606 break;
607 }
608
609 return result;
Andy McFaddend5ab7262009-08-25 07:19:34 -0700610}
Andy McFaddeneb9cbc32009-08-28 14:45:12 -0700611#else
612 /* use trivial inline in JniInternal.h for performance */
613#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800614
615/*
616 * Add a local reference for an object to the current stack frame. When
617 * the native function returns, the reference will be discarded.
618 *
619 * We need to allow the same reference to be added multiple times.
620 *
621 * This will be called on otherwise unreferenced objects. We cannot do
622 * GC allocations here, and it's best if we don't grab a mutex.
623 *
624 * Returns the local reference (currently just the same pointer that was
625 * passed in), or NULL on failure.
626 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700627static jobject addLocalReference(JNIEnv* env, Object* obj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800628{
629 if (obj == NULL)
630 return NULL;
631
Andy McFaddend5ab7262009-08-25 07:19:34 -0700632 jobject jobj;
633
634#ifdef USE_INDIRECT_REF
635 IndirectRefTable* pRefTable = getLocalRefTable(env);
636 void* curFrame = ((JNIEnvExt*)env)->self->curFrame;
637 u4 cookie = SAVEAREA_FROM_FP(curFrame)->xtra.localRefCookie;
638
639 jobj = (jobject) dvmAddToIndirectRefTable(pRefTable, cookie, obj);
640 if (jobj == NULL) {
641 dvmDumpIndirectRefTable(pRefTable, "JNI local");
642 LOGE("Failed adding to JNI local ref table (has %d entries)\n",
643 (int) dvmIndirectRefTableEntries(pRefTable));
644 dvmDumpThread(dvmThreadSelf(), false);
645 dvmAbort(); // spec says call FatalError; this is equivalent
646 } else {
647 LOGVV("LREF add %p (%s.%s) (ent=%d)\n", obj,
648 dvmGetCurrentJNIMethod()->clazz->descriptor,
649 dvmGetCurrentJNIMethod()->name,
650 (int) dvmReferenceTableEntries(pRefTable));
651 }
652#else
Andy McFaddenab00d452009-08-19 07:21:41 -0700653 ReferenceTable* pRefTable = getLocalRefTable(env);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800654
Andy McFaddenab00d452009-08-19 07:21:41 -0700655 if (!dvmAddToReferenceTable(pRefTable, obj)) {
656 dvmDumpReferenceTable(pRefTable, "JNI local");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800657 LOGE("Failed adding to JNI local ref table (has %d entries)\n",
Andy McFaddenab00d452009-08-19 07:21:41 -0700658 (int) dvmReferenceTableEntries(pRefTable));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800659 dvmDumpThread(dvmThreadSelf(), false);
660 dvmAbort(); // spec says call FatalError; this is equivalent
661 } else {
Andy McFadden0083d372009-08-21 14:44:04 -0700662 LOGVV("LREF add %p (%s.%s) (ent=%d)\n", obj,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800663 dvmGetCurrentJNIMethod()->clazz->descriptor,
Andy McFadden0083d372009-08-21 14:44:04 -0700664 dvmGetCurrentJNIMethod()->name,
665 (int) dvmReferenceTableEntries(pRefTable));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800666 }
667
Andy McFaddend5ab7262009-08-25 07:19:34 -0700668 jobj = (jobject) obj;
669#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800670
Andy McFaddend5ab7262009-08-25 07:19:34 -0700671 return jobj;
Andy McFaddenab00d452009-08-19 07:21:41 -0700672}
673
674/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800675 * Ensure that at least "capacity" references can be held in the local
676 * refs table of the current thread.
677 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700678static bool ensureLocalCapacity(JNIEnv* env, int capacity)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800679{
Andy McFaddend5ab7262009-08-25 07:19:34 -0700680#ifdef USE_INDIRECT_REF
681 IndirectRefTable* pRefTable = getLocalRefTable(env);
682 int numEntries = dvmIndirectRefTableEntries(pRefTable);
683 // TODO: this isn't quite right, since "numEntries" includes holes
684 return ((kJniLocalRefMax - numEntries) >= capacity);
685#else
Andy McFaddenab00d452009-08-19 07:21:41 -0700686 ReferenceTable* pRefTable = getLocalRefTable(env);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800687
Andy McFaddenab00d452009-08-19 07:21:41 -0700688 return (kJniLocalRefMax - (pRefTable->nextEntry - pRefTable->table) >= capacity);
Andy McFaddend5ab7262009-08-25 07:19:34 -0700689#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800690}
691
692/*
693 * Explicitly delete a reference from the local list.
694 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700695static void deleteLocalReference(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800696{
Andy McFaddenab00d452009-08-19 07:21:41 -0700697 if (jobj == NULL)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800698 return;
699
Andy McFaddend5ab7262009-08-25 07:19:34 -0700700#ifdef USE_INDIRECT_REF
701 IndirectRefTable* pRefTable = getLocalRefTable(env);
702 Thread* self = ((JNIEnvExt*)env)->self;
703 u4 cookie = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefCookie;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800704
Andy McFaddend5ab7262009-08-25 07:19:34 -0700705 if (!dvmRemoveFromIndirectRefTable(pRefTable, cookie, jobj)) {
706 /*
707 * Attempting to delete a local reference that is not in the
708 * topmost local reference frame is a no-op. DeleteLocalRef returns
709 * void and doesn't throw any exceptions, but we should probably
710 * complain about it so the user will notice that things aren't
711 * going quite the way they expect.
712 */
713 LOGW("JNI WARNING: DeleteLocalRef(%p) failed to find entry\n", jobj);
714 }
715#else
716 ReferenceTable* pRefTable = getLocalRefTable(env);
717 Thread* self = ((JNIEnvExt*)env)->self;
718 Object** bottom = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefCookie;
719
720 if (!dvmRemoveFromReferenceTable(pRefTable, bottom, (Object*) jobj)) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800721 /*
722 * Attempting to delete a local reference that is not in the
723 * topmost local reference frame is a no-op. DeleteLocalRef returns
724 * void and doesn't throw any exceptions, but we should probably
725 * complain about it so the user will notice that things aren't
726 * going quite the way they expect.
727 */
728 LOGW("JNI WARNING: DeleteLocalRef(%p) failed to find entry (valid=%d)\n",
Andy McFaddenab00d452009-08-19 07:21:41 -0700729 jobj, dvmIsValidObject((Object*) jobj));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800730 }
Andy McFaddend5ab7262009-08-25 07:19:34 -0700731#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800732}
733
734/*
735 * Add a global reference for an object.
736 *
737 * We may add the same object more than once. Add/remove calls are paired,
738 * so it needs to appear on the list multiple times.
739 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700740static jobject addGlobalReference(Object* obj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800741{
742 if (obj == NULL)
743 return NULL;
744
745 //LOGI("adding obj=%p\n", obj);
746 //dvmDumpThread(dvmThreadSelf(), false);
747
748 if (false && ((Object*)obj)->clazz == gDvm.classJavaLangClass) {
749 ClassObject* clazz = (ClassObject*) obj;
750 LOGI("-------\n");
751 LOGI("Adding global ref on class %s\n", clazz->descriptor);
752 dvmDumpThread(dvmThreadSelf(), false);
753 }
754 if (false && ((Object*)obj)->clazz == gDvm.classJavaLangString) {
755 StringObject* strObj = (StringObject*) obj;
756 char* str = dvmCreateCstrFromString(strObj);
757 if (strcmp(str, "sync-response") == 0) {
758 LOGI("-------\n");
759 LOGI("Adding global ref on string '%s'\n", str);
760 dvmDumpThread(dvmThreadSelf(), false);
761 //dvmAbort();
762 }
763 free(str);
764 }
765 if (false && ((Object*)obj)->clazz == gDvm.classArrayByte) {
766 ArrayObject* arrayObj = (ArrayObject*) obj;
Andy McFaddend5ab7262009-08-25 07:19:34 -0700767 if (arrayObj->length == 8192 /*&&
768 dvmReferenceTableEntries(&gDvm.jniGlobalRefTable) > 400*/)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800769 {
770 LOGI("Adding global ref on byte array %p (len=%d)\n",
771 arrayObj, arrayObj->length);
772 dvmDumpThread(dvmThreadSelf(), false);
773 }
774 }
775
Andy McFaddend5ab7262009-08-25 07:19:34 -0700776 jobject jobj;
777
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800778 dvmLockMutex(&gDvm.jniGlobalRefLock);
779
780 /*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800781 * Throwing an exception on failure is problematic, because JNI code
782 * may not be expecting an exception, and things sort of cascade. We
783 * want to have a hard limit to catch leaks during debugging, but this
784 * otherwise needs to expand until memory is consumed. As a practical
785 * matter, if we have many thousands of global references, chances are
786 * we're either leaking global ref table entries or we're going to
787 * run out of space in the GC heap.
788 */
Andy McFaddend5ab7262009-08-25 07:19:34 -0700789#ifdef USE_INDIRECT_REF
790 jobj = dvmAddToIndirectRefTable(&gDvm.jniGlobalRefTable, IRT_FIRST_SEGMENT,
791 obj);
792 if (jobj == NULL) {
793 dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable, "JNI global");
794 LOGE("Failed adding to JNI global ref table (%d entries)\n",
795 (int) dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable));
796 dvmAbort();
797 }
798
799 LOGVV("GREF add %p (%s.%s)\n", obj,
800 dvmGetCurrentJNIMethod()->clazz->descriptor,
801 dvmGetCurrentJNIMethod()->name);
802
803 /* GREF usage tracking; should probably be disabled for production env */
804 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
805 int count = dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable);
806 // TODO: adjust for "holes"
807 if (count > gDvm.jniGlobalRefHiMark) {
808 LOGD("GREF has increased to %d\n", count);
809 gDvm.jniGlobalRefHiMark += kGrefWaterInterval;
810 gDvm.jniGlobalRefLoMark += kGrefWaterInterval;
811
812 /* watch for "excessive" use; not generally appropriate */
813 if (count >= gDvm.jniGrefLimit) {
814 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
815 if (vm->warnError) {
816 dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable,
817 "JNI global");
818 LOGE("Excessive JNI global references (%d)\n", count);
819 dvmAbort();
820 } else {
821 LOGW("Excessive JNI global references (%d)\n", count);
822 }
823 }
824 }
825 }
826#else
827 if (!dvmAddToReferenceTable(&gDvm.jniGlobalRefTable, obj)) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800828 dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global");
829 LOGE("Failed adding to JNI global ref table (%d entries)\n",
830 (int) dvmReferenceTableEntries(&gDvm.jniGlobalRefTable));
831 dvmAbort();
832 }
Andy McFaddend5ab7262009-08-25 07:19:34 -0700833 jobj = (jobject) obj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800834
835 LOGVV("GREF add %p (%s.%s)\n", obj,
836 dvmGetCurrentJNIMethod()->clazz->descriptor,
837 dvmGetCurrentJNIMethod()->name);
838
839 /* GREF usage tracking; should probably be disabled for production env */
840 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
841 int count = dvmReferenceTableEntries(&gDvm.jniGlobalRefTable);
842 if (count > gDvm.jniGlobalRefHiMark) {
843 LOGD("GREF has increased to %d\n", count);
844 gDvm.jniGlobalRefHiMark += kGrefWaterInterval;
845 gDvm.jniGlobalRefLoMark += kGrefWaterInterval;
846
847 /* watch for "excessive" use; not generally appropriate */
848 if (count >= gDvm.jniGrefLimit) {
849 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
850 if (vm->warnError) {
851 dvmDumpReferenceTable(&gDvm.jniGlobalRefTable,"JNI global");
852 LOGE("Excessive JNI global references (%d)\n", count);
853 dvmAbort();
854 } else {
855 LOGW("Excessive JNI global references (%d)\n", count);
856 }
857 }
858 }
859 }
Andy McFaddend5ab7262009-08-25 07:19:34 -0700860#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800861
862bail:
863 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
Andy McFaddend5ab7262009-08-25 07:19:34 -0700864 return jobj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800865}
866
867/*
868 * Remove a global reference. In most cases it's the entry most recently
869 * added, which makes this pretty quick.
870 *
871 * Thought: if it's not the most recent entry, just null it out. When we
872 * fill up, do a compaction pass before we expand the list.
873 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700874static void deleteGlobalReference(jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800875{
Andy McFaddenab00d452009-08-19 07:21:41 -0700876 if (jobj == NULL)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800877 return;
878
879 dvmLockMutex(&gDvm.jniGlobalRefLock);
880
Andy McFaddend5ab7262009-08-25 07:19:34 -0700881#ifdef USE_INDIRECT_REF
882 if (!dvmRemoveFromIndirectRefTable(&gDvm.jniGlobalRefTable,
883 IRT_FIRST_SEGMENT, jobj))
884 {
885 LOGW("JNI: DeleteGlobalRef(%p) failed to find entry\n", jobj);
886 goto bail;
887 }
888
889 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
890 int count = dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable);
891 // TODO: not quite right, need to subtract holes
892 if (count < gDvm.jniGlobalRefLoMark) {
893 LOGD("GREF has decreased to %d\n", count);
894 gDvm.jniGlobalRefHiMark -= kGrefWaterInterval;
895 gDvm.jniGlobalRefLoMark -= kGrefWaterInterval;
896 }
897 }
898#else
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800899 if (!dvmRemoveFromReferenceTable(&gDvm.jniGlobalRefTable,
Andy McFaddenab00d452009-08-19 07:21:41 -0700900 gDvm.jniGlobalRefTable.table, jobj))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800901 {
902 LOGW("JNI: DeleteGlobalRef(%p) failed to find entry (valid=%d)\n",
Andy McFaddenab00d452009-08-19 07:21:41 -0700903 jobj, dvmIsValidObject((Object*) jobj));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800904 goto bail;
905 }
906
907 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
908 int count = dvmReferenceTableEntries(&gDvm.jniGlobalRefTable);
909 if (count < gDvm.jniGlobalRefLoMark) {
910 LOGD("GREF has decreased to %d\n", count);
911 gDvm.jniGlobalRefHiMark -= kGrefWaterInterval;
912 gDvm.jniGlobalRefLoMark -= kGrefWaterInterval;
913 }
914 }
Andy McFaddend5ab7262009-08-25 07:19:34 -0700915#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800916
917bail:
918 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
919}
920
Andy McFaddenb18992f2009-09-25 10:42:15 -0700921
922/*
923 * Get the "magic" JNI weak global ReferenceQueue. It's allocated on
924 * first use.
925 *
926 * Returns NULL with an exception raised if allocation fails.
927 */
928static Object* getWeakGlobalRefQueue(void)
929{
930 /* use an indirect variable to avoid "type-punned pointer" complaints */
931 Object** pGlobalQ = &gDvm.jniWeakGlobalRefQueue;
932
933 if (*pGlobalQ != NULL)
934 return *pGlobalQ;
935
936 ClassObject* clazz = dvmFindSystemClass("Ljava/lang/ref/ReferenceQueue;");
937 if (clazz == NULL) {
938 LOGE("Unable to find java.lang.ref.ReferenceQueue");
939 dvmAbort();
940 }
941
942 /*
943 * Create an instance of ReferenceQueue. The object is never actually
944 * used for anything, so we don't need to call a constructor. (We could
945 * get away with using an instance of Object, but this is cleaner.)
946 */
947 Object* queue = dvmAllocObject(clazz, ALLOC_DEFAULT);
948 if (queue == NULL) {
949 LOGW("Failed allocating weak global ref queue\n");
950 assert(dvmCheckException(dvmThreadSelf()));
951 return NULL;
952 }
953 dvmReleaseTrackedAlloc(queue, NULL);
954
955 /*
956 * Save it, using atomic ops to ensure we don't double-up. The gDvm
957 * field is known to the GC.
958 */
959 if (!ATOMIC_CMP_SWAP((int*) pGlobalQ, 0, (int) queue)) {
960 LOGD("WOW: lost race to create weak global ref queue\n");
961 queue = *pGlobalQ;
962 }
963
964 return queue;
965}
966
967
968/*
969 * We create a PhantomReference that references the object, add a
970 * global reference to it, and then flip some bits before returning it.
971 * The last step ensures that we detect it as special and that only
972 * appropriate calls will accept it.
973 *
974 * On failure, returns NULL with an exception pending.
975 */
976static jweak createWeakGlobalRef(JNIEnv* env, jobject jobj)
977{
978 if (jobj == NULL)
979 return NULL;
980
981 Thread* self = ((JNIEnvExt*)env)->self;
982 Object* obj = dvmDecodeIndirectRef(env, jobj);
983 Object* weakGlobalQueue = getWeakGlobalRefQueue();
984 Object* phantomObj;
985 jobject phantomRef;
986
987 /*
988 * Allocate a PhantomReference, then call the constructor to set
989 * the referent and the reference queue.
990 *
991 * We use a "magic" reference queue that the GC knows about; it behaves
992 * more like a queueless WeakReference, clearing the referent and
993 * not calling enqueue().
994 */
995 if (!dvmIsClassInitialized(gDvm.classJavaLangRefPhantomReference))
996 dvmInitClass(gDvm.classJavaLangRefPhantomReference);
997 phantomObj = dvmAllocObject(gDvm.classJavaLangRefPhantomReference,
998 ALLOC_DEFAULT);
999 if (phantomObj == NULL) {
1000 assert(dvmCheckException(self));
1001 LOGW("Failed on WeakGlobalRef alloc\n");
1002 return NULL;
1003 }
1004
1005 JValue unused;
1006 dvmCallMethod(self, gDvm.methJavaLangRefPhantomReference_init, phantomObj,
1007 &unused, jobj, weakGlobalQueue);
1008 dvmReleaseTrackedAlloc(phantomObj, self);
1009
1010 if (dvmCheckException(self)) {
1011 LOGW("PhantomReference init failed\n");
1012 return NULL;
1013 }
1014
1015 LOGV("+++ WGR: created phantom ref %p for object %p\n", phantomObj, obj);
1016
1017 /*
1018 * Add it to the global reference table, and mangle the pointer.
1019 */
1020 phantomRef = addGlobalReference(phantomObj);
1021 return dvmObfuscateWeakGlobalRef(phantomRef);
1022}
1023
1024/*
1025 * Delete the global reference that's keeping the PhantomReference around.
1026 * The PhantomReference will eventually be discarded by the GC.
1027 */
1028static void deleteWeakGlobalRef(JNIEnv* env, jweak wref)
1029{
1030 if (wref == NULL)
1031 return;
1032
1033 jobject phantomRef = dvmNormalizeWeakGlobalRef(wref);
1034 deleteGlobalReference(phantomRef);
1035}
1036
1037/*
1038 * Extract the referent from a PhantomReference. Used for weak global
1039 * references.
1040 *
1041 * "jwobj" is a "mangled" WGR pointer.
1042 */
1043static Object* getPhantomReferent(JNIEnv* env, jweak jwobj)
1044{
1045 jobject jobj = dvmNormalizeWeakGlobalRef(jwobj);
1046 Object* obj = dvmDecodeIndirectRef(env, jobj);
1047
1048 if (obj->clazz != gDvm.classJavaLangRefPhantomReference) {
1049 LOGE("%p is not a phantom reference (%s)\n",
1050 jwobj, obj->clazz->descriptor);
1051 return NULL;
1052 }
1053
1054 return dvmGetFieldObject(obj, gDvm.offJavaLangRefReference_referent);
1055}
1056
1057
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001058/*
Andy McFaddenab00d452009-08-19 07:21:41 -07001059 * Objects don't currently move, so we just need to create a reference
1060 * that will ensure the array object isn't collected.
1061 *
Andy McFaddenc26bb632009-08-21 12:01:31 -07001062 * We use a separate reference table, which is part of the GC root set.
Andy McFaddenab00d452009-08-19 07:21:41 -07001063 */
1064static void pinPrimitiveArray(ArrayObject* arrayObj)
1065{
Andy McFaddenc26bb632009-08-21 12:01:31 -07001066 if (arrayObj == NULL)
1067 return;
1068
1069 dvmLockMutex(&gDvm.jniPinRefLock);
1070 if (!dvmAddToReferenceTable(&gDvm.jniPinRefTable, (Object*)arrayObj)) {
1071 dvmDumpReferenceTable(&gDvm.jniPinRefTable, "JNI pinned array");
1072 LOGE("Failed adding to JNI pinned array ref table (%d entries)\n",
1073 (int) dvmReferenceTableEntries(&gDvm.jniPinRefTable));
1074 dvmDumpThread(dvmThreadSelf(), false);
1075 dvmAbort();
1076 }
1077
1078 /*
1079 * If we're watching global ref usage, also keep an eye on these.
1080 *
1081 * The total number of pinned primitive arrays should be pretty small.
1082 * A single array should not be pinned more than once or twice; any
1083 * more than that is a strong indicator that a Release function is
1084 * not being called.
1085 */
1086 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
1087 int count = 0;
1088 Object** ppObj = gDvm.jniPinRefTable.table;
1089 while (ppObj < gDvm.jniPinRefTable.nextEntry) {
1090 if (*ppObj++ == (Object*) arrayObj)
1091 count++;
1092 }
1093
1094 if (count > kPinComplainThreshold) {
1095 LOGW("JNI: pin count on array %p (%s) is now %d\n",
1096 arrayObj, arrayObj->obj.clazz->descriptor, count);
1097 /* keep going */
1098 }
1099 }
1100
1101 dvmUnlockMutex(&gDvm.jniPinRefLock);
Andy McFaddenab00d452009-08-19 07:21:41 -07001102}
1103
1104/*
1105 * Un-pin the array object. If an object was pinned twice, it must be
1106 * unpinned twice before it's free to move.
Andy McFaddenab00d452009-08-19 07:21:41 -07001107 */
1108static void unpinPrimitiveArray(ArrayObject* arrayObj)
1109{
Andy McFaddenc26bb632009-08-21 12:01:31 -07001110 if (arrayObj == NULL)
1111 return;
1112
1113 dvmLockMutex(&gDvm.jniPinRefLock);
1114 if (!dvmRemoveFromReferenceTable(&gDvm.jniPinRefTable,
1115 gDvm.jniPinRefTable.table, (Object*) arrayObj))
1116 {
1117 LOGW("JNI: unpinPrimitiveArray(%p) failed to find entry (valid=%d)\n",
1118 arrayObj, dvmIsValidObject((Object*) arrayObj));
1119 goto bail;
1120 }
1121
1122bail:
1123 dvmUnlockMutex(&gDvm.jniPinRefLock);
Andy McFaddenab00d452009-08-19 07:21:41 -07001124}
1125
1126/*
Andy McFadden92fa4762009-10-22 17:24:45 -07001127 * Dump the contents of the JNI reference tables to the log file.
1128 *
1129 * We only dump the local refs associated with the current thread.
1130 */
1131void dvmDumpJniReferenceTables(void)
1132{
1133 Thread* self = dvmThreadSelf();
1134 JNIEnv* env = self->jniEnv;
1135 ReferenceTable* pLocalRefs = getLocalRefTable(env);
1136
1137#ifdef USE_INDIRECT_REF
1138 dvmDumpIndirectRefTable(pLocalRefs, "JNI local");
1139 dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable, "JNI global");
1140#else
1141 dvmDumpReferenceTable(pLocalRefs, "JNI local");
1142 dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global");
1143#endif
1144 dvmDumpReferenceTable(&gDvm.jniPinRefTable, "JNI pinned array");
1145}
1146
1147/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001148 * GC helper function to mark all JNI global references.
Andy McFaddenc26bb632009-08-21 12:01:31 -07001149 *
1150 * We're currently handling the "pin" table here too.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001151 */
1152void dvmGcMarkJniGlobalRefs()
1153{
Andy McFaddend5ab7262009-08-25 07:19:34 -07001154 Object** op;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001155
1156 dvmLockMutex(&gDvm.jniGlobalRefLock);
1157
Andy McFaddend5ab7262009-08-25 07:19:34 -07001158#ifdef USE_INDIRECT_REF
1159 IndirectRefTable* pRefTable = &gDvm.jniGlobalRefTable;
1160 op = pRefTable->table;
1161 int numEntries = dvmIndirectRefTableEntries(pRefTable);
1162 int i;
1163
1164 for (i = 0; i < numEntries; i++) {
1165 Object* obj = *op;
1166 if (obj != NULL)
1167 dvmMarkObjectNonNull(obj);
1168 op++;
1169 }
1170#else
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001171 op = gDvm.jniGlobalRefTable.table;
1172 while ((uintptr_t)op < (uintptr_t)gDvm.jniGlobalRefTable.nextEntry) {
1173 dvmMarkObjectNonNull(*(op++));
1174 }
Andy McFaddend5ab7262009-08-25 07:19:34 -07001175#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001176
1177 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
Andy McFaddenc26bb632009-08-21 12:01:31 -07001178
1179
1180 dvmLockMutex(&gDvm.jniPinRefLock);
1181
1182 op = gDvm.jniPinRefTable.table;
1183 while ((uintptr_t)op < (uintptr_t)gDvm.jniPinRefTable.nextEntry) {
1184 dvmMarkObjectNonNull(*(op++));
1185 }
1186
1187 dvmUnlockMutex(&gDvm.jniPinRefLock);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001188}
1189
1190
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001191#ifndef USE_INDIRECT_REF
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001192/*
1193 * Determine if "obj" appears in the argument list for the native method.
1194 *
1195 * We use the "shorty" signature to determine which argument slots hold
1196 * reference types.
1197 */
1198static bool findInArgList(Thread* self, Object* obj)
1199{
1200 const Method* meth;
1201 u4* fp;
1202 int i;
1203
1204 fp = self->curFrame;
1205 while (1) {
1206 /*
1207 * Back up over JNI PushLocalFrame frames. This works because the
1208 * previous frame on the interpreted stack is either a break frame
1209 * (if we called here via native code) or an interpreted method (if
1210 * we called here via the interpreter). In both cases the method
1211 * pointer won't match.
1212 */
1213 StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
1214 meth = saveArea->method;
1215 if (meth != SAVEAREA_FROM_FP(saveArea->prevFrame)->method)
1216 break;
1217 fp = saveArea->prevFrame;
1218 }
1219
1220 LOGVV("+++ scanning %d args in %s (%s)\n",
1221 meth->insSize, meth->name, meth->shorty);
1222 const char* shorty = meth->shorty +1; /* skip return type char */
1223 for (i = 0; i < meth->insSize; i++) {
1224 if (i == 0 && !dvmIsStaticMethod(meth)) {
1225 /* first arg is "this" ref, not represented in "shorty" */
1226 if (fp[i] == (u4) obj)
1227 return true;
1228 } else {
1229 /* if this is a reference type, see if it matches */
1230 switch (*shorty) {
1231 case 'L':
1232 if (fp[i] == (u4) obj)
1233 return true;
1234 break;
1235 case 'D':
1236 case 'J':
1237 i++;
1238 break;
1239 case '\0':
1240 LOGE("Whoops! ran off the end of %s (%d)\n",
1241 meth->shorty, meth->insSize);
1242 break;
1243 default:
1244 if (fp[i] == (u4) obj)
1245 LOGI("NOTE: ref %p match on arg type %c\n", obj, *shorty);
1246 break;
1247 }
1248 shorty++;
1249 }
1250 }
1251
1252 /*
1253 * For static methods, we also pass a class pointer in.
1254 */
1255 if (dvmIsStaticMethod(meth)) {
1256 //LOGI("+++ checking class pointer in %s\n", meth->name);
1257 if ((void*)obj == (void*)meth->clazz)
1258 return true;
1259 }
1260 return false;
1261}
Andy McFadden0083d372009-08-21 14:44:04 -07001262#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001263
1264/*
1265 * Verify that a reference passed in from native code is one that the
1266 * code is allowed to have.
1267 *
1268 * It's okay for native code to pass us a reference that:
Andy McFadden0083d372009-08-21 14:44:04 -07001269 * - was passed in as an argument when invoked by native code (and hence
1270 * is in the JNI local refs table)
1271 * - was returned to it from JNI (and is now in the local refs table)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001272 * - is present in the JNI global refs table
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001273 *
1274 * Used by -Xcheck:jni and GetObjectRefType.
1275 *
1276 * NOTE: in the current VM, global and local references are identical. If
1277 * something is both global and local, we can't tell them apart, and always
1278 * return "local".
1279 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001280jobjectRefType dvmGetJNIRefType(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001281{
Andy McFaddend5ab7262009-08-25 07:19:34 -07001282#ifdef USE_INDIRECT_REF
1283 /*
1284 * IndirectRefKind is currently defined as an exact match of
Andy McFadden0423f0e2009-08-26 07:21:53 -07001285 * jobjectRefType, so this is easy. We have to decode it to determine
1286 * if it's a valid reference and not merely valid-looking.
Andy McFaddend5ab7262009-08-25 07:19:34 -07001287 */
Andy McFadden0423f0e2009-08-26 07:21:53 -07001288 Object* obj = dvmDecodeIndirectRef(env, jobj);
1289
1290 if (obj == NULL) {
1291 /* invalid ref, or jobj was NULL */
1292 return JNIInvalidRefType;
1293 } else {
1294 return (jobjectRefType) dvmGetIndirectRefType(jobj);
1295 }
Andy McFaddend5ab7262009-08-25 07:19:34 -07001296#else
Andy McFaddenab00d452009-08-19 07:21:41 -07001297 ReferenceTable* pRefTable = getLocalRefTable(env);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001298 Thread* self = dvmThreadSelf();
1299 //Object** top;
1300 Object** ptr;
1301
Andy McFaddenb18992f2009-09-25 10:42:15 -07001302 if (dvmIsWeakGlobalRef(jobj)) {
1303 return JNIWeakGlobalRefType;
1304 }
1305
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001306 /* check args */
Andy McFaddenab00d452009-08-19 07:21:41 -07001307 if (findInArgList(self, jobj)) {
1308 //LOGI("--- REF found %p on stack\n", jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001309 return JNILocalRefType;
1310 }
1311
1312 /* check locals */
Andy McFaddenab00d452009-08-19 07:21:41 -07001313 if (dvmFindInReferenceTable(pRefTable, pRefTable->table, jobj) != NULL) {
1314 //LOGI("--- REF found %p in locals\n", jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001315 return JNILocalRefType;
1316 }
1317
1318 /* check globals */
1319 dvmLockMutex(&gDvm.jniGlobalRefLock);
1320 if (dvmFindInReferenceTable(&gDvm.jniGlobalRefTable,
Andy McFaddenab00d452009-08-19 07:21:41 -07001321 gDvm.jniGlobalRefTable.table, jobj))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001322 {
Andy McFaddenab00d452009-08-19 07:21:41 -07001323 //LOGI("--- REF found %p in globals\n", jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001324 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
1325 return JNIGlobalRefType;
1326 }
1327 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
1328
1329 /* not found! */
1330 return JNIInvalidRefType;
Andy McFaddend5ab7262009-08-25 07:19:34 -07001331#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001332}
1333
1334/*
1335 * Register a method that uses JNI calling conventions.
1336 */
1337static bool dvmRegisterJNIMethod(ClassObject* clazz, const char* methodName,
1338 const char* signature, void* fnPtr)
1339{
1340 Method* method;
1341 bool result = false;
1342
1343 if (fnPtr == NULL)
1344 goto bail;
1345
1346 method = dvmFindDirectMethodByDescriptor(clazz, methodName, signature);
1347 if (method == NULL)
1348 method = dvmFindVirtualMethodByDescriptor(clazz, methodName, signature);
1349 if (method == NULL) {
1350 LOGW("ERROR: Unable to find decl for native %s.%s %s\n",
1351 clazz->descriptor, methodName, signature);
1352 goto bail;
1353 }
1354
1355 if (!dvmIsNativeMethod(method)) {
1356 LOGW("Unable to register: not native: %s.%s %s\n",
1357 clazz->descriptor, methodName, signature);
1358 goto bail;
1359 }
1360
1361 if (method->nativeFunc != dvmResolveNativeMethod) {
1362 LOGW("Warning: %s.%s %s was already registered/resolved?\n",
1363 clazz->descriptor, methodName, signature);
1364 /* keep going, I guess */
1365 }
1366
Andy McFadden59b61772009-05-13 16:44:34 -07001367 dvmUseJNIBridge(method, fnPtr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001368
1369 LOGV("JNI-registered %s.%s %s\n", clazz->descriptor, methodName,
1370 signature);
1371 result = true;
1372
1373bail:
1374 return result;
1375}
1376
1377/*
Andy McFadden59b61772009-05-13 16:44:34 -07001378 * Returns "true" if CheckJNI is enabled in the VM.
1379 */
1380static bool dvmIsCheckJNIEnabled(void)
1381{
1382 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
1383 return vm->useChecked;
1384}
1385
1386/*
1387 * Point "method->nativeFunc" at the JNI bridge, and overload "method->insns"
1388 * to point at the actual function.
1389 */
1390void dvmUseJNIBridge(Method* method, void* func)
1391{
Andy McFadden0083d372009-08-21 14:44:04 -07001392 enum {
1393 kJNIGeneral = 0,
1394 kJNISync = 1,
1395 kJNIVirtualNoRef = 2,
1396 kJNIStaticNoRef = 3,
1397 } kind;
1398 static const DalvikBridgeFunc stdFunc[] = {
1399 dvmCallJNIMethod_general,
1400 dvmCallJNIMethod_synchronized,
1401 dvmCallJNIMethod_virtualNoRef,
1402 dvmCallJNIMethod_staticNoRef
1403 };
1404 static const DalvikBridgeFunc checkFunc[] = {
1405 dvmCheckCallJNIMethod_general,
1406 dvmCheckCallJNIMethod_synchronized,
1407 dvmCheckCallJNIMethod_virtualNoRef,
1408 dvmCheckCallJNIMethod_staticNoRef
1409 };
1410
1411 bool hasRefArg = false;
1412
1413 if (dvmIsSynchronizedMethod(method)) {
1414 /* use version with synchronization; calls into general handler */
1415 kind = kJNISync;
Andy McFadden59b61772009-05-13 16:44:34 -07001416 } else {
Andy McFadden0083d372009-08-21 14:44:04 -07001417 /*
1418 * Do a quick scan through the "shorty" signature to see if the method
1419 * takes any reference arguments.
1420 */
1421 const char* cp = method->shorty;
1422 while (*++cp != '\0') { /* pre-incr to skip return type */
1423 if (*cp == 'L') {
1424 /* 'L' used for both object and array references */
1425 hasRefArg = true;
1426 break;
1427 }
1428 }
1429
1430 if (hasRefArg) {
1431 /* use general handler to slurp up reference args */
1432 kind = kJNIGeneral;
1433 } else {
1434 /* virtual methods have a ref in args[0] (not in signature) */
1435 if (dvmIsStaticMethod(method))
1436 kind = kJNIStaticNoRef;
1437 else
1438 kind = kJNIVirtualNoRef;
1439 }
1440 }
1441
1442 if (dvmIsCheckJNIEnabled()) {
1443 dvmSetNativeFunc(method, checkFunc[kind], func);
1444 } else {
1445 dvmSetNativeFunc(method, stdFunc[kind], func);
Andy McFadden59b61772009-05-13 16:44:34 -07001446 }
1447}
1448
1449/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001450 * Get the method currently being executed by examining the interp stack.
1451 */
1452const Method* dvmGetCurrentJNIMethod(void)
1453{
1454 assert(dvmThreadSelf() != NULL);
1455
1456 void* fp = dvmThreadSelf()->curFrame;
1457 const Method* meth = SAVEAREA_FROM_FP(fp)->method;
1458
1459 assert(meth != NULL);
1460 assert(dvmIsNativeMethod(meth));
1461 return meth;
1462}
1463
1464
1465/*
1466 * Track a JNI MonitorEnter in the current thread.
1467 *
1468 * The goal is to be able to "implicitly" release all JNI-held monitors
1469 * when the thread detaches.
1470 *
1471 * Monitors may be entered multiple times, so we add a new entry for each
1472 * enter call. It would be more efficient to keep a counter. At present
1473 * there's no real motivation to improve this however.
1474 */
1475static void trackMonitorEnter(Thread* self, Object* obj)
1476{
1477 static const int kInitialSize = 16;
1478 ReferenceTable* refTable = &self->jniMonitorRefTable;
1479
1480 /* init table on first use */
1481 if (refTable->table == NULL) {
1482 assert(refTable->maxEntries == 0);
1483
1484 if (!dvmInitReferenceTable(refTable, kInitialSize, INT_MAX)) {
1485 LOGE("Unable to initialize monitor tracking table\n");
1486 dvmAbort();
1487 }
1488 }
1489
1490 if (!dvmAddToReferenceTable(refTable, obj)) {
1491 /* ran out of memory? could throw exception instead */
1492 LOGE("Unable to add entry to monitor tracking table\n");
1493 dvmAbort();
1494 } else {
1495 LOGVV("--- added monitor %p\n", obj);
1496 }
1497}
1498
Andy McFaddenab00d452009-08-19 07:21:41 -07001499
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001500/*
1501 * Track a JNI MonitorExit in the current thread.
1502 */
1503static void trackMonitorExit(Thread* self, Object* obj)
1504{
Andy McFaddenab00d452009-08-19 07:21:41 -07001505 ReferenceTable* pRefTable = &self->jniMonitorRefTable;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001506
Andy McFaddenab00d452009-08-19 07:21:41 -07001507 if (!dvmRemoveFromReferenceTable(pRefTable, pRefTable->table, obj)) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001508 LOGE("JNI monitor %p not found in tracking list\n", obj);
1509 /* keep going? */
1510 } else {
1511 LOGVV("--- removed monitor %p\n", obj);
1512 }
1513}
1514
1515/*
1516 * Release all monitors held by the jniMonitorRefTable list.
1517 */
1518void dvmReleaseJniMonitors(Thread* self)
1519{
Andy McFaddenab00d452009-08-19 07:21:41 -07001520 ReferenceTable* pRefTable = &self->jniMonitorRefTable;
1521 Object** top = pRefTable->table;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001522
1523 if (top == NULL)
1524 return;
1525
Andy McFaddenab00d452009-08-19 07:21:41 -07001526 Object** ptr = pRefTable->nextEntry;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001527 while (--ptr >= top) {
1528 if (!dvmUnlockObject(self, *ptr)) {
1529 LOGW("Unable to unlock monitor %p at thread detach\n", *ptr);
1530 } else {
1531 LOGVV("--- detach-releasing monitor %p\n", *ptr);
1532 }
1533 }
1534
1535 /* zap it */
Andy McFaddenab00d452009-08-19 07:21:41 -07001536 pRefTable->nextEntry = pRefTable->table;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001537}
1538
1539#ifdef WITH_JNI_STACK_CHECK
1540/*
1541 * Compute a CRC on the entire interpreted stack.
1542 *
1543 * Would be nice to compute it on "self" as well, but there are parts of
1544 * the Thread that can be altered by other threads (e.g. prev/next pointers).
1545 */
1546static void computeStackSum(Thread* self)
1547{
1548 const u1* low = (const u1*)SAVEAREA_FROM_FP(self->curFrame);
1549 u4 crc = dvmInitCrc32();
1550 self->stackCrc = 0;
1551 crc = dvmComputeCrc32(crc, low, self->interpStackStart - low);
1552 self->stackCrc = crc;
1553}
1554
1555/*
1556 * Compute a CRC on the entire interpreted stack, and compare it to what
1557 * we previously computed.
1558 *
1559 * We can execute JNI directly from native code without calling in from
1560 * interpreted code during VM initialization and immediately after JNI
1561 * thread attachment. Another opportunity exists during JNI_OnLoad. Rather
1562 * than catching these cases we just ignore them here, which is marginally
1563 * less accurate but reduces the amount of code we have to touch with #ifdefs.
1564 */
1565static void checkStackSum(Thread* self)
1566{
1567 const u1* low = (const u1*)SAVEAREA_FROM_FP(self->curFrame);
1568 u4 stackCrc, crc;
1569
1570 stackCrc = self->stackCrc;
1571 self->stackCrc = 0;
1572 crc = dvmInitCrc32();
1573 crc = dvmComputeCrc32(crc, low, self->interpStackStart - low);
1574 if (crc != stackCrc) {
1575 const Method* meth = dvmGetCurrentJNIMethod();
1576 if (dvmComputeExactFrameDepth(self->curFrame) == 1) {
1577 LOGD("JNI: bad stack CRC (0x%08x) -- okay during init\n",
1578 stackCrc);
1579 } else if (strcmp(meth->name, "nativeLoad") == 0 &&
1580 (strcmp(meth->clazz->descriptor, "Ljava/lang/Runtime;") == 0))
1581 {
1582 LOGD("JNI: bad stack CRC (0x%08x) -- okay during JNI_OnLoad\n",
1583 stackCrc);
1584 } else {
1585 LOGW("JNI: bad stack CRC (%08x vs %08x)\n", crc, stackCrc);
1586 dvmAbort();
1587 }
1588 }
1589 self->stackCrc = (u4) -1; /* make logic errors more noticeable */
1590}
1591#endif
1592
1593
1594/*
1595 * ===========================================================================
Andy McFaddend5ab7262009-08-25 07:19:34 -07001596 * JNI call bridge
1597 * ===========================================================================
1598 */
1599
1600/*
1601 * The functions here form a bridge between interpreted code and JNI native
1602 * functions. The basic task is to convert an array of primitives and
1603 * references into C-style function arguments. This is architecture-specific
1604 * and usually requires help from assembly code.
1605 *
1606 * The bridge takes four arguments: the array of parameters, a place to
1607 * store the function result (if any), the method to call, and a pointer
1608 * to the current thread.
1609 *
1610 * These functions aren't called directly from elsewhere in the VM.
1611 * A pointer in the Method struct points to one of these, and when a native
1612 * method is invoked the interpreter jumps to it.
1613 *
1614 * (The "internal native" methods are invoked the same way, but instead
1615 * of calling through a bridge, the target method is called directly.)
1616 *
1617 * The "args" array should not be modified, but we do so anyway for
1618 * performance reasons. We know that it points to the "outs" area on
1619 * the current method's interpreted stack. This area is ignored by the
1620 * precise GC, because there is no register map for a native method (for
1621 * an interpreted method the args would be listed in the argument set).
1622 * We know all of the values exist elsewhere on the interpreted stack,
1623 * because the method call setup copies them right before making the call,
1624 * so we don't have to worry about concealing stuff from the GC.
1625 *
1626 * If we don't want to modify "args", we either have to create a local
1627 * copy and modify it before calling dvmPlatformInvoke, or we have to do
1628 * the local reference replacement within dvmPlatformInvoke. The latter
1629 * has some performance advantages, though if we can inline the local
1630 * reference adds we may win when there's a lot of reference args (unless
1631 * we want to code up some local ref table manipulation in assembly.
1632 */
1633
1634/*
Andy McFadden0423f0e2009-08-26 07:21:53 -07001635 * If necessary, convert the value in pResult from a local/global reference
1636 * to an object pointer.
1637 */
1638static inline void convertReferenceResult(JNIEnv* env, JValue* pResult,
1639 const Method* method, Thread* self)
1640{
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001641#ifdef USE_INDIRECT_REF
Andy McFadden0423f0e2009-08-26 07:21:53 -07001642 if (method->shorty[0] == 'L' && !dvmCheckException(self) &&
1643 pResult->l != NULL)
1644 {
1645 pResult->l = dvmDecodeIndirectRef(env, pResult->l);
1646 }
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001647#endif
Andy McFadden0423f0e2009-08-26 07:21:53 -07001648}
1649
1650/*
Andy McFaddend5ab7262009-08-25 07:19:34 -07001651 * General form, handles all cases.
1652 */
1653void dvmCallJNIMethod_general(const u4* args, JValue* pResult,
1654 const Method* method, Thread* self)
1655{
1656 int oldStatus;
1657 u4* modArgs = (u4*) args;
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001658 jclass staticMethodClass;
1659 JNIEnv* env = self->jniEnv;
Andy McFaddend5ab7262009-08-25 07:19:34 -07001660
1661 assert(method->insns != NULL);
1662
1663 //LOGI("JNI calling %p (%s.%s:%s):\n", method->insns,
1664 // method->clazz->descriptor, method->name, method->shorty);
1665
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001666#ifdef USE_INDIRECT_REF
Andy McFaddend5ab7262009-08-25 07:19:34 -07001667 /*
1668 * Walk the argument list, creating local references for appropriate
1669 * arguments.
1670 */
Andy McFaddend5ab7262009-08-25 07:19:34 -07001671 int idx = 0;
1672 if (dvmIsStaticMethod(method)) {
1673 /* add the class object we pass in */
1674 staticMethodClass = addLocalReference(env, (Object*) method->clazz);
1675 if (staticMethodClass == NULL) {
1676 assert(dvmCheckException(self));
1677 return;
1678 }
1679 } else {
1680 /* add "this" */
1681 staticMethodClass = NULL;
1682 jobject thisObj = addLocalReference(env, (Object*) modArgs[0]);
1683 if (thisObj == NULL) {
1684 assert(dvmCheckException(self));
1685 return;
1686 }
1687 modArgs[idx] = (u4) thisObj;
1688 idx = 1;
1689 }
1690
1691 const char* shorty = &method->shorty[1]; /* skip return type */
1692 while (*shorty != '\0') {
1693 switch (*shorty++) {
1694 case 'L':
1695 //LOGI(" local %d: 0x%08x\n", idx, modArgs[idx]);
1696 if (modArgs[idx] != 0) {
1697 //if (!dvmIsValidObject((Object*) modArgs[idx]))
1698 // dvmAbort();
1699 jobject argObj = addLocalReference(env, (Object*) modArgs[idx]);
1700 if (argObj == NULL) {
1701 assert(dvmCheckException(self));
1702 return;
1703 }
1704 modArgs[idx] = (u4) argObj;
1705 }
1706 break;
1707 case 'D':
1708 case 'J':
1709 idx++;
1710 break;
1711 default:
1712 /* Z B C S I -- do nothing */
1713 break;
1714 }
1715
1716 idx++;
1717 }
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001718#else
1719 staticMethodClass = dvmIsStaticMethod(method) ?
1720 (jclass) method->clazz : NULL;
1721#endif
Andy McFaddend5ab7262009-08-25 07:19:34 -07001722
1723 oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
1724
1725 COMPUTE_STACK_SUM(self);
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001726 dvmPlatformInvoke(env, staticMethodClass,
Andy McFaddend5ab7262009-08-25 07:19:34 -07001727 method->jniArgInfo, method->insSize, modArgs, method->shorty,
1728 (void*)method->insns, pResult);
1729 CHECK_STACK_SUM(self);
1730
1731 dvmChangeStatus(self, oldStatus);
Andy McFadden0423f0e2009-08-26 07:21:53 -07001732
1733 convertReferenceResult(env, pResult, method, self);
Andy McFaddend5ab7262009-08-25 07:19:34 -07001734}
1735
1736/*
1737 * Handler for the unusual case of a synchronized native method.
1738 *
1739 * Lock the object, then call through the general function.
1740 */
1741void dvmCallJNIMethod_synchronized(const u4* args, JValue* pResult,
1742 const Method* method, Thread* self)
1743{
1744 Object* lockObj;
1745
1746 assert(dvmIsSynchronizedMethod(method));
1747
1748 if (dvmIsStaticMethod(method))
1749 lockObj = (Object*) method->clazz;
1750 else
1751 lockObj = (Object*) args[0];
1752
1753 LOGVV("Calling %s.%s: locking %p (%s)\n",
1754 method->clazz->descriptor, method->name,
1755 lockObj, lockObj->clazz->descriptor);
1756
1757 dvmLockObject(self, lockObj);
1758 dvmCallJNIMethod_general(args, pResult, method, self);
1759 dvmUnlockObject(self, lockObj);
1760}
1761
1762/*
1763 * Virtual method call, no reference arguments.
1764 *
1765 * We need to local-ref the "this" argument, found in args[0].
1766 */
1767void dvmCallJNIMethod_virtualNoRef(const u4* args, JValue* pResult,
1768 const Method* method, Thread* self)
1769{
1770 u4* modArgs = (u4*) args;
1771 int oldStatus;
1772
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001773#ifdef USE_INDIRECT_REF
Andy McFaddend5ab7262009-08-25 07:19:34 -07001774 jobject thisObj = addLocalReference(self->jniEnv, (Object*) args[0]);
1775 if (thisObj == NULL) {
1776 assert(dvmCheckException(self));
1777 return;
1778 }
1779 modArgs[0] = (u4) thisObj;
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001780#endif
Andy McFaddend5ab7262009-08-25 07:19:34 -07001781
1782 oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
1783
1784 COMPUTE_STACK_SUM(self);
1785 dvmPlatformInvoke(self->jniEnv, NULL,
1786 method->jniArgInfo, method->insSize, modArgs, method->shorty,
1787 (void*)method->insns, pResult);
1788 CHECK_STACK_SUM(self);
1789
1790 dvmChangeStatus(self, oldStatus);
Andy McFadden0423f0e2009-08-26 07:21:53 -07001791
1792 convertReferenceResult(self->jniEnv, pResult, method, self);
Andy McFaddend5ab7262009-08-25 07:19:34 -07001793}
1794
1795/*
1796 * Static method call, no reference arguments.
1797 *
1798 * We need to local-ref the class reference.
1799 */
1800void dvmCallJNIMethod_staticNoRef(const u4* args, JValue* pResult,
1801 const Method* method, Thread* self)
1802{
1803 jclass staticMethodClass;
1804 int oldStatus;
1805
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001806#ifdef USE_INDIRECT_REF
Andy McFaddend5ab7262009-08-25 07:19:34 -07001807 staticMethodClass = addLocalReference(self->jniEnv, (Object*)method->clazz);
1808 if (staticMethodClass == NULL) {
1809 assert(dvmCheckException(self));
1810 return;
1811 }
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001812#else
1813 staticMethodClass = (jobject) method->clazz;
1814#endif
Andy McFaddend5ab7262009-08-25 07:19:34 -07001815
1816 oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
1817
1818 COMPUTE_STACK_SUM(self);
1819 dvmPlatformInvoke(self->jniEnv, staticMethodClass,
1820 method->jniArgInfo, method->insSize, args, method->shorty,
1821 (void*)method->insns, pResult);
1822 CHECK_STACK_SUM(self);
1823
1824 dvmChangeStatus(self, oldStatus);
Andy McFadden0423f0e2009-08-26 07:21:53 -07001825
1826 convertReferenceResult(self->jniEnv, pResult, method, self);
Andy McFaddend5ab7262009-08-25 07:19:34 -07001827}
1828
1829/*
1830 * Extract the return type enum from the "jniArgInfo" field.
1831 */
1832DalvikJniReturnType dvmGetArgInfoReturnType(int jniArgInfo)
1833{
1834 return (jniArgInfo & DALVIK_JNI_RETURN_MASK) >> DALVIK_JNI_RETURN_SHIFT;
1835}
1836
1837
1838/*
1839 * ===========================================================================
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001840 * JNI implementation
1841 * ===========================================================================
1842 */
1843
1844/*
1845 * Return the version of the native method interface.
1846 */
1847static jint GetVersion(JNIEnv* env)
1848{
1849 JNI_ENTER();
1850 /*
1851 * There is absolutely no need to toggle the mode for correct behavior.
1852 * However, it does provide native code with a simple "suspend self
1853 * if necessary" call.
1854 */
1855 JNI_EXIT();
1856 return JNI_VERSION_1_6;
1857}
1858
1859/*
1860 * Create a new class from a bag of bytes.
1861 *
1862 * This is not currently supported within Dalvik.
1863 */
1864static jclass DefineClass(JNIEnv* env, const char *name, jobject loader,
1865 const jbyte* buf, jsize bufLen)
1866{
1867 UNUSED_PARAMETER(name);
1868 UNUSED_PARAMETER(loader);
1869 UNUSED_PARAMETER(buf);
1870 UNUSED_PARAMETER(bufLen);
1871
1872 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001873 LOGW("JNI DefineClass is not supported\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001874 JNI_EXIT();
1875 return NULL;
1876}
1877
1878/*
1879 * Find a class by name.
1880 *
1881 * We have to use the "no init" version of FindClass here, because we might
1882 * be getting the class prior to registering native methods that will be
1883 * used in <clinit>.
1884 *
1885 * We need to get the class loader associated with the current native
1886 * method. If there is no native method, e.g. we're calling this from native
1887 * code right after creating the VM, the spec says we need to use the class
1888 * loader returned by "ClassLoader.getBaseClassLoader". There is no such
1889 * method, but it's likely they meant ClassLoader.getSystemClassLoader.
1890 * We can't get that until after the VM has initialized though.
1891 */
1892static jclass FindClass(JNIEnv* env, const char* name)
1893{
1894 JNI_ENTER();
1895
1896 const Method* thisMethod;
1897 ClassObject* clazz;
Andy McFaddenab00d452009-08-19 07:21:41 -07001898 jclass jclazz = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001899 Object* loader;
1900 char* descriptor = NULL;
1901
1902 thisMethod = dvmGetCurrentJNIMethod();
1903 assert(thisMethod != NULL);
1904
1905 descriptor = dvmNameToDescriptor(name);
1906 if (descriptor == NULL) {
1907 clazz = NULL;
1908 goto bail;
1909 }
1910
1911 //Thread* self = dvmThreadSelf();
1912 if (_self->classLoaderOverride != NULL) {
1913 /* hack for JNI_OnLoad */
1914 assert(strcmp(thisMethod->name, "nativeLoad") == 0);
1915 loader = _self->classLoaderOverride;
1916 } else if (thisMethod == gDvm.methFakeNativeEntry) {
1917 /* start point of invocation interface */
1918 if (!gDvm.initializing)
1919 loader = dvmGetSystemClassLoader();
1920 else
1921 loader = NULL;
1922 } else {
1923 loader = thisMethod->clazz->classLoader;
1924 }
1925
1926 clazz = dvmFindClassNoInit(descriptor, loader);
Andy McFaddenab00d452009-08-19 07:21:41 -07001927 jclazz = addLocalReference(env, (Object*) clazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001928
1929bail:
1930 free(descriptor);
Andy McFaddenab00d452009-08-19 07:21:41 -07001931
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001932 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001933 return jclazz;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001934}
1935
1936/*
1937 * Return the superclass of a class.
1938 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001939static jclass GetSuperclass(JNIEnv* env, jclass jclazz)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001940{
1941 JNI_ENTER();
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001942 jclass jsuper = NULL;
Andy McFaddenab00d452009-08-19 07:21:41 -07001943
1944 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001945 if (clazz != NULL)
Andy McFaddenab00d452009-08-19 07:21:41 -07001946 jsuper = addLocalReference(env, (Object*)clazz->super);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001947 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001948 return jsuper;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001949}
1950
1951/*
1952 * Determine whether an object of clazz1 can be safely cast to clazz2.
1953 *
1954 * Like IsInstanceOf, but with a pair of class objects instead of obj+class.
1955 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001956static jboolean IsAssignableFrom(JNIEnv* env, jclass jclazz1, jclass jclazz2)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001957{
1958 JNI_ENTER();
1959
Andy McFaddenab00d452009-08-19 07:21:41 -07001960 ClassObject* clazz1 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz1);
1961 ClassObject* clazz2 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz2);
1962
1963 jboolean result = dvmInstanceof(clazz1, clazz2);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001964
1965 JNI_EXIT();
1966 return result;
1967}
1968
1969/*
1970 * Given a java.lang.reflect.Method or .Constructor, return a methodID.
1971 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001972static jmethodID FromReflectedMethod(JNIEnv* env, jobject jmethod)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001973{
1974 JNI_ENTER();
1975 jmethodID methodID;
Andy McFaddenab00d452009-08-19 07:21:41 -07001976 Object* method = dvmDecodeIndirectRef(env, jmethod);
1977 methodID = (jmethodID) dvmGetMethodFromReflectObj(method);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001978 JNI_EXIT();
1979 return methodID;
1980}
1981
1982/*
1983 * Given a java.lang.reflect.Field, return a fieldID.
1984 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001985static jfieldID FromReflectedField(JNIEnv* env, jobject jfield)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001986{
1987 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001988 jfieldID fieldID;
1989 Object* field = dvmDecodeIndirectRef(env, jfield);
1990 fieldID = (jfieldID) dvmGetFieldFromReflectObj(field);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001991 JNI_EXIT();
1992 return fieldID;
1993}
1994
1995/*
1996 * Convert a methodID to a java.lang.reflect.Method or .Constructor.
1997 *
1998 * (The "isStatic" field does not appear in the spec.)
1999 *
2000 * Throws OutOfMemory and returns NULL on failure.
2001 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002002static jobject ToReflectedMethod(JNIEnv* env, jclass jcls, jmethodID methodID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002003 jboolean isStatic)
2004{
2005 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002006 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls);
2007 Object* obj = dvmCreateReflectObjForMethod(clazz, (Method*) methodID);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002008 dvmReleaseTrackedAlloc(obj, NULL);
Andy McFaddenab00d452009-08-19 07:21:41 -07002009 jobject jobj = addLocalReference(env, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002010 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002011 return jobj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002012}
2013
2014/*
2015 * Convert a fieldID to a java.lang.reflect.Field.
2016 *
2017 * (The "isStatic" field does not appear in the spec.)
2018 *
2019 * Throws OutOfMemory and returns NULL on failure.
2020 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002021static jobject ToReflectedField(JNIEnv* env, jclass jcls, jfieldID fieldID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002022 jboolean isStatic)
2023{
2024 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002025 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls);
2026 Object* obj = dvmCreateReflectObjForField(jcls, (Field*) fieldID);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002027 dvmReleaseTrackedAlloc(obj, NULL);
Andy McFaddenab00d452009-08-19 07:21:41 -07002028 jobject jobj = addLocalReference(env, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002029 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002030 return jobj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002031}
2032
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002033/*
2034 * Take this exception and throw it.
2035 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002036static jint Throw(JNIEnv* env, jthrowable jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002037{
2038 JNI_ENTER();
2039
2040 jint retval;
2041
Andy McFaddenab00d452009-08-19 07:21:41 -07002042 if (jobj != NULL) {
2043 Object* obj = dvmDecodeIndirectRef(env, jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002044 dvmSetException(_self, obj);
2045 retval = JNI_OK;
Andy McFaddenab00d452009-08-19 07:21:41 -07002046 } else {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002047 retval = JNI_ERR;
Andy McFaddenab00d452009-08-19 07:21:41 -07002048 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002049
2050 JNI_EXIT();
2051 return retval;
2052}
2053
2054/*
Andy McFaddenab00d452009-08-19 07:21:41 -07002055 * Constructs an exception object from the specified class with the message
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002056 * specified by "message", and throws it.
2057 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002058static jint ThrowNew(JNIEnv* env, jclass jclazz, const char* message)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002059{
2060 JNI_ENTER();
2061
Andy McFaddenab00d452009-08-19 07:21:41 -07002062 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2063 dvmThrowExceptionByClass(clazz, message);
2064 // TODO: should return failure if this didn't work (e.g. OOM)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002065
2066 JNI_EXIT();
2067 return JNI_OK;
2068}
2069
2070/*
2071 * If an exception is being thrown, return the exception object. Otherwise,
2072 * return NULL.
2073 *
2074 * TODO: if there is no pending exception, we should be able to skip the
2075 * enter/exit checks. If we find one, we need to enter and then re-fetch
2076 * the exception (in case it got moved by a compacting GC).
2077 */
2078static jthrowable ExceptionOccurred(JNIEnv* env)
2079{
2080 JNI_ENTER();
2081
2082 Object* exception;
Andy McFaddenab00d452009-08-19 07:21:41 -07002083 jobject localException;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002084
Andy McFaddenab00d452009-08-19 07:21:41 -07002085 exception = dvmGetException(_self);
2086 localException = addLocalReference(env, exception);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002087 if (localException == NULL && exception != NULL) {
2088 /*
2089 * We were unable to add a new local reference, and threw a new
2090 * exception. We can't return "exception", because it's not a
2091 * local reference. So we have to return NULL, indicating that
2092 * there was no exception, even though it's pretty much raining
2093 * exceptions in here.
2094 */
2095 LOGW("JNI WARNING: addLocal/exception combo\n");
2096 }
2097
2098 JNI_EXIT();
2099 return localException;
2100}
2101
2102/*
2103 * Print an exception and stack trace to stderr.
2104 */
2105static void ExceptionDescribe(JNIEnv* env)
2106{
2107 JNI_ENTER();
2108
2109 Object* exception = dvmGetException(_self);
2110 if (exception != NULL) {
2111 dvmPrintExceptionStackTrace();
2112 } else {
2113 LOGI("Odd: ExceptionDescribe called, but no exception pending\n");
2114 }
2115
2116 JNI_EXIT();
2117}
2118
2119/*
2120 * Clear the exception currently being thrown.
2121 *
2122 * TODO: we should be able to skip the enter/exit stuff.
2123 */
2124static void ExceptionClear(JNIEnv* env)
2125{
2126 JNI_ENTER();
2127 dvmClearException(_self);
2128 JNI_EXIT();
2129}
2130
2131/*
2132 * Kill the VM. This function does not return.
2133 */
2134static void FatalError(JNIEnv* env, const char* msg)
2135{
2136 //dvmChangeStatus(NULL, THREAD_RUNNING);
2137 LOGE("JNI posting fatal error: %s\n", msg);
2138 dvmAbort();
2139}
2140
2141/*
2142 * Push a new JNI frame on the stack, with a new set of locals.
2143 *
2144 * The new frame must have the same method pointer. (If for no other
2145 * reason than FindClass needs it to get the appropriate class loader.)
2146 */
2147static jint PushLocalFrame(JNIEnv* env, jint capacity)
2148{
2149 JNI_ENTER();
2150 int result = JNI_OK;
Andy McFaddenab00d452009-08-19 07:21:41 -07002151 if (!ensureLocalCapacity(env, capacity) ||
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002152 !dvmPushLocalFrame(_self /*dvmThreadSelf()*/, dvmGetCurrentJNIMethod()))
2153 {
2154 /* yes, OutOfMemoryError, not StackOverflowError */
2155 dvmClearException(_self);
2156 dvmThrowException("Ljava/lang/OutOfMemoryError;",
2157 "out of stack in JNI PushLocalFrame");
2158 result = JNI_ERR;
2159 }
2160 JNI_EXIT();
2161 return result;
2162}
2163
2164/*
2165 * Pop the local frame off. If "result" is not null, add it as a
2166 * local reference on the now-current frame.
2167 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002168static jobject PopLocalFrame(JNIEnv* env, jobject jresult)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002169{
2170 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002171 Object* result = dvmDecodeIndirectRef(env, jresult);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002172 if (!dvmPopLocalFrame(_self /*dvmThreadSelf()*/)) {
2173 LOGW("JNI WARNING: too many PopLocalFrame calls\n");
2174 dvmClearException(_self);
2175 dvmThrowException("Ljava/lang/RuntimeException;",
2176 "too many PopLocalFrame calls");
2177 }
Andy McFaddenab00d452009-08-19 07:21:41 -07002178 jresult = addLocalReference(env, result);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002179 JNI_EXIT();
2180 return result;
2181}
2182
2183/*
2184 * Add a reference to the global list.
2185 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002186static jobject NewGlobalRef(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002187{
Andy McFaddenb18992f2009-09-25 10:42:15 -07002188 Object* obj;
2189
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002190 JNI_ENTER();
Andy McFaddenb18992f2009-09-25 10:42:15 -07002191 if (dvmIsWeakGlobalRef(jobj))
2192 obj = getPhantomReferent(env, (jweak) jobj);
2193 else
2194 obj = dvmDecodeIndirectRef(env, jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002195 jobject retval = addGlobalReference(obj);
2196 JNI_EXIT();
2197 return retval;
2198}
2199
2200/*
2201 * Delete a reference from the global list.
2202 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002203static void DeleteGlobalRef(JNIEnv* env, jobject jglobalRef)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002204{
2205 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002206 deleteGlobalReference(jglobalRef);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002207 JNI_EXIT();
2208}
2209
2210
2211/*
2212 * Add a reference to the local list.
2213 */
Andy McFaddenb18992f2009-09-25 10:42:15 -07002214static jobject NewLocalRef(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002215{
Andy McFaddenb18992f2009-09-25 10:42:15 -07002216 Object* obj;
2217
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002218 JNI_ENTER();
Andy McFaddenb18992f2009-09-25 10:42:15 -07002219 if (dvmIsWeakGlobalRef(jobj))
2220 obj = getPhantomReferent(env, (jweak) jobj);
2221 else
2222 obj = dvmDecodeIndirectRef(env, jobj);
Andy McFaddenab00d452009-08-19 07:21:41 -07002223 jobject retval = addLocalReference(env, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002224 JNI_EXIT();
2225 return retval;
2226}
2227
2228/*
2229 * Delete a reference from the local list.
2230 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002231static void DeleteLocalRef(JNIEnv* env, jobject jlocalRef)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002232{
2233 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002234 deleteLocalReference(env, jlocalRef);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002235 JNI_EXIT();
2236}
2237
2238/*
2239 * Ensure that the local references table can hold at least this many
2240 * references.
2241 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002242static jint EnsureLocalCapacity(JNIEnv* env, jint capacity)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002243{
2244 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002245 bool okay = ensureLocalCapacity(env, capacity);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002246 if (!okay) {
2247 dvmThrowException("Ljava/lang/OutOfMemoryError;",
2248 "can't ensure local reference capacity");
2249 }
2250 JNI_EXIT();
2251 if (okay)
2252 return 0;
2253 else
2254 return -1;
2255}
2256
2257
2258/*
2259 * Determine whether two Object references refer to the same underlying object.
2260 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002261static jboolean IsSameObject(JNIEnv* env, jobject jref1, jobject jref2)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002262{
2263 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002264 Object* obj1 = dvmDecodeIndirectRef(env, jref1);
2265 Object* obj2 = dvmDecodeIndirectRef(env, jref2);
2266 jboolean result = (obj1 == obj2);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002267 JNI_EXIT();
2268 return result;
2269}
2270
2271/*
2272 * Allocate a new object without invoking any constructors.
2273 */
2274static jobject AllocObject(JNIEnv* env, jclass jclazz)
2275{
2276 JNI_ENTER();
2277
Andy McFaddenab00d452009-08-19 07:21:41 -07002278 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2279 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002280
2281 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2282 assert(dvmCheckException(_self));
Andy McFaddenab00d452009-08-19 07:21:41 -07002283 result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002284 } else {
Andy McFaddenab00d452009-08-19 07:21:41 -07002285 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
2286 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002287 }
2288
2289 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002290 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002291}
2292
2293/*
Andy McFaddenab00d452009-08-19 07:21:41 -07002294 * Allocate a new object and invoke the supplied constructor.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002295 */
2296static jobject NewObject(JNIEnv* env, jclass jclazz, jmethodID methodID, ...)
2297{
2298 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002299 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2300 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002301
2302 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2303 assert(dvmCheckException(_self));
Andy McFaddenab00d452009-08-19 07:21:41 -07002304 result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002305 } else {
Andy McFaddenab00d452009-08-19 07:21:41 -07002306 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
2307 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002308 if (newObj != NULL) {
2309 JValue unused;
2310 va_list args;
2311 va_start(args, methodID);
Andy McFaddend5ab7262009-08-25 07:19:34 -07002312 dvmCallMethodV(_self, (Method*) methodID, newObj, true, &unused,
2313 args);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002314 va_end(args);
2315 }
2316 }
2317
2318 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002319 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002320}
Andy McFaddenab00d452009-08-19 07:21:41 -07002321static jobject NewObjectV(JNIEnv* env, jclass jclazz, jmethodID methodID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002322 va_list args)
2323{
2324 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002325 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2326 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002327
Andy McFaddenab00d452009-08-19 07:21:41 -07002328 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
2329 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002330 if (newObj != NULL) {
2331 JValue unused;
Andy McFaddend5ab7262009-08-25 07:19:34 -07002332 dvmCallMethodV(_self, (Method*) methodID, newObj, true, &unused, args);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002333 }
2334
2335 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002336 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002337}
Andy McFaddenab00d452009-08-19 07:21:41 -07002338static jobject NewObjectA(JNIEnv* env, jclass jclazz, jmethodID methodID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002339 jvalue* args)
2340{
2341 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002342 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2343 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002344
Andy McFaddenab00d452009-08-19 07:21:41 -07002345 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
2346 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002347 if (newObj != NULL) {
2348 JValue unused;
Andy McFaddend5ab7262009-08-25 07:19:34 -07002349 dvmCallMethodA(_self, (Method*) methodID, newObj, true, &unused, args);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002350 }
2351
2352 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002353 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002354}
2355
2356/*
2357 * Returns the class of an object.
2358 *
2359 * JNI spec says: obj must not be NULL.
2360 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002361static jclass GetObjectClass(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002362{
2363 JNI_ENTER();
2364
Andy McFaddenab00d452009-08-19 07:21:41 -07002365 assert(jobj != NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002366
Andy McFaddenab00d452009-08-19 07:21:41 -07002367 Object* obj = dvmDecodeIndirectRef(env, jobj);
2368 jclass jclazz = addLocalReference(env, (Object*) obj->clazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002369
2370 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002371 return jclazz;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002372}
2373
2374/*
2375 * Determine whether "obj" is an instance of "clazz".
2376 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002377static jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass jclazz)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002378{
2379 JNI_ENTER();
2380
Andy McFaddenab00d452009-08-19 07:21:41 -07002381 assert(jclazz != NULL);
2382
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002383 jboolean result;
2384
Andy McFaddenab00d452009-08-19 07:21:41 -07002385 if (jobj == NULL) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002386 result = true;
Andy McFaddenab00d452009-08-19 07:21:41 -07002387 } else {
2388 Object* obj = dvmDecodeIndirectRef(env, jobj);
2389 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2390 result = dvmInstanceof(obj->clazz, clazz);
2391 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002392
2393 JNI_EXIT();
2394 return result;
2395}
2396
2397/*
2398 * Get a method ID for an instance method.
2399 *
2400 * JNI defines <init> as an instance method, but Dalvik considers it a
2401 * "direct" method, so we have to special-case it here.
2402 *
2403 * Dalvik also puts all private methods into the "direct" list, so we
2404 * really need to just search both lists.
2405 */
2406static jmethodID GetMethodID(JNIEnv* env, jclass jclazz, const char* name,
2407 const char* sig)
2408{
2409 JNI_ENTER();
2410
Andy McFaddenab00d452009-08-19 07:21:41 -07002411 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002412 jmethodID id = NULL;
2413
2414 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2415 assert(dvmCheckException(_self));
2416 } else {
2417 Method* meth;
2418
2419 meth = dvmFindVirtualMethodHierByDescriptor(clazz, name, sig);
2420 if (meth == NULL) {
2421 /* search private methods and constructors; non-hierarchical */
2422 meth = dvmFindDirectMethodByDescriptor(clazz, name, sig);
2423 }
2424 if (meth != NULL && dvmIsStaticMethod(meth)) {
2425 IF_LOGD() {
2426 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
2427 LOGD("GetMethodID: not returning static method %s.%s %s\n",
2428 clazz->descriptor, meth->name, desc);
2429 free(desc);
2430 }
2431 meth = NULL;
2432 }
2433 if (meth == NULL) {
Andy McFadden03bd0d52009-08-18 15:32:27 -07002434 LOGD("GetMethodID: method not found: %s.%s:%s\n",
2435 clazz->descriptor, name, sig);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002436 dvmThrowException("Ljava/lang/NoSuchMethodError;", name);
2437 }
2438
2439 /*
2440 * The method's class may not be the same as clazz, but if
2441 * it isn't this must be a virtual method and the class must
2442 * be a superclass (and, hence, already initialized).
2443 */
2444 if (meth != NULL) {
2445 assert(dvmIsClassInitialized(meth->clazz) ||
2446 dvmIsClassInitializing(meth->clazz));
2447 }
2448 id = (jmethodID) meth;
2449 }
2450 JNI_EXIT();
2451 return id;
2452}
2453
2454/*
2455 * Get a field ID (instance fields).
2456 */
2457static jfieldID GetFieldID(JNIEnv* env, jclass jclazz,
2458 const char* name, const char* sig)
2459{
2460 JNI_ENTER();
2461
Andy McFaddenab00d452009-08-19 07:21:41 -07002462 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002463 jfieldID id;
2464
2465 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2466 assert(dvmCheckException(_self));
2467 id = NULL;
2468 } else {
2469 id = (jfieldID) dvmFindInstanceFieldHier(clazz, name, sig);
Andy McFadden03bd0d52009-08-18 15:32:27 -07002470 if (id == NULL) {
2471 LOGD("GetFieldID: unable to find field %s.%s:%s\n",
2472 clazz->descriptor, name, sig);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002473 dvmThrowException("Ljava/lang/NoSuchFieldError;", name);
Andy McFadden03bd0d52009-08-18 15:32:27 -07002474 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002475 }
2476 JNI_EXIT();
2477 return id;
2478}
2479
2480/*
2481 * Get the method ID for a static method in a class.
2482 */
2483static jmethodID GetStaticMethodID(JNIEnv* env, jclass jclazz,
2484 const char* name, const char* sig)
2485{
2486 JNI_ENTER();
2487
Andy McFaddenab00d452009-08-19 07:21:41 -07002488 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002489 jmethodID id;
2490
2491 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2492 assert(dvmCheckException(_self));
2493 id = NULL;
2494 } else {
2495 Method* meth;
2496
2497 meth = dvmFindDirectMethodHierByDescriptor(clazz, name, sig);
2498
2499 /* make sure it's static, not virtual+private */
2500 if (meth != NULL && !dvmIsStaticMethod(meth)) {
2501 IF_LOGD() {
2502 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
2503 LOGD("GetStaticMethodID: "
2504 "not returning nonstatic method %s.%s %s\n",
2505 clazz->descriptor, meth->name, desc);
2506 free(desc);
2507 }
2508 meth = NULL;
2509 }
2510
2511 id = (jmethodID) meth;
2512 if (id == NULL)
2513 dvmThrowException("Ljava/lang/NoSuchMethodError;", name);
2514 }
2515
2516 JNI_EXIT();
2517 return id;
2518}
2519
2520/*
2521 * Get a field ID (static fields).
2522 */
2523static jfieldID GetStaticFieldID(JNIEnv* env, jclass jclazz,
2524 const char* name, const char* sig)
2525{
2526 JNI_ENTER();
2527
Andy McFaddenab00d452009-08-19 07:21:41 -07002528 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002529 jfieldID id;
2530
2531 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2532 assert(dvmCheckException(_self));
2533 id = NULL;
2534 } else {
2535 id = (jfieldID) dvmFindStaticField(clazz, name, sig);
2536 if (id == NULL)
2537 dvmThrowException("Ljava/lang/NoSuchFieldError;", name);
2538 }
2539 JNI_EXIT();
2540 return id;
2541}
2542
2543/*
2544 * Get a static field.
2545 *
2546 * If we get an object reference, add it to the local refs list.
2547 */
2548#define GET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002549 static _ctype GetStatic##_jname##Field(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002550 jfieldID fieldID) \
2551 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002552 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002553 JNI_ENTER(); \
2554 StaticField* sfield = (StaticField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002555 _ctype value; \
2556 if (_isref) { /* only when _ctype==jobject */ \
2557 Object* obj = dvmGetStaticFieldObject(sfield); \
2558 value = (_ctype)(u4)addLocalReference(env, obj); \
2559 } else { \
2560 value = dvmGetStaticField##_jname(sfield); \
2561 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002562 JNI_EXIT(); \
2563 return value; \
2564 }
2565GET_STATIC_TYPE_FIELD(jobject, Object, true);
2566GET_STATIC_TYPE_FIELD(jboolean, Boolean, false);
2567GET_STATIC_TYPE_FIELD(jbyte, Byte, false);
2568GET_STATIC_TYPE_FIELD(jchar, Char, false);
2569GET_STATIC_TYPE_FIELD(jshort, Short, false);
2570GET_STATIC_TYPE_FIELD(jint, Int, false);
2571GET_STATIC_TYPE_FIELD(jlong, Long, false);
2572GET_STATIC_TYPE_FIELD(jfloat, Float, false);
2573GET_STATIC_TYPE_FIELD(jdouble, Double, false);
2574
2575/*
2576 * Set a static field.
2577 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002578#define SET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) \
2579 static void SetStatic##_jname##Field(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002580 jfieldID fieldID, _ctype value) \
2581 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002582 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002583 JNI_ENTER(); \
2584 StaticField* sfield = (StaticField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002585 if (_isref) { /* only when _ctype==jobject */ \
2586 Object* valObj = dvmDecodeIndirectRef(env, (jobject)(u4)value); \
2587 dvmSetStaticFieldObject(sfield, valObj); \
2588 } else { \
2589 dvmSetStaticField##_jname(sfield, value); \
2590 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002591 JNI_EXIT(); \
2592 }
Andy McFaddenab00d452009-08-19 07:21:41 -07002593SET_STATIC_TYPE_FIELD(jobject, Object, true);
2594SET_STATIC_TYPE_FIELD(jboolean, Boolean, false);
2595SET_STATIC_TYPE_FIELD(jbyte, Byte, false);
2596SET_STATIC_TYPE_FIELD(jchar, Char, false);
2597SET_STATIC_TYPE_FIELD(jshort, Short, false);
2598SET_STATIC_TYPE_FIELD(jint, Int, false);
2599SET_STATIC_TYPE_FIELD(jlong, Long, false);
2600SET_STATIC_TYPE_FIELD(jfloat, Float, false);
2601SET_STATIC_TYPE_FIELD(jdouble, Double, false);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002602
2603/*
2604 * Get an instance field.
2605 *
2606 * If we get an object reference, add it to the local refs list.
2607 */
2608#define GET_TYPE_FIELD(_ctype, _jname, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002609 static _ctype Get##_jname##Field(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002610 jfieldID fieldID) \
2611 { \
2612 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002613 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002614 InstField* field = (InstField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002615 _ctype value; \
2616 if (_isref) { /* only when _ctype==jobject */ \
2617 Object* valObj = dvmGetFieldObject(obj, field->byteOffset); \
2618 value = (_ctype)(u4)addLocalReference(env, valObj); \
2619 } else { \
2620 value = dvmGetField##_jname(obj, field->byteOffset); \
2621 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002622 JNI_EXIT(); \
2623 return value; \
2624 }
2625GET_TYPE_FIELD(jobject, Object, true);
2626GET_TYPE_FIELD(jboolean, Boolean, false);
2627GET_TYPE_FIELD(jbyte, Byte, false);
2628GET_TYPE_FIELD(jchar, Char, false);
2629GET_TYPE_FIELD(jshort, Short, false);
2630GET_TYPE_FIELD(jint, Int, false);
2631GET_TYPE_FIELD(jlong, Long, false);
2632GET_TYPE_FIELD(jfloat, Float, false);
2633GET_TYPE_FIELD(jdouble, Double, false);
2634
2635/*
2636 * Set an instance field.
2637 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002638#define SET_TYPE_FIELD(_ctype, _jname, _isref) \
2639 static void Set##_jname##Field(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002640 jfieldID fieldID, _ctype value) \
2641 { \
2642 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002643 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002644 InstField* field = (InstField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002645 if (_isref) { /* only when _ctype==jobject */ \
2646 Object* valObj = dvmDecodeIndirectRef(env, (jobject)(u4)value); \
2647 dvmSetFieldObject(obj, field->byteOffset, valObj); \
2648 } else { \
2649 dvmSetField##_jname(obj, field->byteOffset, value); \
2650 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002651 JNI_EXIT(); \
2652 }
Andy McFaddenab00d452009-08-19 07:21:41 -07002653SET_TYPE_FIELD(jobject, Object, true);
2654SET_TYPE_FIELD(jboolean, Boolean, false);
2655SET_TYPE_FIELD(jbyte, Byte, false);
2656SET_TYPE_FIELD(jchar, Char, false);
2657SET_TYPE_FIELD(jshort, Short, false);
2658SET_TYPE_FIELD(jint, Int, false);
2659SET_TYPE_FIELD(jlong, Long, false);
2660SET_TYPE_FIELD(jfloat, Float, false);
2661SET_TYPE_FIELD(jdouble, Double, false);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002662
2663/*
2664 * Make a virtual method call.
2665 *
2666 * Three versions (..., va_list, jvalue[]) for each return type. If we're
2667 * returning an Object, we have to add it to the local references table.
2668 */
2669#define CALL_VIRTUAL(_ctype, _jname, _retfail, _retok, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002670 static _ctype Call##_jname##Method(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002671 jmethodID methodID, ...) \
2672 { \
2673 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002674 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002675 const Method* meth; \
2676 va_list args; \
2677 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002678 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002679 if (meth == NULL) { \
2680 JNI_EXIT(); \
2681 return _retfail; \
2682 } \
2683 va_start(args, methodID); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002684 dvmCallMethodV(_self, meth, obj, true, &result, args); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002685 va_end(args); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002686 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002687 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002688 JNI_EXIT(); \
2689 return _retok; \
2690 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002691 static _ctype Call##_jname##MethodV(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002692 jmethodID methodID, va_list args) \
2693 { \
2694 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002695 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002696 const Method* meth; \
2697 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002698 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002699 if (meth == NULL) { \
2700 JNI_EXIT(); \
2701 return _retfail; \
2702 } \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002703 dvmCallMethodV(_self, meth, obj, true, &result, args); \
2704 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002705 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002706 JNI_EXIT(); \
2707 return _retok; \
2708 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002709 static _ctype Call##_jname##MethodA(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002710 jmethodID methodID, jvalue* args) \
2711 { \
2712 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002713 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002714 const Method* meth; \
2715 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002716 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002717 if (meth == NULL) { \
2718 JNI_EXIT(); \
2719 return _retfail; \
2720 } \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002721 dvmCallMethodA(_self, meth, obj, true, &result, args); \
2722 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002723 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002724 JNI_EXIT(); \
2725 return _retok; \
2726 }
2727CALL_VIRTUAL(jobject, Object, NULL, result.l, true);
2728CALL_VIRTUAL(jboolean, Boolean, 0, result.z, false);
2729CALL_VIRTUAL(jbyte, Byte, 0, result.b, false);
2730CALL_VIRTUAL(jchar, Char, 0, result.c, false);
2731CALL_VIRTUAL(jshort, Short, 0, result.s, false);
2732CALL_VIRTUAL(jint, Int, 0, result.i, false);
2733CALL_VIRTUAL(jlong, Long, 0, result.j, false);
2734CALL_VIRTUAL(jfloat, Float, 0.0f, result.f, false);
2735CALL_VIRTUAL(jdouble, Double, 0.0, result.d, false);
2736CALL_VIRTUAL(void, Void, , , false);
2737
2738/*
2739 * Make a "non-virtual" method call. We're still calling a virtual method,
2740 * but this time we're not doing an indirection through the object's vtable.
2741 * The "clazz" parameter defines which implementation of a method we want.
2742 *
2743 * Three versions (..., va_list, jvalue[]) for each return type.
2744 */
2745#define CALL_NONVIRTUAL(_ctype, _jname, _retfail, _retok, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002746 static _ctype CallNonvirtual##_jname##Method(JNIEnv* env, jobject jobj, \
2747 jclass jclazz, jmethodID methodID, ...) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002748 { \
2749 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002750 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2751 ClassObject* clazz = \
2752 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002753 const Method* meth; \
2754 va_list args; \
2755 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002756 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002757 if (meth == NULL) { \
2758 JNI_EXIT(); \
2759 return _retfail; \
2760 } \
2761 va_start(args, methodID); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002762 dvmCallMethodV(_self, meth, obj, true, &result, args); \
2763 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002764 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002765 va_end(args); \
2766 JNI_EXIT(); \
2767 return _retok; \
2768 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002769 static _ctype CallNonvirtual##_jname##MethodV(JNIEnv* env, jobject jobj,\
2770 jclass jclazz, jmethodID methodID, va_list args) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002771 { \
2772 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002773 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2774 ClassObject* clazz = \
2775 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002776 const Method* meth; \
2777 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002778 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002779 if (meth == NULL) { \
2780 JNI_EXIT(); \
2781 return _retfail; \
2782 } \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002783 dvmCallMethodV(_self, meth, obj, true, &result, args); \
2784 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002785 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002786 JNI_EXIT(); \
2787 return _retok; \
2788 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002789 static _ctype CallNonvirtual##_jname##MethodA(JNIEnv* env, jobject jobj,\
2790 jclass jclazz, jmethodID methodID, jvalue* args) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002791 { \
2792 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002793 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2794 ClassObject* clazz = \
2795 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002796 const Method* meth; \
2797 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002798 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002799 if (meth == NULL) { \
2800 JNI_EXIT(); \
2801 return _retfail; \
2802 } \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002803 dvmCallMethodA(_self, meth, obj, true, &result, args); \
2804 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002805 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002806 JNI_EXIT(); \
2807 return _retok; \
2808 }
2809CALL_NONVIRTUAL(jobject, Object, NULL, result.l, true);
2810CALL_NONVIRTUAL(jboolean, Boolean, 0, result.z, false);
2811CALL_NONVIRTUAL(jbyte, Byte, 0, result.b, false);
2812CALL_NONVIRTUAL(jchar, Char, 0, result.c, false);
2813CALL_NONVIRTUAL(jshort, Short, 0, result.s, false);
2814CALL_NONVIRTUAL(jint, Int, 0, result.i, false);
2815CALL_NONVIRTUAL(jlong, Long, 0, result.j, false);
2816CALL_NONVIRTUAL(jfloat, Float, 0.0f, result.f, false);
2817CALL_NONVIRTUAL(jdouble, Double, 0.0, result.d, false);
2818CALL_NONVIRTUAL(void, Void, , , false);
2819
2820
2821/*
2822 * Call a static method.
2823 */
2824#define CALL_STATIC(_ctype, _jname, _retfail, _retok, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002825 static _ctype CallStatic##_jname##Method(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002826 jmethodID methodID, ...) \
2827 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002828 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002829 JNI_ENTER(); \
2830 JValue result; \
2831 va_list args; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002832 va_start(args, methodID); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002833 dvmCallMethodV(_self, (Method*)methodID, NULL, true, &result, args);\
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002834 va_end(args); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002835 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002836 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002837 JNI_EXIT(); \
2838 return _retok; \
2839 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002840 static _ctype CallStatic##_jname##MethodV(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002841 jmethodID methodID, va_list args) \
2842 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002843 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002844 JNI_ENTER(); \
2845 JValue result; \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002846 dvmCallMethodV(_self, (Method*)methodID, NULL, true, &result, args);\
2847 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002848 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002849 JNI_EXIT(); \
2850 return _retok; \
2851 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002852 static _ctype CallStatic##_jname##MethodA(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002853 jmethodID methodID, jvalue* args) \
2854 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002855 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002856 JNI_ENTER(); \
2857 JValue result; \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002858 dvmCallMethodA(_self, (Method*)methodID, NULL, true, &result, args);\
2859 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002860 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002861 JNI_EXIT(); \
2862 return _retok; \
2863 }
2864CALL_STATIC(jobject, Object, NULL, result.l, true);
2865CALL_STATIC(jboolean, Boolean, 0, result.z, false);
2866CALL_STATIC(jbyte, Byte, 0, result.b, false);
2867CALL_STATIC(jchar, Char, 0, result.c, false);
2868CALL_STATIC(jshort, Short, 0, result.s, false);
2869CALL_STATIC(jint, Int, 0, result.i, false);
2870CALL_STATIC(jlong, Long, 0, result.j, false);
2871CALL_STATIC(jfloat, Float, 0.0f, result.f, false);
2872CALL_STATIC(jdouble, Double, 0.0, result.d, false);
2873CALL_STATIC(void, Void, , , false);
2874
2875/*
2876 * Create a new String from Unicode data.
2877 *
2878 * If "len" is zero, we will return an empty string even if "unicodeChars"
2879 * is NULL. (The JNI spec is vague here.)
2880 */
2881static jstring NewString(JNIEnv* env, const jchar* unicodeChars, jsize len)
2882{
2883 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002884 jobject retval;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002885
Andy McFaddenab00d452009-08-19 07:21:41 -07002886 StringObject* jstr = dvmCreateStringFromUnicode(unicodeChars, len);
2887 if (jstr == NULL) {
2888 retval = NULL;
2889 } else {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002890 dvmReleaseTrackedAlloc((Object*) jstr, NULL);
Andy McFaddenab00d452009-08-19 07:21:41 -07002891 retval = addLocalReference(env, (Object*) jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002892 }
2893
2894 JNI_EXIT();
Andy McFadden0423f0e2009-08-26 07:21:53 -07002895 return retval;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002896}
2897
2898/*
2899 * Return the length of a String in Unicode character units.
2900 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002901static jsize GetStringLength(JNIEnv* env, jstring jstr)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002902{
2903 JNI_ENTER();
2904
Andy McFaddenab00d452009-08-19 07:21:41 -07002905 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2906 jsize len = dvmStringLen(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002907
2908 JNI_EXIT();
2909 return len;
2910}
2911
Andy McFaddenab00d452009-08-19 07:21:41 -07002912
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002913/*
Andy McFaddenab00d452009-08-19 07:21:41 -07002914 * Get a string's character data.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002915 *
2916 * The result is guaranteed to be valid until ReleaseStringChars is
Andy McFaddenab00d452009-08-19 07:21:41 -07002917 * called, which means we have to pin it or return a copy.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002918 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002919static const jchar* GetStringChars(JNIEnv* env, jstring jstr, jboolean* isCopy)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002920{
2921 JNI_ENTER();
2922
Andy McFaddenab00d452009-08-19 07:21:41 -07002923 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
Andy McFaddend5ab7262009-08-25 07:19:34 -07002924 ArrayObject* strChars = dvmStringCharArray(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002925
Andy McFaddenab00d452009-08-19 07:21:41 -07002926 pinPrimitiveArray(strChars);
2927
2928 const u2* data = dvmStringChars(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002929 if (isCopy != NULL)
2930 *isCopy = JNI_FALSE;
2931
2932 JNI_EXIT();
2933 return (jchar*)data;
2934}
2935
2936/*
2937 * Release our grip on some characters from a string.
2938 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002939static void ReleaseStringChars(JNIEnv* env, jstring jstr, const jchar* chars)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002940{
2941 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002942 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
Andy McFaddend5ab7262009-08-25 07:19:34 -07002943 ArrayObject* strChars = dvmStringCharArray(strObj);
Andy McFaddenab00d452009-08-19 07:21:41 -07002944 unpinPrimitiveArray(strChars);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002945 JNI_EXIT();
2946}
2947
2948/*
2949 * Create a new java.lang.String object from chars in modified UTF-8 form.
2950 *
2951 * The spec doesn't say how to handle a NULL string. Popular desktop VMs
2952 * accept it and return a NULL pointer in response.
2953 */
2954static jstring NewStringUTF(JNIEnv* env, const char* bytes)
2955{
2956 JNI_ENTER();
2957
Andy McFaddenab00d452009-08-19 07:21:41 -07002958 jstring result;
2959
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002960 if (bytes == NULL) {
Andy McFaddenab00d452009-08-19 07:21:41 -07002961 result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002962 } else {
Andy McFaddenab00d452009-08-19 07:21:41 -07002963 /* note newStr could come back NULL on OOM */
2964 StringObject* newStr = dvmCreateStringFromCstr(bytes, ALLOC_DEFAULT);
2965 result = addLocalReference(env, (Object*) newStr);
2966 dvmReleaseTrackedAlloc((Object*)newStr, NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002967 }
2968
2969 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002970 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002971}
2972
2973/*
2974 * Return the length in bytes of the modified UTF-8 form of the string.
2975 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002976static jsize GetStringUTFLength(JNIEnv* env, jstring jstr)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002977{
2978 JNI_ENTER();
2979
Andy McFaddenab00d452009-08-19 07:21:41 -07002980 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2981 jsize len = dvmStringUtf8ByteLen(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002982
2983 JNI_EXIT();
2984 return len;
2985}
2986
2987/*
2988 * Convert "string" to modified UTF-8 and return a pointer. The returned
2989 * value must be released with ReleaseStringUTFChars.
2990 *
2991 * According to the JNI reference, "Returns a pointer to a UTF-8 string,
2992 * or NULL if the operation fails. Returns NULL if and only if an invocation
2993 * of this function has thrown an exception."
2994 *
2995 * The behavior here currently follows that of other open-source VMs, which
2996 * quietly return NULL if "string" is NULL. We should consider throwing an
2997 * NPE. (The CheckJNI code blows up if you try to pass in a NULL string,
2998 * which should catch this sort of thing during development.) Certain other
2999 * VMs will crash with a segmentation fault.
3000 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003001static const char* GetStringUTFChars(JNIEnv* env, jstring jstr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003002 jboolean* isCopy)
3003{
3004 JNI_ENTER();
3005 char* newStr;
3006
Andy McFaddenab00d452009-08-19 07:21:41 -07003007 if (jstr == NULL) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003008 /* this shouldn't happen; throw NPE? */
3009 newStr = NULL;
3010 } else {
3011 if (isCopy != NULL)
3012 *isCopy = JNI_TRUE;
3013
Andy McFaddenab00d452009-08-19 07:21:41 -07003014 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
3015 newStr = dvmCreateCstrFromString(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003016 if (newStr == NULL) {
3017 /* assume memory failure */
3018 dvmThrowException("Ljava/lang/OutOfMemoryError;",
3019 "native heap string alloc failed");
3020 }
3021 }
3022
3023 JNI_EXIT();
3024 return newStr;
3025}
3026
3027/*
3028 * Release a string created by GetStringUTFChars().
3029 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003030static void ReleaseStringUTFChars(JNIEnv* env, jstring jstr, const char* utf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003031{
3032 JNI_ENTER();
3033 free((char*)utf);
3034 JNI_EXIT();
3035}
3036
3037/*
3038 * Return the capacity of the array.
3039 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003040static jsize GetArrayLength(JNIEnv* env, jarray jarr)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003041{
3042 JNI_ENTER();
3043
Andy McFaddenab00d452009-08-19 07:21:41 -07003044 ArrayObject* arrObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
3045 jsize length = arrObj->length;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003046
3047 JNI_EXIT();
3048 return length;
3049}
3050
3051/*
3052 * Construct a new array that holds objects from class "elementClass".
3053 */
3054static jobjectArray NewObjectArray(JNIEnv* env, jsize length,
Andy McFaddenab00d452009-08-19 07:21:41 -07003055 jclass jelementClass, jobject jinitialElement)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003056{
3057 JNI_ENTER();
3058
Andy McFaddenab00d452009-08-19 07:21:41 -07003059 jobjectArray newArray = NULL;
3060 ClassObject* elemClassObj =
3061 (ClassObject*) dvmDecodeIndirectRef(env, jelementClass);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003062
3063 if (elemClassObj == NULL) {
3064 dvmThrowException("Ljava/lang/NullPointerException;",
3065 "JNI NewObjectArray");
3066 goto bail;
3067 }
3068
Andy McFaddenab00d452009-08-19 07:21:41 -07003069 ArrayObject* newObj =
3070 dvmAllocObjectArray(elemClassObj, length, ALLOC_DEFAULT);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003071 if (newObj == NULL) {
3072 assert(dvmCheckException(_self));
3073 goto bail;
3074 }
Andy McFaddenab00d452009-08-19 07:21:41 -07003075 newArray = addLocalReference(env, (Object*) newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003076 dvmReleaseTrackedAlloc((Object*) newObj, NULL);
3077
3078 /*
3079 * Initialize the array. Trashes "length".
3080 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003081 if (jinitialElement != NULL) {
3082 Object* initialElement = dvmDecodeIndirectRef(env, jinitialElement);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003083 Object** arrayData = (Object**) newObj->contents;
3084
3085 while (length--)
Andy McFaddenab00d452009-08-19 07:21:41 -07003086 *arrayData++ = initialElement;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003087 }
3088
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003089
3090bail:
3091 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07003092 return newArray;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003093}
3094
3095/*
3096 * Get one element of an Object array.
3097 *
3098 * Add the object to the local references table in case the array goes away.
3099 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003100static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray jarr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003101 jsize index)
3102{
3103 JNI_ENTER();
3104
Andy McFaddenab00d452009-08-19 07:21:41 -07003105 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
3106 jobject retval = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003107
Andy McFaddenab00d452009-08-19 07:21:41 -07003108 assert(arrayObj != NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003109
3110 /* check the array bounds */
3111 if (index < 0 || index >= (int) arrayObj->length) {
3112 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
3113 arrayObj->obj.clazz->descriptor);
3114 goto bail;
3115 }
3116
Andy McFaddenab00d452009-08-19 07:21:41 -07003117 Object* value = ((Object**) arrayObj->contents)[index];
3118 retval = addLocalReference(env, value);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003119
3120bail:
3121 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07003122 return retval;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003123}
3124
3125/*
3126 * Set one element of an Object array.
3127 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003128static void SetObjectArrayElement(JNIEnv* env, jobjectArray jarr,
3129 jsize index, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003130{
3131 JNI_ENTER();
3132
Andy McFaddenab00d452009-08-19 07:21:41 -07003133 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003134
Andy McFaddenab00d452009-08-19 07:21:41 -07003135 assert(arrayObj != NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003136
3137 /* check the array bounds */
3138 if (index < 0 || index >= (int) arrayObj->length) {
3139 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
3140 arrayObj->obj.clazz->descriptor);
3141 goto bail;
3142 }
3143
3144 //LOGV("JNI: set element %d in array %p to %p\n", index, array, value);
3145
Andy McFadden0423f0e2009-08-26 07:21:53 -07003146 Object* obj = dvmDecodeIndirectRef(env, jobj);
Andy McFaddenab00d452009-08-19 07:21:41 -07003147 ((Object**) arrayObj->contents)[index] = obj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003148
3149bail:
3150 JNI_EXIT();
3151}
3152
3153/*
3154 * Create a new array of primitive elements.
3155 */
3156#define NEW_PRIMITIVE_ARRAY(_artype, _jname, _typechar) \
3157 static _artype New##_jname##Array(JNIEnv* env, jsize length) \
3158 { \
3159 JNI_ENTER(); \
3160 ArrayObject* arrayObj; \
3161 arrayObj = dvmAllocPrimitiveArray(_typechar, length, \
3162 ALLOC_DEFAULT); \
Andy McFaddenab00d452009-08-19 07:21:41 -07003163 jarray jarr = NULL; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003164 if (arrayObj != NULL) { \
Andy McFaddenab00d452009-08-19 07:21:41 -07003165 jarr = addLocalReference(env, (Object*) arrayObj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003166 dvmReleaseTrackedAlloc((Object*) arrayObj, NULL); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003167 } \
3168 JNI_EXIT(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07003169 return (_artype)jarr; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003170 }
3171NEW_PRIMITIVE_ARRAY(jbooleanArray, Boolean, 'Z');
3172NEW_PRIMITIVE_ARRAY(jbyteArray, Byte, 'B');
3173NEW_PRIMITIVE_ARRAY(jcharArray, Char, 'C');
3174NEW_PRIMITIVE_ARRAY(jshortArray, Short, 'S');
3175NEW_PRIMITIVE_ARRAY(jintArray, Int, 'I');
3176NEW_PRIMITIVE_ARRAY(jlongArray, Long, 'J');
3177NEW_PRIMITIVE_ARRAY(jfloatArray, Float, 'F');
3178NEW_PRIMITIVE_ARRAY(jdoubleArray, Double, 'D');
3179
3180/*
3181 * Get a pointer to a C array of primitive elements from an array object
3182 * of the matching type.
3183 *
Andy McFaddenab00d452009-08-19 07:21:41 -07003184 * In a compacting GC, we either need to return a copy of the elements or
3185 * "pin" the memory. Otherwise we run the risk of native code using the
3186 * buffer as the destination of e.g. a blocking read() call that wakes up
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003187 * during a GC.
3188 */
3189#define GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
3190 static _ctype* Get##_jname##ArrayElements(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07003191 _ctype##Array jarr, jboolean* isCopy) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003192 { \
3193 JNI_ENTER(); \
3194 _ctype* data; \
Andy McFaddenab00d452009-08-19 07:21:41 -07003195 ArrayObject* arrayObj = \
3196 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
3197 pinPrimitiveArray(arrayObj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003198 data = (_ctype*) arrayObj->contents; \
3199 if (isCopy != NULL) \
3200 *isCopy = JNI_FALSE; \
3201 JNI_EXIT(); \
3202 return data; \
3203 }
3204
3205/*
3206 * Release the storage locked down by the "get" function.
3207 *
Andy McFaddenab00d452009-08-19 07:21:41 -07003208 * 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 -08003209 * elements in 'array'." They apparently did not anticipate the need to
Andy McFaddenab00d452009-08-19 07:21:41 -07003210 * un-pin memory.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003211 */
3212#define RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
3213 static void Release##_jname##ArrayElements(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07003214 _ctype##Array jarr, _ctype* elems, jint mode) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003215 { \
3216 UNUSED_PARAMETER(elems); \
3217 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07003218 if (mode != JNI_COMMIT) { \
3219 ArrayObject* arrayObj = \
3220 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
3221 unpinPrimitiveArray(arrayObj); \
3222 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003223 JNI_EXIT(); \
3224 }
3225
3226/*
3227 * Copy a section of a primitive array to a buffer.
3228 */
3229#define GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
3230 static void Get##_jname##ArrayRegion(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07003231 _ctype##Array jarr, jsize start, jsize len, _ctype* buf) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003232 { \
3233 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07003234 ArrayObject* arrayObj = \
3235 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003236 _ctype* data = (_ctype*) arrayObj->contents; \
3237 if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
3238 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
3239 arrayObj->obj.clazz->descriptor); \
3240 } else { \
3241 memcpy(buf, data + start, len * sizeof(_ctype)); \
3242 } \
3243 JNI_EXIT(); \
3244 }
3245
3246/*
3247 * Copy a section of a primitive array to a buffer.
3248 */
3249#define SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
3250 static void Set##_jname##ArrayRegion(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07003251 _ctype##Array jarr, jsize start, jsize len, const _ctype* buf) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003252 { \
3253 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07003254 ArrayObject* arrayObj = \
3255 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003256 _ctype* data = (_ctype*) arrayObj->contents; \
3257 if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
3258 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
3259 arrayObj->obj.clazz->descriptor); \
3260 } else { \
3261 memcpy(data + start, buf, len * sizeof(_ctype)); \
3262 } \
3263 JNI_EXIT(); \
3264 }
3265
3266/*
3267 * 4-in-1:
3268 * Get<Type>ArrayElements
3269 * Release<Type>ArrayElements
3270 * Get<Type>ArrayRegion
3271 * Set<Type>ArrayRegion
3272 */
3273#define PRIMITIVE_ARRAY_FUNCTIONS(_ctype, _jname) \
3274 GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
3275 RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
3276 GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname); \
3277 SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname);
3278
3279PRIMITIVE_ARRAY_FUNCTIONS(jboolean, Boolean);
3280PRIMITIVE_ARRAY_FUNCTIONS(jbyte, Byte);
3281PRIMITIVE_ARRAY_FUNCTIONS(jchar, Char);
3282PRIMITIVE_ARRAY_FUNCTIONS(jshort, Short);
3283PRIMITIVE_ARRAY_FUNCTIONS(jint, Int);
3284PRIMITIVE_ARRAY_FUNCTIONS(jlong, Long);
3285PRIMITIVE_ARRAY_FUNCTIONS(jfloat, Float);
3286PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double);
3287
3288/*
3289 * Register one or more native functions in one class.
3290 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003291static jint RegisterNatives(JNIEnv* env, jclass jclazz,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003292 const JNINativeMethod* methods, jint nMethods)
3293{
3294 JNI_ENTER();
3295
Andy McFaddenab00d452009-08-19 07:21:41 -07003296 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
Elliott Hughes09239e32009-09-29 18:35:43 -07003297 jint retval = JNI_OK;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003298 int i;
3299
3300 if (gDvm.verboseJni) {
3301 LOGI("[Registering JNI native methods for class %s]\n",
Andy McFaddenab00d452009-08-19 07:21:41 -07003302 clazz->descriptor);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003303 }
3304
3305 for (i = 0; i < nMethods; i++) {
Andy McFaddenab00d452009-08-19 07:21:41 -07003306 if (!dvmRegisterJNIMethod(clazz, methods[i].name,
3307 methods[i].signature, methods[i].fnPtr))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003308 {
3309 retval = JNI_ERR;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003310 }
3311 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003312
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003313 JNI_EXIT();
3314 return retval;
3315}
3316
3317/*
3318 * Un-register a native function.
3319 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003320static jint UnregisterNatives(JNIEnv* env, jclass jclazz)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003321{
3322 JNI_ENTER();
3323 /*
3324 * The JNI docs refer to this as a way to reload/relink native libraries,
3325 * and say it "should not be used in normal native code".
3326 *
3327 * We can implement it if we decide we need it.
3328 */
3329 JNI_EXIT();
3330 return JNI_ERR;
3331}
3332
3333/*
3334 * Lock the monitor.
3335 *
3336 * We have to track all monitor enters and exits, so that we can undo any
3337 * outstanding synchronization before the thread exits.
3338 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003339static jint MonitorEnter(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003340{
3341 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003342 Object* obj = dvmDecodeIndirectRef(env, jobj);
3343 dvmLockObject(_self, obj);
3344 trackMonitorEnter(_self, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003345 JNI_EXIT();
3346 return JNI_OK;
3347}
3348
3349/*
3350 * Unlock the monitor.
3351 *
3352 * Throws an IllegalMonitorStateException if the current thread
Andy McFaddenab00d452009-08-19 07:21:41 -07003353 * doesn't own the monitor. (dvmUnlockObject() takes care of the throw.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003354 *
3355 * According to the 1.6 spec, it's legal to call here with an exception
3356 * pending. If this fails, we'll stomp the original exception.
3357 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003358static jint MonitorExit(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003359{
3360 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003361 Object* obj = dvmDecodeIndirectRef(env, jobj);
3362 bool success = dvmUnlockObject(_self, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003363 if (success)
Andy McFaddenab00d452009-08-19 07:21:41 -07003364 trackMonitorExit(_self, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003365 JNI_EXIT();
3366 return success ? JNI_OK : JNI_ERR;
3367}
3368
3369/*
3370 * Return the JavaVM interface associated with the current thread.
3371 */
3372static jint GetJavaVM(JNIEnv* env, JavaVM** vm)
3373{
3374 JNI_ENTER();
3375 //*vm = gDvm.vmList;
3376 *vm = (JavaVM*) ((JNIEnvExt*)env)->vm;
3377 JNI_EXIT();
3378 if (*vm == NULL)
3379 return JNI_ERR;
3380 else
3381 return JNI_OK;
3382}
3383
3384/*
3385 * Copies "len" Unicode characters, from offset "start".
3386 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003387static void GetStringRegion(JNIEnv* env, jstring jstr, jsize start, jsize len,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003388 jchar* buf)
3389{
3390 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003391 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003392 if (start + len > dvmStringLen(strObj))
3393 dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
3394 else
3395 memcpy(buf, dvmStringChars(strObj) + start, len * sizeof(u2));
3396 JNI_EXIT();
3397}
3398
3399/*
3400 * Translates "len" Unicode characters, from offset "start", into
3401 * modified UTF-8 encoding.
3402 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003403static void GetStringUTFRegion(JNIEnv* env, jstring jstr, jsize start,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003404 jsize len, char* buf)
3405{
3406 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003407 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003408 if (start + len > dvmStringLen(strObj))
3409 dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
3410 else
3411 dvmCreateCstrFromStringRegion(strObj, start, len, buf);
3412 JNI_EXIT();
3413}
3414
3415/*
3416 * Get a raw pointer to array data.
3417 *
3418 * The caller is expected to call "release" before doing any JNI calls
3419 * or blocking I/O operations.
3420 *
Andy McFaddenab00d452009-08-19 07:21:41 -07003421 * We need to pin the memory or block GC.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003422 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003423static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray jarr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003424 jboolean* isCopy)
3425{
3426 JNI_ENTER();
3427 void* data;
Andy McFaddenab00d452009-08-19 07:21:41 -07003428 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
3429 pinPrimitiveArray(arrayObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003430 data = arrayObj->contents;
3431 if (isCopy != NULL)
3432 *isCopy = JNI_FALSE;
3433 JNI_EXIT();
3434 return data;
3435}
3436
3437/*
3438 * Release an array obtained with GetPrimitiveArrayCritical.
3439 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003440static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray jarr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003441 void* carray, jint mode)
3442{
3443 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003444 if (mode != JNI_COMMIT) {
3445 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
3446 unpinPrimitiveArray(arrayObj);
3447 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003448 JNI_EXIT();
3449}
3450
3451/*
3452 * Like GetStringChars, but with restricted use.
3453 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003454static const jchar* GetStringCritical(JNIEnv* env, jstring jstr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003455 jboolean* isCopy)
3456{
3457 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003458 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
3459 ArrayObject* strChars = dvmStringCharArray(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003460
Andy McFaddenab00d452009-08-19 07:21:41 -07003461 pinPrimitiveArray(strChars);
3462
3463 const u2* data = dvmStringChars(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003464 if (isCopy != NULL)
3465 *isCopy = JNI_FALSE;
3466
3467 JNI_EXIT();
3468 return (jchar*)data;
3469}
3470
3471/*
3472 * Like ReleaseStringChars, but with restricted use.
3473 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003474static void ReleaseStringCritical(JNIEnv* env, jstring jstr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003475 const jchar* carray)
3476{
3477 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003478 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
Andy McFadden0423f0e2009-08-26 07:21:53 -07003479 ArrayObject* strChars = dvmStringCharArray(strObj);
Andy McFaddenab00d452009-08-19 07:21:41 -07003480 unpinPrimitiveArray(strChars);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003481 JNI_EXIT();
3482}
3483
3484/*
3485 * Create a new weak global reference.
3486 */
3487static jweak NewWeakGlobalRef(JNIEnv* env, jobject obj)
3488{
3489 JNI_ENTER();
Andy McFaddenb18992f2009-09-25 10:42:15 -07003490 jweak wref = createWeakGlobalRef(env, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003491 JNI_EXIT();
Andy McFaddenb18992f2009-09-25 10:42:15 -07003492 return wref;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003493}
3494
3495/*
3496 * Delete the specified weak global reference.
3497 */
Andy McFaddenb18992f2009-09-25 10:42:15 -07003498static void DeleteWeakGlobalRef(JNIEnv* env, jweak wref)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003499{
3500 JNI_ENTER();
Andy McFaddenb18992f2009-09-25 10:42:15 -07003501 deleteWeakGlobalRef(env, wref);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003502 JNI_EXIT();
3503}
3504
3505/*
3506 * Quick check for pending exceptions.
3507 *
3508 * TODO: we should be able to skip the enter/exit macros here.
3509 */
3510static jboolean ExceptionCheck(JNIEnv* env)
3511{
3512 JNI_ENTER();
3513 bool result = dvmCheckException(_self);
3514 JNI_EXIT();
3515 return result;
3516}
3517
3518/*
3519 * Returns the type of the object referred to by "obj". It can be local,
3520 * global, or weak global.
3521 *
3522 * In the current implementation, references can be global and local at
3523 * the same time, so while the return value is accurate it may not tell
3524 * the whole story.
3525 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003526static jobjectRefType GetObjectRefType(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003527{
3528 JNI_ENTER();
Andy McFadden0423f0e2009-08-26 07:21:53 -07003529 jobjectRefType type = dvmGetJNIRefType(env, jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003530 JNI_EXIT();
3531 return type;
3532}
3533
3534/*
3535 * Allocate and return a new java.nio.ByteBuffer for this block of memory.
3536 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003537 * "address" may not be NULL, and "capacity" must be > 0. (These are only
3538 * verified when CheckJNI is enabled.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003539 */
Andy McFadden8e5c7842009-07-23 17:47:18 -07003540static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003541{
Andy McFadden8e5c7842009-07-23 17:47:18 -07003542 JNI_ENTER();
3543
3544 Thread* self = _self /*dvmThreadSelf()*/;
3545 Object* platformAddress = NULL;
3546 JValue callResult;
The Android Open Source Project99409882009-03-18 22:20:24 -07003547 jobject result = NULL;
Andy McFaddenac175b42009-12-17 11:21:09 -08003548 ClassObject* tmpClazz;
3549
3550 tmpClazz = gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on->clazz;
3551 if (!dvmIsClassInitialized(tmpClazz) && !dvmInitClass(tmpClazz))
3552 goto bail;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003553
Andy McFadden8e5c7842009-07-23 17:47:18 -07003554 /* get an instance of PlatformAddress that wraps the provided address */
3555 dvmCallMethod(self,
3556 gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on,
3557 NULL, &callResult, address);
3558 if (dvmGetException(self) != NULL || callResult.l == NULL)
The Android Open Source Project99409882009-03-18 22:20:24 -07003559 goto bail;
Andy McFadden8e5c7842009-07-23 17:47:18 -07003560
3561 /* don't let the GC discard it */
3562 platformAddress = (Object*) callResult.l;
3563 dvmAddTrackedAlloc(platformAddress, self);
3564 LOGV("tracking %p for address=%p\n", platformAddress, address);
3565
3566 /* create an instance of java.nio.ReadWriteDirectByteBuffer */
Andy McFaddenac175b42009-12-17 11:21:09 -08003567 tmpClazz = gDvm.classJavaNioReadWriteDirectByteBuffer;
3568 if (!dvmIsClassInitialized(tmpClazz) && !dvmInitClass(tmpClazz))
Andy McFadden8e5c7842009-07-23 17:47:18 -07003569 goto bail;
Andy McFaddenac175b42009-12-17 11:21:09 -08003570 Object* newObj = dvmAllocObject(tmpClazz, ALLOC_DONT_TRACK);
Andy McFadden8e5c7842009-07-23 17:47:18 -07003571 if (newObj != NULL) {
3572 /* call the (PlatformAddress, int, int) constructor */
Andy McFaddenab00d452009-08-19 07:21:41 -07003573 result = addLocalReference(env, newObj);
Andy McFadden8e5c7842009-07-23 17:47:18 -07003574 dvmCallMethod(self, gDvm.methJavaNioReadWriteDirectByteBuffer_init,
3575 newObj, &callResult, platformAddress, (jint) capacity, (jint) 0);
Andy McFaddenab00d452009-08-19 07:21:41 -07003576 if (dvmGetException(self) != NULL) {
3577 deleteLocalReference(env, result);
3578 result = NULL;
Andy McFadden8e5c7842009-07-23 17:47:18 -07003579 goto bail;
Andy McFaddenab00d452009-08-19 07:21:41 -07003580 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003581 }
3582
The Android Open Source Project99409882009-03-18 22:20:24 -07003583bail:
Andy McFadden8e5c7842009-07-23 17:47:18 -07003584 if (platformAddress != NULL)
3585 dvmReleaseTrackedAlloc(platformAddress, self);
3586 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07003587 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003588}
3589
3590/*
3591 * Get the starting address of the buffer for the specified java.nio.Buffer.
3592 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003593 * If this is not a "direct" buffer, we return NULL.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003594 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003595static void* GetDirectBufferAddress(JNIEnv* env, jobject jbuf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003596{
Andy McFadden8e5c7842009-07-23 17:47:18 -07003597 JNI_ENTER();
3598
Andy McFaddenab00d452009-08-19 07:21:41 -07003599 Object* bufObj = dvmDecodeIndirectRef(env, jbuf);
Andy McFadden8e5c7842009-07-23 17:47:18 -07003600 Thread* self = _self /*dvmThreadSelf()*/;
Andy McFadden8e696dc2009-07-24 15:28:16 -07003601 void* result;
3602
3603 /*
3604 * All Buffer objects have an effectiveDirectAddress field. If it's
3605 * nonzero, we can just return that value. If not, we have to call
3606 * through DirectBuffer.getEffectiveAddress(), which as a side-effect
3607 * will set the effectiveDirectAddress field for direct buffers (and
3608 * things that wrap direct buffers).
3609 */
3610 result = (void*) dvmGetFieldInt(bufObj,
3611 gDvm.offJavaNioBuffer_effectiveDirectAddress);
3612 if (result != NULL) {
3613 //LOGI("fast path for %p\n", buf);
3614 goto bail;
3615 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003616
Andy McFadden72d61fb2009-06-24 16:56:06 -07003617 /*
3618 * Start by determining if the object supports the DirectBuffer
3619 * interfaces. Note this does not guarantee that it's a direct buffer.
3620 */
Andy McFadden8e5c7842009-07-23 17:47:18 -07003621 if (!dvmInstanceof(bufObj->clazz,
3622 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003623 {
The Android Open Source Project99409882009-03-18 22:20:24 -07003624 goto bail;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003625 }
3626
Andy McFadden72d61fb2009-06-24 16:56:06 -07003627 /*
Andy McFadden8e5c7842009-07-23 17:47:18 -07003628 * Get a PlatformAddress object with the effective address.
Andy McFadden5f612b82009-07-22 15:07:27 -07003629 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003630 * If this isn't a direct buffer, the result will be NULL and/or an
Andy McFadden72d61fb2009-06-24 16:56:06 -07003631 * exception will have been thrown.
3632 */
Andy McFadden8e696dc2009-07-24 15:28:16 -07003633 JValue callResult;
Andy McFadden8e5c7842009-07-23 17:47:18 -07003634 const Method* meth = dvmGetVirtualizedMethod(bufObj->clazz,
3635 gDvm.methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress);
Andy McFaddend5ab7262009-08-25 07:19:34 -07003636 dvmCallMethodA(self, meth, bufObj, false, &callResult, NULL);
Andy McFadden8e5c7842009-07-23 17:47:18 -07003637 if (dvmGetException(self) != NULL) {
3638 dvmClearException(self);
3639 callResult.l = NULL;
Andy McFadden72d61fb2009-06-24 16:56:06 -07003640 }
Andy McFadden8e5c7842009-07-23 17:47:18 -07003641
Andy McFadden8e696dc2009-07-24 15:28:16 -07003642 Object* platformAddr = callResult.l;
Andy McFadden72d61fb2009-06-24 16:56:06 -07003643 if (platformAddr == NULL) {
Andy McFadden8e696dc2009-07-24 15:28:16 -07003644 LOGV("Got request for address of non-direct buffer\n");
Andy McFadden72d61fb2009-06-24 16:56:06 -07003645 goto bail;
3646 }
3647
Andy McFadden8e5c7842009-07-23 17:47:18 -07003648 /*
3649 * Extract the address from the PlatformAddress object. Instead of
3650 * calling the toLong() method, just grab the field directly. This
3651 * is faster but more fragile.
3652 */
3653 result = (void*) dvmGetFieldInt(platformAddr,
3654 gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr);
The Android Open Source Project99409882009-03-18 22:20:24 -07003655
Andy McFadden8e696dc2009-07-24 15:28:16 -07003656 //LOGI("slow path for %p --> %p\n", buf, result);
3657
The Android Open Source Project99409882009-03-18 22:20:24 -07003658bail:
Andy McFadden8e5c7842009-07-23 17:47:18 -07003659 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07003660 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003661}
3662
3663/*
3664 * Get the capacity of the buffer for the specified java.nio.Buffer.
3665 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003666 * Returns -1 if the object is not a direct buffer. (We actually skip
3667 * this check, since it's expensive to determine, and just return the
3668 * capacity regardless.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003669 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003670static jlong GetDirectBufferCapacity(JNIEnv* env, jobject jbuf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003671{
Andy McFadden8e5c7842009-07-23 17:47:18 -07003672 JNI_ENTER();
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003673
Andy McFadden8e5c7842009-07-23 17:47:18 -07003674 /*
3675 * The capacity is always in the Buffer.capacity field.
3676 *
3677 * (The "check" version should verify that this is actually a Buffer,
3678 * but we're not required to do so here.)
3679 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003680 Object* buf = dvmDecodeIndirectRef(env, jbuf);
3681 jlong result = dvmGetFieldInt(buf, gDvm.offJavaNioBuffer_capacity);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003682
Andy McFadden8e5c7842009-07-23 17:47:18 -07003683 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07003684 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003685}
3686
3687
3688/*
3689 * ===========================================================================
3690 * JNI invocation functions
3691 * ===========================================================================
3692 */
3693
3694/*
3695 * Handle AttachCurrentThread{AsDaemon}.
3696 *
3697 * We need to make sure the VM is actually running. For example, if we start
3698 * up, issue an Attach, and the VM exits almost immediately, by the time the
3699 * attaching happens the VM could already be shutting down.
3700 *
3701 * It's hard to avoid a race condition here because we don't want to hold
3702 * a lock across the entire operation. What we can do is temporarily
3703 * increment the thread count to prevent a VM exit.
3704 *
3705 * This could potentially still have problems if a daemon thread calls here
3706 * while the VM is shutting down. dvmThreadSelf() will work, since it just
3707 * uses pthread TLS, but dereferencing "vm" could fail. Such is life when
3708 * you shut down a VM while threads are still running inside it.
3709 *
3710 * Remember that some code may call this as a way to find the per-thread
3711 * JNIEnv pointer. Don't do excess work for that case.
3712 */
3713static jint attachThread(JavaVM* vm, JNIEnv** p_env, void* thr_args,
3714 bool isDaemon)
3715{
3716 JavaVMAttachArgs* args = (JavaVMAttachArgs*) thr_args;
3717 Thread* self;
3718 bool result = false;
3719
3720 /*
3721 * Return immediately if we're already one with the VM.
3722 */
3723 self = dvmThreadSelf();
3724 if (self != NULL) {
3725 *p_env = self->jniEnv;
3726 return JNI_OK;
3727 }
3728
3729 /*
3730 * No threads allowed in zygote mode.
3731 */
3732 if (gDvm.zygote) {
3733 return JNI_ERR;
3734 }
3735
3736 /* increment the count to keep the VM from bailing while we run */
3737 dvmLockThreadList(NULL);
3738 if (gDvm.nonDaemonThreadCount == 0) {
3739 // dead or dying
3740 LOGV("Refusing to attach thread '%s' -- VM is shutting down\n",
3741 (thr_args == NULL) ? "(unknown)" : args->name);
3742 dvmUnlockThreadList();
3743 return JNI_ERR;
3744 }
3745 gDvm.nonDaemonThreadCount++;
3746 dvmUnlockThreadList();
3747
3748 /* tweak the JavaVMAttachArgs as needed */
3749 JavaVMAttachArgs argsCopy;
3750 if (args == NULL) {
3751 /* allow the v1.1 calling convention */
3752 argsCopy.version = JNI_VERSION_1_2;
3753 argsCopy.name = NULL;
3754 argsCopy.group = dvmGetMainThreadGroup();
3755 } else {
3756 assert(args->version >= JNI_VERSION_1_2);
3757
3758 argsCopy.version = args->version;
3759 argsCopy.name = args->name;
3760 if (args->group != NULL)
3761 argsCopy.group = args->group;
3762 else
3763 argsCopy.group = dvmGetMainThreadGroup();
3764 }
3765
3766 result = dvmAttachCurrentThread(&argsCopy, isDaemon);
3767
3768 /* restore the count */
3769 dvmLockThreadList(NULL);
3770 gDvm.nonDaemonThreadCount--;
3771 dvmUnlockThreadList();
3772
3773 /*
3774 * Change the status to indicate that we're out in native code. This
3775 * call is not guarded with state-change macros, so we have to do it
3776 * by hand.
3777 */
3778 if (result) {
3779 self = dvmThreadSelf();
3780 assert(self != NULL);
3781 dvmChangeStatus(self, THREAD_NATIVE);
3782 *p_env = self->jniEnv;
3783 return JNI_OK;
3784 } else {
3785 return JNI_ERR;
3786 }
3787}
3788
3789/*
3790 * Attach the current thread to the VM. If the thread is already attached,
3791 * this is a no-op.
3792 */
3793static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args)
3794{
3795 return attachThread(vm, p_env, thr_args, false);
3796}
3797
3798/*
3799 * Like AttachCurrentThread, but set the "daemon" flag.
3800 */
3801static jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env,
3802 void* thr_args)
3803{
3804 return attachThread(vm, p_env, thr_args, true);
3805}
3806
3807/*
3808 * Dissociate the current thread from the VM.
3809 */
3810static jint DetachCurrentThread(JavaVM* vm)
3811{
3812 Thread* self = dvmThreadSelf();
3813
3814 if (self == NULL) /* not attached, can't do anything */
3815 return JNI_ERR;
3816
3817 /* switch to "running" to check for suspension */
3818 dvmChangeStatus(self, THREAD_RUNNING);
3819
3820 /* detach the thread */
3821 dvmDetachCurrentThread();
3822
3823 /* (no need to change status back -- we have no status) */
3824 return JNI_OK;
3825}
3826
3827/*
3828 * If current thread is attached to VM, return the associated JNIEnv.
3829 * Otherwise, stuff NULL in and return JNI_EDETACHED.
3830 *
3831 * JVMTI overloads this by specifying a magic value for "version", so we
3832 * do want to check that here.
3833 */
3834static jint GetEnv(JavaVM* vm, void** env, jint version)
3835{
3836 Thread* self = dvmThreadSelf();
3837
3838 if (version < JNI_VERSION_1_1 || version > JNI_VERSION_1_6)
3839 return JNI_EVERSION;
3840
3841 if (self == NULL) {
3842 *env = NULL;
3843 } else {
3844 /* TODO: status change is probably unnecessary */
3845 dvmChangeStatus(self, THREAD_RUNNING);
3846 *env = (void*) dvmGetThreadJNIEnv(self);
3847 dvmChangeStatus(self, THREAD_NATIVE);
3848 }
3849 if (*env == NULL)
3850 return JNI_EDETACHED;
3851 else
3852 return JNI_OK;
3853}
3854
3855/*
3856 * Destroy the VM. This may be called from any thread.
3857 *
3858 * If the current thread is attached, wait until the current thread is
3859 * the only non-daemon user-level thread. If the current thread is not
3860 * attached, we attach it and do the processing as usual. (If the attach
3861 * fails, it's probably because all the non-daemon threads have already
3862 * exited and the VM doesn't want to let us back in.)
3863 *
3864 * TODO: we don't really deal with the situation where more than one thread
3865 * has called here. One thread wins, the other stays trapped waiting on
3866 * the condition variable forever. Not sure this situation is interesting
3867 * in real life.
3868 */
3869static jint DestroyJavaVM(JavaVM* vm)
3870{
3871 JavaVMExt* ext = (JavaVMExt*) vm;
3872 Thread* self;
3873
3874 if (ext == NULL)
3875 return JNI_ERR;
3876
Andy McFadden43eb5012010-02-01 16:56:53 -08003877 if (gDvm.verboseShutdown)
3878 LOGD("DestroyJavaVM waiting for non-daemon threads to exit\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003879
3880 /*
3881 * Sleep on a condition variable until it's okay to exit.
3882 */
3883 self = dvmThreadSelf();
3884 if (self == NULL) {
3885 JNIEnv* tmpEnv;
3886 if (AttachCurrentThread(vm, &tmpEnv, NULL) != JNI_OK) {
3887 LOGV("Unable to reattach main for Destroy; assuming VM is "
3888 "shutting down (count=%d)\n",
3889 gDvm.nonDaemonThreadCount);
3890 goto shutdown;
3891 } else {
3892 LOGV("Attached to wait for shutdown in Destroy\n");
3893 }
3894 }
3895 dvmChangeStatus(self, THREAD_VMWAIT);
3896
3897 dvmLockThreadList(self);
3898 gDvm.nonDaemonThreadCount--; // remove current thread from count
3899
3900 while (gDvm.nonDaemonThreadCount > 0)
3901 pthread_cond_wait(&gDvm.vmExitCond, &gDvm.threadListLock);
3902
3903 dvmUnlockThreadList();
3904 self = NULL;
3905
3906shutdown:
3907 // TODO: call System.exit() to run any registered shutdown hooks
3908 // (this may not return -- figure out how this should work)
3909
Andy McFadden43eb5012010-02-01 16:56:53 -08003910 if (gDvm.verboseShutdown)
3911 LOGD("DestroyJavaVM shutting VM down\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003912 dvmShutdown();
3913
3914 // TODO - free resources associated with JNI-attached daemon threads
3915 free(ext->envList);
3916 free(ext);
3917
3918 return JNI_OK;
3919}
3920
3921
3922/*
3923 * ===========================================================================
3924 * Function tables
3925 * ===========================================================================
3926 */
3927
3928static const struct JNINativeInterface gNativeInterface = {
3929 NULL,
3930 NULL,
3931 NULL,
3932 NULL,
3933
3934 GetVersion,
3935
3936 DefineClass,
3937 FindClass,
3938
3939 FromReflectedMethod,
3940 FromReflectedField,
3941 ToReflectedMethod,
3942
3943 GetSuperclass,
3944 IsAssignableFrom,
3945
3946 ToReflectedField,
3947
3948 Throw,
3949 ThrowNew,
3950 ExceptionOccurred,
3951 ExceptionDescribe,
3952 ExceptionClear,
3953 FatalError,
3954
3955 PushLocalFrame,
3956 PopLocalFrame,
3957
3958 NewGlobalRef,
3959 DeleteGlobalRef,
3960 DeleteLocalRef,
3961 IsSameObject,
3962 NewLocalRef,
3963 EnsureLocalCapacity,
3964
3965 AllocObject,
3966 NewObject,
3967 NewObjectV,
3968 NewObjectA,
3969
3970 GetObjectClass,
3971 IsInstanceOf,
3972
3973 GetMethodID,
3974
3975 CallObjectMethod,
3976 CallObjectMethodV,
3977 CallObjectMethodA,
3978 CallBooleanMethod,
3979 CallBooleanMethodV,
3980 CallBooleanMethodA,
3981 CallByteMethod,
3982 CallByteMethodV,
3983 CallByteMethodA,
3984 CallCharMethod,
3985 CallCharMethodV,
3986 CallCharMethodA,
3987 CallShortMethod,
3988 CallShortMethodV,
3989 CallShortMethodA,
3990 CallIntMethod,
3991 CallIntMethodV,
3992 CallIntMethodA,
3993 CallLongMethod,
3994 CallLongMethodV,
3995 CallLongMethodA,
3996 CallFloatMethod,
3997 CallFloatMethodV,
3998 CallFloatMethodA,
3999 CallDoubleMethod,
4000 CallDoubleMethodV,
4001 CallDoubleMethodA,
4002 CallVoidMethod,
4003 CallVoidMethodV,
4004 CallVoidMethodA,
4005
4006 CallNonvirtualObjectMethod,
4007 CallNonvirtualObjectMethodV,
4008 CallNonvirtualObjectMethodA,
4009 CallNonvirtualBooleanMethod,
4010 CallNonvirtualBooleanMethodV,
4011 CallNonvirtualBooleanMethodA,
4012 CallNonvirtualByteMethod,
4013 CallNonvirtualByteMethodV,
4014 CallNonvirtualByteMethodA,
4015 CallNonvirtualCharMethod,
4016 CallNonvirtualCharMethodV,
4017 CallNonvirtualCharMethodA,
4018 CallNonvirtualShortMethod,
4019 CallNonvirtualShortMethodV,
4020 CallNonvirtualShortMethodA,
4021 CallNonvirtualIntMethod,
4022 CallNonvirtualIntMethodV,
4023 CallNonvirtualIntMethodA,
4024 CallNonvirtualLongMethod,
4025 CallNonvirtualLongMethodV,
4026 CallNonvirtualLongMethodA,
4027 CallNonvirtualFloatMethod,
4028 CallNonvirtualFloatMethodV,
4029 CallNonvirtualFloatMethodA,
4030 CallNonvirtualDoubleMethod,
4031 CallNonvirtualDoubleMethodV,
4032 CallNonvirtualDoubleMethodA,
4033 CallNonvirtualVoidMethod,
4034 CallNonvirtualVoidMethodV,
4035 CallNonvirtualVoidMethodA,
4036
4037 GetFieldID,
4038
4039 GetObjectField,
4040 GetBooleanField,
4041 GetByteField,
4042 GetCharField,
4043 GetShortField,
4044 GetIntField,
4045 GetLongField,
4046 GetFloatField,
4047 GetDoubleField,
4048 SetObjectField,
4049 SetBooleanField,
4050 SetByteField,
4051 SetCharField,
4052 SetShortField,
4053 SetIntField,
4054 SetLongField,
4055 SetFloatField,
4056 SetDoubleField,
4057
4058 GetStaticMethodID,
4059
4060 CallStaticObjectMethod,
4061 CallStaticObjectMethodV,
4062 CallStaticObjectMethodA,
4063 CallStaticBooleanMethod,
4064 CallStaticBooleanMethodV,
4065 CallStaticBooleanMethodA,
4066 CallStaticByteMethod,
4067 CallStaticByteMethodV,
4068 CallStaticByteMethodA,
4069 CallStaticCharMethod,
4070 CallStaticCharMethodV,
4071 CallStaticCharMethodA,
4072 CallStaticShortMethod,
4073 CallStaticShortMethodV,
4074 CallStaticShortMethodA,
4075 CallStaticIntMethod,
4076 CallStaticIntMethodV,
4077 CallStaticIntMethodA,
4078 CallStaticLongMethod,
4079 CallStaticLongMethodV,
4080 CallStaticLongMethodA,
4081 CallStaticFloatMethod,
4082 CallStaticFloatMethodV,
4083 CallStaticFloatMethodA,
4084 CallStaticDoubleMethod,
4085 CallStaticDoubleMethodV,
4086 CallStaticDoubleMethodA,
4087 CallStaticVoidMethod,
4088 CallStaticVoidMethodV,
4089 CallStaticVoidMethodA,
4090
4091 GetStaticFieldID,
4092
4093 GetStaticObjectField,
4094 GetStaticBooleanField,
4095 GetStaticByteField,
4096 GetStaticCharField,
4097 GetStaticShortField,
4098 GetStaticIntField,
4099 GetStaticLongField,
4100 GetStaticFloatField,
4101 GetStaticDoubleField,
4102
4103 SetStaticObjectField,
4104 SetStaticBooleanField,
4105 SetStaticByteField,
4106 SetStaticCharField,
4107 SetStaticShortField,
4108 SetStaticIntField,
4109 SetStaticLongField,
4110 SetStaticFloatField,
4111 SetStaticDoubleField,
4112
4113 NewString,
4114
4115 GetStringLength,
4116 GetStringChars,
4117 ReleaseStringChars,
4118
4119 NewStringUTF,
4120 GetStringUTFLength,
4121 GetStringUTFChars,
4122 ReleaseStringUTFChars,
4123
4124 GetArrayLength,
4125 NewObjectArray,
4126 GetObjectArrayElement,
4127 SetObjectArrayElement,
4128
4129 NewBooleanArray,
4130 NewByteArray,
4131 NewCharArray,
4132 NewShortArray,
4133 NewIntArray,
4134 NewLongArray,
4135 NewFloatArray,
4136 NewDoubleArray,
4137
4138 GetBooleanArrayElements,
4139 GetByteArrayElements,
4140 GetCharArrayElements,
4141 GetShortArrayElements,
4142 GetIntArrayElements,
4143 GetLongArrayElements,
4144 GetFloatArrayElements,
4145 GetDoubleArrayElements,
4146
4147 ReleaseBooleanArrayElements,
4148 ReleaseByteArrayElements,
4149 ReleaseCharArrayElements,
4150 ReleaseShortArrayElements,
4151 ReleaseIntArrayElements,
4152 ReleaseLongArrayElements,
4153 ReleaseFloatArrayElements,
4154 ReleaseDoubleArrayElements,
4155
4156 GetBooleanArrayRegion,
4157 GetByteArrayRegion,
4158 GetCharArrayRegion,
4159 GetShortArrayRegion,
4160 GetIntArrayRegion,
4161 GetLongArrayRegion,
4162 GetFloatArrayRegion,
4163 GetDoubleArrayRegion,
4164 SetBooleanArrayRegion,
4165 SetByteArrayRegion,
4166 SetCharArrayRegion,
4167 SetShortArrayRegion,
4168 SetIntArrayRegion,
4169 SetLongArrayRegion,
4170 SetFloatArrayRegion,
4171 SetDoubleArrayRegion,
4172
4173 RegisterNatives,
4174 UnregisterNatives,
4175
4176 MonitorEnter,
4177 MonitorExit,
4178
4179 GetJavaVM,
4180
4181 GetStringRegion,
4182 GetStringUTFRegion,
4183
4184 GetPrimitiveArrayCritical,
4185 ReleasePrimitiveArrayCritical,
4186
4187 GetStringCritical,
4188 ReleaseStringCritical,
4189
4190 NewWeakGlobalRef,
4191 DeleteWeakGlobalRef,
4192
4193 ExceptionCheck,
4194
4195 NewDirectByteBuffer,
4196 GetDirectBufferAddress,
4197 GetDirectBufferCapacity,
4198
4199 GetObjectRefType
4200};
4201static const struct JNIInvokeInterface gInvokeInterface = {
4202 NULL,
4203 NULL,
4204 NULL,
4205
4206 DestroyJavaVM,
4207 AttachCurrentThread,
4208 DetachCurrentThread,
4209
4210 GetEnv,
4211
4212 AttachCurrentThreadAsDaemon,
4213};
4214
4215
4216/*
4217 * ===========================================================================
4218 * VM/Env creation
4219 * ===========================================================================
4220 */
4221
4222/*
4223 * Enable "checked JNI" after the VM has partially started. This must
4224 * only be called in "zygote" mode, when we have one thread running.
Andy McFadden59b61772009-05-13 16:44:34 -07004225 *
4226 * This doesn't attempt to rewrite the JNI call bridge associated with
4227 * native methods, so we won't get those checks for any methods that have
4228 * already been resolved.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08004229 */
4230void dvmLateEnableCheckedJni(void)
4231{
4232 JNIEnvExt* extEnv;
4233 JavaVMExt* extVm;
Andy McFaddenab00d452009-08-19 07:21:41 -07004234
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08004235 extEnv = dvmGetJNIEnvForThread();
4236 if (extEnv == NULL) {
4237 LOGE("dvmLateEnableCheckedJni: thread has no JNIEnv\n");
4238 return;
4239 }
4240 extVm = extEnv->vm;
4241 assert(extVm != NULL);
4242
4243 if (!extVm->useChecked) {
4244 LOGD("Late-enabling CheckJNI\n");
4245 dvmUseCheckedJniVm(extVm);
4246 extVm->useChecked = true;
4247 dvmUseCheckedJniEnv(extEnv);
4248
4249 /* currently no way to pick up jniopts features */
4250 } else {
4251 LOGD("Not late-enabling CheckJNI (already on)\n");
4252 }
4253}
4254
4255/*
4256 * Not supported.
4257 */
4258jint JNI_GetDefaultJavaVMInitArgs(void* vm_args)
4259{
4260 return JNI_ERR;
4261}
4262
4263/*
4264 * Return a buffer full of created VMs.
4265 *
4266 * We always have zero or one.
4267 */
4268jint JNI_GetCreatedJavaVMs(JavaVM** vmBuf, jsize bufLen, jsize* nVMs)
4269{
4270 if (gDvm.vmList != NULL) {
4271 *nVMs = 1;
4272
4273 if (bufLen > 0)
4274 *vmBuf++ = gDvm.vmList;
4275 } else {
4276 *nVMs = 0;
4277 }
4278
4279 return JNI_OK;
4280}
4281
4282
4283/*
4284 * Create a new VM instance.
4285 *
4286 * The current thread becomes the main VM thread. We return immediately,
4287 * which effectively means the caller is executing in a native method.
4288 */
4289jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args)
4290{
4291 const JavaVMInitArgs* args = (JavaVMInitArgs*) vm_args;
4292 JNIEnvExt* pEnv = NULL;
4293 JavaVMExt* pVM = NULL;
4294 const char** argv;
4295 int argc = 0;
4296 int i, curOpt;
4297 int result = JNI_ERR;
4298 bool checkJni = false;
4299 bool warnError = true;
4300 bool forceDataCopy = false;
4301
4302 if (args->version < JNI_VERSION_1_2)
4303 return JNI_EVERSION;
4304
4305 // TODO: don't allow creation of multiple VMs -- one per customer for now
4306
4307 /* zero globals; not strictly necessary the first time a VM is started */
4308 memset(&gDvm, 0, sizeof(gDvm));
4309
4310 /*
4311 * Set up structures for JNIEnv and VM.
4312 */
4313 //pEnv = (JNIEnvExt*) malloc(sizeof(JNIEnvExt));
4314 pVM = (JavaVMExt*) malloc(sizeof(JavaVMExt));
4315
4316 //memset(pEnv, 0, sizeof(JNIEnvExt));
4317 //pEnv->funcTable = &gNativeInterface;
4318 //pEnv->vm = pVM;
4319 memset(pVM, 0, sizeof(JavaVMExt));
4320 pVM->funcTable = &gInvokeInterface;
4321 pVM->envList = pEnv;
4322 dvmInitMutex(&pVM->envListLock);
4323
4324 argv = (const char**) malloc(sizeof(char*) * (args->nOptions));
4325 memset(argv, 0, sizeof(char*) * (args->nOptions));
4326
4327 curOpt = 0;
4328
4329 /*
4330 * Convert JNI args to argv.
4331 *
4332 * We have to pull out vfprintf/exit/abort, because they use the
4333 * "extraInfo" field to pass function pointer "hooks" in. We also
4334 * look for the -Xcheck:jni stuff here.
4335 */
4336 for (i = 0; i < args->nOptions; i++) {
4337 const char* optStr = args->options[i].optionString;
4338
4339 if (optStr == NULL) {
4340 fprintf(stderr, "ERROR: arg %d string was null\n", i);
4341 goto bail;
4342 } else if (strcmp(optStr, "vfprintf") == 0) {
4343 gDvm.vfprintfHook = args->options[i].extraInfo;
4344 } else if (strcmp(optStr, "exit") == 0) {
4345 gDvm.exitHook = args->options[i].extraInfo;
4346 } else if (strcmp(optStr, "abort") == 0) {
4347 gDvm.abortHook = args->options[i].extraInfo;
4348 } else if (strcmp(optStr, "-Xcheck:jni") == 0) {
4349 checkJni = true;
4350 } else if (strncmp(optStr, "-Xjniopts:", 10) == 0) {
4351 const char* jniOpts = optStr + 9;
4352 while (jniOpts != NULL) {
4353 jniOpts++; /* skip past ':' or ',' */
4354 if (strncmp(jniOpts, "warnonly", 8) == 0) {
4355 warnError = false;
4356 } else if (strncmp(jniOpts, "forcecopy", 9) == 0) {
4357 forceDataCopy = true;
4358 } else {
4359 LOGW("unknown jni opt starting at '%s'\n", jniOpts);
4360 }
4361 jniOpts = strchr(jniOpts, ',');
4362 }
4363 } else {
4364 /* regular option */
4365 argv[curOpt++] = optStr;
4366 }
4367 }
4368 argc = curOpt;
4369
4370 if (checkJni) {
4371 dvmUseCheckedJniVm(pVM);
4372 pVM->useChecked = true;
4373 }
4374 pVM->warnError = warnError;
4375 pVM->forceDataCopy = forceDataCopy;
4376
4377 /* set this up before initializing VM, so it can create some JNIEnvs */
4378 gDvm.vmList = (JavaVM*) pVM;
4379
4380 /*
4381 * Create an env for main thread. We need to have something set up
4382 * here because some of the class initialization we do when starting
4383 * up the VM will call into native code.
4384 */
4385 pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL);
4386
4387 /* initialize VM */
4388 gDvm.initializing = true;
4389 if (dvmStartup(argc, argv, args->ignoreUnrecognized, (JNIEnv*)pEnv) != 0) {
4390 free(pEnv);
4391 free(pVM);
4392 goto bail;
4393 }
4394
4395 /*
4396 * Success! Return stuff to caller.
4397 */
4398 dvmChangeStatus(NULL, THREAD_NATIVE);
4399 *p_env = (JNIEnv*) pEnv;
4400 *p_vm = (JavaVM*) pVM;
4401 result = JNI_OK;
4402
4403bail:
4404 gDvm.initializing = false;
4405 if (result == JNI_OK)
4406 LOGV("JNI_CreateJavaVM succeeded\n");
4407 else
4408 LOGW("JNI_CreateJavaVM failed\n");
4409 free(argv);
4410 return result;
4411}