blob: 1dcea10708aa75afd731d1a7563a5922cee3cf25 [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
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800444}
445
446
447/*
448 * Find the JNIEnv associated with the current thread.
449 *
450 * Currently stored in the Thread struct. Could also just drop this into
451 * thread-local storage.
452 */
453JNIEnvExt* dvmGetJNIEnvForThread(void)
454{
455 Thread* self = dvmThreadSelf();
456 if (self == NULL)
457 return NULL;
458 return (JNIEnvExt*) dvmGetThreadJNIEnv(self);
459}
460
461/*
462 * Create a new JNIEnv struct and add it to the VM's list.
463 *
464 * "self" will be NULL for the main thread, since the VM hasn't started
465 * yet; the value will be filled in later.
466 */
467JNIEnv* dvmCreateJNIEnv(Thread* self)
468{
469 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
470 JNIEnvExt* newEnv;
471
472 //if (self != NULL)
473 // LOGI("Ent CreateJNIEnv: threadid=%d %p\n", self->threadId, self);
474
475 assert(vm != NULL);
476
477 newEnv = (JNIEnvExt*) calloc(1, sizeof(JNIEnvExt));
478 newEnv->funcTable = &gNativeInterface;
479 newEnv->vm = vm;
480 newEnv->forceDataCopy = vm->forceDataCopy;
481 if (self != NULL) {
482 dvmSetJniEnvThreadId((JNIEnv*) newEnv, self);
483 assert(newEnv->envThreadId != 0);
484 } else {
485 /* make it obvious if we fail to initialize these later */
486 newEnv->envThreadId = 0x77777775;
487 newEnv->self = (Thread*) 0x77777779;
488 }
489 if (vm->useChecked)
490 dvmUseCheckedJniEnv(newEnv);
491
492 dvmLockMutex(&vm->envListLock);
493
494 /* insert at head of list */
495 newEnv->next = vm->envList;
496 assert(newEnv->prev == NULL);
497 if (vm->envList == NULL) // rare, but possible
498 vm->envList = newEnv;
499 else
500 vm->envList->prev = newEnv;
501 vm->envList = newEnv;
502
503 dvmUnlockMutex(&vm->envListLock);
504
505 //if (self != NULL)
506 // LOGI("Xit CreateJNIEnv: threadid=%d %p\n", self->threadId, self);
507 return (JNIEnv*) newEnv;
508}
509
510/*
511 * Remove a JNIEnv struct from the list and free it.
512 */
513void dvmDestroyJNIEnv(JNIEnv* env)
514{
515 JNIEnvExt* extEnv = (JNIEnvExt*) env;
516 JavaVMExt* vm = extEnv->vm;
517 Thread* self;
518
519 if (env == NULL)
520 return;
521
522 self = dvmThreadSelf();
523 assert(self != NULL);
524
525 //LOGI("Ent DestroyJNIEnv: threadid=%d %p\n", self->threadId, self);
526
527 dvmLockMutex(&vm->envListLock);
528
529 if (extEnv == vm->envList) {
530 assert(extEnv->prev == NULL);
531 vm->envList = extEnv->next;
532 } else {
533 assert(extEnv->prev != NULL);
534 extEnv->prev->next = extEnv->next;
535 }
536 if (extEnv->next != NULL)
537 extEnv->next->prev = extEnv->prev;
538
539 dvmUnlockMutex(&extEnv->vm->envListLock);
540
541 free(env);
542 //LOGI("Xit DestroyJNIEnv: threadid=%d %p\n", self->threadId, self);
543}
544
545
546/*
547 * Retrieve the ReferenceTable struct for the current thread.
548 *
Andy McFaddenab00d452009-08-19 07:21:41 -0700549 * Going through "env" rather than dvmThreadSelf() is faster but will
550 * get weird if the JNI code is passing the wrong JNIEnv around.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800551 */
Andy McFaddend5ab7262009-08-25 07:19:34 -0700552#ifdef USE_INDIRECT_REF
553static inline IndirectRefTable* getLocalRefTable(JNIEnv* env)
Andy McFaddend5ab7262009-08-25 07:19:34 -0700554#else
Andy McFaddenab00d452009-08-19 07:21:41 -0700555static inline ReferenceTable* getLocalRefTable(JNIEnv* env)
Andy McFaddeneb9cbc32009-08-28 14:45:12 -0700556#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800557{
Andy McFaddenab00d452009-08-19 07:21:41 -0700558 //return &dvmThreadSelf()->jniLocalRefTable;
559 return &((JNIEnvExt*)env)->self->jniLocalRefTable;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800560}
Andy McFaddend5ab7262009-08-25 07:19:34 -0700561
Andy McFaddeneb9cbc32009-08-28 14:45:12 -0700562#ifdef USE_INDIRECT_REF
Andy McFaddend5ab7262009-08-25 07:19:34 -0700563/*
564 * Convert an indirect reference to an Object reference. The indirect
565 * reference may be local, global, or weak-global.
566 *
567 * If "jobj" is NULL or an invalid indirect reference, this returns NULL.
568 */
569Object* dvmDecodeIndirectRef(JNIEnv* env, jobject jobj)
570{
Andy McFaddend5ab7262009-08-25 07:19:34 -0700571 if (jobj == NULL)
572 return NULL;
573
574 Object* result;
575
576 switch (dvmGetIndirectRefType(jobj)) {
577 case kIndirectKindLocal:
578 {
579 IndirectRefTable* pRefTable = getLocalRefTable(env);
580 result = dvmGetFromIndirectRefTable(pRefTable, jobj);
581 }
582 break;
583 case kIndirectKindGlobal:
584 {
585 // TODO: find a way to avoid the mutex activity here
586 IndirectRefTable* pRefTable = &gDvm.jniGlobalRefTable;
587 dvmLockMutex(&gDvm.jniGlobalRefLock);
588 result = dvmGetFromIndirectRefTable(pRefTable, jobj);
589 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
590 }
591 break;
592 case kIndirectKindWeakGlobal:
593 {
Andy McFaddenb18992f2009-09-25 10:42:15 -0700594 // TODO: implement
Andy McFaddend5ab7262009-08-25 07:19:34 -0700595 LOGE("weak-global not yet supported\n");
596 result = NULL;
597 dvmAbort();
598 }
599 break;
600 case kIndirectKindInvalid:
601 default:
602 LOGW("Invalid indirect reference %p in decodeIndirectRef\n", jobj);
603 dvmAbort();
604 result = NULL;
605 break;
606 }
607
608 return result;
Andy McFaddend5ab7262009-08-25 07:19:34 -0700609}
Andy McFaddeneb9cbc32009-08-28 14:45:12 -0700610#else
611 /* use trivial inline in JniInternal.h for performance */
612#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800613
614/*
615 * Add a local reference for an object to the current stack frame. When
616 * the native function returns, the reference will be discarded.
617 *
618 * We need to allow the same reference to be added multiple times.
619 *
620 * This will be called on otherwise unreferenced objects. We cannot do
621 * GC allocations here, and it's best if we don't grab a mutex.
622 *
623 * Returns the local reference (currently just the same pointer that was
624 * passed in), or NULL on failure.
625 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700626static jobject addLocalReference(JNIEnv* env, Object* obj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800627{
628 if (obj == NULL)
629 return NULL;
630
Andy McFaddend5ab7262009-08-25 07:19:34 -0700631 jobject jobj;
632
633#ifdef USE_INDIRECT_REF
634 IndirectRefTable* pRefTable = getLocalRefTable(env);
635 void* curFrame = ((JNIEnvExt*)env)->self->curFrame;
636 u4 cookie = SAVEAREA_FROM_FP(curFrame)->xtra.localRefCookie;
637
638 jobj = (jobject) dvmAddToIndirectRefTable(pRefTable, cookie, obj);
639 if (jobj == NULL) {
640 dvmDumpIndirectRefTable(pRefTable, "JNI local");
641 LOGE("Failed adding to JNI local ref table (has %d entries)\n",
642 (int) dvmIndirectRefTableEntries(pRefTable));
643 dvmDumpThread(dvmThreadSelf(), false);
644 dvmAbort(); // spec says call FatalError; this is equivalent
645 } else {
646 LOGVV("LREF add %p (%s.%s) (ent=%d)\n", obj,
647 dvmGetCurrentJNIMethod()->clazz->descriptor,
648 dvmGetCurrentJNIMethod()->name,
649 (int) dvmReferenceTableEntries(pRefTable));
650 }
651#else
Andy McFaddenab00d452009-08-19 07:21:41 -0700652 ReferenceTable* pRefTable = getLocalRefTable(env);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800653
Andy McFaddenab00d452009-08-19 07:21:41 -0700654 if (!dvmAddToReferenceTable(pRefTable, obj)) {
655 dvmDumpReferenceTable(pRefTable, "JNI local");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800656 LOGE("Failed adding to JNI local ref table (has %d entries)\n",
Andy McFaddenab00d452009-08-19 07:21:41 -0700657 (int) dvmReferenceTableEntries(pRefTable));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800658 dvmDumpThread(dvmThreadSelf(), false);
659 dvmAbort(); // spec says call FatalError; this is equivalent
660 } else {
Andy McFadden0083d372009-08-21 14:44:04 -0700661 LOGVV("LREF add %p (%s.%s) (ent=%d)\n", obj,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800662 dvmGetCurrentJNIMethod()->clazz->descriptor,
Andy McFadden0083d372009-08-21 14:44:04 -0700663 dvmGetCurrentJNIMethod()->name,
664 (int) dvmReferenceTableEntries(pRefTable));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800665 }
666
Andy McFaddend5ab7262009-08-25 07:19:34 -0700667 jobj = (jobject) obj;
668#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800669
Andy McFaddend5ab7262009-08-25 07:19:34 -0700670 return jobj;
Andy McFaddenab00d452009-08-19 07:21:41 -0700671}
672
673/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800674 * Ensure that at least "capacity" references can be held in the local
675 * refs table of the current thread.
676 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700677static bool ensureLocalCapacity(JNIEnv* env, int capacity)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800678{
Andy McFaddend5ab7262009-08-25 07:19:34 -0700679#ifdef USE_INDIRECT_REF
680 IndirectRefTable* pRefTable = getLocalRefTable(env);
681 int numEntries = dvmIndirectRefTableEntries(pRefTable);
682 // TODO: this isn't quite right, since "numEntries" includes holes
683 return ((kJniLocalRefMax - numEntries) >= capacity);
684#else
Andy McFaddenab00d452009-08-19 07:21:41 -0700685 ReferenceTable* pRefTable = getLocalRefTable(env);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800686
Andy McFaddenab00d452009-08-19 07:21:41 -0700687 return (kJniLocalRefMax - (pRefTable->nextEntry - pRefTable->table) >= capacity);
Andy McFaddend5ab7262009-08-25 07:19:34 -0700688#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800689}
690
691/*
692 * Explicitly delete a reference from the local list.
693 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700694static void deleteLocalReference(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800695{
Andy McFaddenab00d452009-08-19 07:21:41 -0700696 if (jobj == NULL)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800697 return;
698
Andy McFaddend5ab7262009-08-25 07:19:34 -0700699#ifdef USE_INDIRECT_REF
700 IndirectRefTable* pRefTable = getLocalRefTable(env);
701 Thread* self = ((JNIEnvExt*)env)->self;
702 u4 cookie = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefCookie;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800703
Andy McFaddend5ab7262009-08-25 07:19:34 -0700704 if (!dvmRemoveFromIndirectRefTable(pRefTable, cookie, jobj)) {
705 /*
706 * Attempting to delete a local reference that is not in the
707 * topmost local reference frame is a no-op. DeleteLocalRef returns
708 * void and doesn't throw any exceptions, but we should probably
709 * complain about it so the user will notice that things aren't
710 * going quite the way they expect.
711 */
712 LOGW("JNI WARNING: DeleteLocalRef(%p) failed to find entry\n", jobj);
713 }
714#else
715 ReferenceTable* pRefTable = getLocalRefTable(env);
716 Thread* self = ((JNIEnvExt*)env)->self;
717 Object** bottom = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefCookie;
718
719 if (!dvmRemoveFromReferenceTable(pRefTable, bottom, (Object*) jobj)) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800720 /*
721 * Attempting to delete a local reference that is not in the
722 * topmost local reference frame is a no-op. DeleteLocalRef returns
723 * void and doesn't throw any exceptions, but we should probably
724 * complain about it so the user will notice that things aren't
725 * going quite the way they expect.
726 */
727 LOGW("JNI WARNING: DeleteLocalRef(%p) failed to find entry (valid=%d)\n",
Andy McFaddenab00d452009-08-19 07:21:41 -0700728 jobj, dvmIsValidObject((Object*) jobj));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800729 }
Andy McFaddend5ab7262009-08-25 07:19:34 -0700730#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800731}
732
733/*
734 * Add a global reference for an object.
735 *
736 * We may add the same object more than once. Add/remove calls are paired,
737 * so it needs to appear on the list multiple times.
738 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700739static jobject addGlobalReference(Object* obj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800740{
741 if (obj == NULL)
742 return NULL;
743
744 //LOGI("adding obj=%p\n", obj);
745 //dvmDumpThread(dvmThreadSelf(), false);
746
747 if (false && ((Object*)obj)->clazz == gDvm.classJavaLangClass) {
748 ClassObject* clazz = (ClassObject*) obj;
749 LOGI("-------\n");
750 LOGI("Adding global ref on class %s\n", clazz->descriptor);
751 dvmDumpThread(dvmThreadSelf(), false);
752 }
753 if (false && ((Object*)obj)->clazz == gDvm.classJavaLangString) {
754 StringObject* strObj = (StringObject*) obj;
755 char* str = dvmCreateCstrFromString(strObj);
756 if (strcmp(str, "sync-response") == 0) {
757 LOGI("-------\n");
758 LOGI("Adding global ref on string '%s'\n", str);
759 dvmDumpThread(dvmThreadSelf(), false);
760 //dvmAbort();
761 }
762 free(str);
763 }
764 if (false && ((Object*)obj)->clazz == gDvm.classArrayByte) {
765 ArrayObject* arrayObj = (ArrayObject*) obj;
Andy McFaddend5ab7262009-08-25 07:19:34 -0700766 if (arrayObj->length == 8192 /*&&
767 dvmReferenceTableEntries(&gDvm.jniGlobalRefTable) > 400*/)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800768 {
769 LOGI("Adding global ref on byte array %p (len=%d)\n",
770 arrayObj, arrayObj->length);
771 dvmDumpThread(dvmThreadSelf(), false);
772 }
773 }
774
Andy McFaddend5ab7262009-08-25 07:19:34 -0700775 jobject jobj;
776
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800777 dvmLockMutex(&gDvm.jniGlobalRefLock);
778
779 /*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800780 * Throwing an exception on failure is problematic, because JNI code
781 * may not be expecting an exception, and things sort of cascade. We
782 * want to have a hard limit to catch leaks during debugging, but this
783 * otherwise needs to expand until memory is consumed. As a practical
784 * matter, if we have many thousands of global references, chances are
785 * we're either leaking global ref table entries or we're going to
786 * run out of space in the GC heap.
787 */
Andy McFaddend5ab7262009-08-25 07:19:34 -0700788#ifdef USE_INDIRECT_REF
789 jobj = dvmAddToIndirectRefTable(&gDvm.jniGlobalRefTable, IRT_FIRST_SEGMENT,
790 obj);
791 if (jobj == NULL) {
792 dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable, "JNI global");
793 LOGE("Failed adding to JNI global ref table (%d entries)\n",
794 (int) dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable));
795 dvmAbort();
796 }
797
798 LOGVV("GREF add %p (%s.%s)\n", obj,
799 dvmGetCurrentJNIMethod()->clazz->descriptor,
800 dvmGetCurrentJNIMethod()->name);
801
802 /* GREF usage tracking; should probably be disabled for production env */
803 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
804 int count = dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable);
805 // TODO: adjust for "holes"
806 if (count > gDvm.jniGlobalRefHiMark) {
807 LOGD("GREF has increased to %d\n", count);
808 gDvm.jniGlobalRefHiMark += kGrefWaterInterval;
809 gDvm.jniGlobalRefLoMark += kGrefWaterInterval;
810
811 /* watch for "excessive" use; not generally appropriate */
812 if (count >= gDvm.jniGrefLimit) {
813 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
814 if (vm->warnError) {
815 dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable,
816 "JNI global");
817 LOGE("Excessive JNI global references (%d)\n", count);
818 dvmAbort();
819 } else {
820 LOGW("Excessive JNI global references (%d)\n", count);
821 }
822 }
823 }
824 }
825#else
826 if (!dvmAddToReferenceTable(&gDvm.jniGlobalRefTable, obj)) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800827 dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global");
828 LOGE("Failed adding to JNI global ref table (%d entries)\n",
829 (int) dvmReferenceTableEntries(&gDvm.jniGlobalRefTable));
830 dvmAbort();
831 }
Andy McFaddend5ab7262009-08-25 07:19:34 -0700832 jobj = (jobject) obj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800833
834 LOGVV("GREF add %p (%s.%s)\n", obj,
835 dvmGetCurrentJNIMethod()->clazz->descriptor,
836 dvmGetCurrentJNIMethod()->name);
837
838 /* GREF usage tracking; should probably be disabled for production env */
839 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
840 int count = dvmReferenceTableEntries(&gDvm.jniGlobalRefTable);
841 if (count > gDvm.jniGlobalRefHiMark) {
842 LOGD("GREF has increased to %d\n", count);
843 gDvm.jniGlobalRefHiMark += kGrefWaterInterval;
844 gDvm.jniGlobalRefLoMark += kGrefWaterInterval;
845
846 /* watch for "excessive" use; not generally appropriate */
847 if (count >= gDvm.jniGrefLimit) {
848 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
849 if (vm->warnError) {
850 dvmDumpReferenceTable(&gDvm.jniGlobalRefTable,"JNI global");
851 LOGE("Excessive JNI global references (%d)\n", count);
852 dvmAbort();
853 } else {
854 LOGW("Excessive JNI global references (%d)\n", count);
855 }
856 }
857 }
858 }
Andy McFaddend5ab7262009-08-25 07:19:34 -0700859#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800860
861bail:
862 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
Andy McFaddend5ab7262009-08-25 07:19:34 -0700863 return jobj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800864}
865
866/*
867 * Remove a global reference. In most cases it's the entry most recently
868 * added, which makes this pretty quick.
869 *
870 * Thought: if it's not the most recent entry, just null it out. When we
871 * fill up, do a compaction pass before we expand the list.
872 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700873static void deleteGlobalReference(jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800874{
Andy McFaddenab00d452009-08-19 07:21:41 -0700875 if (jobj == NULL)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800876 return;
877
878 dvmLockMutex(&gDvm.jniGlobalRefLock);
879
Andy McFaddend5ab7262009-08-25 07:19:34 -0700880#ifdef USE_INDIRECT_REF
881 if (!dvmRemoveFromIndirectRefTable(&gDvm.jniGlobalRefTable,
882 IRT_FIRST_SEGMENT, jobj))
883 {
884 LOGW("JNI: DeleteGlobalRef(%p) failed to find entry\n", jobj);
885 goto bail;
886 }
887
888 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
889 int count = dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable);
890 // TODO: not quite right, need to subtract holes
891 if (count < gDvm.jniGlobalRefLoMark) {
892 LOGD("GREF has decreased to %d\n", count);
893 gDvm.jniGlobalRefHiMark -= kGrefWaterInterval;
894 gDvm.jniGlobalRefLoMark -= kGrefWaterInterval;
895 }
896 }
897#else
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800898 if (!dvmRemoveFromReferenceTable(&gDvm.jniGlobalRefTable,
Andy McFaddenab00d452009-08-19 07:21:41 -0700899 gDvm.jniGlobalRefTable.table, jobj))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800900 {
901 LOGW("JNI: DeleteGlobalRef(%p) failed to find entry (valid=%d)\n",
Andy McFaddenab00d452009-08-19 07:21:41 -0700902 jobj, dvmIsValidObject((Object*) jobj));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800903 goto bail;
904 }
905
906 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
907 int count = dvmReferenceTableEntries(&gDvm.jniGlobalRefTable);
908 if (count < gDvm.jniGlobalRefLoMark) {
909 LOGD("GREF has decreased to %d\n", count);
910 gDvm.jniGlobalRefHiMark -= kGrefWaterInterval;
911 gDvm.jniGlobalRefLoMark -= kGrefWaterInterval;
912 }
913 }
Andy McFaddend5ab7262009-08-25 07:19:34 -0700914#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800915
916bail:
917 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
918}
919
Andy McFaddenb18992f2009-09-25 10:42:15 -0700920
921/*
922 * Get the "magic" JNI weak global ReferenceQueue. It's allocated on
923 * first use.
924 *
925 * Returns NULL with an exception raised if allocation fails.
926 */
927static Object* getWeakGlobalRefQueue(void)
928{
929 /* use an indirect variable to avoid "type-punned pointer" complaints */
930 Object** pGlobalQ = &gDvm.jniWeakGlobalRefQueue;
931
932 if (*pGlobalQ != NULL)
933 return *pGlobalQ;
934
935 ClassObject* clazz = dvmFindSystemClass("Ljava/lang/ref/ReferenceQueue;");
936 if (clazz == NULL) {
937 LOGE("Unable to find java.lang.ref.ReferenceQueue");
938 dvmAbort();
939 }
940
941 /*
942 * Create an instance of ReferenceQueue. The object is never actually
943 * used for anything, so we don't need to call a constructor. (We could
944 * get away with using an instance of Object, but this is cleaner.)
945 */
946 Object* queue = dvmAllocObject(clazz, ALLOC_DEFAULT);
947 if (queue == NULL) {
948 LOGW("Failed allocating weak global ref queue\n");
949 assert(dvmCheckException(dvmThreadSelf()));
950 return NULL;
951 }
952 dvmReleaseTrackedAlloc(queue, NULL);
953
954 /*
955 * Save it, using atomic ops to ensure we don't double-up. The gDvm
956 * field is known to the GC.
957 */
958 if (!ATOMIC_CMP_SWAP((int*) pGlobalQ, 0, (int) queue)) {
959 LOGD("WOW: lost race to create weak global ref queue\n");
960 queue = *pGlobalQ;
961 }
962
963 return queue;
964}
965
966
967/*
968 * We create a PhantomReference that references the object, add a
969 * global reference to it, and then flip some bits before returning it.
970 * The last step ensures that we detect it as special and that only
971 * appropriate calls will accept it.
972 *
973 * On failure, returns NULL with an exception pending.
974 */
975static jweak createWeakGlobalRef(JNIEnv* env, jobject jobj)
976{
977 if (jobj == NULL)
978 return NULL;
979
980 Thread* self = ((JNIEnvExt*)env)->self;
981 Object* obj = dvmDecodeIndirectRef(env, jobj);
982 Object* weakGlobalQueue = getWeakGlobalRefQueue();
983 Object* phantomObj;
984 jobject phantomRef;
985
986 /*
987 * Allocate a PhantomReference, then call the constructor to set
988 * the referent and the reference queue.
989 *
990 * We use a "magic" reference queue that the GC knows about; it behaves
991 * more like a queueless WeakReference, clearing the referent and
992 * not calling enqueue().
993 */
994 if (!dvmIsClassInitialized(gDvm.classJavaLangRefPhantomReference))
995 dvmInitClass(gDvm.classJavaLangRefPhantomReference);
996 phantomObj = dvmAllocObject(gDvm.classJavaLangRefPhantomReference,
997 ALLOC_DEFAULT);
998 if (phantomObj == NULL) {
999 assert(dvmCheckException(self));
1000 LOGW("Failed on WeakGlobalRef alloc\n");
1001 return NULL;
1002 }
1003
1004 JValue unused;
1005 dvmCallMethod(self, gDvm.methJavaLangRefPhantomReference_init, phantomObj,
1006 &unused, jobj, weakGlobalQueue);
1007 dvmReleaseTrackedAlloc(phantomObj, self);
1008
1009 if (dvmCheckException(self)) {
1010 LOGW("PhantomReference init failed\n");
1011 return NULL;
1012 }
1013
1014 LOGV("+++ WGR: created phantom ref %p for object %p\n", phantomObj, obj);
1015
1016 /*
1017 * Add it to the global reference table, and mangle the pointer.
1018 */
1019 phantomRef = addGlobalReference(phantomObj);
1020 return dvmObfuscateWeakGlobalRef(phantomRef);
1021}
1022
1023/*
1024 * Delete the global reference that's keeping the PhantomReference around.
1025 * The PhantomReference will eventually be discarded by the GC.
1026 */
1027static void deleteWeakGlobalRef(JNIEnv* env, jweak wref)
1028{
1029 if (wref == NULL)
1030 return;
1031
1032 jobject phantomRef = dvmNormalizeWeakGlobalRef(wref);
1033 deleteGlobalReference(phantomRef);
1034}
1035
1036/*
1037 * Extract the referent from a PhantomReference. Used for weak global
1038 * references.
1039 *
1040 * "jwobj" is a "mangled" WGR pointer.
1041 */
1042static Object* getPhantomReferent(JNIEnv* env, jweak jwobj)
1043{
1044 jobject jobj = dvmNormalizeWeakGlobalRef(jwobj);
1045 Object* obj = dvmDecodeIndirectRef(env, jobj);
1046
1047 if (obj->clazz != gDvm.classJavaLangRefPhantomReference) {
1048 LOGE("%p is not a phantom reference (%s)\n",
1049 jwobj, obj->clazz->descriptor);
1050 return NULL;
1051 }
1052
1053 return dvmGetFieldObject(obj, gDvm.offJavaLangRefReference_referent);
1054}
1055
1056
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001057/*
Andy McFaddenab00d452009-08-19 07:21:41 -07001058 * Objects don't currently move, so we just need to create a reference
1059 * that will ensure the array object isn't collected.
1060 *
Andy McFaddenc26bb632009-08-21 12:01:31 -07001061 * We use a separate reference table, which is part of the GC root set.
Andy McFaddenab00d452009-08-19 07:21:41 -07001062 */
1063static void pinPrimitiveArray(ArrayObject* arrayObj)
1064{
Andy McFaddenc26bb632009-08-21 12:01:31 -07001065 if (arrayObj == NULL)
1066 return;
1067
1068 dvmLockMutex(&gDvm.jniPinRefLock);
1069 if (!dvmAddToReferenceTable(&gDvm.jniPinRefTable, (Object*)arrayObj)) {
1070 dvmDumpReferenceTable(&gDvm.jniPinRefTable, "JNI pinned array");
1071 LOGE("Failed adding to JNI pinned array ref table (%d entries)\n",
1072 (int) dvmReferenceTableEntries(&gDvm.jniPinRefTable));
1073 dvmDumpThread(dvmThreadSelf(), false);
1074 dvmAbort();
1075 }
1076
1077 /*
1078 * If we're watching global ref usage, also keep an eye on these.
1079 *
1080 * The total number of pinned primitive arrays should be pretty small.
1081 * A single array should not be pinned more than once or twice; any
1082 * more than that is a strong indicator that a Release function is
1083 * not being called.
1084 */
1085 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
1086 int count = 0;
1087 Object** ppObj = gDvm.jniPinRefTable.table;
1088 while (ppObj < gDvm.jniPinRefTable.nextEntry) {
1089 if (*ppObj++ == (Object*) arrayObj)
1090 count++;
1091 }
1092
1093 if (count > kPinComplainThreshold) {
1094 LOGW("JNI: pin count on array %p (%s) is now %d\n",
1095 arrayObj, arrayObj->obj.clazz->descriptor, count);
1096 /* keep going */
1097 }
1098 }
1099
1100 dvmUnlockMutex(&gDvm.jniPinRefLock);
Andy McFaddenab00d452009-08-19 07:21:41 -07001101}
1102
1103/*
1104 * Un-pin the array object. If an object was pinned twice, it must be
1105 * unpinned twice before it's free to move.
Andy McFaddenab00d452009-08-19 07:21:41 -07001106 */
1107static void unpinPrimitiveArray(ArrayObject* arrayObj)
1108{
Andy McFaddenc26bb632009-08-21 12:01:31 -07001109 if (arrayObj == NULL)
1110 return;
1111
1112 dvmLockMutex(&gDvm.jniPinRefLock);
1113 if (!dvmRemoveFromReferenceTable(&gDvm.jniPinRefTable,
1114 gDvm.jniPinRefTable.table, (Object*) arrayObj))
1115 {
1116 LOGW("JNI: unpinPrimitiveArray(%p) failed to find entry (valid=%d)\n",
1117 arrayObj, dvmIsValidObject((Object*) arrayObj));
1118 goto bail;
1119 }
1120
1121bail:
1122 dvmUnlockMutex(&gDvm.jniPinRefLock);
Andy McFaddenab00d452009-08-19 07:21:41 -07001123}
1124
1125/*
Andy McFadden92fa4762009-10-22 17:24:45 -07001126 * Dump the contents of the JNI reference tables to the log file.
1127 *
1128 * We only dump the local refs associated with the current thread.
1129 */
1130void dvmDumpJniReferenceTables(void)
1131{
1132 Thread* self = dvmThreadSelf();
1133 JNIEnv* env = self->jniEnv;
1134 ReferenceTable* pLocalRefs = getLocalRefTable(env);
1135
1136#ifdef USE_INDIRECT_REF
1137 dvmDumpIndirectRefTable(pLocalRefs, "JNI local");
1138 dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable, "JNI global");
1139#else
1140 dvmDumpReferenceTable(pLocalRefs, "JNI local");
1141 dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global");
1142#endif
1143 dvmDumpReferenceTable(&gDvm.jniPinRefTable, "JNI pinned array");
1144}
1145
1146/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001147 * GC helper function to mark all JNI global references.
Andy McFaddenc26bb632009-08-21 12:01:31 -07001148 *
1149 * We're currently handling the "pin" table here too.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001150 */
1151void dvmGcMarkJniGlobalRefs()
1152{
Andy McFaddend5ab7262009-08-25 07:19:34 -07001153 Object** op;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001154
1155 dvmLockMutex(&gDvm.jniGlobalRefLock);
1156
Andy McFaddend5ab7262009-08-25 07:19:34 -07001157#ifdef USE_INDIRECT_REF
1158 IndirectRefTable* pRefTable = &gDvm.jniGlobalRefTable;
1159 op = pRefTable->table;
1160 int numEntries = dvmIndirectRefTableEntries(pRefTable);
1161 int i;
1162
1163 for (i = 0; i < numEntries; i++) {
1164 Object* obj = *op;
1165 if (obj != NULL)
1166 dvmMarkObjectNonNull(obj);
1167 op++;
1168 }
1169#else
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001170 op = gDvm.jniGlobalRefTable.table;
1171 while ((uintptr_t)op < (uintptr_t)gDvm.jniGlobalRefTable.nextEntry) {
1172 dvmMarkObjectNonNull(*(op++));
1173 }
Andy McFaddend5ab7262009-08-25 07:19:34 -07001174#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001175
1176 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
Andy McFaddenc26bb632009-08-21 12:01:31 -07001177
1178
1179 dvmLockMutex(&gDvm.jniPinRefLock);
1180
1181 op = gDvm.jniPinRefTable.table;
1182 while ((uintptr_t)op < (uintptr_t)gDvm.jniPinRefTable.nextEntry) {
1183 dvmMarkObjectNonNull(*(op++));
1184 }
1185
1186 dvmUnlockMutex(&gDvm.jniPinRefLock);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001187}
1188
1189
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001190#ifndef USE_INDIRECT_REF
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001191/*
1192 * Determine if "obj" appears in the argument list for the native method.
1193 *
1194 * We use the "shorty" signature to determine which argument slots hold
1195 * reference types.
1196 */
1197static bool findInArgList(Thread* self, Object* obj)
1198{
1199 const Method* meth;
1200 u4* fp;
1201 int i;
1202
1203 fp = self->curFrame;
1204 while (1) {
1205 /*
1206 * Back up over JNI PushLocalFrame frames. This works because the
1207 * previous frame on the interpreted stack is either a break frame
1208 * (if we called here via native code) or an interpreted method (if
1209 * we called here via the interpreter). In both cases the method
1210 * pointer won't match.
1211 */
1212 StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
1213 meth = saveArea->method;
1214 if (meth != SAVEAREA_FROM_FP(saveArea->prevFrame)->method)
1215 break;
1216 fp = saveArea->prevFrame;
1217 }
1218
1219 LOGVV("+++ scanning %d args in %s (%s)\n",
1220 meth->insSize, meth->name, meth->shorty);
1221 const char* shorty = meth->shorty +1; /* skip return type char */
1222 for (i = 0; i < meth->insSize; i++) {
1223 if (i == 0 && !dvmIsStaticMethod(meth)) {
1224 /* first arg is "this" ref, not represented in "shorty" */
1225 if (fp[i] == (u4) obj)
1226 return true;
1227 } else {
1228 /* if this is a reference type, see if it matches */
1229 switch (*shorty) {
1230 case 'L':
1231 if (fp[i] == (u4) obj)
1232 return true;
1233 break;
1234 case 'D':
1235 case 'J':
1236 i++;
1237 break;
1238 case '\0':
1239 LOGE("Whoops! ran off the end of %s (%d)\n",
1240 meth->shorty, meth->insSize);
1241 break;
1242 default:
1243 if (fp[i] == (u4) obj)
1244 LOGI("NOTE: ref %p match on arg type %c\n", obj, *shorty);
1245 break;
1246 }
1247 shorty++;
1248 }
1249 }
1250
1251 /*
1252 * For static methods, we also pass a class pointer in.
1253 */
1254 if (dvmIsStaticMethod(meth)) {
1255 //LOGI("+++ checking class pointer in %s\n", meth->name);
1256 if ((void*)obj == (void*)meth->clazz)
1257 return true;
1258 }
1259 return false;
1260}
Andy McFadden0083d372009-08-21 14:44:04 -07001261#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001262
1263/*
1264 * Verify that a reference passed in from native code is one that the
1265 * code is allowed to have.
1266 *
1267 * It's okay for native code to pass us a reference that:
Andy McFadden0083d372009-08-21 14:44:04 -07001268 * - was passed in as an argument when invoked by native code (and hence
1269 * is in the JNI local refs table)
1270 * - was returned to it from JNI (and is now in the local refs table)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001271 * - is present in the JNI global refs table
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001272 *
1273 * Used by -Xcheck:jni and GetObjectRefType.
1274 *
1275 * NOTE: in the current VM, global and local references are identical. If
1276 * something is both global and local, we can't tell them apart, and always
1277 * return "local".
1278 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001279jobjectRefType dvmGetJNIRefType(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001280{
Andy McFaddend5ab7262009-08-25 07:19:34 -07001281#ifdef USE_INDIRECT_REF
1282 /*
1283 * IndirectRefKind is currently defined as an exact match of
Andy McFadden0423f0e2009-08-26 07:21:53 -07001284 * jobjectRefType, so this is easy. We have to decode it to determine
1285 * if it's a valid reference and not merely valid-looking.
Andy McFaddend5ab7262009-08-25 07:19:34 -07001286 */
Andy McFadden0423f0e2009-08-26 07:21:53 -07001287 Object* obj = dvmDecodeIndirectRef(env, jobj);
1288
1289 if (obj == NULL) {
1290 /* invalid ref, or jobj was NULL */
1291 return JNIInvalidRefType;
1292 } else {
1293 return (jobjectRefType) dvmGetIndirectRefType(jobj);
1294 }
Andy McFaddend5ab7262009-08-25 07:19:34 -07001295#else
Andy McFaddenab00d452009-08-19 07:21:41 -07001296 ReferenceTable* pRefTable = getLocalRefTable(env);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001297 Thread* self = dvmThreadSelf();
1298 //Object** top;
1299 Object** ptr;
1300
Andy McFaddenb18992f2009-09-25 10:42:15 -07001301 if (dvmIsWeakGlobalRef(jobj)) {
1302 return JNIWeakGlobalRefType;
1303 }
1304
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001305 /* check args */
Andy McFaddenab00d452009-08-19 07:21:41 -07001306 if (findInArgList(self, jobj)) {
1307 //LOGI("--- REF found %p on stack\n", jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001308 return JNILocalRefType;
1309 }
1310
1311 /* check locals */
Andy McFaddenab00d452009-08-19 07:21:41 -07001312 if (dvmFindInReferenceTable(pRefTable, pRefTable->table, jobj) != NULL) {
1313 //LOGI("--- REF found %p in locals\n", jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001314 return JNILocalRefType;
1315 }
1316
1317 /* check globals */
1318 dvmLockMutex(&gDvm.jniGlobalRefLock);
1319 if (dvmFindInReferenceTable(&gDvm.jniGlobalRefTable,
Andy McFaddenab00d452009-08-19 07:21:41 -07001320 gDvm.jniGlobalRefTable.table, jobj))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001321 {
Andy McFaddenab00d452009-08-19 07:21:41 -07001322 //LOGI("--- REF found %p in globals\n", jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001323 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
1324 return JNIGlobalRefType;
1325 }
1326 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
1327
1328 /* not found! */
1329 return JNIInvalidRefType;
Andy McFaddend5ab7262009-08-25 07:19:34 -07001330#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001331}
1332
1333/*
1334 * Register a method that uses JNI calling conventions.
1335 */
1336static bool dvmRegisterJNIMethod(ClassObject* clazz, const char* methodName,
1337 const char* signature, void* fnPtr)
1338{
1339 Method* method;
1340 bool result = false;
1341
1342 if (fnPtr == NULL)
1343 goto bail;
1344
1345 method = dvmFindDirectMethodByDescriptor(clazz, methodName, signature);
1346 if (method == NULL)
1347 method = dvmFindVirtualMethodByDescriptor(clazz, methodName, signature);
1348 if (method == NULL) {
1349 LOGW("ERROR: Unable to find decl for native %s.%s %s\n",
1350 clazz->descriptor, methodName, signature);
1351 goto bail;
1352 }
1353
1354 if (!dvmIsNativeMethod(method)) {
1355 LOGW("Unable to register: not native: %s.%s %s\n",
1356 clazz->descriptor, methodName, signature);
1357 goto bail;
1358 }
1359
1360 if (method->nativeFunc != dvmResolveNativeMethod) {
1361 LOGW("Warning: %s.%s %s was already registered/resolved?\n",
1362 clazz->descriptor, methodName, signature);
1363 /* keep going, I guess */
1364 }
1365
Andy McFadden59b61772009-05-13 16:44:34 -07001366 dvmUseJNIBridge(method, fnPtr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001367
1368 LOGV("JNI-registered %s.%s %s\n", clazz->descriptor, methodName,
1369 signature);
1370 result = true;
1371
1372bail:
1373 return result;
1374}
1375
1376/*
Andy McFadden59b61772009-05-13 16:44:34 -07001377 * Returns "true" if CheckJNI is enabled in the VM.
1378 */
1379static bool dvmIsCheckJNIEnabled(void)
1380{
1381 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
1382 return vm->useChecked;
1383}
1384
1385/*
1386 * Point "method->nativeFunc" at the JNI bridge, and overload "method->insns"
1387 * to point at the actual function.
1388 */
1389void dvmUseJNIBridge(Method* method, void* func)
1390{
Andy McFadden0083d372009-08-21 14:44:04 -07001391 enum {
1392 kJNIGeneral = 0,
1393 kJNISync = 1,
1394 kJNIVirtualNoRef = 2,
1395 kJNIStaticNoRef = 3,
1396 } kind;
1397 static const DalvikBridgeFunc stdFunc[] = {
1398 dvmCallJNIMethod_general,
1399 dvmCallJNIMethod_synchronized,
1400 dvmCallJNIMethod_virtualNoRef,
1401 dvmCallJNIMethod_staticNoRef
1402 };
1403 static const DalvikBridgeFunc checkFunc[] = {
1404 dvmCheckCallJNIMethod_general,
1405 dvmCheckCallJNIMethod_synchronized,
1406 dvmCheckCallJNIMethod_virtualNoRef,
1407 dvmCheckCallJNIMethod_staticNoRef
1408 };
1409
1410 bool hasRefArg = false;
1411
1412 if (dvmIsSynchronizedMethod(method)) {
1413 /* use version with synchronization; calls into general handler */
1414 kind = kJNISync;
Andy McFadden59b61772009-05-13 16:44:34 -07001415 } else {
Andy McFadden0083d372009-08-21 14:44:04 -07001416 /*
1417 * Do a quick scan through the "shorty" signature to see if the method
1418 * takes any reference arguments.
1419 */
1420 const char* cp = method->shorty;
1421 while (*++cp != '\0') { /* pre-incr to skip return type */
1422 if (*cp == 'L') {
1423 /* 'L' used for both object and array references */
1424 hasRefArg = true;
1425 break;
1426 }
1427 }
1428
1429 if (hasRefArg) {
1430 /* use general handler to slurp up reference args */
1431 kind = kJNIGeneral;
1432 } else {
1433 /* virtual methods have a ref in args[0] (not in signature) */
1434 if (dvmIsStaticMethod(method))
1435 kind = kJNIStaticNoRef;
1436 else
1437 kind = kJNIVirtualNoRef;
1438 }
1439 }
1440
1441 if (dvmIsCheckJNIEnabled()) {
1442 dvmSetNativeFunc(method, checkFunc[kind], func);
1443 } else {
1444 dvmSetNativeFunc(method, stdFunc[kind], func);
Andy McFadden59b61772009-05-13 16:44:34 -07001445 }
1446}
1447
1448/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001449 * Get the method currently being executed by examining the interp stack.
1450 */
1451const Method* dvmGetCurrentJNIMethod(void)
1452{
1453 assert(dvmThreadSelf() != NULL);
1454
1455 void* fp = dvmThreadSelf()->curFrame;
1456 const Method* meth = SAVEAREA_FROM_FP(fp)->method;
1457
1458 assert(meth != NULL);
1459 assert(dvmIsNativeMethod(meth));
1460 return meth;
1461}
1462
1463
1464/*
1465 * Track a JNI MonitorEnter in the current thread.
1466 *
1467 * The goal is to be able to "implicitly" release all JNI-held monitors
1468 * when the thread detaches.
1469 *
1470 * Monitors may be entered multiple times, so we add a new entry for each
1471 * enter call. It would be more efficient to keep a counter. At present
1472 * there's no real motivation to improve this however.
1473 */
1474static void trackMonitorEnter(Thread* self, Object* obj)
1475{
1476 static const int kInitialSize = 16;
1477 ReferenceTable* refTable = &self->jniMonitorRefTable;
1478
1479 /* init table on first use */
1480 if (refTable->table == NULL) {
1481 assert(refTable->maxEntries == 0);
1482
1483 if (!dvmInitReferenceTable(refTable, kInitialSize, INT_MAX)) {
1484 LOGE("Unable to initialize monitor tracking table\n");
1485 dvmAbort();
1486 }
1487 }
1488
1489 if (!dvmAddToReferenceTable(refTable, obj)) {
1490 /* ran out of memory? could throw exception instead */
1491 LOGE("Unable to add entry to monitor tracking table\n");
1492 dvmAbort();
1493 } else {
1494 LOGVV("--- added monitor %p\n", obj);
1495 }
1496}
1497
Andy McFaddenab00d452009-08-19 07:21:41 -07001498
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001499/*
1500 * Track a JNI MonitorExit in the current thread.
1501 */
1502static void trackMonitorExit(Thread* self, Object* obj)
1503{
Andy McFaddenab00d452009-08-19 07:21:41 -07001504 ReferenceTable* pRefTable = &self->jniMonitorRefTable;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001505
Andy McFaddenab00d452009-08-19 07:21:41 -07001506 if (!dvmRemoveFromReferenceTable(pRefTable, pRefTable->table, obj)) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001507 LOGE("JNI monitor %p not found in tracking list\n", obj);
1508 /* keep going? */
1509 } else {
1510 LOGVV("--- removed monitor %p\n", obj);
1511 }
1512}
1513
1514/*
1515 * Release all monitors held by the jniMonitorRefTable list.
1516 */
1517void dvmReleaseJniMonitors(Thread* self)
1518{
Andy McFaddenab00d452009-08-19 07:21:41 -07001519 ReferenceTable* pRefTable = &self->jniMonitorRefTable;
1520 Object** top = pRefTable->table;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001521
1522 if (top == NULL)
1523 return;
1524
Andy McFaddenab00d452009-08-19 07:21:41 -07001525 Object** ptr = pRefTable->nextEntry;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001526 while (--ptr >= top) {
1527 if (!dvmUnlockObject(self, *ptr)) {
1528 LOGW("Unable to unlock monitor %p at thread detach\n", *ptr);
1529 } else {
1530 LOGVV("--- detach-releasing monitor %p\n", *ptr);
1531 }
1532 }
1533
1534 /* zap it */
Andy McFaddenab00d452009-08-19 07:21:41 -07001535 pRefTable->nextEntry = pRefTable->table;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001536}
1537
1538#ifdef WITH_JNI_STACK_CHECK
1539/*
1540 * Compute a CRC on the entire interpreted stack.
1541 *
1542 * Would be nice to compute it on "self" as well, but there are parts of
1543 * the Thread that can be altered by other threads (e.g. prev/next pointers).
1544 */
1545static void computeStackSum(Thread* self)
1546{
1547 const u1* low = (const u1*)SAVEAREA_FROM_FP(self->curFrame);
1548 u4 crc = dvmInitCrc32();
1549 self->stackCrc = 0;
1550 crc = dvmComputeCrc32(crc, low, self->interpStackStart - low);
1551 self->stackCrc = crc;
1552}
1553
1554/*
1555 * Compute a CRC on the entire interpreted stack, and compare it to what
1556 * we previously computed.
1557 *
1558 * We can execute JNI directly from native code without calling in from
1559 * interpreted code during VM initialization and immediately after JNI
1560 * thread attachment. Another opportunity exists during JNI_OnLoad. Rather
1561 * than catching these cases we just ignore them here, which is marginally
1562 * less accurate but reduces the amount of code we have to touch with #ifdefs.
1563 */
1564static void checkStackSum(Thread* self)
1565{
1566 const u1* low = (const u1*)SAVEAREA_FROM_FP(self->curFrame);
1567 u4 stackCrc, crc;
1568
1569 stackCrc = self->stackCrc;
1570 self->stackCrc = 0;
1571 crc = dvmInitCrc32();
1572 crc = dvmComputeCrc32(crc, low, self->interpStackStart - low);
1573 if (crc != stackCrc) {
1574 const Method* meth = dvmGetCurrentJNIMethod();
1575 if (dvmComputeExactFrameDepth(self->curFrame) == 1) {
1576 LOGD("JNI: bad stack CRC (0x%08x) -- okay during init\n",
1577 stackCrc);
1578 } else if (strcmp(meth->name, "nativeLoad") == 0 &&
1579 (strcmp(meth->clazz->descriptor, "Ljava/lang/Runtime;") == 0))
1580 {
1581 LOGD("JNI: bad stack CRC (0x%08x) -- okay during JNI_OnLoad\n",
1582 stackCrc);
1583 } else {
1584 LOGW("JNI: bad stack CRC (%08x vs %08x)\n", crc, stackCrc);
1585 dvmAbort();
1586 }
1587 }
1588 self->stackCrc = (u4) -1; /* make logic errors more noticeable */
1589}
1590#endif
1591
1592
1593/*
1594 * ===========================================================================
Andy McFaddend5ab7262009-08-25 07:19:34 -07001595 * JNI call bridge
1596 * ===========================================================================
1597 */
1598
1599/*
1600 * The functions here form a bridge between interpreted code and JNI native
1601 * functions. The basic task is to convert an array of primitives and
1602 * references into C-style function arguments. This is architecture-specific
1603 * and usually requires help from assembly code.
1604 *
1605 * The bridge takes four arguments: the array of parameters, a place to
1606 * store the function result (if any), the method to call, and a pointer
1607 * to the current thread.
1608 *
1609 * These functions aren't called directly from elsewhere in the VM.
1610 * A pointer in the Method struct points to one of these, and when a native
1611 * method is invoked the interpreter jumps to it.
1612 *
1613 * (The "internal native" methods are invoked the same way, but instead
1614 * of calling through a bridge, the target method is called directly.)
1615 *
1616 * The "args" array should not be modified, but we do so anyway for
1617 * performance reasons. We know that it points to the "outs" area on
1618 * the current method's interpreted stack. This area is ignored by the
1619 * precise GC, because there is no register map for a native method (for
1620 * an interpreted method the args would be listed in the argument set).
1621 * We know all of the values exist elsewhere on the interpreted stack,
1622 * because the method call setup copies them right before making the call,
1623 * so we don't have to worry about concealing stuff from the GC.
1624 *
1625 * If we don't want to modify "args", we either have to create a local
1626 * copy and modify it before calling dvmPlatformInvoke, or we have to do
1627 * the local reference replacement within dvmPlatformInvoke. The latter
1628 * has some performance advantages, though if we can inline the local
1629 * reference adds we may win when there's a lot of reference args (unless
1630 * we want to code up some local ref table manipulation in assembly.
1631 */
1632
1633/*
Andy McFadden0423f0e2009-08-26 07:21:53 -07001634 * If necessary, convert the value in pResult from a local/global reference
1635 * to an object pointer.
1636 */
1637static inline void convertReferenceResult(JNIEnv* env, JValue* pResult,
1638 const Method* method, Thread* self)
1639{
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001640#ifdef USE_INDIRECT_REF
Andy McFadden0423f0e2009-08-26 07:21:53 -07001641 if (method->shorty[0] == 'L' && !dvmCheckException(self) &&
1642 pResult->l != NULL)
1643 {
1644 pResult->l = dvmDecodeIndirectRef(env, pResult->l);
1645 }
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001646#endif
Andy McFadden0423f0e2009-08-26 07:21:53 -07001647}
1648
1649/*
Andy McFaddend5ab7262009-08-25 07:19:34 -07001650 * General form, handles all cases.
1651 */
1652void dvmCallJNIMethod_general(const u4* args, JValue* pResult,
1653 const Method* method, Thread* self)
1654{
1655 int oldStatus;
1656 u4* modArgs = (u4*) args;
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001657 jclass staticMethodClass;
1658 JNIEnv* env = self->jniEnv;
Andy McFaddend5ab7262009-08-25 07:19:34 -07001659
1660 assert(method->insns != NULL);
1661
1662 //LOGI("JNI calling %p (%s.%s:%s):\n", method->insns,
1663 // method->clazz->descriptor, method->name, method->shorty);
1664
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001665#ifdef USE_INDIRECT_REF
Andy McFaddend5ab7262009-08-25 07:19:34 -07001666 /*
1667 * Walk the argument list, creating local references for appropriate
1668 * arguments.
1669 */
Andy McFaddend5ab7262009-08-25 07:19:34 -07001670 int idx = 0;
1671 if (dvmIsStaticMethod(method)) {
1672 /* add the class object we pass in */
1673 staticMethodClass = addLocalReference(env, (Object*) method->clazz);
1674 if (staticMethodClass == NULL) {
1675 assert(dvmCheckException(self));
1676 return;
1677 }
1678 } else {
1679 /* add "this" */
1680 staticMethodClass = NULL;
1681 jobject thisObj = addLocalReference(env, (Object*) modArgs[0]);
1682 if (thisObj == NULL) {
1683 assert(dvmCheckException(self));
1684 return;
1685 }
1686 modArgs[idx] = (u4) thisObj;
1687 idx = 1;
1688 }
1689
1690 const char* shorty = &method->shorty[1]; /* skip return type */
1691 while (*shorty != '\0') {
1692 switch (*shorty++) {
1693 case 'L':
1694 //LOGI(" local %d: 0x%08x\n", idx, modArgs[idx]);
1695 if (modArgs[idx] != 0) {
1696 //if (!dvmIsValidObject((Object*) modArgs[idx]))
1697 // dvmAbort();
1698 jobject argObj = addLocalReference(env, (Object*) modArgs[idx]);
1699 if (argObj == NULL) {
1700 assert(dvmCheckException(self));
1701 return;
1702 }
1703 modArgs[idx] = (u4) argObj;
1704 }
1705 break;
1706 case 'D':
1707 case 'J':
1708 idx++;
1709 break;
1710 default:
1711 /* Z B C S I -- do nothing */
1712 break;
1713 }
1714
1715 idx++;
1716 }
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001717#else
1718 staticMethodClass = dvmIsStaticMethod(method) ?
1719 (jclass) method->clazz : NULL;
1720#endif
Andy McFaddend5ab7262009-08-25 07:19:34 -07001721
1722 oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
1723
1724 COMPUTE_STACK_SUM(self);
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001725 dvmPlatformInvoke(env, staticMethodClass,
Andy McFaddend5ab7262009-08-25 07:19:34 -07001726 method->jniArgInfo, method->insSize, modArgs, method->shorty,
1727 (void*)method->insns, pResult);
1728 CHECK_STACK_SUM(self);
1729
1730 dvmChangeStatus(self, oldStatus);
Andy McFadden0423f0e2009-08-26 07:21:53 -07001731
1732 convertReferenceResult(env, pResult, method, self);
Andy McFaddend5ab7262009-08-25 07:19:34 -07001733}
1734
1735/*
1736 * Handler for the unusual case of a synchronized native method.
1737 *
1738 * Lock the object, then call through the general function.
1739 */
1740void dvmCallJNIMethod_synchronized(const u4* args, JValue* pResult,
1741 const Method* method, Thread* self)
1742{
1743 Object* lockObj;
1744
1745 assert(dvmIsSynchronizedMethod(method));
1746
1747 if (dvmIsStaticMethod(method))
1748 lockObj = (Object*) method->clazz;
1749 else
1750 lockObj = (Object*) args[0];
1751
1752 LOGVV("Calling %s.%s: locking %p (%s)\n",
1753 method->clazz->descriptor, method->name,
1754 lockObj, lockObj->clazz->descriptor);
1755
1756 dvmLockObject(self, lockObj);
1757 dvmCallJNIMethod_general(args, pResult, method, self);
1758 dvmUnlockObject(self, lockObj);
1759}
1760
1761/*
1762 * Virtual method call, no reference arguments.
1763 *
1764 * We need to local-ref the "this" argument, found in args[0].
1765 */
1766void dvmCallJNIMethod_virtualNoRef(const u4* args, JValue* pResult,
1767 const Method* method, Thread* self)
1768{
1769 u4* modArgs = (u4*) args;
1770 int oldStatus;
1771
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001772#ifdef USE_INDIRECT_REF
Andy McFaddend5ab7262009-08-25 07:19:34 -07001773 jobject thisObj = addLocalReference(self->jniEnv, (Object*) args[0]);
1774 if (thisObj == NULL) {
1775 assert(dvmCheckException(self));
1776 return;
1777 }
1778 modArgs[0] = (u4) thisObj;
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001779#endif
Andy McFaddend5ab7262009-08-25 07:19:34 -07001780
1781 oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
1782
1783 COMPUTE_STACK_SUM(self);
1784 dvmPlatformInvoke(self->jniEnv, NULL,
1785 method->jniArgInfo, method->insSize, modArgs, method->shorty,
1786 (void*)method->insns, pResult);
1787 CHECK_STACK_SUM(self);
1788
1789 dvmChangeStatus(self, oldStatus);
Andy McFadden0423f0e2009-08-26 07:21:53 -07001790
1791 convertReferenceResult(self->jniEnv, pResult, method, self);
Andy McFaddend5ab7262009-08-25 07:19:34 -07001792}
1793
1794/*
1795 * Static method call, no reference arguments.
1796 *
1797 * We need to local-ref the class reference.
1798 */
1799void dvmCallJNIMethod_staticNoRef(const u4* args, JValue* pResult,
1800 const Method* method, Thread* self)
1801{
1802 jclass staticMethodClass;
1803 int oldStatus;
1804
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001805#ifdef USE_INDIRECT_REF
Andy McFaddend5ab7262009-08-25 07:19:34 -07001806 staticMethodClass = addLocalReference(self->jniEnv, (Object*)method->clazz);
1807 if (staticMethodClass == NULL) {
1808 assert(dvmCheckException(self));
1809 return;
1810 }
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001811#else
1812 staticMethodClass = (jobject) method->clazz;
1813#endif
Andy McFaddend5ab7262009-08-25 07:19:34 -07001814
1815 oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
1816
1817 COMPUTE_STACK_SUM(self);
1818 dvmPlatformInvoke(self->jniEnv, staticMethodClass,
1819 method->jniArgInfo, method->insSize, args, method->shorty,
1820 (void*)method->insns, pResult);
1821 CHECK_STACK_SUM(self);
1822
1823 dvmChangeStatus(self, oldStatus);
Andy McFadden0423f0e2009-08-26 07:21:53 -07001824
1825 convertReferenceResult(self->jniEnv, pResult, method, self);
Andy McFaddend5ab7262009-08-25 07:19:34 -07001826}
1827
1828/*
1829 * Extract the return type enum from the "jniArgInfo" field.
1830 */
1831DalvikJniReturnType dvmGetArgInfoReturnType(int jniArgInfo)
1832{
1833 return (jniArgInfo & DALVIK_JNI_RETURN_MASK) >> DALVIK_JNI_RETURN_SHIFT;
1834}
1835
1836
1837/*
1838 * ===========================================================================
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001839 * JNI implementation
1840 * ===========================================================================
1841 */
1842
1843/*
1844 * Return the version of the native method interface.
1845 */
1846static jint GetVersion(JNIEnv* env)
1847{
1848 JNI_ENTER();
1849 /*
1850 * There is absolutely no need to toggle the mode for correct behavior.
1851 * However, it does provide native code with a simple "suspend self
1852 * if necessary" call.
1853 */
1854 JNI_EXIT();
1855 return JNI_VERSION_1_6;
1856}
1857
1858/*
1859 * Create a new class from a bag of bytes.
1860 *
1861 * This is not currently supported within Dalvik.
1862 */
1863static jclass DefineClass(JNIEnv* env, const char *name, jobject loader,
1864 const jbyte* buf, jsize bufLen)
1865{
1866 UNUSED_PARAMETER(name);
1867 UNUSED_PARAMETER(loader);
1868 UNUSED_PARAMETER(buf);
1869 UNUSED_PARAMETER(bufLen);
1870
1871 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001872 LOGW("JNI DefineClass is not supported\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001873 JNI_EXIT();
1874 return NULL;
1875}
1876
1877/*
1878 * Find a class by name.
1879 *
1880 * We have to use the "no init" version of FindClass here, because we might
1881 * be getting the class prior to registering native methods that will be
1882 * used in <clinit>.
1883 *
1884 * We need to get the class loader associated with the current native
1885 * method. If there is no native method, e.g. we're calling this from native
1886 * code right after creating the VM, the spec says we need to use the class
1887 * loader returned by "ClassLoader.getBaseClassLoader". There is no such
1888 * method, but it's likely they meant ClassLoader.getSystemClassLoader.
1889 * We can't get that until after the VM has initialized though.
1890 */
1891static jclass FindClass(JNIEnv* env, const char* name)
1892{
1893 JNI_ENTER();
1894
1895 const Method* thisMethod;
1896 ClassObject* clazz;
Andy McFaddenab00d452009-08-19 07:21:41 -07001897 jclass jclazz = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001898 Object* loader;
1899 char* descriptor = NULL;
1900
1901 thisMethod = dvmGetCurrentJNIMethod();
1902 assert(thisMethod != NULL);
1903
1904 descriptor = dvmNameToDescriptor(name);
1905 if (descriptor == NULL) {
1906 clazz = NULL;
1907 goto bail;
1908 }
1909
1910 //Thread* self = dvmThreadSelf();
1911 if (_self->classLoaderOverride != NULL) {
1912 /* hack for JNI_OnLoad */
1913 assert(strcmp(thisMethod->name, "nativeLoad") == 0);
1914 loader = _self->classLoaderOverride;
1915 } else if (thisMethod == gDvm.methFakeNativeEntry) {
1916 /* start point of invocation interface */
1917 if (!gDvm.initializing)
1918 loader = dvmGetSystemClassLoader();
1919 else
1920 loader = NULL;
1921 } else {
1922 loader = thisMethod->clazz->classLoader;
1923 }
1924
1925 clazz = dvmFindClassNoInit(descriptor, loader);
Andy McFaddenab00d452009-08-19 07:21:41 -07001926 jclazz = addLocalReference(env, (Object*) clazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001927
1928bail:
1929 free(descriptor);
Andy McFaddenab00d452009-08-19 07:21:41 -07001930
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001931 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001932 return jclazz;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001933}
1934
1935/*
1936 * Return the superclass of a class.
1937 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001938static jclass GetSuperclass(JNIEnv* env, jclass jclazz)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001939{
1940 JNI_ENTER();
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001941 jclass jsuper = NULL;
Andy McFaddenab00d452009-08-19 07:21:41 -07001942
1943 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001944 if (clazz != NULL)
Andy McFaddenab00d452009-08-19 07:21:41 -07001945 jsuper = addLocalReference(env, (Object*)clazz->super);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001946 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001947 return jsuper;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001948}
1949
1950/*
1951 * Determine whether an object of clazz1 can be safely cast to clazz2.
1952 *
1953 * Like IsInstanceOf, but with a pair of class objects instead of obj+class.
1954 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001955static jboolean IsAssignableFrom(JNIEnv* env, jclass jclazz1, jclass jclazz2)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001956{
1957 JNI_ENTER();
1958
Andy McFaddenab00d452009-08-19 07:21:41 -07001959 ClassObject* clazz1 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz1);
1960 ClassObject* clazz2 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz2);
1961
1962 jboolean result = dvmInstanceof(clazz1, clazz2);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001963
1964 JNI_EXIT();
1965 return result;
1966}
1967
1968/*
1969 * Given a java.lang.reflect.Method or .Constructor, return a methodID.
1970 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001971static jmethodID FromReflectedMethod(JNIEnv* env, jobject jmethod)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001972{
1973 JNI_ENTER();
1974 jmethodID methodID;
Andy McFaddenab00d452009-08-19 07:21:41 -07001975 Object* method = dvmDecodeIndirectRef(env, jmethod);
1976 methodID = (jmethodID) dvmGetMethodFromReflectObj(method);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001977 JNI_EXIT();
1978 return methodID;
1979}
1980
1981/*
1982 * Given a java.lang.reflect.Field, return a fieldID.
1983 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001984static jfieldID FromReflectedField(JNIEnv* env, jobject jfield)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001985{
1986 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001987 jfieldID fieldID;
1988 Object* field = dvmDecodeIndirectRef(env, jfield);
1989 fieldID = (jfieldID) dvmGetFieldFromReflectObj(field);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001990 JNI_EXIT();
1991 return fieldID;
1992}
1993
1994/*
1995 * Convert a methodID to a java.lang.reflect.Method or .Constructor.
1996 *
1997 * (The "isStatic" field does not appear in the spec.)
1998 *
1999 * Throws OutOfMemory and returns NULL on failure.
2000 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002001static jobject ToReflectedMethod(JNIEnv* env, jclass jcls, jmethodID methodID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002002 jboolean isStatic)
2003{
2004 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002005 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls);
2006 Object* obj = dvmCreateReflectObjForMethod(clazz, (Method*) methodID);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002007 dvmReleaseTrackedAlloc(obj, NULL);
Andy McFaddenab00d452009-08-19 07:21:41 -07002008 jobject jobj = addLocalReference(env, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002009 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002010 return jobj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002011}
2012
2013/*
2014 * Convert a fieldID to a java.lang.reflect.Field.
2015 *
2016 * (The "isStatic" field does not appear in the spec.)
2017 *
2018 * Throws OutOfMemory and returns NULL on failure.
2019 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002020static jobject ToReflectedField(JNIEnv* env, jclass jcls, jfieldID fieldID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002021 jboolean isStatic)
2022{
2023 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002024 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls);
2025 Object* obj = dvmCreateReflectObjForField(jcls, (Field*) fieldID);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002026 dvmReleaseTrackedAlloc(obj, NULL);
Andy McFaddenab00d452009-08-19 07:21:41 -07002027 jobject jobj = addLocalReference(env, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002028 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002029 return jobj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002030}
2031
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002032/*
2033 * Take this exception and throw it.
2034 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002035static jint Throw(JNIEnv* env, jthrowable jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002036{
2037 JNI_ENTER();
2038
2039 jint retval;
2040
Andy McFaddenab00d452009-08-19 07:21:41 -07002041 if (jobj != NULL) {
2042 Object* obj = dvmDecodeIndirectRef(env, jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002043 dvmSetException(_self, obj);
2044 retval = JNI_OK;
Andy McFaddenab00d452009-08-19 07:21:41 -07002045 } else {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002046 retval = JNI_ERR;
Andy McFaddenab00d452009-08-19 07:21:41 -07002047 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002048
2049 JNI_EXIT();
2050 return retval;
2051}
2052
2053/*
Andy McFaddenab00d452009-08-19 07:21:41 -07002054 * Constructs an exception object from the specified class with the message
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002055 * specified by "message", and throws it.
2056 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002057static jint ThrowNew(JNIEnv* env, jclass jclazz, const char* message)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002058{
2059 JNI_ENTER();
2060
Andy McFaddenab00d452009-08-19 07:21:41 -07002061 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2062 dvmThrowExceptionByClass(clazz, message);
2063 // TODO: should return failure if this didn't work (e.g. OOM)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002064
2065 JNI_EXIT();
2066 return JNI_OK;
2067}
2068
2069/*
2070 * If an exception is being thrown, return the exception object. Otherwise,
2071 * return NULL.
2072 *
2073 * TODO: if there is no pending exception, we should be able to skip the
2074 * enter/exit checks. If we find one, we need to enter and then re-fetch
2075 * the exception (in case it got moved by a compacting GC).
2076 */
2077static jthrowable ExceptionOccurred(JNIEnv* env)
2078{
2079 JNI_ENTER();
2080
2081 Object* exception;
Andy McFaddenab00d452009-08-19 07:21:41 -07002082 jobject localException;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002083
Andy McFaddenab00d452009-08-19 07:21:41 -07002084 exception = dvmGetException(_self);
2085 localException = addLocalReference(env, exception);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002086 if (localException == NULL && exception != NULL) {
2087 /*
2088 * We were unable to add a new local reference, and threw a new
2089 * exception. We can't return "exception", because it's not a
2090 * local reference. So we have to return NULL, indicating that
2091 * there was no exception, even though it's pretty much raining
2092 * exceptions in here.
2093 */
2094 LOGW("JNI WARNING: addLocal/exception combo\n");
2095 }
2096
2097 JNI_EXIT();
2098 return localException;
2099}
2100
2101/*
2102 * Print an exception and stack trace to stderr.
2103 */
2104static void ExceptionDescribe(JNIEnv* env)
2105{
2106 JNI_ENTER();
2107
2108 Object* exception = dvmGetException(_self);
2109 if (exception != NULL) {
2110 dvmPrintExceptionStackTrace();
2111 } else {
2112 LOGI("Odd: ExceptionDescribe called, but no exception pending\n");
2113 }
2114
2115 JNI_EXIT();
2116}
2117
2118/*
2119 * Clear the exception currently being thrown.
2120 *
2121 * TODO: we should be able to skip the enter/exit stuff.
2122 */
2123static void ExceptionClear(JNIEnv* env)
2124{
2125 JNI_ENTER();
2126 dvmClearException(_self);
2127 JNI_EXIT();
2128}
2129
2130/*
2131 * Kill the VM. This function does not return.
2132 */
2133static void FatalError(JNIEnv* env, const char* msg)
2134{
2135 //dvmChangeStatus(NULL, THREAD_RUNNING);
2136 LOGE("JNI posting fatal error: %s\n", msg);
2137 dvmAbort();
2138}
2139
2140/*
2141 * Push a new JNI frame on the stack, with a new set of locals.
2142 *
2143 * The new frame must have the same method pointer. (If for no other
2144 * reason than FindClass needs it to get the appropriate class loader.)
2145 */
2146static jint PushLocalFrame(JNIEnv* env, jint capacity)
2147{
2148 JNI_ENTER();
2149 int result = JNI_OK;
Andy McFaddenab00d452009-08-19 07:21:41 -07002150 if (!ensureLocalCapacity(env, capacity) ||
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002151 !dvmPushLocalFrame(_self /*dvmThreadSelf()*/, dvmGetCurrentJNIMethod()))
2152 {
2153 /* yes, OutOfMemoryError, not StackOverflowError */
2154 dvmClearException(_self);
2155 dvmThrowException("Ljava/lang/OutOfMemoryError;",
2156 "out of stack in JNI PushLocalFrame");
2157 result = JNI_ERR;
2158 }
2159 JNI_EXIT();
2160 return result;
2161}
2162
2163/*
2164 * Pop the local frame off. If "result" is not null, add it as a
2165 * local reference on the now-current frame.
2166 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002167static jobject PopLocalFrame(JNIEnv* env, jobject jresult)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002168{
2169 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002170 Object* result = dvmDecodeIndirectRef(env, jresult);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002171 if (!dvmPopLocalFrame(_self /*dvmThreadSelf()*/)) {
2172 LOGW("JNI WARNING: too many PopLocalFrame calls\n");
2173 dvmClearException(_self);
2174 dvmThrowException("Ljava/lang/RuntimeException;",
2175 "too many PopLocalFrame calls");
2176 }
Andy McFaddenab00d452009-08-19 07:21:41 -07002177 jresult = addLocalReference(env, result);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002178 JNI_EXIT();
2179 return result;
2180}
2181
2182/*
2183 * Add a reference to the global list.
2184 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002185static jobject NewGlobalRef(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002186{
Andy McFaddenb18992f2009-09-25 10:42:15 -07002187 Object* obj;
2188
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002189 JNI_ENTER();
Andy McFaddenb18992f2009-09-25 10:42:15 -07002190 if (dvmIsWeakGlobalRef(jobj))
2191 obj = getPhantomReferent(env, (jweak) jobj);
2192 else
2193 obj = dvmDecodeIndirectRef(env, jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002194 jobject retval = addGlobalReference(obj);
2195 JNI_EXIT();
2196 return retval;
2197}
2198
2199/*
2200 * Delete a reference from the global list.
2201 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002202static void DeleteGlobalRef(JNIEnv* env, jobject jglobalRef)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002203{
2204 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002205 deleteGlobalReference(jglobalRef);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002206 JNI_EXIT();
2207}
2208
2209
2210/*
2211 * Add a reference to the local list.
2212 */
Andy McFaddenb18992f2009-09-25 10:42:15 -07002213static jobject NewLocalRef(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002214{
Andy McFaddenb18992f2009-09-25 10:42:15 -07002215 Object* obj;
2216
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002217 JNI_ENTER();
Andy McFaddenb18992f2009-09-25 10:42:15 -07002218 if (dvmIsWeakGlobalRef(jobj))
2219 obj = getPhantomReferent(env, (jweak) jobj);
2220 else
2221 obj = dvmDecodeIndirectRef(env, jobj);
Andy McFaddenab00d452009-08-19 07:21:41 -07002222 jobject retval = addLocalReference(env, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002223 JNI_EXIT();
2224 return retval;
2225}
2226
2227/*
2228 * Delete a reference from the local list.
2229 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002230static void DeleteLocalRef(JNIEnv* env, jobject jlocalRef)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002231{
2232 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002233 deleteLocalReference(env, jlocalRef);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002234 JNI_EXIT();
2235}
2236
2237/*
2238 * Ensure that the local references table can hold at least this many
2239 * references.
2240 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002241static jint EnsureLocalCapacity(JNIEnv* env, jint capacity)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002242{
2243 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002244 bool okay = ensureLocalCapacity(env, capacity);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002245 if (!okay) {
2246 dvmThrowException("Ljava/lang/OutOfMemoryError;",
2247 "can't ensure local reference capacity");
2248 }
2249 JNI_EXIT();
2250 if (okay)
2251 return 0;
2252 else
2253 return -1;
2254}
2255
2256
2257/*
2258 * Determine whether two Object references refer to the same underlying object.
2259 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002260static jboolean IsSameObject(JNIEnv* env, jobject jref1, jobject jref2)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002261{
2262 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002263 Object* obj1 = dvmDecodeIndirectRef(env, jref1);
2264 Object* obj2 = dvmDecodeIndirectRef(env, jref2);
2265 jboolean result = (obj1 == obj2);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002266 JNI_EXIT();
2267 return result;
2268}
2269
2270/*
2271 * Allocate a new object without invoking any constructors.
2272 */
2273static jobject AllocObject(JNIEnv* env, jclass jclazz)
2274{
2275 JNI_ENTER();
2276
Andy McFaddenab00d452009-08-19 07:21:41 -07002277 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2278 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002279
2280 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2281 assert(dvmCheckException(_self));
Andy McFaddenab00d452009-08-19 07:21:41 -07002282 result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002283 } else {
Andy McFaddenab00d452009-08-19 07:21:41 -07002284 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
2285 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002286 }
2287
2288 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002289 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002290}
2291
2292/*
Andy McFaddenab00d452009-08-19 07:21:41 -07002293 * Allocate a new object and invoke the supplied constructor.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002294 */
2295static jobject NewObject(JNIEnv* env, jclass jclazz, jmethodID methodID, ...)
2296{
2297 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002298 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2299 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002300
2301 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2302 assert(dvmCheckException(_self));
Andy McFaddenab00d452009-08-19 07:21:41 -07002303 result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002304 } else {
Andy McFaddenab00d452009-08-19 07:21:41 -07002305 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
2306 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002307 if (newObj != NULL) {
2308 JValue unused;
2309 va_list args;
2310 va_start(args, methodID);
Andy McFaddend5ab7262009-08-25 07:19:34 -07002311 dvmCallMethodV(_self, (Method*) methodID, newObj, true, &unused,
2312 args);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002313 va_end(args);
2314 }
2315 }
2316
2317 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002318 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002319}
Andy McFaddenab00d452009-08-19 07:21:41 -07002320static jobject NewObjectV(JNIEnv* env, jclass jclazz, jmethodID methodID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002321 va_list args)
2322{
2323 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002324 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2325 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002326
Andy McFaddenab00d452009-08-19 07:21:41 -07002327 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
2328 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002329 if (newObj != NULL) {
2330 JValue unused;
Andy McFaddend5ab7262009-08-25 07:19:34 -07002331 dvmCallMethodV(_self, (Method*) methodID, newObj, true, &unused, args);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002332 }
2333
2334 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002335 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002336}
Andy McFaddenab00d452009-08-19 07:21:41 -07002337static jobject NewObjectA(JNIEnv* env, jclass jclazz, jmethodID methodID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002338 jvalue* args)
2339{
2340 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002341 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2342 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002343
Andy McFaddenab00d452009-08-19 07:21:41 -07002344 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
2345 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002346 if (newObj != NULL) {
2347 JValue unused;
Andy McFaddend5ab7262009-08-25 07:19:34 -07002348 dvmCallMethodA(_self, (Method*) methodID, newObj, true, &unused, args);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002349 }
2350
2351 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002352 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002353}
2354
2355/*
2356 * Returns the class of an object.
2357 *
2358 * JNI spec says: obj must not be NULL.
2359 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002360static jclass GetObjectClass(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002361{
2362 JNI_ENTER();
2363
Andy McFaddenab00d452009-08-19 07:21:41 -07002364 assert(jobj != NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002365
Andy McFaddenab00d452009-08-19 07:21:41 -07002366 Object* obj = dvmDecodeIndirectRef(env, jobj);
2367 jclass jclazz = addLocalReference(env, (Object*) obj->clazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002368
2369 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002370 return jclazz;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002371}
2372
2373/*
2374 * Determine whether "obj" is an instance of "clazz".
2375 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002376static jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass jclazz)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002377{
2378 JNI_ENTER();
2379
Andy McFaddenab00d452009-08-19 07:21:41 -07002380 assert(jclazz != NULL);
2381
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002382 jboolean result;
2383
Andy McFaddenab00d452009-08-19 07:21:41 -07002384 if (jobj == NULL) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002385 result = true;
Andy McFaddenab00d452009-08-19 07:21:41 -07002386 } else {
2387 Object* obj = dvmDecodeIndirectRef(env, jobj);
2388 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2389 result = dvmInstanceof(obj->clazz, clazz);
2390 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002391
2392 JNI_EXIT();
2393 return result;
2394}
2395
2396/*
2397 * Get a method ID for an instance method.
2398 *
2399 * JNI defines <init> as an instance method, but Dalvik considers it a
2400 * "direct" method, so we have to special-case it here.
2401 *
2402 * Dalvik also puts all private methods into the "direct" list, so we
2403 * really need to just search both lists.
2404 */
2405static jmethodID GetMethodID(JNIEnv* env, jclass jclazz, const char* name,
2406 const char* sig)
2407{
2408 JNI_ENTER();
2409
Andy McFaddenab00d452009-08-19 07:21:41 -07002410 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002411 jmethodID id = NULL;
2412
2413 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2414 assert(dvmCheckException(_self));
2415 } else {
2416 Method* meth;
2417
2418 meth = dvmFindVirtualMethodHierByDescriptor(clazz, name, sig);
2419 if (meth == NULL) {
2420 /* search private methods and constructors; non-hierarchical */
2421 meth = dvmFindDirectMethodByDescriptor(clazz, name, sig);
2422 }
2423 if (meth != NULL && dvmIsStaticMethod(meth)) {
2424 IF_LOGD() {
2425 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
2426 LOGD("GetMethodID: not returning static method %s.%s %s\n",
2427 clazz->descriptor, meth->name, desc);
2428 free(desc);
2429 }
2430 meth = NULL;
2431 }
2432 if (meth == NULL) {
Andy McFadden03bd0d52009-08-18 15:32:27 -07002433 LOGD("GetMethodID: method not found: %s.%s:%s\n",
2434 clazz->descriptor, name, sig);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002435 dvmThrowException("Ljava/lang/NoSuchMethodError;", name);
2436 }
2437
2438 /*
2439 * The method's class may not be the same as clazz, but if
2440 * it isn't this must be a virtual method and the class must
2441 * be a superclass (and, hence, already initialized).
2442 */
2443 if (meth != NULL) {
2444 assert(dvmIsClassInitialized(meth->clazz) ||
2445 dvmIsClassInitializing(meth->clazz));
2446 }
2447 id = (jmethodID) meth;
2448 }
2449 JNI_EXIT();
2450 return id;
2451}
2452
2453/*
2454 * Get a field ID (instance fields).
2455 */
2456static jfieldID GetFieldID(JNIEnv* env, jclass jclazz,
2457 const char* name, const char* sig)
2458{
2459 JNI_ENTER();
2460
Andy McFaddenab00d452009-08-19 07:21:41 -07002461 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002462 jfieldID id;
2463
2464 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2465 assert(dvmCheckException(_self));
2466 id = NULL;
2467 } else {
2468 id = (jfieldID) dvmFindInstanceFieldHier(clazz, name, sig);
Andy McFadden03bd0d52009-08-18 15:32:27 -07002469 if (id == NULL) {
2470 LOGD("GetFieldID: unable to find field %s.%s:%s\n",
2471 clazz->descriptor, name, sig);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002472 dvmThrowException("Ljava/lang/NoSuchFieldError;", name);
Andy McFadden03bd0d52009-08-18 15:32:27 -07002473 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002474 }
2475 JNI_EXIT();
2476 return id;
2477}
2478
2479/*
2480 * Get the method ID for a static method in a class.
2481 */
2482static jmethodID GetStaticMethodID(JNIEnv* env, jclass jclazz,
2483 const char* name, const char* sig)
2484{
2485 JNI_ENTER();
2486
Andy McFaddenab00d452009-08-19 07:21:41 -07002487 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002488 jmethodID id;
2489
2490 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2491 assert(dvmCheckException(_self));
2492 id = NULL;
2493 } else {
2494 Method* meth;
2495
2496 meth = dvmFindDirectMethodHierByDescriptor(clazz, name, sig);
2497
2498 /* make sure it's static, not virtual+private */
2499 if (meth != NULL && !dvmIsStaticMethod(meth)) {
2500 IF_LOGD() {
2501 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
2502 LOGD("GetStaticMethodID: "
2503 "not returning nonstatic method %s.%s %s\n",
2504 clazz->descriptor, meth->name, desc);
2505 free(desc);
2506 }
2507 meth = NULL;
2508 }
2509
2510 id = (jmethodID) meth;
2511 if (id == NULL)
2512 dvmThrowException("Ljava/lang/NoSuchMethodError;", name);
2513 }
2514
2515 JNI_EXIT();
2516 return id;
2517}
2518
2519/*
2520 * Get a field ID (static fields).
2521 */
2522static jfieldID GetStaticFieldID(JNIEnv* env, jclass jclazz,
2523 const char* name, const char* sig)
2524{
2525 JNI_ENTER();
2526
Andy McFaddenab00d452009-08-19 07:21:41 -07002527 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002528 jfieldID id;
2529
2530 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2531 assert(dvmCheckException(_self));
2532 id = NULL;
2533 } else {
2534 id = (jfieldID) dvmFindStaticField(clazz, name, sig);
2535 if (id == NULL)
2536 dvmThrowException("Ljava/lang/NoSuchFieldError;", name);
2537 }
2538 JNI_EXIT();
2539 return id;
2540}
2541
2542/*
2543 * Get a static field.
2544 *
2545 * If we get an object reference, add it to the local refs list.
2546 */
2547#define GET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002548 static _ctype GetStatic##_jname##Field(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002549 jfieldID fieldID) \
2550 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002551 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002552 JNI_ENTER(); \
2553 StaticField* sfield = (StaticField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002554 _ctype value; \
2555 if (_isref) { /* only when _ctype==jobject */ \
2556 Object* obj = dvmGetStaticFieldObject(sfield); \
2557 value = (_ctype)(u4)addLocalReference(env, obj); \
2558 } else { \
2559 value = dvmGetStaticField##_jname(sfield); \
2560 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002561 JNI_EXIT(); \
2562 return value; \
2563 }
2564GET_STATIC_TYPE_FIELD(jobject, Object, true);
2565GET_STATIC_TYPE_FIELD(jboolean, Boolean, false);
2566GET_STATIC_TYPE_FIELD(jbyte, Byte, false);
2567GET_STATIC_TYPE_FIELD(jchar, Char, false);
2568GET_STATIC_TYPE_FIELD(jshort, Short, false);
2569GET_STATIC_TYPE_FIELD(jint, Int, false);
2570GET_STATIC_TYPE_FIELD(jlong, Long, false);
2571GET_STATIC_TYPE_FIELD(jfloat, Float, false);
2572GET_STATIC_TYPE_FIELD(jdouble, Double, false);
2573
2574/*
2575 * Set a static field.
2576 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002577#define SET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) \
2578 static void SetStatic##_jname##Field(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002579 jfieldID fieldID, _ctype value) \
2580 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002581 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002582 JNI_ENTER(); \
2583 StaticField* sfield = (StaticField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002584 if (_isref) { /* only when _ctype==jobject */ \
2585 Object* valObj = dvmDecodeIndirectRef(env, (jobject)(u4)value); \
2586 dvmSetStaticFieldObject(sfield, valObj); \
2587 } else { \
2588 dvmSetStaticField##_jname(sfield, value); \
2589 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002590 JNI_EXIT(); \
2591 }
Andy McFaddenab00d452009-08-19 07:21:41 -07002592SET_STATIC_TYPE_FIELD(jobject, Object, true);
2593SET_STATIC_TYPE_FIELD(jboolean, Boolean, false);
2594SET_STATIC_TYPE_FIELD(jbyte, Byte, false);
2595SET_STATIC_TYPE_FIELD(jchar, Char, false);
2596SET_STATIC_TYPE_FIELD(jshort, Short, false);
2597SET_STATIC_TYPE_FIELD(jint, Int, false);
2598SET_STATIC_TYPE_FIELD(jlong, Long, false);
2599SET_STATIC_TYPE_FIELD(jfloat, Float, false);
2600SET_STATIC_TYPE_FIELD(jdouble, Double, false);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002601
2602/*
2603 * Get an instance field.
2604 *
2605 * If we get an object reference, add it to the local refs list.
2606 */
2607#define GET_TYPE_FIELD(_ctype, _jname, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002608 static _ctype Get##_jname##Field(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002609 jfieldID fieldID) \
2610 { \
2611 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002612 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002613 InstField* field = (InstField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002614 _ctype value; \
2615 if (_isref) { /* only when _ctype==jobject */ \
2616 Object* valObj = dvmGetFieldObject(obj, field->byteOffset); \
2617 value = (_ctype)(u4)addLocalReference(env, valObj); \
2618 } else { \
2619 value = dvmGetField##_jname(obj, field->byteOffset); \
2620 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002621 JNI_EXIT(); \
2622 return value; \
2623 }
2624GET_TYPE_FIELD(jobject, Object, true);
2625GET_TYPE_FIELD(jboolean, Boolean, false);
2626GET_TYPE_FIELD(jbyte, Byte, false);
2627GET_TYPE_FIELD(jchar, Char, false);
2628GET_TYPE_FIELD(jshort, Short, false);
2629GET_TYPE_FIELD(jint, Int, false);
2630GET_TYPE_FIELD(jlong, Long, false);
2631GET_TYPE_FIELD(jfloat, Float, false);
2632GET_TYPE_FIELD(jdouble, Double, false);
2633
2634/*
2635 * Set an instance field.
2636 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002637#define SET_TYPE_FIELD(_ctype, _jname, _isref) \
2638 static void Set##_jname##Field(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002639 jfieldID fieldID, _ctype value) \
2640 { \
2641 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002642 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002643 InstField* field = (InstField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002644 if (_isref) { /* only when _ctype==jobject */ \
2645 Object* valObj = dvmDecodeIndirectRef(env, (jobject)(u4)value); \
2646 dvmSetFieldObject(obj, field->byteOffset, valObj); \
2647 } else { \
2648 dvmSetField##_jname(obj, field->byteOffset, value); \
2649 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002650 JNI_EXIT(); \
2651 }
Andy McFaddenab00d452009-08-19 07:21:41 -07002652SET_TYPE_FIELD(jobject, Object, true);
2653SET_TYPE_FIELD(jboolean, Boolean, false);
2654SET_TYPE_FIELD(jbyte, Byte, false);
2655SET_TYPE_FIELD(jchar, Char, false);
2656SET_TYPE_FIELD(jshort, Short, false);
2657SET_TYPE_FIELD(jint, Int, false);
2658SET_TYPE_FIELD(jlong, Long, false);
2659SET_TYPE_FIELD(jfloat, Float, false);
2660SET_TYPE_FIELD(jdouble, Double, false);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002661
2662/*
2663 * Make a virtual method call.
2664 *
2665 * Three versions (..., va_list, jvalue[]) for each return type. If we're
2666 * returning an Object, we have to add it to the local references table.
2667 */
2668#define CALL_VIRTUAL(_ctype, _jname, _retfail, _retok, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002669 static _ctype Call##_jname##Method(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002670 jmethodID methodID, ...) \
2671 { \
2672 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002673 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002674 const Method* meth; \
2675 va_list args; \
2676 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002677 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002678 if (meth == NULL) { \
2679 JNI_EXIT(); \
2680 return _retfail; \
2681 } \
2682 va_start(args, methodID); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002683 dvmCallMethodV(_self, meth, obj, true, &result, args); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002684 va_end(args); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002685 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002686 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002687 JNI_EXIT(); \
2688 return _retok; \
2689 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002690 static _ctype Call##_jname##MethodV(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002691 jmethodID methodID, va_list args) \
2692 { \
2693 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002694 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002695 const Method* meth; \
2696 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002697 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002698 if (meth == NULL) { \
2699 JNI_EXIT(); \
2700 return _retfail; \
2701 } \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002702 dvmCallMethodV(_self, meth, obj, true, &result, args); \
2703 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002704 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002705 JNI_EXIT(); \
2706 return _retok; \
2707 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002708 static _ctype Call##_jname##MethodA(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002709 jmethodID methodID, jvalue* args) \
2710 { \
2711 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002712 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002713 const Method* meth; \
2714 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002715 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002716 if (meth == NULL) { \
2717 JNI_EXIT(); \
2718 return _retfail; \
2719 } \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002720 dvmCallMethodA(_self, meth, obj, true, &result, args); \
2721 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002722 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002723 JNI_EXIT(); \
2724 return _retok; \
2725 }
2726CALL_VIRTUAL(jobject, Object, NULL, result.l, true);
2727CALL_VIRTUAL(jboolean, Boolean, 0, result.z, false);
2728CALL_VIRTUAL(jbyte, Byte, 0, result.b, false);
2729CALL_VIRTUAL(jchar, Char, 0, result.c, false);
2730CALL_VIRTUAL(jshort, Short, 0, result.s, false);
2731CALL_VIRTUAL(jint, Int, 0, result.i, false);
2732CALL_VIRTUAL(jlong, Long, 0, result.j, false);
2733CALL_VIRTUAL(jfloat, Float, 0.0f, result.f, false);
2734CALL_VIRTUAL(jdouble, Double, 0.0, result.d, false);
2735CALL_VIRTUAL(void, Void, , , false);
2736
2737/*
2738 * Make a "non-virtual" method call. We're still calling a virtual method,
2739 * but this time we're not doing an indirection through the object's vtable.
2740 * The "clazz" parameter defines which implementation of a method we want.
2741 *
2742 * Three versions (..., va_list, jvalue[]) for each return type.
2743 */
2744#define CALL_NONVIRTUAL(_ctype, _jname, _retfail, _retok, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002745 static _ctype CallNonvirtual##_jname##Method(JNIEnv* env, jobject jobj, \
2746 jclass jclazz, jmethodID methodID, ...) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002747 { \
2748 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002749 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2750 ClassObject* clazz = \
2751 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002752 const Method* meth; \
2753 va_list args; \
2754 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002755 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002756 if (meth == NULL) { \
2757 JNI_EXIT(); \
2758 return _retfail; \
2759 } \
2760 va_start(args, methodID); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002761 dvmCallMethodV(_self, meth, obj, true, &result, args); \
2762 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002763 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002764 va_end(args); \
2765 JNI_EXIT(); \
2766 return _retok; \
2767 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002768 static _ctype CallNonvirtual##_jname##MethodV(JNIEnv* env, jobject jobj,\
2769 jclass jclazz, jmethodID methodID, va_list args) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002770 { \
2771 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002772 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2773 ClassObject* clazz = \
2774 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002775 const Method* meth; \
2776 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002777 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002778 if (meth == NULL) { \
2779 JNI_EXIT(); \
2780 return _retfail; \
2781 } \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002782 dvmCallMethodV(_self, meth, obj, true, &result, args); \
2783 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002784 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002785 JNI_EXIT(); \
2786 return _retok; \
2787 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002788 static _ctype CallNonvirtual##_jname##MethodA(JNIEnv* env, jobject jobj,\
2789 jclass jclazz, jmethodID methodID, jvalue* args) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002790 { \
2791 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002792 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2793 ClassObject* clazz = \
2794 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002795 const Method* meth; \
2796 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002797 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002798 if (meth == NULL) { \
2799 JNI_EXIT(); \
2800 return _retfail; \
2801 } \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002802 dvmCallMethodA(_self, meth, obj, true, &result, args); \
2803 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002804 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002805 JNI_EXIT(); \
2806 return _retok; \
2807 }
2808CALL_NONVIRTUAL(jobject, Object, NULL, result.l, true);
2809CALL_NONVIRTUAL(jboolean, Boolean, 0, result.z, false);
2810CALL_NONVIRTUAL(jbyte, Byte, 0, result.b, false);
2811CALL_NONVIRTUAL(jchar, Char, 0, result.c, false);
2812CALL_NONVIRTUAL(jshort, Short, 0, result.s, false);
2813CALL_NONVIRTUAL(jint, Int, 0, result.i, false);
2814CALL_NONVIRTUAL(jlong, Long, 0, result.j, false);
2815CALL_NONVIRTUAL(jfloat, Float, 0.0f, result.f, false);
2816CALL_NONVIRTUAL(jdouble, Double, 0.0, result.d, false);
2817CALL_NONVIRTUAL(void, Void, , , false);
2818
2819
2820/*
2821 * Call a static method.
2822 */
2823#define CALL_STATIC(_ctype, _jname, _retfail, _retok, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002824 static _ctype CallStatic##_jname##Method(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002825 jmethodID methodID, ...) \
2826 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002827 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002828 JNI_ENTER(); \
2829 JValue result; \
2830 va_list args; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002831 va_start(args, methodID); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002832 dvmCallMethodV(_self, (Method*)methodID, NULL, true, &result, args);\
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002833 va_end(args); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002834 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002835 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002836 JNI_EXIT(); \
2837 return _retok; \
2838 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002839 static _ctype CallStatic##_jname##MethodV(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002840 jmethodID methodID, va_list args) \
2841 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002842 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002843 JNI_ENTER(); \
2844 JValue result; \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002845 dvmCallMethodV(_self, (Method*)methodID, NULL, true, &result, args);\
2846 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002847 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002848 JNI_EXIT(); \
2849 return _retok; \
2850 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002851 static _ctype CallStatic##_jname##MethodA(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002852 jmethodID methodID, jvalue* args) \
2853 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002854 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002855 JNI_ENTER(); \
2856 JValue result; \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002857 dvmCallMethodA(_self, (Method*)methodID, NULL, true, &result, args);\
2858 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002859 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002860 JNI_EXIT(); \
2861 return _retok; \
2862 }
2863CALL_STATIC(jobject, Object, NULL, result.l, true);
2864CALL_STATIC(jboolean, Boolean, 0, result.z, false);
2865CALL_STATIC(jbyte, Byte, 0, result.b, false);
2866CALL_STATIC(jchar, Char, 0, result.c, false);
2867CALL_STATIC(jshort, Short, 0, result.s, false);
2868CALL_STATIC(jint, Int, 0, result.i, false);
2869CALL_STATIC(jlong, Long, 0, result.j, false);
2870CALL_STATIC(jfloat, Float, 0.0f, result.f, false);
2871CALL_STATIC(jdouble, Double, 0.0, result.d, false);
2872CALL_STATIC(void, Void, , , false);
2873
2874/*
2875 * Create a new String from Unicode data.
2876 *
2877 * If "len" is zero, we will return an empty string even if "unicodeChars"
2878 * is NULL. (The JNI spec is vague here.)
2879 */
2880static jstring NewString(JNIEnv* env, const jchar* unicodeChars, jsize len)
2881{
2882 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002883 jobject retval;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002884
Andy McFaddenab00d452009-08-19 07:21:41 -07002885 StringObject* jstr = dvmCreateStringFromUnicode(unicodeChars, len);
2886 if (jstr == NULL) {
2887 retval = NULL;
2888 } else {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002889 dvmReleaseTrackedAlloc((Object*) jstr, NULL);
Andy McFaddenab00d452009-08-19 07:21:41 -07002890 retval = addLocalReference(env, (Object*) jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002891 }
2892
2893 JNI_EXIT();
Andy McFadden0423f0e2009-08-26 07:21:53 -07002894 return retval;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002895}
2896
2897/*
2898 * Return the length of a String in Unicode character units.
2899 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002900static jsize GetStringLength(JNIEnv* env, jstring jstr)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002901{
2902 JNI_ENTER();
2903
Andy McFaddenab00d452009-08-19 07:21:41 -07002904 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2905 jsize len = dvmStringLen(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002906
2907 JNI_EXIT();
2908 return len;
2909}
2910
Andy McFaddenab00d452009-08-19 07:21:41 -07002911
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002912/*
Andy McFaddenab00d452009-08-19 07:21:41 -07002913 * Get a string's character data.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002914 *
2915 * The result is guaranteed to be valid until ReleaseStringChars is
Andy McFaddenab00d452009-08-19 07:21:41 -07002916 * called, which means we have to pin it or return a copy.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002917 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002918static const jchar* GetStringChars(JNIEnv* env, jstring jstr, jboolean* isCopy)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002919{
2920 JNI_ENTER();
2921
Andy McFaddenab00d452009-08-19 07:21:41 -07002922 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
Andy McFaddend5ab7262009-08-25 07:19:34 -07002923 ArrayObject* strChars = dvmStringCharArray(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002924
Andy McFaddenab00d452009-08-19 07:21:41 -07002925 pinPrimitiveArray(strChars);
2926
2927 const u2* data = dvmStringChars(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002928 if (isCopy != NULL)
2929 *isCopy = JNI_FALSE;
2930
2931 JNI_EXIT();
2932 return (jchar*)data;
2933}
2934
2935/*
2936 * Release our grip on some characters from a string.
2937 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002938static void ReleaseStringChars(JNIEnv* env, jstring jstr, const jchar* chars)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002939{
2940 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002941 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
Andy McFaddend5ab7262009-08-25 07:19:34 -07002942 ArrayObject* strChars = dvmStringCharArray(strObj);
Andy McFaddenab00d452009-08-19 07:21:41 -07002943 unpinPrimitiveArray(strChars);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002944 JNI_EXIT();
2945}
2946
2947/*
2948 * Create a new java.lang.String object from chars in modified UTF-8 form.
2949 *
2950 * The spec doesn't say how to handle a NULL string. Popular desktop VMs
2951 * accept it and return a NULL pointer in response.
2952 */
2953static jstring NewStringUTF(JNIEnv* env, const char* bytes)
2954{
2955 JNI_ENTER();
2956
Andy McFaddenab00d452009-08-19 07:21:41 -07002957 jstring result;
2958
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002959 if (bytes == NULL) {
Andy McFaddenab00d452009-08-19 07:21:41 -07002960 result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002961 } else {
Andy McFaddenab00d452009-08-19 07:21:41 -07002962 /* note newStr could come back NULL on OOM */
2963 StringObject* newStr = dvmCreateStringFromCstr(bytes, ALLOC_DEFAULT);
2964 result = addLocalReference(env, (Object*) newStr);
2965 dvmReleaseTrackedAlloc((Object*)newStr, NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002966 }
2967
2968 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002969 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002970}
2971
2972/*
2973 * Return the length in bytes of the modified UTF-8 form of the string.
2974 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002975static jsize GetStringUTFLength(JNIEnv* env, jstring jstr)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002976{
2977 JNI_ENTER();
2978
Andy McFaddenab00d452009-08-19 07:21:41 -07002979 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2980 jsize len = dvmStringUtf8ByteLen(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002981
2982 JNI_EXIT();
2983 return len;
2984}
2985
2986/*
2987 * Convert "string" to modified UTF-8 and return a pointer. The returned
2988 * value must be released with ReleaseStringUTFChars.
2989 *
2990 * According to the JNI reference, "Returns a pointer to a UTF-8 string,
2991 * or NULL if the operation fails. Returns NULL if and only if an invocation
2992 * of this function has thrown an exception."
2993 *
2994 * The behavior here currently follows that of other open-source VMs, which
2995 * quietly return NULL if "string" is NULL. We should consider throwing an
2996 * NPE. (The CheckJNI code blows up if you try to pass in a NULL string,
2997 * which should catch this sort of thing during development.) Certain other
2998 * VMs will crash with a segmentation fault.
2999 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003000static const char* GetStringUTFChars(JNIEnv* env, jstring jstr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003001 jboolean* isCopy)
3002{
3003 JNI_ENTER();
3004 char* newStr;
3005
Andy McFaddenab00d452009-08-19 07:21:41 -07003006 if (jstr == NULL) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003007 /* this shouldn't happen; throw NPE? */
3008 newStr = NULL;
3009 } else {
3010 if (isCopy != NULL)
3011 *isCopy = JNI_TRUE;
3012
Andy McFaddenab00d452009-08-19 07:21:41 -07003013 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
3014 newStr = dvmCreateCstrFromString(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003015 if (newStr == NULL) {
3016 /* assume memory failure */
3017 dvmThrowException("Ljava/lang/OutOfMemoryError;",
3018 "native heap string alloc failed");
3019 }
3020 }
3021
3022 JNI_EXIT();
3023 return newStr;
3024}
3025
3026/*
3027 * Release a string created by GetStringUTFChars().
3028 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003029static void ReleaseStringUTFChars(JNIEnv* env, jstring jstr, const char* utf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003030{
3031 JNI_ENTER();
3032 free((char*)utf);
3033 JNI_EXIT();
3034}
3035
3036/*
3037 * Return the capacity of the array.
3038 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003039static jsize GetArrayLength(JNIEnv* env, jarray jarr)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003040{
3041 JNI_ENTER();
3042
Andy McFaddenab00d452009-08-19 07:21:41 -07003043 ArrayObject* arrObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
3044 jsize length = arrObj->length;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003045
3046 JNI_EXIT();
3047 return length;
3048}
3049
3050/*
3051 * Construct a new array that holds objects from class "elementClass".
3052 */
3053static jobjectArray NewObjectArray(JNIEnv* env, jsize length,
Andy McFaddenab00d452009-08-19 07:21:41 -07003054 jclass jelementClass, jobject jinitialElement)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003055{
3056 JNI_ENTER();
3057
Andy McFaddenab00d452009-08-19 07:21:41 -07003058 jobjectArray newArray = NULL;
3059 ClassObject* elemClassObj =
3060 (ClassObject*) dvmDecodeIndirectRef(env, jelementClass);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003061
3062 if (elemClassObj == NULL) {
3063 dvmThrowException("Ljava/lang/NullPointerException;",
3064 "JNI NewObjectArray");
3065 goto bail;
3066 }
3067
Andy McFaddenab00d452009-08-19 07:21:41 -07003068 ArrayObject* newObj =
3069 dvmAllocObjectArray(elemClassObj, length, ALLOC_DEFAULT);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003070 if (newObj == NULL) {
3071 assert(dvmCheckException(_self));
3072 goto bail;
3073 }
Andy McFaddenab00d452009-08-19 07:21:41 -07003074 newArray = addLocalReference(env, (Object*) newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003075 dvmReleaseTrackedAlloc((Object*) newObj, NULL);
3076
3077 /*
3078 * Initialize the array. Trashes "length".
3079 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003080 if (jinitialElement != NULL) {
3081 Object* initialElement = dvmDecodeIndirectRef(env, jinitialElement);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003082 Object** arrayData = (Object**) newObj->contents;
3083
3084 while (length--)
Andy McFaddenab00d452009-08-19 07:21:41 -07003085 *arrayData++ = initialElement;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003086 }
3087
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003088
3089bail:
3090 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07003091 return newArray;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003092}
3093
3094/*
3095 * Get one element of an Object array.
3096 *
3097 * Add the object to the local references table in case the array goes away.
3098 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003099static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray jarr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003100 jsize index)
3101{
3102 JNI_ENTER();
3103
Andy McFaddenab00d452009-08-19 07:21:41 -07003104 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
3105 jobject retval = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003106
Andy McFaddenab00d452009-08-19 07:21:41 -07003107 assert(arrayObj != NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003108
3109 /* check the array bounds */
3110 if (index < 0 || index >= (int) arrayObj->length) {
3111 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
3112 arrayObj->obj.clazz->descriptor);
3113 goto bail;
3114 }
3115
Andy McFaddenab00d452009-08-19 07:21:41 -07003116 Object* value = ((Object**) arrayObj->contents)[index];
3117 retval = addLocalReference(env, value);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003118
3119bail:
3120 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07003121 return retval;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003122}
3123
3124/*
3125 * Set one element of an Object array.
3126 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003127static void SetObjectArrayElement(JNIEnv* env, jobjectArray jarr,
3128 jsize index, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003129{
3130 JNI_ENTER();
3131
Andy McFaddenab00d452009-08-19 07:21:41 -07003132 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003133
Andy McFaddenab00d452009-08-19 07:21:41 -07003134 assert(arrayObj != NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003135
3136 /* check the array bounds */
3137 if (index < 0 || index >= (int) arrayObj->length) {
3138 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
3139 arrayObj->obj.clazz->descriptor);
3140 goto bail;
3141 }
3142
3143 //LOGV("JNI: set element %d in array %p to %p\n", index, array, value);
3144
Andy McFadden0423f0e2009-08-26 07:21:53 -07003145 Object* obj = dvmDecodeIndirectRef(env, jobj);
Andy McFaddenab00d452009-08-19 07:21:41 -07003146 ((Object**) arrayObj->contents)[index] = obj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003147
3148bail:
3149 JNI_EXIT();
3150}
3151
3152/*
3153 * Create a new array of primitive elements.
3154 */
3155#define NEW_PRIMITIVE_ARRAY(_artype, _jname, _typechar) \
3156 static _artype New##_jname##Array(JNIEnv* env, jsize length) \
3157 { \
3158 JNI_ENTER(); \
3159 ArrayObject* arrayObj; \
3160 arrayObj = dvmAllocPrimitiveArray(_typechar, length, \
3161 ALLOC_DEFAULT); \
Andy McFaddenab00d452009-08-19 07:21:41 -07003162 jarray jarr = NULL; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003163 if (arrayObj != NULL) { \
Andy McFaddenab00d452009-08-19 07:21:41 -07003164 jarr = addLocalReference(env, (Object*) arrayObj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003165 dvmReleaseTrackedAlloc((Object*) arrayObj, NULL); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003166 } \
3167 JNI_EXIT(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07003168 return (_artype)jarr; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003169 }
3170NEW_PRIMITIVE_ARRAY(jbooleanArray, Boolean, 'Z');
3171NEW_PRIMITIVE_ARRAY(jbyteArray, Byte, 'B');
3172NEW_PRIMITIVE_ARRAY(jcharArray, Char, 'C');
3173NEW_PRIMITIVE_ARRAY(jshortArray, Short, 'S');
3174NEW_PRIMITIVE_ARRAY(jintArray, Int, 'I');
3175NEW_PRIMITIVE_ARRAY(jlongArray, Long, 'J');
3176NEW_PRIMITIVE_ARRAY(jfloatArray, Float, 'F');
3177NEW_PRIMITIVE_ARRAY(jdoubleArray, Double, 'D');
3178
3179/*
3180 * Get a pointer to a C array of primitive elements from an array object
3181 * of the matching type.
3182 *
Andy McFaddenab00d452009-08-19 07:21:41 -07003183 * In a compacting GC, we either need to return a copy of the elements or
3184 * "pin" the memory. Otherwise we run the risk of native code using the
3185 * buffer as the destination of e.g. a blocking read() call that wakes up
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003186 * during a GC.
3187 */
3188#define GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
3189 static _ctype* Get##_jname##ArrayElements(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07003190 _ctype##Array jarr, jboolean* isCopy) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003191 { \
3192 JNI_ENTER(); \
3193 _ctype* data; \
Andy McFaddenab00d452009-08-19 07:21:41 -07003194 ArrayObject* arrayObj = \
3195 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
3196 pinPrimitiveArray(arrayObj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003197 data = (_ctype*) arrayObj->contents; \
3198 if (isCopy != NULL) \
3199 *isCopy = JNI_FALSE; \
3200 JNI_EXIT(); \
3201 return data; \
3202 }
3203
3204/*
3205 * Release the storage locked down by the "get" function.
3206 *
Andy McFaddenab00d452009-08-19 07:21:41 -07003207 * 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 -08003208 * elements in 'array'." They apparently did not anticipate the need to
Andy McFaddenab00d452009-08-19 07:21:41 -07003209 * un-pin memory.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003210 */
3211#define RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
3212 static void Release##_jname##ArrayElements(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07003213 _ctype##Array jarr, _ctype* elems, jint mode) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003214 { \
3215 UNUSED_PARAMETER(elems); \
3216 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07003217 if (mode != JNI_COMMIT) { \
3218 ArrayObject* arrayObj = \
3219 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
3220 unpinPrimitiveArray(arrayObj); \
3221 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003222 JNI_EXIT(); \
3223 }
3224
3225/*
3226 * Copy a section of a primitive array to a buffer.
3227 */
3228#define GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
3229 static void Get##_jname##ArrayRegion(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07003230 _ctype##Array jarr, jsize start, jsize len, _ctype* buf) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003231 { \
3232 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07003233 ArrayObject* arrayObj = \
3234 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003235 _ctype* data = (_ctype*) arrayObj->contents; \
3236 if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
3237 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
3238 arrayObj->obj.clazz->descriptor); \
3239 } else { \
3240 memcpy(buf, data + start, len * sizeof(_ctype)); \
3241 } \
3242 JNI_EXIT(); \
3243 }
3244
3245/*
3246 * Copy a section of a primitive array to a buffer.
3247 */
3248#define SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
3249 static void Set##_jname##ArrayRegion(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07003250 _ctype##Array jarr, jsize start, jsize len, const _ctype* buf) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003251 { \
3252 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07003253 ArrayObject* arrayObj = \
3254 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003255 _ctype* data = (_ctype*) arrayObj->contents; \
3256 if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
3257 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
3258 arrayObj->obj.clazz->descriptor); \
3259 } else { \
3260 memcpy(data + start, buf, len * sizeof(_ctype)); \
3261 } \
3262 JNI_EXIT(); \
3263 }
3264
3265/*
3266 * 4-in-1:
3267 * Get<Type>ArrayElements
3268 * Release<Type>ArrayElements
3269 * Get<Type>ArrayRegion
3270 * Set<Type>ArrayRegion
3271 */
3272#define PRIMITIVE_ARRAY_FUNCTIONS(_ctype, _jname) \
3273 GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
3274 RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
3275 GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname); \
3276 SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname);
3277
3278PRIMITIVE_ARRAY_FUNCTIONS(jboolean, Boolean);
3279PRIMITIVE_ARRAY_FUNCTIONS(jbyte, Byte);
3280PRIMITIVE_ARRAY_FUNCTIONS(jchar, Char);
3281PRIMITIVE_ARRAY_FUNCTIONS(jshort, Short);
3282PRIMITIVE_ARRAY_FUNCTIONS(jint, Int);
3283PRIMITIVE_ARRAY_FUNCTIONS(jlong, Long);
3284PRIMITIVE_ARRAY_FUNCTIONS(jfloat, Float);
3285PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double);
3286
3287/*
3288 * Register one or more native functions in one class.
3289 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003290static jint RegisterNatives(JNIEnv* env, jclass jclazz,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003291 const JNINativeMethod* methods, jint nMethods)
3292{
3293 JNI_ENTER();
3294
Andy McFaddenab00d452009-08-19 07:21:41 -07003295 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
Elliott Hughes09239e32009-09-29 18:35:43 -07003296 jint retval = JNI_OK;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003297 int i;
3298
3299 if (gDvm.verboseJni) {
3300 LOGI("[Registering JNI native methods for class %s]\n",
Andy McFaddenab00d452009-08-19 07:21:41 -07003301 clazz->descriptor);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003302 }
3303
3304 for (i = 0; i < nMethods; i++) {
Andy McFaddenab00d452009-08-19 07:21:41 -07003305 if (!dvmRegisterJNIMethod(clazz, methods[i].name,
3306 methods[i].signature, methods[i].fnPtr))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003307 {
3308 retval = JNI_ERR;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003309 }
3310 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003311
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003312 JNI_EXIT();
3313 return retval;
3314}
3315
3316/*
3317 * Un-register a native function.
3318 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003319static jint UnregisterNatives(JNIEnv* env, jclass jclazz)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003320{
3321 JNI_ENTER();
3322 /*
3323 * The JNI docs refer to this as a way to reload/relink native libraries,
3324 * and say it "should not be used in normal native code".
3325 *
3326 * We can implement it if we decide we need it.
3327 */
3328 JNI_EXIT();
3329 return JNI_ERR;
3330}
3331
3332/*
3333 * Lock the monitor.
3334 *
3335 * We have to track all monitor enters and exits, so that we can undo any
3336 * outstanding synchronization before the thread exits.
3337 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003338static jint MonitorEnter(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003339{
3340 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003341 Object* obj = dvmDecodeIndirectRef(env, jobj);
3342 dvmLockObject(_self, obj);
3343 trackMonitorEnter(_self, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003344 JNI_EXIT();
3345 return JNI_OK;
3346}
3347
3348/*
3349 * Unlock the monitor.
3350 *
3351 * Throws an IllegalMonitorStateException if the current thread
Andy McFaddenab00d452009-08-19 07:21:41 -07003352 * doesn't own the monitor. (dvmUnlockObject() takes care of the throw.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003353 *
3354 * According to the 1.6 spec, it's legal to call here with an exception
3355 * pending. If this fails, we'll stomp the original exception.
3356 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003357static jint MonitorExit(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003358{
3359 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003360 Object* obj = dvmDecodeIndirectRef(env, jobj);
3361 bool success = dvmUnlockObject(_self, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003362 if (success)
Andy McFaddenab00d452009-08-19 07:21:41 -07003363 trackMonitorExit(_self, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003364 JNI_EXIT();
3365 return success ? JNI_OK : JNI_ERR;
3366}
3367
3368/*
3369 * Return the JavaVM interface associated with the current thread.
3370 */
3371static jint GetJavaVM(JNIEnv* env, JavaVM** vm)
3372{
3373 JNI_ENTER();
3374 //*vm = gDvm.vmList;
3375 *vm = (JavaVM*) ((JNIEnvExt*)env)->vm;
3376 JNI_EXIT();
3377 if (*vm == NULL)
3378 return JNI_ERR;
3379 else
3380 return JNI_OK;
3381}
3382
3383/*
3384 * Copies "len" Unicode characters, from offset "start".
3385 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003386static void GetStringRegion(JNIEnv* env, jstring jstr, jsize start, jsize len,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003387 jchar* buf)
3388{
3389 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003390 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003391 if (start + len > dvmStringLen(strObj))
3392 dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
3393 else
3394 memcpy(buf, dvmStringChars(strObj) + start, len * sizeof(u2));
3395 JNI_EXIT();
3396}
3397
3398/*
3399 * Translates "len" Unicode characters, from offset "start", into
3400 * modified UTF-8 encoding.
3401 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003402static void GetStringUTFRegion(JNIEnv* env, jstring jstr, jsize start,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003403 jsize len, char* buf)
3404{
3405 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003406 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003407 if (start + len > dvmStringLen(strObj))
3408 dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
3409 else
3410 dvmCreateCstrFromStringRegion(strObj, start, len, buf);
3411 JNI_EXIT();
3412}
3413
3414/*
3415 * Get a raw pointer to array data.
3416 *
3417 * The caller is expected to call "release" before doing any JNI calls
3418 * or blocking I/O operations.
3419 *
Andy McFaddenab00d452009-08-19 07:21:41 -07003420 * We need to pin the memory or block GC.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003421 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003422static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray jarr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003423 jboolean* isCopy)
3424{
3425 JNI_ENTER();
3426 void* data;
Andy McFaddenab00d452009-08-19 07:21:41 -07003427 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
3428 pinPrimitiveArray(arrayObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003429 data = arrayObj->contents;
3430 if (isCopy != NULL)
3431 *isCopy = JNI_FALSE;
3432 JNI_EXIT();
3433 return data;
3434}
3435
3436/*
3437 * Release an array obtained with GetPrimitiveArrayCritical.
3438 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003439static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray jarr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003440 void* carray, jint mode)
3441{
3442 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003443 if (mode != JNI_COMMIT) {
3444 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
3445 unpinPrimitiveArray(arrayObj);
3446 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003447 JNI_EXIT();
3448}
3449
3450/*
3451 * Like GetStringChars, but with restricted use.
3452 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003453static const jchar* GetStringCritical(JNIEnv* env, jstring jstr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003454 jboolean* isCopy)
3455{
3456 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003457 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
3458 ArrayObject* strChars = dvmStringCharArray(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003459
Andy McFaddenab00d452009-08-19 07:21:41 -07003460 pinPrimitiveArray(strChars);
3461
3462 const u2* data = dvmStringChars(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003463 if (isCopy != NULL)
3464 *isCopy = JNI_FALSE;
3465
3466 JNI_EXIT();
3467 return (jchar*)data;
3468}
3469
3470/*
3471 * Like ReleaseStringChars, but with restricted use.
3472 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003473static void ReleaseStringCritical(JNIEnv* env, jstring jstr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003474 const jchar* carray)
3475{
3476 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003477 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
Andy McFadden0423f0e2009-08-26 07:21:53 -07003478 ArrayObject* strChars = dvmStringCharArray(strObj);
Andy McFaddenab00d452009-08-19 07:21:41 -07003479 unpinPrimitiveArray(strChars);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003480 JNI_EXIT();
3481}
3482
3483/*
3484 * Create a new weak global reference.
3485 */
3486static jweak NewWeakGlobalRef(JNIEnv* env, jobject obj)
3487{
3488 JNI_ENTER();
Andy McFaddenb18992f2009-09-25 10:42:15 -07003489 jweak wref = createWeakGlobalRef(env, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003490 JNI_EXIT();
Andy McFaddenb18992f2009-09-25 10:42:15 -07003491 return wref;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003492}
3493
3494/*
3495 * Delete the specified weak global reference.
3496 */
Andy McFaddenb18992f2009-09-25 10:42:15 -07003497static void DeleteWeakGlobalRef(JNIEnv* env, jweak wref)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003498{
3499 JNI_ENTER();
Andy McFaddenb18992f2009-09-25 10:42:15 -07003500 deleteWeakGlobalRef(env, wref);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003501 JNI_EXIT();
3502}
3503
3504/*
3505 * Quick check for pending exceptions.
3506 *
3507 * TODO: we should be able to skip the enter/exit macros here.
3508 */
3509static jboolean ExceptionCheck(JNIEnv* env)
3510{
3511 JNI_ENTER();
3512 bool result = dvmCheckException(_self);
3513 JNI_EXIT();
3514 return result;
3515}
3516
3517/*
3518 * Returns the type of the object referred to by "obj". It can be local,
3519 * global, or weak global.
3520 *
3521 * In the current implementation, references can be global and local at
3522 * the same time, so while the return value is accurate it may not tell
3523 * the whole story.
3524 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003525static jobjectRefType GetObjectRefType(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003526{
3527 JNI_ENTER();
Andy McFadden0423f0e2009-08-26 07:21:53 -07003528 jobjectRefType type = dvmGetJNIRefType(env, jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003529 JNI_EXIT();
3530 return type;
3531}
3532
3533/*
3534 * Allocate and return a new java.nio.ByteBuffer for this block of memory.
3535 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003536 * "address" may not be NULL, and "capacity" must be > 0. (These are only
3537 * verified when CheckJNI is enabled.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003538 */
Andy McFadden8e5c7842009-07-23 17:47:18 -07003539static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003540{
Andy McFadden8e5c7842009-07-23 17:47:18 -07003541 JNI_ENTER();
3542
3543 Thread* self = _self /*dvmThreadSelf()*/;
3544 Object* platformAddress = NULL;
3545 JValue callResult;
The Android Open Source Project99409882009-03-18 22:20:24 -07003546 jobject result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003547
Andy McFadden8e5c7842009-07-23 17:47:18 -07003548 /* get an instance of PlatformAddress that wraps the provided address */
3549 dvmCallMethod(self,
3550 gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on,
3551 NULL, &callResult, address);
3552 if (dvmGetException(self) != NULL || callResult.l == NULL)
The Android Open Source Project99409882009-03-18 22:20:24 -07003553 goto bail;
Andy McFadden8e5c7842009-07-23 17:47:18 -07003554
3555 /* don't let the GC discard it */
3556 platformAddress = (Object*) callResult.l;
3557 dvmAddTrackedAlloc(platformAddress, self);
3558 LOGV("tracking %p for address=%p\n", platformAddress, address);
3559
3560 /* create an instance of java.nio.ReadWriteDirectByteBuffer */
3561 ClassObject* clazz = gDvm.classJavaNioReadWriteDirectByteBuffer;
3562 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))
3563 goto bail;
3564 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
3565 if (newObj != NULL) {
3566 /* call the (PlatformAddress, int, int) constructor */
Andy McFaddenab00d452009-08-19 07:21:41 -07003567 result = addLocalReference(env, newObj);
Andy McFadden8e5c7842009-07-23 17:47:18 -07003568 dvmCallMethod(self, gDvm.methJavaNioReadWriteDirectByteBuffer_init,
3569 newObj, &callResult, platformAddress, (jint) capacity, (jint) 0);
Andy McFaddenab00d452009-08-19 07:21:41 -07003570 if (dvmGetException(self) != NULL) {
3571 deleteLocalReference(env, result);
3572 result = NULL;
Andy McFadden8e5c7842009-07-23 17:47:18 -07003573 goto bail;
Andy McFaddenab00d452009-08-19 07:21:41 -07003574 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003575 }
3576
The Android Open Source Project99409882009-03-18 22:20:24 -07003577bail:
Andy McFadden8e5c7842009-07-23 17:47:18 -07003578 if (platformAddress != NULL)
3579 dvmReleaseTrackedAlloc(platformAddress, self);
3580 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07003581 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003582}
3583
3584/*
3585 * Get the starting address of the buffer for the specified java.nio.Buffer.
3586 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003587 * If this is not a "direct" buffer, we return NULL.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003588 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003589static void* GetDirectBufferAddress(JNIEnv* env, jobject jbuf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003590{
Andy McFadden8e5c7842009-07-23 17:47:18 -07003591 JNI_ENTER();
3592
Andy McFaddenab00d452009-08-19 07:21:41 -07003593 Object* bufObj = dvmDecodeIndirectRef(env, jbuf);
Andy McFadden8e5c7842009-07-23 17:47:18 -07003594 Thread* self = _self /*dvmThreadSelf()*/;
Andy McFadden8e696dc2009-07-24 15:28:16 -07003595 void* result;
3596
3597 /*
3598 * All Buffer objects have an effectiveDirectAddress field. If it's
3599 * nonzero, we can just return that value. If not, we have to call
3600 * through DirectBuffer.getEffectiveAddress(), which as a side-effect
3601 * will set the effectiveDirectAddress field for direct buffers (and
3602 * things that wrap direct buffers).
3603 */
3604 result = (void*) dvmGetFieldInt(bufObj,
3605 gDvm.offJavaNioBuffer_effectiveDirectAddress);
3606 if (result != NULL) {
3607 //LOGI("fast path for %p\n", buf);
3608 goto bail;
3609 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003610
Andy McFadden72d61fb2009-06-24 16:56:06 -07003611 /*
3612 * Start by determining if the object supports the DirectBuffer
3613 * interfaces. Note this does not guarantee that it's a direct buffer.
3614 */
Andy McFadden8e5c7842009-07-23 17:47:18 -07003615 if (!dvmInstanceof(bufObj->clazz,
3616 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003617 {
The Android Open Source Project99409882009-03-18 22:20:24 -07003618 goto bail;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003619 }
3620
Andy McFadden72d61fb2009-06-24 16:56:06 -07003621 /*
Andy McFadden8e5c7842009-07-23 17:47:18 -07003622 * Get a PlatformAddress object with the effective address.
Andy McFadden5f612b82009-07-22 15:07:27 -07003623 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003624 * If this isn't a direct buffer, the result will be NULL and/or an
Andy McFadden72d61fb2009-06-24 16:56:06 -07003625 * exception will have been thrown.
3626 */
Andy McFadden8e696dc2009-07-24 15:28:16 -07003627 JValue callResult;
Andy McFadden8e5c7842009-07-23 17:47:18 -07003628 const Method* meth = dvmGetVirtualizedMethod(bufObj->clazz,
3629 gDvm.methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress);
Andy McFaddend5ab7262009-08-25 07:19:34 -07003630 dvmCallMethodA(self, meth, bufObj, false, &callResult, NULL);
Andy McFadden8e5c7842009-07-23 17:47:18 -07003631 if (dvmGetException(self) != NULL) {
3632 dvmClearException(self);
3633 callResult.l = NULL;
Andy McFadden72d61fb2009-06-24 16:56:06 -07003634 }
Andy McFadden8e5c7842009-07-23 17:47:18 -07003635
Andy McFadden8e696dc2009-07-24 15:28:16 -07003636 Object* platformAddr = callResult.l;
Andy McFadden72d61fb2009-06-24 16:56:06 -07003637 if (platformAddr == NULL) {
Andy McFadden8e696dc2009-07-24 15:28:16 -07003638 LOGV("Got request for address of non-direct buffer\n");
Andy McFadden72d61fb2009-06-24 16:56:06 -07003639 goto bail;
3640 }
3641
Andy McFadden8e5c7842009-07-23 17:47:18 -07003642 /*
3643 * Extract the address from the PlatformAddress object. Instead of
3644 * calling the toLong() method, just grab the field directly. This
3645 * is faster but more fragile.
3646 */
3647 result = (void*) dvmGetFieldInt(platformAddr,
3648 gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr);
The Android Open Source Project99409882009-03-18 22:20:24 -07003649
Andy McFadden8e696dc2009-07-24 15:28:16 -07003650 //LOGI("slow path for %p --> %p\n", buf, result);
3651
The Android Open Source Project99409882009-03-18 22:20:24 -07003652bail:
Andy McFadden8e5c7842009-07-23 17:47:18 -07003653 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07003654 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003655}
3656
3657/*
3658 * Get the capacity of the buffer for the specified java.nio.Buffer.
3659 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003660 * Returns -1 if the object is not a direct buffer. (We actually skip
3661 * this check, since it's expensive to determine, and just return the
3662 * capacity regardless.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003663 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003664static jlong GetDirectBufferCapacity(JNIEnv* env, jobject jbuf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003665{
Andy McFadden8e5c7842009-07-23 17:47:18 -07003666 JNI_ENTER();
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003667
Andy McFadden8e5c7842009-07-23 17:47:18 -07003668 /*
3669 * The capacity is always in the Buffer.capacity field.
3670 *
3671 * (The "check" version should verify that this is actually a Buffer,
3672 * but we're not required to do so here.)
3673 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003674 Object* buf = dvmDecodeIndirectRef(env, jbuf);
3675 jlong result = dvmGetFieldInt(buf, gDvm.offJavaNioBuffer_capacity);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003676
Andy McFadden8e5c7842009-07-23 17:47:18 -07003677 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07003678 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003679}
3680
3681
3682/*
3683 * ===========================================================================
3684 * JNI invocation functions
3685 * ===========================================================================
3686 */
3687
3688/*
3689 * Handle AttachCurrentThread{AsDaemon}.
3690 *
3691 * We need to make sure the VM is actually running. For example, if we start
3692 * up, issue an Attach, and the VM exits almost immediately, by the time the
3693 * attaching happens the VM could already be shutting down.
3694 *
3695 * It's hard to avoid a race condition here because we don't want to hold
3696 * a lock across the entire operation. What we can do is temporarily
3697 * increment the thread count to prevent a VM exit.
3698 *
3699 * This could potentially still have problems if a daemon thread calls here
3700 * while the VM is shutting down. dvmThreadSelf() will work, since it just
3701 * uses pthread TLS, but dereferencing "vm" could fail. Such is life when
3702 * you shut down a VM while threads are still running inside it.
3703 *
3704 * Remember that some code may call this as a way to find the per-thread
3705 * JNIEnv pointer. Don't do excess work for that case.
3706 */
3707static jint attachThread(JavaVM* vm, JNIEnv** p_env, void* thr_args,
3708 bool isDaemon)
3709{
3710 JavaVMAttachArgs* args = (JavaVMAttachArgs*) thr_args;
3711 Thread* self;
3712 bool result = false;
3713
3714 /*
3715 * Return immediately if we're already one with the VM.
3716 */
3717 self = dvmThreadSelf();
3718 if (self != NULL) {
3719 *p_env = self->jniEnv;
3720 return JNI_OK;
3721 }
3722
3723 /*
3724 * No threads allowed in zygote mode.
3725 */
3726 if (gDvm.zygote) {
3727 return JNI_ERR;
3728 }
3729
3730 /* increment the count to keep the VM from bailing while we run */
3731 dvmLockThreadList(NULL);
3732 if (gDvm.nonDaemonThreadCount == 0) {
3733 // dead or dying
3734 LOGV("Refusing to attach thread '%s' -- VM is shutting down\n",
3735 (thr_args == NULL) ? "(unknown)" : args->name);
3736 dvmUnlockThreadList();
3737 return JNI_ERR;
3738 }
3739 gDvm.nonDaemonThreadCount++;
3740 dvmUnlockThreadList();
3741
3742 /* tweak the JavaVMAttachArgs as needed */
3743 JavaVMAttachArgs argsCopy;
3744 if (args == NULL) {
3745 /* allow the v1.1 calling convention */
3746 argsCopy.version = JNI_VERSION_1_2;
3747 argsCopy.name = NULL;
3748 argsCopy.group = dvmGetMainThreadGroup();
3749 } else {
3750 assert(args->version >= JNI_VERSION_1_2);
3751
3752 argsCopy.version = args->version;
3753 argsCopy.name = args->name;
3754 if (args->group != NULL)
3755 argsCopy.group = args->group;
3756 else
3757 argsCopy.group = dvmGetMainThreadGroup();
3758 }
3759
3760 result = dvmAttachCurrentThread(&argsCopy, isDaemon);
3761
3762 /* restore the count */
3763 dvmLockThreadList(NULL);
3764 gDvm.nonDaemonThreadCount--;
3765 dvmUnlockThreadList();
3766
3767 /*
3768 * Change the status to indicate that we're out in native code. This
3769 * call is not guarded with state-change macros, so we have to do it
3770 * by hand.
3771 */
3772 if (result) {
3773 self = dvmThreadSelf();
3774 assert(self != NULL);
3775 dvmChangeStatus(self, THREAD_NATIVE);
3776 *p_env = self->jniEnv;
3777 return JNI_OK;
3778 } else {
3779 return JNI_ERR;
3780 }
3781}
3782
3783/*
3784 * Attach the current thread to the VM. If the thread is already attached,
3785 * this is a no-op.
3786 */
3787static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args)
3788{
3789 return attachThread(vm, p_env, thr_args, false);
3790}
3791
3792/*
3793 * Like AttachCurrentThread, but set the "daemon" flag.
3794 */
3795static jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env,
3796 void* thr_args)
3797{
3798 return attachThread(vm, p_env, thr_args, true);
3799}
3800
3801/*
3802 * Dissociate the current thread from the VM.
3803 */
3804static jint DetachCurrentThread(JavaVM* vm)
3805{
3806 Thread* self = dvmThreadSelf();
3807
3808 if (self == NULL) /* not attached, can't do anything */
3809 return JNI_ERR;
3810
3811 /* switch to "running" to check for suspension */
3812 dvmChangeStatus(self, THREAD_RUNNING);
3813
3814 /* detach the thread */
3815 dvmDetachCurrentThread();
3816
3817 /* (no need to change status back -- we have no status) */
3818 return JNI_OK;
3819}
3820
3821/*
3822 * If current thread is attached to VM, return the associated JNIEnv.
3823 * Otherwise, stuff NULL in and return JNI_EDETACHED.
3824 *
3825 * JVMTI overloads this by specifying a magic value for "version", so we
3826 * do want to check that here.
3827 */
3828static jint GetEnv(JavaVM* vm, void** env, jint version)
3829{
3830 Thread* self = dvmThreadSelf();
3831
3832 if (version < JNI_VERSION_1_1 || version > JNI_VERSION_1_6)
3833 return JNI_EVERSION;
3834
3835 if (self == NULL) {
3836 *env = NULL;
3837 } else {
3838 /* TODO: status change is probably unnecessary */
3839 dvmChangeStatus(self, THREAD_RUNNING);
3840 *env = (void*) dvmGetThreadJNIEnv(self);
3841 dvmChangeStatus(self, THREAD_NATIVE);
3842 }
3843 if (*env == NULL)
3844 return JNI_EDETACHED;
3845 else
3846 return JNI_OK;
3847}
3848
3849/*
3850 * Destroy the VM. This may be called from any thread.
3851 *
3852 * If the current thread is attached, wait until the current thread is
3853 * the only non-daemon user-level thread. If the current thread is not
3854 * attached, we attach it and do the processing as usual. (If the attach
3855 * fails, it's probably because all the non-daemon threads have already
3856 * exited and the VM doesn't want to let us back in.)
3857 *
3858 * TODO: we don't really deal with the situation where more than one thread
3859 * has called here. One thread wins, the other stays trapped waiting on
3860 * the condition variable forever. Not sure this situation is interesting
3861 * in real life.
3862 */
3863static jint DestroyJavaVM(JavaVM* vm)
3864{
3865 JavaVMExt* ext = (JavaVMExt*) vm;
3866 Thread* self;
3867
3868 if (ext == NULL)
3869 return JNI_ERR;
3870
3871 LOGD("DestroyJavaVM waiting for non-daemon threads to exit\n");
3872
3873 /*
3874 * Sleep on a condition variable until it's okay to exit.
3875 */
3876 self = dvmThreadSelf();
3877 if (self == NULL) {
3878 JNIEnv* tmpEnv;
3879 if (AttachCurrentThread(vm, &tmpEnv, NULL) != JNI_OK) {
3880 LOGV("Unable to reattach main for Destroy; assuming VM is "
3881 "shutting down (count=%d)\n",
3882 gDvm.nonDaemonThreadCount);
3883 goto shutdown;
3884 } else {
3885 LOGV("Attached to wait for shutdown in Destroy\n");
3886 }
3887 }
3888 dvmChangeStatus(self, THREAD_VMWAIT);
3889
3890 dvmLockThreadList(self);
3891 gDvm.nonDaemonThreadCount--; // remove current thread from count
3892
3893 while (gDvm.nonDaemonThreadCount > 0)
3894 pthread_cond_wait(&gDvm.vmExitCond, &gDvm.threadListLock);
3895
3896 dvmUnlockThreadList();
3897 self = NULL;
3898
3899shutdown:
3900 // TODO: call System.exit() to run any registered shutdown hooks
3901 // (this may not return -- figure out how this should work)
3902
3903 LOGD("DestroyJavaVM shutting VM down\n");
3904 dvmShutdown();
3905
3906 // TODO - free resources associated with JNI-attached daemon threads
3907 free(ext->envList);
3908 free(ext);
3909
3910 return JNI_OK;
3911}
3912
3913
3914/*
3915 * ===========================================================================
3916 * Function tables
3917 * ===========================================================================
3918 */
3919
3920static const struct JNINativeInterface gNativeInterface = {
3921 NULL,
3922 NULL,
3923 NULL,
3924 NULL,
3925
3926 GetVersion,
3927
3928 DefineClass,
3929 FindClass,
3930
3931 FromReflectedMethod,
3932 FromReflectedField,
3933 ToReflectedMethod,
3934
3935 GetSuperclass,
3936 IsAssignableFrom,
3937
3938 ToReflectedField,
3939
3940 Throw,
3941 ThrowNew,
3942 ExceptionOccurred,
3943 ExceptionDescribe,
3944 ExceptionClear,
3945 FatalError,
3946
3947 PushLocalFrame,
3948 PopLocalFrame,
3949
3950 NewGlobalRef,
3951 DeleteGlobalRef,
3952 DeleteLocalRef,
3953 IsSameObject,
3954 NewLocalRef,
3955 EnsureLocalCapacity,
3956
3957 AllocObject,
3958 NewObject,
3959 NewObjectV,
3960 NewObjectA,
3961
3962 GetObjectClass,
3963 IsInstanceOf,
3964
3965 GetMethodID,
3966
3967 CallObjectMethod,
3968 CallObjectMethodV,
3969 CallObjectMethodA,
3970 CallBooleanMethod,
3971 CallBooleanMethodV,
3972 CallBooleanMethodA,
3973 CallByteMethod,
3974 CallByteMethodV,
3975 CallByteMethodA,
3976 CallCharMethod,
3977 CallCharMethodV,
3978 CallCharMethodA,
3979 CallShortMethod,
3980 CallShortMethodV,
3981 CallShortMethodA,
3982 CallIntMethod,
3983 CallIntMethodV,
3984 CallIntMethodA,
3985 CallLongMethod,
3986 CallLongMethodV,
3987 CallLongMethodA,
3988 CallFloatMethod,
3989 CallFloatMethodV,
3990 CallFloatMethodA,
3991 CallDoubleMethod,
3992 CallDoubleMethodV,
3993 CallDoubleMethodA,
3994 CallVoidMethod,
3995 CallVoidMethodV,
3996 CallVoidMethodA,
3997
3998 CallNonvirtualObjectMethod,
3999 CallNonvirtualObjectMethodV,
4000 CallNonvirtualObjectMethodA,
4001 CallNonvirtualBooleanMethod,
4002 CallNonvirtualBooleanMethodV,
4003 CallNonvirtualBooleanMethodA,
4004 CallNonvirtualByteMethod,
4005 CallNonvirtualByteMethodV,
4006 CallNonvirtualByteMethodA,
4007 CallNonvirtualCharMethod,
4008 CallNonvirtualCharMethodV,
4009 CallNonvirtualCharMethodA,
4010 CallNonvirtualShortMethod,
4011 CallNonvirtualShortMethodV,
4012 CallNonvirtualShortMethodA,
4013 CallNonvirtualIntMethod,
4014 CallNonvirtualIntMethodV,
4015 CallNonvirtualIntMethodA,
4016 CallNonvirtualLongMethod,
4017 CallNonvirtualLongMethodV,
4018 CallNonvirtualLongMethodA,
4019 CallNonvirtualFloatMethod,
4020 CallNonvirtualFloatMethodV,
4021 CallNonvirtualFloatMethodA,
4022 CallNonvirtualDoubleMethod,
4023 CallNonvirtualDoubleMethodV,
4024 CallNonvirtualDoubleMethodA,
4025 CallNonvirtualVoidMethod,
4026 CallNonvirtualVoidMethodV,
4027 CallNonvirtualVoidMethodA,
4028
4029 GetFieldID,
4030
4031 GetObjectField,
4032 GetBooleanField,
4033 GetByteField,
4034 GetCharField,
4035 GetShortField,
4036 GetIntField,
4037 GetLongField,
4038 GetFloatField,
4039 GetDoubleField,
4040 SetObjectField,
4041 SetBooleanField,
4042 SetByteField,
4043 SetCharField,
4044 SetShortField,
4045 SetIntField,
4046 SetLongField,
4047 SetFloatField,
4048 SetDoubleField,
4049
4050 GetStaticMethodID,
4051
4052 CallStaticObjectMethod,
4053 CallStaticObjectMethodV,
4054 CallStaticObjectMethodA,
4055 CallStaticBooleanMethod,
4056 CallStaticBooleanMethodV,
4057 CallStaticBooleanMethodA,
4058 CallStaticByteMethod,
4059 CallStaticByteMethodV,
4060 CallStaticByteMethodA,
4061 CallStaticCharMethod,
4062 CallStaticCharMethodV,
4063 CallStaticCharMethodA,
4064 CallStaticShortMethod,
4065 CallStaticShortMethodV,
4066 CallStaticShortMethodA,
4067 CallStaticIntMethod,
4068 CallStaticIntMethodV,
4069 CallStaticIntMethodA,
4070 CallStaticLongMethod,
4071 CallStaticLongMethodV,
4072 CallStaticLongMethodA,
4073 CallStaticFloatMethod,
4074 CallStaticFloatMethodV,
4075 CallStaticFloatMethodA,
4076 CallStaticDoubleMethod,
4077 CallStaticDoubleMethodV,
4078 CallStaticDoubleMethodA,
4079 CallStaticVoidMethod,
4080 CallStaticVoidMethodV,
4081 CallStaticVoidMethodA,
4082
4083 GetStaticFieldID,
4084
4085 GetStaticObjectField,
4086 GetStaticBooleanField,
4087 GetStaticByteField,
4088 GetStaticCharField,
4089 GetStaticShortField,
4090 GetStaticIntField,
4091 GetStaticLongField,
4092 GetStaticFloatField,
4093 GetStaticDoubleField,
4094
4095 SetStaticObjectField,
4096 SetStaticBooleanField,
4097 SetStaticByteField,
4098 SetStaticCharField,
4099 SetStaticShortField,
4100 SetStaticIntField,
4101 SetStaticLongField,
4102 SetStaticFloatField,
4103 SetStaticDoubleField,
4104
4105 NewString,
4106
4107 GetStringLength,
4108 GetStringChars,
4109 ReleaseStringChars,
4110
4111 NewStringUTF,
4112 GetStringUTFLength,
4113 GetStringUTFChars,
4114 ReleaseStringUTFChars,
4115
4116 GetArrayLength,
4117 NewObjectArray,
4118 GetObjectArrayElement,
4119 SetObjectArrayElement,
4120
4121 NewBooleanArray,
4122 NewByteArray,
4123 NewCharArray,
4124 NewShortArray,
4125 NewIntArray,
4126 NewLongArray,
4127 NewFloatArray,
4128 NewDoubleArray,
4129
4130 GetBooleanArrayElements,
4131 GetByteArrayElements,
4132 GetCharArrayElements,
4133 GetShortArrayElements,
4134 GetIntArrayElements,
4135 GetLongArrayElements,
4136 GetFloatArrayElements,
4137 GetDoubleArrayElements,
4138
4139 ReleaseBooleanArrayElements,
4140 ReleaseByteArrayElements,
4141 ReleaseCharArrayElements,
4142 ReleaseShortArrayElements,
4143 ReleaseIntArrayElements,
4144 ReleaseLongArrayElements,
4145 ReleaseFloatArrayElements,
4146 ReleaseDoubleArrayElements,
4147
4148 GetBooleanArrayRegion,
4149 GetByteArrayRegion,
4150 GetCharArrayRegion,
4151 GetShortArrayRegion,
4152 GetIntArrayRegion,
4153 GetLongArrayRegion,
4154 GetFloatArrayRegion,
4155 GetDoubleArrayRegion,
4156 SetBooleanArrayRegion,
4157 SetByteArrayRegion,
4158 SetCharArrayRegion,
4159 SetShortArrayRegion,
4160 SetIntArrayRegion,
4161 SetLongArrayRegion,
4162 SetFloatArrayRegion,
4163 SetDoubleArrayRegion,
4164
4165 RegisterNatives,
4166 UnregisterNatives,
4167
4168 MonitorEnter,
4169 MonitorExit,
4170
4171 GetJavaVM,
4172
4173 GetStringRegion,
4174 GetStringUTFRegion,
4175
4176 GetPrimitiveArrayCritical,
4177 ReleasePrimitiveArrayCritical,
4178
4179 GetStringCritical,
4180 ReleaseStringCritical,
4181
4182 NewWeakGlobalRef,
4183 DeleteWeakGlobalRef,
4184
4185 ExceptionCheck,
4186
4187 NewDirectByteBuffer,
4188 GetDirectBufferAddress,
4189 GetDirectBufferCapacity,
4190
4191 GetObjectRefType
4192};
4193static const struct JNIInvokeInterface gInvokeInterface = {
4194 NULL,
4195 NULL,
4196 NULL,
4197
4198 DestroyJavaVM,
4199 AttachCurrentThread,
4200 DetachCurrentThread,
4201
4202 GetEnv,
4203
4204 AttachCurrentThreadAsDaemon,
4205};
4206
4207
4208/*
4209 * ===========================================================================
4210 * VM/Env creation
4211 * ===========================================================================
4212 */
4213
4214/*
4215 * Enable "checked JNI" after the VM has partially started. This must
4216 * only be called in "zygote" mode, when we have one thread running.
Andy McFadden59b61772009-05-13 16:44:34 -07004217 *
4218 * This doesn't attempt to rewrite the JNI call bridge associated with
4219 * native methods, so we won't get those checks for any methods that have
4220 * already been resolved.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08004221 */
4222void dvmLateEnableCheckedJni(void)
4223{
4224 JNIEnvExt* extEnv;
4225 JavaVMExt* extVm;
Andy McFaddenab00d452009-08-19 07:21:41 -07004226
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08004227 extEnv = dvmGetJNIEnvForThread();
4228 if (extEnv == NULL) {
4229 LOGE("dvmLateEnableCheckedJni: thread has no JNIEnv\n");
4230 return;
4231 }
4232 extVm = extEnv->vm;
4233 assert(extVm != NULL);
4234
4235 if (!extVm->useChecked) {
4236 LOGD("Late-enabling CheckJNI\n");
4237 dvmUseCheckedJniVm(extVm);
4238 extVm->useChecked = true;
4239 dvmUseCheckedJniEnv(extEnv);
4240
4241 /* currently no way to pick up jniopts features */
4242 } else {
4243 LOGD("Not late-enabling CheckJNI (already on)\n");
4244 }
4245}
4246
4247/*
4248 * Not supported.
4249 */
4250jint JNI_GetDefaultJavaVMInitArgs(void* vm_args)
4251{
4252 return JNI_ERR;
4253}
4254
4255/*
4256 * Return a buffer full of created VMs.
4257 *
4258 * We always have zero or one.
4259 */
4260jint JNI_GetCreatedJavaVMs(JavaVM** vmBuf, jsize bufLen, jsize* nVMs)
4261{
4262 if (gDvm.vmList != NULL) {
4263 *nVMs = 1;
4264
4265 if (bufLen > 0)
4266 *vmBuf++ = gDvm.vmList;
4267 } else {
4268 *nVMs = 0;
4269 }
4270
4271 return JNI_OK;
4272}
4273
4274
4275/*
4276 * Create a new VM instance.
4277 *
4278 * The current thread becomes the main VM thread. We return immediately,
4279 * which effectively means the caller is executing in a native method.
4280 */
4281jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args)
4282{
4283 const JavaVMInitArgs* args = (JavaVMInitArgs*) vm_args;
4284 JNIEnvExt* pEnv = NULL;
4285 JavaVMExt* pVM = NULL;
4286 const char** argv;
4287 int argc = 0;
4288 int i, curOpt;
4289 int result = JNI_ERR;
4290 bool checkJni = false;
4291 bool warnError = true;
4292 bool forceDataCopy = false;
4293
4294 if (args->version < JNI_VERSION_1_2)
4295 return JNI_EVERSION;
4296
4297 // TODO: don't allow creation of multiple VMs -- one per customer for now
4298
4299 /* zero globals; not strictly necessary the first time a VM is started */
4300 memset(&gDvm, 0, sizeof(gDvm));
4301
4302 /*
4303 * Set up structures for JNIEnv and VM.
4304 */
4305 //pEnv = (JNIEnvExt*) malloc(sizeof(JNIEnvExt));
4306 pVM = (JavaVMExt*) malloc(sizeof(JavaVMExt));
4307
4308 //memset(pEnv, 0, sizeof(JNIEnvExt));
4309 //pEnv->funcTable = &gNativeInterface;
4310 //pEnv->vm = pVM;
4311 memset(pVM, 0, sizeof(JavaVMExt));
4312 pVM->funcTable = &gInvokeInterface;
4313 pVM->envList = pEnv;
4314 dvmInitMutex(&pVM->envListLock);
4315
4316 argv = (const char**) malloc(sizeof(char*) * (args->nOptions));
4317 memset(argv, 0, sizeof(char*) * (args->nOptions));
4318
4319 curOpt = 0;
4320
4321 /*
4322 * Convert JNI args to argv.
4323 *
4324 * We have to pull out vfprintf/exit/abort, because they use the
4325 * "extraInfo" field to pass function pointer "hooks" in. We also
4326 * look for the -Xcheck:jni stuff here.
4327 */
4328 for (i = 0; i < args->nOptions; i++) {
4329 const char* optStr = args->options[i].optionString;
4330
4331 if (optStr == NULL) {
4332 fprintf(stderr, "ERROR: arg %d string was null\n", i);
4333 goto bail;
4334 } else if (strcmp(optStr, "vfprintf") == 0) {
4335 gDvm.vfprintfHook = args->options[i].extraInfo;
4336 } else if (strcmp(optStr, "exit") == 0) {
4337 gDvm.exitHook = args->options[i].extraInfo;
4338 } else if (strcmp(optStr, "abort") == 0) {
4339 gDvm.abortHook = args->options[i].extraInfo;
4340 } else if (strcmp(optStr, "-Xcheck:jni") == 0) {
4341 checkJni = true;
4342 } else if (strncmp(optStr, "-Xjniopts:", 10) == 0) {
4343 const char* jniOpts = optStr + 9;
4344 while (jniOpts != NULL) {
4345 jniOpts++; /* skip past ':' or ',' */
4346 if (strncmp(jniOpts, "warnonly", 8) == 0) {
4347 warnError = false;
4348 } else if (strncmp(jniOpts, "forcecopy", 9) == 0) {
4349 forceDataCopy = true;
4350 } else {
4351 LOGW("unknown jni opt starting at '%s'\n", jniOpts);
4352 }
4353 jniOpts = strchr(jniOpts, ',');
4354 }
4355 } else {
4356 /* regular option */
4357 argv[curOpt++] = optStr;
4358 }
4359 }
4360 argc = curOpt;
4361
4362 if (checkJni) {
4363 dvmUseCheckedJniVm(pVM);
4364 pVM->useChecked = true;
4365 }
4366 pVM->warnError = warnError;
4367 pVM->forceDataCopy = forceDataCopy;
4368
4369 /* set this up before initializing VM, so it can create some JNIEnvs */
4370 gDvm.vmList = (JavaVM*) pVM;
4371
4372 /*
4373 * Create an env for main thread. We need to have something set up
4374 * here because some of the class initialization we do when starting
4375 * up the VM will call into native code.
4376 */
4377 pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL);
4378
4379 /* initialize VM */
4380 gDvm.initializing = true;
4381 if (dvmStartup(argc, argv, args->ignoreUnrecognized, (JNIEnv*)pEnv) != 0) {
4382 free(pEnv);
4383 free(pVM);
4384 goto bail;
4385 }
4386
4387 /*
4388 * Success! Return stuff to caller.
4389 */
4390 dvmChangeStatus(NULL, THREAD_NATIVE);
4391 *p_env = (JNIEnv*) pEnv;
4392 *p_vm = (JavaVM*) pVM;
4393 result = JNI_OK;
4394
4395bail:
4396 gDvm.initializing = false;
4397 if (result == JNI_OK)
4398 LOGV("JNI_CreateJavaVM succeeded\n");
4399 else
4400 LOGW("JNI_CreateJavaVM failed\n");
4401 free(argv);
4402 return result;
4403}