blob: 1029cddacd2da785cabb864931f0b32e054150ea [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;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003548
Andy McFadden8e5c7842009-07-23 17:47:18 -07003549 /* get an instance of PlatformAddress that wraps the provided address */
3550 dvmCallMethod(self,
3551 gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on,
3552 NULL, &callResult, address);
3553 if (dvmGetException(self) != NULL || callResult.l == NULL)
The Android Open Source Project99409882009-03-18 22:20:24 -07003554 goto bail;
Andy McFadden8e5c7842009-07-23 17:47:18 -07003555
3556 /* don't let the GC discard it */
3557 platformAddress = (Object*) callResult.l;
3558 dvmAddTrackedAlloc(platformAddress, self);
3559 LOGV("tracking %p for address=%p\n", platformAddress, address);
3560
3561 /* create an instance of java.nio.ReadWriteDirectByteBuffer */
3562 ClassObject* clazz = gDvm.classJavaNioReadWriteDirectByteBuffer;
3563 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))
3564 goto bail;
3565 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
3566 if (newObj != NULL) {
3567 /* call the (PlatformAddress, int, int) constructor */
Andy McFaddenab00d452009-08-19 07:21:41 -07003568 result = addLocalReference(env, newObj);
Andy McFadden8e5c7842009-07-23 17:47:18 -07003569 dvmCallMethod(self, gDvm.methJavaNioReadWriteDirectByteBuffer_init,
3570 newObj, &callResult, platformAddress, (jint) capacity, (jint) 0);
Andy McFaddenab00d452009-08-19 07:21:41 -07003571 if (dvmGetException(self) != NULL) {
3572 deleteLocalReference(env, result);
3573 result = NULL;
Andy McFadden8e5c7842009-07-23 17:47:18 -07003574 goto bail;
Andy McFaddenab00d452009-08-19 07:21:41 -07003575 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003576 }
3577
The Android Open Source Project99409882009-03-18 22:20:24 -07003578bail:
Andy McFadden8e5c7842009-07-23 17:47:18 -07003579 if (platformAddress != NULL)
3580 dvmReleaseTrackedAlloc(platformAddress, self);
3581 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07003582 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003583}
3584
3585/*
3586 * Get the starting address of the buffer for the specified java.nio.Buffer.
3587 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003588 * If this is not a "direct" buffer, we return NULL.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003589 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003590static void* GetDirectBufferAddress(JNIEnv* env, jobject jbuf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003591{
Andy McFadden8e5c7842009-07-23 17:47:18 -07003592 JNI_ENTER();
3593
Andy McFaddenab00d452009-08-19 07:21:41 -07003594 Object* bufObj = dvmDecodeIndirectRef(env, jbuf);
Andy McFadden8e5c7842009-07-23 17:47:18 -07003595 Thread* self = _self /*dvmThreadSelf()*/;
Andy McFadden8e696dc2009-07-24 15:28:16 -07003596 void* result;
3597
3598 /*
3599 * All Buffer objects have an effectiveDirectAddress field. If it's
3600 * nonzero, we can just return that value. If not, we have to call
3601 * through DirectBuffer.getEffectiveAddress(), which as a side-effect
3602 * will set the effectiveDirectAddress field for direct buffers (and
3603 * things that wrap direct buffers).
3604 */
3605 result = (void*) dvmGetFieldInt(bufObj,
3606 gDvm.offJavaNioBuffer_effectiveDirectAddress);
3607 if (result != NULL) {
3608 //LOGI("fast path for %p\n", buf);
3609 goto bail;
3610 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003611
Andy McFadden72d61fb2009-06-24 16:56:06 -07003612 /*
3613 * Start by determining if the object supports the DirectBuffer
3614 * interfaces. Note this does not guarantee that it's a direct buffer.
3615 */
Andy McFadden8e5c7842009-07-23 17:47:18 -07003616 if (!dvmInstanceof(bufObj->clazz,
3617 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003618 {
The Android Open Source Project99409882009-03-18 22:20:24 -07003619 goto bail;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003620 }
3621
Andy McFadden72d61fb2009-06-24 16:56:06 -07003622 /*
Andy McFadden8e5c7842009-07-23 17:47:18 -07003623 * Get a PlatformAddress object with the effective address.
Andy McFadden5f612b82009-07-22 15:07:27 -07003624 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003625 * If this isn't a direct buffer, the result will be NULL and/or an
Andy McFadden72d61fb2009-06-24 16:56:06 -07003626 * exception will have been thrown.
3627 */
Andy McFadden8e696dc2009-07-24 15:28:16 -07003628 JValue callResult;
Andy McFadden8e5c7842009-07-23 17:47:18 -07003629 const Method* meth = dvmGetVirtualizedMethod(bufObj->clazz,
3630 gDvm.methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress);
Andy McFaddend5ab7262009-08-25 07:19:34 -07003631 dvmCallMethodA(self, meth, bufObj, false, &callResult, NULL);
Andy McFadden8e5c7842009-07-23 17:47:18 -07003632 if (dvmGetException(self) != NULL) {
3633 dvmClearException(self);
3634 callResult.l = NULL;
Andy McFadden72d61fb2009-06-24 16:56:06 -07003635 }
Andy McFadden8e5c7842009-07-23 17:47:18 -07003636
Andy McFadden8e696dc2009-07-24 15:28:16 -07003637 Object* platformAddr = callResult.l;
Andy McFadden72d61fb2009-06-24 16:56:06 -07003638 if (platformAddr == NULL) {
Andy McFadden8e696dc2009-07-24 15:28:16 -07003639 LOGV("Got request for address of non-direct buffer\n");
Andy McFadden72d61fb2009-06-24 16:56:06 -07003640 goto bail;
3641 }
3642
Andy McFadden8e5c7842009-07-23 17:47:18 -07003643 /*
3644 * Extract the address from the PlatformAddress object. Instead of
3645 * calling the toLong() method, just grab the field directly. This
3646 * is faster but more fragile.
3647 */
3648 result = (void*) dvmGetFieldInt(platformAddr,
3649 gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr);
The Android Open Source Project99409882009-03-18 22:20:24 -07003650
Andy McFadden8e696dc2009-07-24 15:28:16 -07003651 //LOGI("slow path for %p --> %p\n", buf, result);
3652
The Android Open Source Project99409882009-03-18 22:20:24 -07003653bail:
Andy McFadden8e5c7842009-07-23 17:47:18 -07003654 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07003655 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003656}
3657
3658/*
3659 * Get the capacity of the buffer for the specified java.nio.Buffer.
3660 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003661 * Returns -1 if the object is not a direct buffer. (We actually skip
3662 * this check, since it's expensive to determine, and just return the
3663 * capacity regardless.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003664 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003665static jlong GetDirectBufferCapacity(JNIEnv* env, jobject jbuf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003666{
Andy McFadden8e5c7842009-07-23 17:47:18 -07003667 JNI_ENTER();
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003668
Andy McFadden8e5c7842009-07-23 17:47:18 -07003669 /*
3670 * The capacity is always in the Buffer.capacity field.
3671 *
3672 * (The "check" version should verify that this is actually a Buffer,
3673 * but we're not required to do so here.)
3674 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003675 Object* buf = dvmDecodeIndirectRef(env, jbuf);
3676 jlong result = dvmGetFieldInt(buf, gDvm.offJavaNioBuffer_capacity);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003677
Andy McFadden8e5c7842009-07-23 17:47:18 -07003678 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07003679 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003680}
3681
3682
3683/*
3684 * ===========================================================================
3685 * JNI invocation functions
3686 * ===========================================================================
3687 */
3688
3689/*
3690 * Handle AttachCurrentThread{AsDaemon}.
3691 *
3692 * We need to make sure the VM is actually running. For example, if we start
3693 * up, issue an Attach, and the VM exits almost immediately, by the time the
3694 * attaching happens the VM could already be shutting down.
3695 *
3696 * It's hard to avoid a race condition here because we don't want to hold
3697 * a lock across the entire operation. What we can do is temporarily
3698 * increment the thread count to prevent a VM exit.
3699 *
3700 * This could potentially still have problems if a daemon thread calls here
3701 * while the VM is shutting down. dvmThreadSelf() will work, since it just
3702 * uses pthread TLS, but dereferencing "vm" could fail. Such is life when
3703 * you shut down a VM while threads are still running inside it.
3704 *
3705 * Remember that some code may call this as a way to find the per-thread
3706 * JNIEnv pointer. Don't do excess work for that case.
3707 */
3708static jint attachThread(JavaVM* vm, JNIEnv** p_env, void* thr_args,
3709 bool isDaemon)
3710{
3711 JavaVMAttachArgs* args = (JavaVMAttachArgs*) thr_args;
3712 Thread* self;
3713 bool result = false;
3714
3715 /*
3716 * Return immediately if we're already one with the VM.
3717 */
3718 self = dvmThreadSelf();
3719 if (self != NULL) {
3720 *p_env = self->jniEnv;
3721 return JNI_OK;
3722 }
3723
3724 /*
3725 * No threads allowed in zygote mode.
3726 */
3727 if (gDvm.zygote) {
3728 return JNI_ERR;
3729 }
3730
3731 /* increment the count to keep the VM from bailing while we run */
3732 dvmLockThreadList(NULL);
3733 if (gDvm.nonDaemonThreadCount == 0) {
3734 // dead or dying
3735 LOGV("Refusing to attach thread '%s' -- VM is shutting down\n",
3736 (thr_args == NULL) ? "(unknown)" : args->name);
3737 dvmUnlockThreadList();
3738 return JNI_ERR;
3739 }
3740 gDvm.nonDaemonThreadCount++;
3741 dvmUnlockThreadList();
3742
3743 /* tweak the JavaVMAttachArgs as needed */
3744 JavaVMAttachArgs argsCopy;
3745 if (args == NULL) {
3746 /* allow the v1.1 calling convention */
3747 argsCopy.version = JNI_VERSION_1_2;
3748 argsCopy.name = NULL;
3749 argsCopy.group = dvmGetMainThreadGroup();
3750 } else {
3751 assert(args->version >= JNI_VERSION_1_2);
3752
3753 argsCopy.version = args->version;
3754 argsCopy.name = args->name;
3755 if (args->group != NULL)
3756 argsCopy.group = args->group;
3757 else
3758 argsCopy.group = dvmGetMainThreadGroup();
3759 }
3760
3761 result = dvmAttachCurrentThread(&argsCopy, isDaemon);
3762
3763 /* restore the count */
3764 dvmLockThreadList(NULL);
3765 gDvm.nonDaemonThreadCount--;
3766 dvmUnlockThreadList();
3767
3768 /*
3769 * Change the status to indicate that we're out in native code. This
3770 * call is not guarded with state-change macros, so we have to do it
3771 * by hand.
3772 */
3773 if (result) {
3774 self = dvmThreadSelf();
3775 assert(self != NULL);
3776 dvmChangeStatus(self, THREAD_NATIVE);
3777 *p_env = self->jniEnv;
3778 return JNI_OK;
3779 } else {
3780 return JNI_ERR;
3781 }
3782}
3783
3784/*
3785 * Attach the current thread to the VM. If the thread is already attached,
3786 * this is a no-op.
3787 */
3788static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args)
3789{
3790 return attachThread(vm, p_env, thr_args, false);
3791}
3792
3793/*
3794 * Like AttachCurrentThread, but set the "daemon" flag.
3795 */
3796static jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env,
3797 void* thr_args)
3798{
3799 return attachThread(vm, p_env, thr_args, true);
3800}
3801
3802/*
3803 * Dissociate the current thread from the VM.
3804 */
3805static jint DetachCurrentThread(JavaVM* vm)
3806{
3807 Thread* self = dvmThreadSelf();
3808
3809 if (self == NULL) /* not attached, can't do anything */
3810 return JNI_ERR;
3811
3812 /* switch to "running" to check for suspension */
3813 dvmChangeStatus(self, THREAD_RUNNING);
3814
3815 /* detach the thread */
3816 dvmDetachCurrentThread();
3817
3818 /* (no need to change status back -- we have no status) */
3819 return JNI_OK;
3820}
3821
3822/*
3823 * If current thread is attached to VM, return the associated JNIEnv.
3824 * Otherwise, stuff NULL in and return JNI_EDETACHED.
3825 *
3826 * JVMTI overloads this by specifying a magic value for "version", so we
3827 * do want to check that here.
3828 */
3829static jint GetEnv(JavaVM* vm, void** env, jint version)
3830{
3831 Thread* self = dvmThreadSelf();
3832
3833 if (version < JNI_VERSION_1_1 || version > JNI_VERSION_1_6)
3834 return JNI_EVERSION;
3835
3836 if (self == NULL) {
3837 *env = NULL;
3838 } else {
3839 /* TODO: status change is probably unnecessary */
3840 dvmChangeStatus(self, THREAD_RUNNING);
3841 *env = (void*) dvmGetThreadJNIEnv(self);
3842 dvmChangeStatus(self, THREAD_NATIVE);
3843 }
3844 if (*env == NULL)
3845 return JNI_EDETACHED;
3846 else
3847 return JNI_OK;
3848}
3849
3850/*
3851 * Destroy the VM. This may be called from any thread.
3852 *
3853 * If the current thread is attached, wait until the current thread is
3854 * the only non-daemon user-level thread. If the current thread is not
3855 * attached, we attach it and do the processing as usual. (If the attach
3856 * fails, it's probably because all the non-daemon threads have already
3857 * exited and the VM doesn't want to let us back in.)
3858 *
3859 * TODO: we don't really deal with the situation where more than one thread
3860 * has called here. One thread wins, the other stays trapped waiting on
3861 * the condition variable forever. Not sure this situation is interesting
3862 * in real life.
3863 */
3864static jint DestroyJavaVM(JavaVM* vm)
3865{
3866 JavaVMExt* ext = (JavaVMExt*) vm;
3867 Thread* self;
3868
3869 if (ext == NULL)
3870 return JNI_ERR;
3871
3872 LOGD("DestroyJavaVM waiting for non-daemon threads to exit\n");
3873
3874 /*
3875 * Sleep on a condition variable until it's okay to exit.
3876 */
3877 self = dvmThreadSelf();
3878 if (self == NULL) {
3879 JNIEnv* tmpEnv;
3880 if (AttachCurrentThread(vm, &tmpEnv, NULL) != JNI_OK) {
3881 LOGV("Unable to reattach main for Destroy; assuming VM is "
3882 "shutting down (count=%d)\n",
3883 gDvm.nonDaemonThreadCount);
3884 goto shutdown;
3885 } else {
3886 LOGV("Attached to wait for shutdown in Destroy\n");
3887 }
3888 }
3889 dvmChangeStatus(self, THREAD_VMWAIT);
3890
3891 dvmLockThreadList(self);
3892 gDvm.nonDaemonThreadCount--; // remove current thread from count
3893
3894 while (gDvm.nonDaemonThreadCount > 0)
3895 pthread_cond_wait(&gDvm.vmExitCond, &gDvm.threadListLock);
3896
3897 dvmUnlockThreadList();
3898 self = NULL;
3899
3900shutdown:
3901 // TODO: call System.exit() to run any registered shutdown hooks
3902 // (this may not return -- figure out how this should work)
3903
3904 LOGD("DestroyJavaVM shutting VM down\n");
3905 dvmShutdown();
3906
3907 // TODO - free resources associated with JNI-attached daemon threads
3908 free(ext->envList);
3909 free(ext);
3910
3911 return JNI_OK;
3912}
3913
3914
3915/*
3916 * ===========================================================================
3917 * Function tables
3918 * ===========================================================================
3919 */
3920
3921static const struct JNINativeInterface gNativeInterface = {
3922 NULL,
3923 NULL,
3924 NULL,
3925 NULL,
3926
3927 GetVersion,
3928
3929 DefineClass,
3930 FindClass,
3931
3932 FromReflectedMethod,
3933 FromReflectedField,
3934 ToReflectedMethod,
3935
3936 GetSuperclass,
3937 IsAssignableFrom,
3938
3939 ToReflectedField,
3940
3941 Throw,
3942 ThrowNew,
3943 ExceptionOccurred,
3944 ExceptionDescribe,
3945 ExceptionClear,
3946 FatalError,
3947
3948 PushLocalFrame,
3949 PopLocalFrame,
3950
3951 NewGlobalRef,
3952 DeleteGlobalRef,
3953 DeleteLocalRef,
3954 IsSameObject,
3955 NewLocalRef,
3956 EnsureLocalCapacity,
3957
3958 AllocObject,
3959 NewObject,
3960 NewObjectV,
3961 NewObjectA,
3962
3963 GetObjectClass,
3964 IsInstanceOf,
3965
3966 GetMethodID,
3967
3968 CallObjectMethod,
3969 CallObjectMethodV,
3970 CallObjectMethodA,
3971 CallBooleanMethod,
3972 CallBooleanMethodV,
3973 CallBooleanMethodA,
3974 CallByteMethod,
3975 CallByteMethodV,
3976 CallByteMethodA,
3977 CallCharMethod,
3978 CallCharMethodV,
3979 CallCharMethodA,
3980 CallShortMethod,
3981 CallShortMethodV,
3982 CallShortMethodA,
3983 CallIntMethod,
3984 CallIntMethodV,
3985 CallIntMethodA,
3986 CallLongMethod,
3987 CallLongMethodV,
3988 CallLongMethodA,
3989 CallFloatMethod,
3990 CallFloatMethodV,
3991 CallFloatMethodA,
3992 CallDoubleMethod,
3993 CallDoubleMethodV,
3994 CallDoubleMethodA,
3995 CallVoidMethod,
3996 CallVoidMethodV,
3997 CallVoidMethodA,
3998
3999 CallNonvirtualObjectMethod,
4000 CallNonvirtualObjectMethodV,
4001 CallNonvirtualObjectMethodA,
4002 CallNonvirtualBooleanMethod,
4003 CallNonvirtualBooleanMethodV,
4004 CallNonvirtualBooleanMethodA,
4005 CallNonvirtualByteMethod,
4006 CallNonvirtualByteMethodV,
4007 CallNonvirtualByteMethodA,
4008 CallNonvirtualCharMethod,
4009 CallNonvirtualCharMethodV,
4010 CallNonvirtualCharMethodA,
4011 CallNonvirtualShortMethod,
4012 CallNonvirtualShortMethodV,
4013 CallNonvirtualShortMethodA,
4014 CallNonvirtualIntMethod,
4015 CallNonvirtualIntMethodV,
4016 CallNonvirtualIntMethodA,
4017 CallNonvirtualLongMethod,
4018 CallNonvirtualLongMethodV,
4019 CallNonvirtualLongMethodA,
4020 CallNonvirtualFloatMethod,
4021 CallNonvirtualFloatMethodV,
4022 CallNonvirtualFloatMethodA,
4023 CallNonvirtualDoubleMethod,
4024 CallNonvirtualDoubleMethodV,
4025 CallNonvirtualDoubleMethodA,
4026 CallNonvirtualVoidMethod,
4027 CallNonvirtualVoidMethodV,
4028 CallNonvirtualVoidMethodA,
4029
4030 GetFieldID,
4031
4032 GetObjectField,
4033 GetBooleanField,
4034 GetByteField,
4035 GetCharField,
4036 GetShortField,
4037 GetIntField,
4038 GetLongField,
4039 GetFloatField,
4040 GetDoubleField,
4041 SetObjectField,
4042 SetBooleanField,
4043 SetByteField,
4044 SetCharField,
4045 SetShortField,
4046 SetIntField,
4047 SetLongField,
4048 SetFloatField,
4049 SetDoubleField,
4050
4051 GetStaticMethodID,
4052
4053 CallStaticObjectMethod,
4054 CallStaticObjectMethodV,
4055 CallStaticObjectMethodA,
4056 CallStaticBooleanMethod,
4057 CallStaticBooleanMethodV,
4058 CallStaticBooleanMethodA,
4059 CallStaticByteMethod,
4060 CallStaticByteMethodV,
4061 CallStaticByteMethodA,
4062 CallStaticCharMethod,
4063 CallStaticCharMethodV,
4064 CallStaticCharMethodA,
4065 CallStaticShortMethod,
4066 CallStaticShortMethodV,
4067 CallStaticShortMethodA,
4068 CallStaticIntMethod,
4069 CallStaticIntMethodV,
4070 CallStaticIntMethodA,
4071 CallStaticLongMethod,
4072 CallStaticLongMethodV,
4073 CallStaticLongMethodA,
4074 CallStaticFloatMethod,
4075 CallStaticFloatMethodV,
4076 CallStaticFloatMethodA,
4077 CallStaticDoubleMethod,
4078 CallStaticDoubleMethodV,
4079 CallStaticDoubleMethodA,
4080 CallStaticVoidMethod,
4081 CallStaticVoidMethodV,
4082 CallStaticVoidMethodA,
4083
4084 GetStaticFieldID,
4085
4086 GetStaticObjectField,
4087 GetStaticBooleanField,
4088 GetStaticByteField,
4089 GetStaticCharField,
4090 GetStaticShortField,
4091 GetStaticIntField,
4092 GetStaticLongField,
4093 GetStaticFloatField,
4094 GetStaticDoubleField,
4095
4096 SetStaticObjectField,
4097 SetStaticBooleanField,
4098 SetStaticByteField,
4099 SetStaticCharField,
4100 SetStaticShortField,
4101 SetStaticIntField,
4102 SetStaticLongField,
4103 SetStaticFloatField,
4104 SetStaticDoubleField,
4105
4106 NewString,
4107
4108 GetStringLength,
4109 GetStringChars,
4110 ReleaseStringChars,
4111
4112 NewStringUTF,
4113 GetStringUTFLength,
4114 GetStringUTFChars,
4115 ReleaseStringUTFChars,
4116
4117 GetArrayLength,
4118 NewObjectArray,
4119 GetObjectArrayElement,
4120 SetObjectArrayElement,
4121
4122 NewBooleanArray,
4123 NewByteArray,
4124 NewCharArray,
4125 NewShortArray,
4126 NewIntArray,
4127 NewLongArray,
4128 NewFloatArray,
4129 NewDoubleArray,
4130
4131 GetBooleanArrayElements,
4132 GetByteArrayElements,
4133 GetCharArrayElements,
4134 GetShortArrayElements,
4135 GetIntArrayElements,
4136 GetLongArrayElements,
4137 GetFloatArrayElements,
4138 GetDoubleArrayElements,
4139
4140 ReleaseBooleanArrayElements,
4141 ReleaseByteArrayElements,
4142 ReleaseCharArrayElements,
4143 ReleaseShortArrayElements,
4144 ReleaseIntArrayElements,
4145 ReleaseLongArrayElements,
4146 ReleaseFloatArrayElements,
4147 ReleaseDoubleArrayElements,
4148
4149 GetBooleanArrayRegion,
4150 GetByteArrayRegion,
4151 GetCharArrayRegion,
4152 GetShortArrayRegion,
4153 GetIntArrayRegion,
4154 GetLongArrayRegion,
4155 GetFloatArrayRegion,
4156 GetDoubleArrayRegion,
4157 SetBooleanArrayRegion,
4158 SetByteArrayRegion,
4159 SetCharArrayRegion,
4160 SetShortArrayRegion,
4161 SetIntArrayRegion,
4162 SetLongArrayRegion,
4163 SetFloatArrayRegion,
4164 SetDoubleArrayRegion,
4165
4166 RegisterNatives,
4167 UnregisterNatives,
4168
4169 MonitorEnter,
4170 MonitorExit,
4171
4172 GetJavaVM,
4173
4174 GetStringRegion,
4175 GetStringUTFRegion,
4176
4177 GetPrimitiveArrayCritical,
4178 ReleasePrimitiveArrayCritical,
4179
4180 GetStringCritical,
4181 ReleaseStringCritical,
4182
4183 NewWeakGlobalRef,
4184 DeleteWeakGlobalRef,
4185
4186 ExceptionCheck,
4187
4188 NewDirectByteBuffer,
4189 GetDirectBufferAddress,
4190 GetDirectBufferCapacity,
4191
4192 GetObjectRefType
4193};
4194static const struct JNIInvokeInterface gInvokeInterface = {
4195 NULL,
4196 NULL,
4197 NULL,
4198
4199 DestroyJavaVM,
4200 AttachCurrentThread,
4201 DetachCurrentThread,
4202
4203 GetEnv,
4204
4205 AttachCurrentThreadAsDaemon,
4206};
4207
4208
4209/*
4210 * ===========================================================================
4211 * VM/Env creation
4212 * ===========================================================================
4213 */
4214
4215/*
4216 * Enable "checked JNI" after the VM has partially started. This must
4217 * only be called in "zygote" mode, when we have one thread running.
Andy McFadden59b61772009-05-13 16:44:34 -07004218 *
4219 * This doesn't attempt to rewrite the JNI call bridge associated with
4220 * native methods, so we won't get those checks for any methods that have
4221 * already been resolved.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08004222 */
4223void dvmLateEnableCheckedJni(void)
4224{
4225 JNIEnvExt* extEnv;
4226 JavaVMExt* extVm;
Andy McFaddenab00d452009-08-19 07:21:41 -07004227
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08004228 extEnv = dvmGetJNIEnvForThread();
4229 if (extEnv == NULL) {
4230 LOGE("dvmLateEnableCheckedJni: thread has no JNIEnv\n");
4231 return;
4232 }
4233 extVm = extEnv->vm;
4234 assert(extVm != NULL);
4235
4236 if (!extVm->useChecked) {
4237 LOGD("Late-enabling CheckJNI\n");
4238 dvmUseCheckedJniVm(extVm);
4239 extVm->useChecked = true;
4240 dvmUseCheckedJniEnv(extEnv);
4241
4242 /* currently no way to pick up jniopts features */
4243 } else {
4244 LOGD("Not late-enabling CheckJNI (already on)\n");
4245 }
4246}
4247
4248/*
4249 * Not supported.
4250 */
4251jint JNI_GetDefaultJavaVMInitArgs(void* vm_args)
4252{
4253 return JNI_ERR;
4254}
4255
4256/*
4257 * Return a buffer full of created VMs.
4258 *
4259 * We always have zero or one.
4260 */
4261jint JNI_GetCreatedJavaVMs(JavaVM** vmBuf, jsize bufLen, jsize* nVMs)
4262{
4263 if (gDvm.vmList != NULL) {
4264 *nVMs = 1;
4265
4266 if (bufLen > 0)
4267 *vmBuf++ = gDvm.vmList;
4268 } else {
4269 *nVMs = 0;
4270 }
4271
4272 return JNI_OK;
4273}
4274
4275
4276/*
4277 * Create a new VM instance.
4278 *
4279 * The current thread becomes the main VM thread. We return immediately,
4280 * which effectively means the caller is executing in a native method.
4281 */
4282jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args)
4283{
4284 const JavaVMInitArgs* args = (JavaVMInitArgs*) vm_args;
4285 JNIEnvExt* pEnv = NULL;
4286 JavaVMExt* pVM = NULL;
4287 const char** argv;
4288 int argc = 0;
4289 int i, curOpt;
4290 int result = JNI_ERR;
4291 bool checkJni = false;
4292 bool warnError = true;
4293 bool forceDataCopy = false;
4294
4295 if (args->version < JNI_VERSION_1_2)
4296 return JNI_EVERSION;
4297
4298 // TODO: don't allow creation of multiple VMs -- one per customer for now
4299
4300 /* zero globals; not strictly necessary the first time a VM is started */
4301 memset(&gDvm, 0, sizeof(gDvm));
4302
4303 /*
4304 * Set up structures for JNIEnv and VM.
4305 */
4306 //pEnv = (JNIEnvExt*) malloc(sizeof(JNIEnvExt));
4307 pVM = (JavaVMExt*) malloc(sizeof(JavaVMExt));
4308
4309 //memset(pEnv, 0, sizeof(JNIEnvExt));
4310 //pEnv->funcTable = &gNativeInterface;
4311 //pEnv->vm = pVM;
4312 memset(pVM, 0, sizeof(JavaVMExt));
4313 pVM->funcTable = &gInvokeInterface;
4314 pVM->envList = pEnv;
4315 dvmInitMutex(&pVM->envListLock);
4316
4317 argv = (const char**) malloc(sizeof(char*) * (args->nOptions));
4318 memset(argv, 0, sizeof(char*) * (args->nOptions));
4319
4320 curOpt = 0;
4321
4322 /*
4323 * Convert JNI args to argv.
4324 *
4325 * We have to pull out vfprintf/exit/abort, because they use the
4326 * "extraInfo" field to pass function pointer "hooks" in. We also
4327 * look for the -Xcheck:jni stuff here.
4328 */
4329 for (i = 0; i < args->nOptions; i++) {
4330 const char* optStr = args->options[i].optionString;
4331
4332 if (optStr == NULL) {
4333 fprintf(stderr, "ERROR: arg %d string was null\n", i);
4334 goto bail;
4335 } else if (strcmp(optStr, "vfprintf") == 0) {
4336 gDvm.vfprintfHook = args->options[i].extraInfo;
4337 } else if (strcmp(optStr, "exit") == 0) {
4338 gDvm.exitHook = args->options[i].extraInfo;
4339 } else if (strcmp(optStr, "abort") == 0) {
4340 gDvm.abortHook = args->options[i].extraInfo;
4341 } else if (strcmp(optStr, "-Xcheck:jni") == 0) {
4342 checkJni = true;
4343 } else if (strncmp(optStr, "-Xjniopts:", 10) == 0) {
4344 const char* jniOpts = optStr + 9;
4345 while (jniOpts != NULL) {
4346 jniOpts++; /* skip past ':' or ',' */
4347 if (strncmp(jniOpts, "warnonly", 8) == 0) {
4348 warnError = false;
4349 } else if (strncmp(jniOpts, "forcecopy", 9) == 0) {
4350 forceDataCopy = true;
4351 } else {
4352 LOGW("unknown jni opt starting at '%s'\n", jniOpts);
4353 }
4354 jniOpts = strchr(jniOpts, ',');
4355 }
4356 } else {
4357 /* regular option */
4358 argv[curOpt++] = optStr;
4359 }
4360 }
4361 argc = curOpt;
4362
4363 if (checkJni) {
4364 dvmUseCheckedJniVm(pVM);
4365 pVM->useChecked = true;
4366 }
4367 pVM->warnError = warnError;
4368 pVM->forceDataCopy = forceDataCopy;
4369
4370 /* set this up before initializing VM, so it can create some JNIEnvs */
4371 gDvm.vmList = (JavaVM*) pVM;
4372
4373 /*
4374 * Create an env for main thread. We need to have something set up
4375 * here because some of the class initialization we do when starting
4376 * up the VM will call into native code.
4377 */
4378 pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL);
4379
4380 /* initialize VM */
4381 gDvm.initializing = true;
4382 if (dvmStartup(argc, argv, args->ignoreUnrecognized, (JNIEnv*)pEnv) != 0) {
4383 free(pEnv);
4384 free(pVM);
4385 goto bail;
4386 }
4387
4388 /*
4389 * Success! Return stuff to caller.
4390 */
4391 dvmChangeStatus(NULL, THREAD_NATIVE);
4392 *p_env = (JNIEnv*) pEnv;
4393 *p_vm = (JavaVM*) pVM;
4394 result = JNI_OK;
4395
4396bail:
4397 gDvm.initializing = false;
4398 if (result == JNI_OK)
4399 LOGV("JNI_CreateJavaVM succeeded\n");
4400 else
4401 LOGW("JNI_CreateJavaVM failed\n");
4402 free(argv);
4403 return result;
4404}