blob: 54a1f4514a7c32be32c51e8b67f5020d5f32bc95 [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/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001126 * GC helper function to mark all JNI global references.
Andy McFaddenc26bb632009-08-21 12:01:31 -07001127 *
1128 * We're currently handling the "pin" table here too.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001129 */
1130void dvmGcMarkJniGlobalRefs()
1131{
Andy McFaddend5ab7262009-08-25 07:19:34 -07001132 Object** op;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001133
1134 dvmLockMutex(&gDvm.jniGlobalRefLock);
1135
Andy McFaddend5ab7262009-08-25 07:19:34 -07001136#ifdef USE_INDIRECT_REF
1137 IndirectRefTable* pRefTable = &gDvm.jniGlobalRefTable;
1138 op = pRefTable->table;
1139 int numEntries = dvmIndirectRefTableEntries(pRefTable);
1140 int i;
1141
1142 for (i = 0; i < numEntries; i++) {
1143 Object* obj = *op;
1144 if (obj != NULL)
1145 dvmMarkObjectNonNull(obj);
1146 op++;
1147 }
1148#else
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001149 op = gDvm.jniGlobalRefTable.table;
1150 while ((uintptr_t)op < (uintptr_t)gDvm.jniGlobalRefTable.nextEntry) {
1151 dvmMarkObjectNonNull(*(op++));
1152 }
Andy McFaddend5ab7262009-08-25 07:19:34 -07001153#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001154
1155 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
Andy McFaddenc26bb632009-08-21 12:01:31 -07001156
1157
1158 dvmLockMutex(&gDvm.jniPinRefLock);
1159
1160 op = gDvm.jniPinRefTable.table;
1161 while ((uintptr_t)op < (uintptr_t)gDvm.jniPinRefTable.nextEntry) {
1162 dvmMarkObjectNonNull(*(op++));
1163 }
1164
1165 dvmUnlockMutex(&gDvm.jniPinRefLock);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001166}
1167
1168
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001169#ifndef USE_INDIRECT_REF
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001170/*
1171 * Determine if "obj" appears in the argument list for the native method.
1172 *
1173 * We use the "shorty" signature to determine which argument slots hold
1174 * reference types.
1175 */
1176static bool findInArgList(Thread* self, Object* obj)
1177{
1178 const Method* meth;
1179 u4* fp;
1180 int i;
1181
1182 fp = self->curFrame;
1183 while (1) {
1184 /*
1185 * Back up over JNI PushLocalFrame frames. This works because the
1186 * previous frame on the interpreted stack is either a break frame
1187 * (if we called here via native code) or an interpreted method (if
1188 * we called here via the interpreter). In both cases the method
1189 * pointer won't match.
1190 */
1191 StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
1192 meth = saveArea->method;
1193 if (meth != SAVEAREA_FROM_FP(saveArea->prevFrame)->method)
1194 break;
1195 fp = saveArea->prevFrame;
1196 }
1197
1198 LOGVV("+++ scanning %d args in %s (%s)\n",
1199 meth->insSize, meth->name, meth->shorty);
1200 const char* shorty = meth->shorty +1; /* skip return type char */
1201 for (i = 0; i < meth->insSize; i++) {
1202 if (i == 0 && !dvmIsStaticMethod(meth)) {
1203 /* first arg is "this" ref, not represented in "shorty" */
1204 if (fp[i] == (u4) obj)
1205 return true;
1206 } else {
1207 /* if this is a reference type, see if it matches */
1208 switch (*shorty) {
1209 case 'L':
1210 if (fp[i] == (u4) obj)
1211 return true;
1212 break;
1213 case 'D':
1214 case 'J':
1215 i++;
1216 break;
1217 case '\0':
1218 LOGE("Whoops! ran off the end of %s (%d)\n",
1219 meth->shorty, meth->insSize);
1220 break;
1221 default:
1222 if (fp[i] == (u4) obj)
1223 LOGI("NOTE: ref %p match on arg type %c\n", obj, *shorty);
1224 break;
1225 }
1226 shorty++;
1227 }
1228 }
1229
1230 /*
1231 * For static methods, we also pass a class pointer in.
1232 */
1233 if (dvmIsStaticMethod(meth)) {
1234 //LOGI("+++ checking class pointer in %s\n", meth->name);
1235 if ((void*)obj == (void*)meth->clazz)
1236 return true;
1237 }
1238 return false;
1239}
Andy McFadden0083d372009-08-21 14:44:04 -07001240#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001241
1242/*
1243 * Verify that a reference passed in from native code is one that the
1244 * code is allowed to have.
1245 *
1246 * It's okay for native code to pass us a reference that:
Andy McFadden0083d372009-08-21 14:44:04 -07001247 * - was passed in as an argument when invoked by native code (and hence
1248 * is in the JNI local refs table)
1249 * - was returned to it from JNI (and is now in the local refs table)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001250 * - is present in the JNI global refs table
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001251 *
1252 * Used by -Xcheck:jni and GetObjectRefType.
1253 *
1254 * NOTE: in the current VM, global and local references are identical. If
1255 * something is both global and local, we can't tell them apart, and always
1256 * return "local".
1257 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001258jobjectRefType dvmGetJNIRefType(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001259{
Andy McFaddend5ab7262009-08-25 07:19:34 -07001260#ifdef USE_INDIRECT_REF
1261 /*
1262 * IndirectRefKind is currently defined as an exact match of
Andy McFadden0423f0e2009-08-26 07:21:53 -07001263 * jobjectRefType, so this is easy. We have to decode it to determine
1264 * if it's a valid reference and not merely valid-looking.
Andy McFaddend5ab7262009-08-25 07:19:34 -07001265 */
Andy McFadden0423f0e2009-08-26 07:21:53 -07001266 Object* obj = dvmDecodeIndirectRef(env, jobj);
1267
1268 if (obj == NULL) {
1269 /* invalid ref, or jobj was NULL */
1270 return JNIInvalidRefType;
1271 } else {
1272 return (jobjectRefType) dvmGetIndirectRefType(jobj);
1273 }
Andy McFaddend5ab7262009-08-25 07:19:34 -07001274#else
Andy McFaddenab00d452009-08-19 07:21:41 -07001275 ReferenceTable* pRefTable = getLocalRefTable(env);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001276 Thread* self = dvmThreadSelf();
1277 //Object** top;
1278 Object** ptr;
1279
Andy McFaddenb18992f2009-09-25 10:42:15 -07001280 if (dvmIsWeakGlobalRef(jobj)) {
1281 return JNIWeakGlobalRefType;
1282 }
1283
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001284 /* check args */
Andy McFaddenab00d452009-08-19 07:21:41 -07001285 if (findInArgList(self, jobj)) {
1286 //LOGI("--- REF found %p on stack\n", jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001287 return JNILocalRefType;
1288 }
1289
1290 /* check locals */
Andy McFaddenab00d452009-08-19 07:21:41 -07001291 if (dvmFindInReferenceTable(pRefTable, pRefTable->table, jobj) != NULL) {
1292 //LOGI("--- REF found %p in locals\n", jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001293 return JNILocalRefType;
1294 }
1295
1296 /* check globals */
1297 dvmLockMutex(&gDvm.jniGlobalRefLock);
1298 if (dvmFindInReferenceTable(&gDvm.jniGlobalRefTable,
Andy McFaddenab00d452009-08-19 07:21:41 -07001299 gDvm.jniGlobalRefTable.table, jobj))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001300 {
Andy McFaddenab00d452009-08-19 07:21:41 -07001301 //LOGI("--- REF found %p in globals\n", jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001302 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
1303 return JNIGlobalRefType;
1304 }
1305 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
1306
1307 /* not found! */
1308 return JNIInvalidRefType;
Andy McFaddend5ab7262009-08-25 07:19:34 -07001309#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001310}
1311
1312/*
1313 * Register a method that uses JNI calling conventions.
1314 */
1315static bool dvmRegisterJNIMethod(ClassObject* clazz, const char* methodName,
1316 const char* signature, void* fnPtr)
1317{
1318 Method* method;
1319 bool result = false;
1320
1321 if (fnPtr == NULL)
1322 goto bail;
1323
1324 method = dvmFindDirectMethodByDescriptor(clazz, methodName, signature);
1325 if (method == NULL)
1326 method = dvmFindVirtualMethodByDescriptor(clazz, methodName, signature);
1327 if (method == NULL) {
1328 LOGW("ERROR: Unable to find decl for native %s.%s %s\n",
1329 clazz->descriptor, methodName, signature);
1330 goto bail;
1331 }
1332
1333 if (!dvmIsNativeMethod(method)) {
1334 LOGW("Unable to register: not native: %s.%s %s\n",
1335 clazz->descriptor, methodName, signature);
1336 goto bail;
1337 }
1338
1339 if (method->nativeFunc != dvmResolveNativeMethod) {
1340 LOGW("Warning: %s.%s %s was already registered/resolved?\n",
1341 clazz->descriptor, methodName, signature);
1342 /* keep going, I guess */
1343 }
1344
Andy McFadden59b61772009-05-13 16:44:34 -07001345 dvmUseJNIBridge(method, fnPtr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001346
1347 LOGV("JNI-registered %s.%s %s\n", clazz->descriptor, methodName,
1348 signature);
1349 result = true;
1350
1351bail:
1352 return result;
1353}
1354
1355/*
Andy McFadden59b61772009-05-13 16:44:34 -07001356 * Returns "true" if CheckJNI is enabled in the VM.
1357 */
1358static bool dvmIsCheckJNIEnabled(void)
1359{
1360 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
1361 return vm->useChecked;
1362}
1363
1364/*
1365 * Point "method->nativeFunc" at the JNI bridge, and overload "method->insns"
1366 * to point at the actual function.
1367 */
1368void dvmUseJNIBridge(Method* method, void* func)
1369{
Andy McFadden0083d372009-08-21 14:44:04 -07001370 enum {
1371 kJNIGeneral = 0,
1372 kJNISync = 1,
1373 kJNIVirtualNoRef = 2,
1374 kJNIStaticNoRef = 3,
1375 } kind;
1376 static const DalvikBridgeFunc stdFunc[] = {
1377 dvmCallJNIMethod_general,
1378 dvmCallJNIMethod_synchronized,
1379 dvmCallJNIMethod_virtualNoRef,
1380 dvmCallJNIMethod_staticNoRef
1381 };
1382 static const DalvikBridgeFunc checkFunc[] = {
1383 dvmCheckCallJNIMethod_general,
1384 dvmCheckCallJNIMethod_synchronized,
1385 dvmCheckCallJNIMethod_virtualNoRef,
1386 dvmCheckCallJNIMethod_staticNoRef
1387 };
1388
1389 bool hasRefArg = false;
1390
1391 if (dvmIsSynchronizedMethod(method)) {
1392 /* use version with synchronization; calls into general handler */
1393 kind = kJNISync;
Andy McFadden59b61772009-05-13 16:44:34 -07001394 } else {
Andy McFadden0083d372009-08-21 14:44:04 -07001395 /*
1396 * Do a quick scan through the "shorty" signature to see if the method
1397 * takes any reference arguments.
1398 */
1399 const char* cp = method->shorty;
1400 while (*++cp != '\0') { /* pre-incr to skip return type */
1401 if (*cp == 'L') {
1402 /* 'L' used for both object and array references */
1403 hasRefArg = true;
1404 break;
1405 }
1406 }
1407
1408 if (hasRefArg) {
1409 /* use general handler to slurp up reference args */
1410 kind = kJNIGeneral;
1411 } else {
1412 /* virtual methods have a ref in args[0] (not in signature) */
1413 if (dvmIsStaticMethod(method))
1414 kind = kJNIStaticNoRef;
1415 else
1416 kind = kJNIVirtualNoRef;
1417 }
1418 }
1419
1420 if (dvmIsCheckJNIEnabled()) {
1421 dvmSetNativeFunc(method, checkFunc[kind], func);
1422 } else {
1423 dvmSetNativeFunc(method, stdFunc[kind], func);
Andy McFadden59b61772009-05-13 16:44:34 -07001424 }
1425}
1426
1427/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001428 * Get the method currently being executed by examining the interp stack.
1429 */
1430const Method* dvmGetCurrentJNIMethod(void)
1431{
1432 assert(dvmThreadSelf() != NULL);
1433
1434 void* fp = dvmThreadSelf()->curFrame;
1435 const Method* meth = SAVEAREA_FROM_FP(fp)->method;
1436
1437 assert(meth != NULL);
1438 assert(dvmIsNativeMethod(meth));
1439 return meth;
1440}
1441
1442
1443/*
1444 * Track a JNI MonitorEnter in the current thread.
1445 *
1446 * The goal is to be able to "implicitly" release all JNI-held monitors
1447 * when the thread detaches.
1448 *
1449 * Monitors may be entered multiple times, so we add a new entry for each
1450 * enter call. It would be more efficient to keep a counter. At present
1451 * there's no real motivation to improve this however.
1452 */
1453static void trackMonitorEnter(Thread* self, Object* obj)
1454{
1455 static const int kInitialSize = 16;
1456 ReferenceTable* refTable = &self->jniMonitorRefTable;
1457
1458 /* init table on first use */
1459 if (refTable->table == NULL) {
1460 assert(refTable->maxEntries == 0);
1461
1462 if (!dvmInitReferenceTable(refTable, kInitialSize, INT_MAX)) {
1463 LOGE("Unable to initialize monitor tracking table\n");
1464 dvmAbort();
1465 }
1466 }
1467
1468 if (!dvmAddToReferenceTable(refTable, obj)) {
1469 /* ran out of memory? could throw exception instead */
1470 LOGE("Unable to add entry to monitor tracking table\n");
1471 dvmAbort();
1472 } else {
1473 LOGVV("--- added monitor %p\n", obj);
1474 }
1475}
1476
Andy McFaddenab00d452009-08-19 07:21:41 -07001477
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001478/*
1479 * Track a JNI MonitorExit in the current thread.
1480 */
1481static void trackMonitorExit(Thread* self, Object* obj)
1482{
Andy McFaddenab00d452009-08-19 07:21:41 -07001483 ReferenceTable* pRefTable = &self->jniMonitorRefTable;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001484
Andy McFaddenab00d452009-08-19 07:21:41 -07001485 if (!dvmRemoveFromReferenceTable(pRefTable, pRefTable->table, obj)) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001486 LOGE("JNI monitor %p not found in tracking list\n", obj);
1487 /* keep going? */
1488 } else {
1489 LOGVV("--- removed monitor %p\n", obj);
1490 }
1491}
1492
1493/*
1494 * Release all monitors held by the jniMonitorRefTable list.
1495 */
1496void dvmReleaseJniMonitors(Thread* self)
1497{
Andy McFaddenab00d452009-08-19 07:21:41 -07001498 ReferenceTable* pRefTable = &self->jniMonitorRefTable;
1499 Object** top = pRefTable->table;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001500
1501 if (top == NULL)
1502 return;
1503
Andy McFaddenab00d452009-08-19 07:21:41 -07001504 Object** ptr = pRefTable->nextEntry;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001505 while (--ptr >= top) {
1506 if (!dvmUnlockObject(self, *ptr)) {
1507 LOGW("Unable to unlock monitor %p at thread detach\n", *ptr);
1508 } else {
1509 LOGVV("--- detach-releasing monitor %p\n", *ptr);
1510 }
1511 }
1512
1513 /* zap it */
Andy McFaddenab00d452009-08-19 07:21:41 -07001514 pRefTable->nextEntry = pRefTable->table;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001515}
1516
1517#ifdef WITH_JNI_STACK_CHECK
1518/*
1519 * Compute a CRC on the entire interpreted stack.
1520 *
1521 * Would be nice to compute it on "self" as well, but there are parts of
1522 * the Thread that can be altered by other threads (e.g. prev/next pointers).
1523 */
1524static void computeStackSum(Thread* self)
1525{
1526 const u1* low = (const u1*)SAVEAREA_FROM_FP(self->curFrame);
1527 u4 crc = dvmInitCrc32();
1528 self->stackCrc = 0;
1529 crc = dvmComputeCrc32(crc, low, self->interpStackStart - low);
1530 self->stackCrc = crc;
1531}
1532
1533/*
1534 * Compute a CRC on the entire interpreted stack, and compare it to what
1535 * we previously computed.
1536 *
1537 * We can execute JNI directly from native code without calling in from
1538 * interpreted code during VM initialization and immediately after JNI
1539 * thread attachment. Another opportunity exists during JNI_OnLoad. Rather
1540 * than catching these cases we just ignore them here, which is marginally
1541 * less accurate but reduces the amount of code we have to touch with #ifdefs.
1542 */
1543static void checkStackSum(Thread* self)
1544{
1545 const u1* low = (const u1*)SAVEAREA_FROM_FP(self->curFrame);
1546 u4 stackCrc, crc;
1547
1548 stackCrc = self->stackCrc;
1549 self->stackCrc = 0;
1550 crc = dvmInitCrc32();
1551 crc = dvmComputeCrc32(crc, low, self->interpStackStart - low);
1552 if (crc != stackCrc) {
1553 const Method* meth = dvmGetCurrentJNIMethod();
1554 if (dvmComputeExactFrameDepth(self->curFrame) == 1) {
1555 LOGD("JNI: bad stack CRC (0x%08x) -- okay during init\n",
1556 stackCrc);
1557 } else if (strcmp(meth->name, "nativeLoad") == 0 &&
1558 (strcmp(meth->clazz->descriptor, "Ljava/lang/Runtime;") == 0))
1559 {
1560 LOGD("JNI: bad stack CRC (0x%08x) -- okay during JNI_OnLoad\n",
1561 stackCrc);
1562 } else {
1563 LOGW("JNI: bad stack CRC (%08x vs %08x)\n", crc, stackCrc);
1564 dvmAbort();
1565 }
1566 }
1567 self->stackCrc = (u4) -1; /* make logic errors more noticeable */
1568}
1569#endif
1570
1571
1572/*
1573 * ===========================================================================
Andy McFaddend5ab7262009-08-25 07:19:34 -07001574 * JNI call bridge
1575 * ===========================================================================
1576 */
1577
1578/*
1579 * The functions here form a bridge between interpreted code and JNI native
1580 * functions. The basic task is to convert an array of primitives and
1581 * references into C-style function arguments. This is architecture-specific
1582 * and usually requires help from assembly code.
1583 *
1584 * The bridge takes four arguments: the array of parameters, a place to
1585 * store the function result (if any), the method to call, and a pointer
1586 * to the current thread.
1587 *
1588 * These functions aren't called directly from elsewhere in the VM.
1589 * A pointer in the Method struct points to one of these, and when a native
1590 * method is invoked the interpreter jumps to it.
1591 *
1592 * (The "internal native" methods are invoked the same way, but instead
1593 * of calling through a bridge, the target method is called directly.)
1594 *
1595 * The "args" array should not be modified, but we do so anyway for
1596 * performance reasons. We know that it points to the "outs" area on
1597 * the current method's interpreted stack. This area is ignored by the
1598 * precise GC, because there is no register map for a native method (for
1599 * an interpreted method the args would be listed in the argument set).
1600 * We know all of the values exist elsewhere on the interpreted stack,
1601 * because the method call setup copies them right before making the call,
1602 * so we don't have to worry about concealing stuff from the GC.
1603 *
1604 * If we don't want to modify "args", we either have to create a local
1605 * copy and modify it before calling dvmPlatformInvoke, or we have to do
1606 * the local reference replacement within dvmPlatformInvoke. The latter
1607 * has some performance advantages, though if we can inline the local
1608 * reference adds we may win when there's a lot of reference args (unless
1609 * we want to code up some local ref table manipulation in assembly.
1610 */
1611
1612/*
Andy McFadden0423f0e2009-08-26 07:21:53 -07001613 * If necessary, convert the value in pResult from a local/global reference
1614 * to an object pointer.
1615 */
1616static inline void convertReferenceResult(JNIEnv* env, JValue* pResult,
1617 const Method* method, Thread* self)
1618{
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001619#ifdef USE_INDIRECT_REF
Andy McFadden0423f0e2009-08-26 07:21:53 -07001620 if (method->shorty[0] == 'L' && !dvmCheckException(self) &&
1621 pResult->l != NULL)
1622 {
1623 pResult->l = dvmDecodeIndirectRef(env, pResult->l);
1624 }
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001625#endif
Andy McFadden0423f0e2009-08-26 07:21:53 -07001626}
1627
1628/*
Andy McFaddend5ab7262009-08-25 07:19:34 -07001629 * General form, handles all cases.
1630 */
1631void dvmCallJNIMethod_general(const u4* args, JValue* pResult,
1632 const Method* method, Thread* self)
1633{
1634 int oldStatus;
1635 u4* modArgs = (u4*) args;
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001636 jclass staticMethodClass;
1637 JNIEnv* env = self->jniEnv;
Andy McFaddend5ab7262009-08-25 07:19:34 -07001638
1639 assert(method->insns != NULL);
1640
1641 //LOGI("JNI calling %p (%s.%s:%s):\n", method->insns,
1642 // method->clazz->descriptor, method->name, method->shorty);
1643
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001644#ifdef USE_INDIRECT_REF
Andy McFaddend5ab7262009-08-25 07:19:34 -07001645 /*
1646 * Walk the argument list, creating local references for appropriate
1647 * arguments.
1648 */
Andy McFaddend5ab7262009-08-25 07:19:34 -07001649 int idx = 0;
1650 if (dvmIsStaticMethod(method)) {
1651 /* add the class object we pass in */
1652 staticMethodClass = addLocalReference(env, (Object*) method->clazz);
1653 if (staticMethodClass == NULL) {
1654 assert(dvmCheckException(self));
1655 return;
1656 }
1657 } else {
1658 /* add "this" */
1659 staticMethodClass = NULL;
1660 jobject thisObj = addLocalReference(env, (Object*) modArgs[0]);
1661 if (thisObj == NULL) {
1662 assert(dvmCheckException(self));
1663 return;
1664 }
1665 modArgs[idx] = (u4) thisObj;
1666 idx = 1;
1667 }
1668
1669 const char* shorty = &method->shorty[1]; /* skip return type */
1670 while (*shorty != '\0') {
1671 switch (*shorty++) {
1672 case 'L':
1673 //LOGI(" local %d: 0x%08x\n", idx, modArgs[idx]);
1674 if (modArgs[idx] != 0) {
1675 //if (!dvmIsValidObject((Object*) modArgs[idx]))
1676 // dvmAbort();
1677 jobject argObj = addLocalReference(env, (Object*) modArgs[idx]);
1678 if (argObj == NULL) {
1679 assert(dvmCheckException(self));
1680 return;
1681 }
1682 modArgs[idx] = (u4) argObj;
1683 }
1684 break;
1685 case 'D':
1686 case 'J':
1687 idx++;
1688 break;
1689 default:
1690 /* Z B C S I -- do nothing */
1691 break;
1692 }
1693
1694 idx++;
1695 }
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001696#else
1697 staticMethodClass = dvmIsStaticMethod(method) ?
1698 (jclass) method->clazz : NULL;
1699#endif
Andy McFaddend5ab7262009-08-25 07:19:34 -07001700
1701 oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
1702
1703 COMPUTE_STACK_SUM(self);
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001704 dvmPlatformInvoke(env, staticMethodClass,
Andy McFaddend5ab7262009-08-25 07:19:34 -07001705 method->jniArgInfo, method->insSize, modArgs, method->shorty,
1706 (void*)method->insns, pResult);
1707 CHECK_STACK_SUM(self);
1708
1709 dvmChangeStatus(self, oldStatus);
Andy McFadden0423f0e2009-08-26 07:21:53 -07001710
1711 convertReferenceResult(env, pResult, method, self);
Andy McFaddend5ab7262009-08-25 07:19:34 -07001712}
1713
1714/*
1715 * Handler for the unusual case of a synchronized native method.
1716 *
1717 * Lock the object, then call through the general function.
1718 */
1719void dvmCallJNIMethod_synchronized(const u4* args, JValue* pResult,
1720 const Method* method, Thread* self)
1721{
1722 Object* lockObj;
1723
1724 assert(dvmIsSynchronizedMethod(method));
1725
1726 if (dvmIsStaticMethod(method))
1727 lockObj = (Object*) method->clazz;
1728 else
1729 lockObj = (Object*) args[0];
1730
1731 LOGVV("Calling %s.%s: locking %p (%s)\n",
1732 method->clazz->descriptor, method->name,
1733 lockObj, lockObj->clazz->descriptor);
1734
1735 dvmLockObject(self, lockObj);
1736 dvmCallJNIMethod_general(args, pResult, method, self);
1737 dvmUnlockObject(self, lockObj);
1738}
1739
1740/*
1741 * Virtual method call, no reference arguments.
1742 *
1743 * We need to local-ref the "this" argument, found in args[0].
1744 */
1745void dvmCallJNIMethod_virtualNoRef(const u4* args, JValue* pResult,
1746 const Method* method, Thread* self)
1747{
1748 u4* modArgs = (u4*) args;
1749 int oldStatus;
1750
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001751#ifdef USE_INDIRECT_REF
Andy McFaddend5ab7262009-08-25 07:19:34 -07001752 jobject thisObj = addLocalReference(self->jniEnv, (Object*) args[0]);
1753 if (thisObj == NULL) {
1754 assert(dvmCheckException(self));
1755 return;
1756 }
1757 modArgs[0] = (u4) thisObj;
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001758#endif
Andy McFaddend5ab7262009-08-25 07:19:34 -07001759
1760 oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
1761
1762 COMPUTE_STACK_SUM(self);
1763 dvmPlatformInvoke(self->jniEnv, NULL,
1764 method->jniArgInfo, method->insSize, modArgs, method->shorty,
1765 (void*)method->insns, pResult);
1766 CHECK_STACK_SUM(self);
1767
1768 dvmChangeStatus(self, oldStatus);
Andy McFadden0423f0e2009-08-26 07:21:53 -07001769
1770 convertReferenceResult(self->jniEnv, pResult, method, self);
Andy McFaddend5ab7262009-08-25 07:19:34 -07001771}
1772
1773/*
1774 * Static method call, no reference arguments.
1775 *
1776 * We need to local-ref the class reference.
1777 */
1778void dvmCallJNIMethod_staticNoRef(const u4* args, JValue* pResult,
1779 const Method* method, Thread* self)
1780{
1781 jclass staticMethodClass;
1782 int oldStatus;
1783
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001784#ifdef USE_INDIRECT_REF
Andy McFaddend5ab7262009-08-25 07:19:34 -07001785 staticMethodClass = addLocalReference(self->jniEnv, (Object*)method->clazz);
1786 if (staticMethodClass == NULL) {
1787 assert(dvmCheckException(self));
1788 return;
1789 }
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001790#else
1791 staticMethodClass = (jobject) method->clazz;
1792#endif
Andy McFaddend5ab7262009-08-25 07:19:34 -07001793
1794 oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
1795
1796 COMPUTE_STACK_SUM(self);
1797 dvmPlatformInvoke(self->jniEnv, staticMethodClass,
1798 method->jniArgInfo, method->insSize, args, method->shorty,
1799 (void*)method->insns, pResult);
1800 CHECK_STACK_SUM(self);
1801
1802 dvmChangeStatus(self, oldStatus);
Andy McFadden0423f0e2009-08-26 07:21:53 -07001803
1804 convertReferenceResult(self->jniEnv, pResult, method, self);
Andy McFaddend5ab7262009-08-25 07:19:34 -07001805}
1806
1807/*
1808 * Extract the return type enum from the "jniArgInfo" field.
1809 */
1810DalvikJniReturnType dvmGetArgInfoReturnType(int jniArgInfo)
1811{
1812 return (jniArgInfo & DALVIK_JNI_RETURN_MASK) >> DALVIK_JNI_RETURN_SHIFT;
1813}
1814
1815
1816/*
1817 * ===========================================================================
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001818 * JNI implementation
1819 * ===========================================================================
1820 */
1821
1822/*
1823 * Return the version of the native method interface.
1824 */
1825static jint GetVersion(JNIEnv* env)
1826{
1827 JNI_ENTER();
1828 /*
1829 * There is absolutely no need to toggle the mode for correct behavior.
1830 * However, it does provide native code with a simple "suspend self
1831 * if necessary" call.
1832 */
1833 JNI_EXIT();
1834 return JNI_VERSION_1_6;
1835}
1836
1837/*
1838 * Create a new class from a bag of bytes.
1839 *
1840 * This is not currently supported within Dalvik.
1841 */
1842static jclass DefineClass(JNIEnv* env, const char *name, jobject loader,
1843 const jbyte* buf, jsize bufLen)
1844{
1845 UNUSED_PARAMETER(name);
1846 UNUSED_PARAMETER(loader);
1847 UNUSED_PARAMETER(buf);
1848 UNUSED_PARAMETER(bufLen);
1849
1850 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001851 LOGW("JNI DefineClass is not supported\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001852 JNI_EXIT();
1853 return NULL;
1854}
1855
1856/*
1857 * Find a class by name.
1858 *
1859 * We have to use the "no init" version of FindClass here, because we might
1860 * be getting the class prior to registering native methods that will be
1861 * used in <clinit>.
1862 *
1863 * We need to get the class loader associated with the current native
1864 * method. If there is no native method, e.g. we're calling this from native
1865 * code right after creating the VM, the spec says we need to use the class
1866 * loader returned by "ClassLoader.getBaseClassLoader". There is no such
1867 * method, but it's likely they meant ClassLoader.getSystemClassLoader.
1868 * We can't get that until after the VM has initialized though.
1869 */
1870static jclass FindClass(JNIEnv* env, const char* name)
1871{
1872 JNI_ENTER();
1873
1874 const Method* thisMethod;
1875 ClassObject* clazz;
Andy McFaddenab00d452009-08-19 07:21:41 -07001876 jclass jclazz = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001877 Object* loader;
1878 char* descriptor = NULL;
1879
1880 thisMethod = dvmGetCurrentJNIMethod();
1881 assert(thisMethod != NULL);
1882
1883 descriptor = dvmNameToDescriptor(name);
1884 if (descriptor == NULL) {
1885 clazz = NULL;
1886 goto bail;
1887 }
1888
1889 //Thread* self = dvmThreadSelf();
1890 if (_self->classLoaderOverride != NULL) {
1891 /* hack for JNI_OnLoad */
1892 assert(strcmp(thisMethod->name, "nativeLoad") == 0);
1893 loader = _self->classLoaderOverride;
1894 } else if (thisMethod == gDvm.methFakeNativeEntry) {
1895 /* start point of invocation interface */
1896 if (!gDvm.initializing)
1897 loader = dvmGetSystemClassLoader();
1898 else
1899 loader = NULL;
1900 } else {
1901 loader = thisMethod->clazz->classLoader;
1902 }
1903
1904 clazz = dvmFindClassNoInit(descriptor, loader);
Andy McFaddenab00d452009-08-19 07:21:41 -07001905 jclazz = addLocalReference(env, (Object*) clazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001906
1907bail:
1908 free(descriptor);
Andy McFaddenab00d452009-08-19 07:21:41 -07001909
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001910 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001911 return jclazz;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001912}
1913
1914/*
1915 * Return the superclass of a class.
1916 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001917static jclass GetSuperclass(JNIEnv* env, jclass jclazz)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001918{
1919 JNI_ENTER();
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001920 jclass jsuper = NULL;
Andy McFaddenab00d452009-08-19 07:21:41 -07001921
1922 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001923 if (clazz != NULL)
Andy McFaddenab00d452009-08-19 07:21:41 -07001924 jsuper = addLocalReference(env, (Object*)clazz->super);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001925 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001926 return jsuper;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001927}
1928
1929/*
1930 * Determine whether an object of clazz1 can be safely cast to clazz2.
1931 *
1932 * Like IsInstanceOf, but with a pair of class objects instead of obj+class.
1933 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001934static jboolean IsAssignableFrom(JNIEnv* env, jclass jclazz1, jclass jclazz2)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001935{
1936 JNI_ENTER();
1937
Andy McFaddenab00d452009-08-19 07:21:41 -07001938 ClassObject* clazz1 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz1);
1939 ClassObject* clazz2 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz2);
1940
1941 jboolean result = dvmInstanceof(clazz1, clazz2);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001942
1943 JNI_EXIT();
1944 return result;
1945}
1946
1947/*
1948 * Given a java.lang.reflect.Method or .Constructor, return a methodID.
1949 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001950static jmethodID FromReflectedMethod(JNIEnv* env, jobject jmethod)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001951{
1952 JNI_ENTER();
1953 jmethodID methodID;
Andy McFaddenab00d452009-08-19 07:21:41 -07001954 Object* method = dvmDecodeIndirectRef(env, jmethod);
1955 methodID = (jmethodID) dvmGetMethodFromReflectObj(method);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001956 JNI_EXIT();
1957 return methodID;
1958}
1959
1960/*
1961 * Given a java.lang.reflect.Field, return a fieldID.
1962 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001963static jfieldID FromReflectedField(JNIEnv* env, jobject jfield)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001964{
1965 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001966 jfieldID fieldID;
1967 Object* field = dvmDecodeIndirectRef(env, jfield);
1968 fieldID = (jfieldID) dvmGetFieldFromReflectObj(field);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001969 JNI_EXIT();
1970 return fieldID;
1971}
1972
1973/*
1974 * Convert a methodID to a java.lang.reflect.Method or .Constructor.
1975 *
1976 * (The "isStatic" field does not appear in the spec.)
1977 *
1978 * Throws OutOfMemory and returns NULL on failure.
1979 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001980static jobject ToReflectedMethod(JNIEnv* env, jclass jcls, jmethodID methodID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001981 jboolean isStatic)
1982{
1983 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001984 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls);
1985 Object* obj = dvmCreateReflectObjForMethod(clazz, (Method*) methodID);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001986 dvmReleaseTrackedAlloc(obj, NULL);
Andy McFaddenab00d452009-08-19 07:21:41 -07001987 jobject jobj = addLocalReference(env, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001988 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001989 return jobj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001990}
1991
1992/*
1993 * Convert a fieldID to a java.lang.reflect.Field.
1994 *
1995 * (The "isStatic" field does not appear in the spec.)
1996 *
1997 * Throws OutOfMemory and returns NULL on failure.
1998 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001999static jobject ToReflectedField(JNIEnv* env, jclass jcls, jfieldID fieldID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002000 jboolean isStatic)
2001{
2002 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002003 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls);
2004 Object* obj = dvmCreateReflectObjForField(jcls, (Field*) fieldID);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002005 dvmReleaseTrackedAlloc(obj, NULL);
Andy McFaddenab00d452009-08-19 07:21:41 -07002006 jobject jobj = addLocalReference(env, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002007 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002008 return jobj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002009}
2010
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002011/*
2012 * Take this exception and throw it.
2013 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002014static jint Throw(JNIEnv* env, jthrowable jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002015{
2016 JNI_ENTER();
2017
2018 jint retval;
2019
Andy McFaddenab00d452009-08-19 07:21:41 -07002020 if (jobj != NULL) {
2021 Object* obj = dvmDecodeIndirectRef(env, jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002022 dvmSetException(_self, obj);
2023 retval = JNI_OK;
Andy McFaddenab00d452009-08-19 07:21:41 -07002024 } else {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002025 retval = JNI_ERR;
Andy McFaddenab00d452009-08-19 07:21:41 -07002026 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002027
2028 JNI_EXIT();
2029 return retval;
2030}
2031
2032/*
Andy McFaddenab00d452009-08-19 07:21:41 -07002033 * Constructs an exception object from the specified class with the message
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002034 * specified by "message", and throws it.
2035 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002036static jint ThrowNew(JNIEnv* env, jclass jclazz, const char* message)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002037{
2038 JNI_ENTER();
2039
Andy McFaddenab00d452009-08-19 07:21:41 -07002040 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2041 dvmThrowExceptionByClass(clazz, message);
2042 // TODO: should return failure if this didn't work (e.g. OOM)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002043
2044 JNI_EXIT();
2045 return JNI_OK;
2046}
2047
2048/*
2049 * If an exception is being thrown, return the exception object. Otherwise,
2050 * return NULL.
2051 *
2052 * TODO: if there is no pending exception, we should be able to skip the
2053 * enter/exit checks. If we find one, we need to enter and then re-fetch
2054 * the exception (in case it got moved by a compacting GC).
2055 */
2056static jthrowable ExceptionOccurred(JNIEnv* env)
2057{
2058 JNI_ENTER();
2059
2060 Object* exception;
Andy McFaddenab00d452009-08-19 07:21:41 -07002061 jobject localException;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002062
Andy McFaddenab00d452009-08-19 07:21:41 -07002063 exception = dvmGetException(_self);
2064 localException = addLocalReference(env, exception);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002065 if (localException == NULL && exception != NULL) {
2066 /*
2067 * We were unable to add a new local reference, and threw a new
2068 * exception. We can't return "exception", because it's not a
2069 * local reference. So we have to return NULL, indicating that
2070 * there was no exception, even though it's pretty much raining
2071 * exceptions in here.
2072 */
2073 LOGW("JNI WARNING: addLocal/exception combo\n");
2074 }
2075
2076 JNI_EXIT();
2077 return localException;
2078}
2079
2080/*
2081 * Print an exception and stack trace to stderr.
2082 */
2083static void ExceptionDescribe(JNIEnv* env)
2084{
2085 JNI_ENTER();
2086
2087 Object* exception = dvmGetException(_self);
2088 if (exception != NULL) {
2089 dvmPrintExceptionStackTrace();
2090 } else {
2091 LOGI("Odd: ExceptionDescribe called, but no exception pending\n");
2092 }
2093
2094 JNI_EXIT();
2095}
2096
2097/*
2098 * Clear the exception currently being thrown.
2099 *
2100 * TODO: we should be able to skip the enter/exit stuff.
2101 */
2102static void ExceptionClear(JNIEnv* env)
2103{
2104 JNI_ENTER();
2105 dvmClearException(_self);
2106 JNI_EXIT();
2107}
2108
2109/*
2110 * Kill the VM. This function does not return.
2111 */
2112static void FatalError(JNIEnv* env, const char* msg)
2113{
2114 //dvmChangeStatus(NULL, THREAD_RUNNING);
2115 LOGE("JNI posting fatal error: %s\n", msg);
2116 dvmAbort();
2117}
2118
2119/*
2120 * Push a new JNI frame on the stack, with a new set of locals.
2121 *
2122 * The new frame must have the same method pointer. (If for no other
2123 * reason than FindClass needs it to get the appropriate class loader.)
2124 */
2125static jint PushLocalFrame(JNIEnv* env, jint capacity)
2126{
2127 JNI_ENTER();
2128 int result = JNI_OK;
Andy McFaddenab00d452009-08-19 07:21:41 -07002129 if (!ensureLocalCapacity(env, capacity) ||
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002130 !dvmPushLocalFrame(_self /*dvmThreadSelf()*/, dvmGetCurrentJNIMethod()))
2131 {
2132 /* yes, OutOfMemoryError, not StackOverflowError */
2133 dvmClearException(_self);
2134 dvmThrowException("Ljava/lang/OutOfMemoryError;",
2135 "out of stack in JNI PushLocalFrame");
2136 result = JNI_ERR;
2137 }
2138 JNI_EXIT();
2139 return result;
2140}
2141
2142/*
2143 * Pop the local frame off. If "result" is not null, add it as a
2144 * local reference on the now-current frame.
2145 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002146static jobject PopLocalFrame(JNIEnv* env, jobject jresult)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002147{
2148 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002149 Object* result = dvmDecodeIndirectRef(env, jresult);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002150 if (!dvmPopLocalFrame(_self /*dvmThreadSelf()*/)) {
2151 LOGW("JNI WARNING: too many PopLocalFrame calls\n");
2152 dvmClearException(_self);
2153 dvmThrowException("Ljava/lang/RuntimeException;",
2154 "too many PopLocalFrame calls");
2155 }
Andy McFaddenab00d452009-08-19 07:21:41 -07002156 jresult = addLocalReference(env, result);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002157 JNI_EXIT();
2158 return result;
2159}
2160
2161/*
2162 * Add a reference to the global list.
2163 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002164static jobject NewGlobalRef(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002165{
Andy McFaddenb18992f2009-09-25 10:42:15 -07002166 Object* obj;
2167
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002168 JNI_ENTER();
Andy McFaddenb18992f2009-09-25 10:42:15 -07002169 if (dvmIsWeakGlobalRef(jobj))
2170 obj = getPhantomReferent(env, (jweak) jobj);
2171 else
2172 obj = dvmDecodeIndirectRef(env, jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002173 jobject retval = addGlobalReference(obj);
2174 JNI_EXIT();
2175 return retval;
2176}
2177
2178/*
2179 * Delete a reference from the global list.
2180 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002181static void DeleteGlobalRef(JNIEnv* env, jobject jglobalRef)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002182{
2183 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002184 deleteGlobalReference(jglobalRef);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002185 JNI_EXIT();
2186}
2187
2188
2189/*
2190 * Add a reference to the local list.
2191 */
Andy McFaddenb18992f2009-09-25 10:42:15 -07002192static jobject NewLocalRef(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002193{
Andy McFaddenb18992f2009-09-25 10:42:15 -07002194 Object* obj;
2195
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002196 JNI_ENTER();
Andy McFaddenb18992f2009-09-25 10:42:15 -07002197 if (dvmIsWeakGlobalRef(jobj))
2198 obj = getPhantomReferent(env, (jweak) jobj);
2199 else
2200 obj = dvmDecodeIndirectRef(env, jobj);
Andy McFaddenab00d452009-08-19 07:21:41 -07002201 jobject retval = addLocalReference(env, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002202 JNI_EXIT();
2203 return retval;
2204}
2205
2206/*
2207 * Delete a reference from the local list.
2208 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002209static void DeleteLocalRef(JNIEnv* env, jobject jlocalRef)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002210{
2211 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002212 deleteLocalReference(env, jlocalRef);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002213 JNI_EXIT();
2214}
2215
2216/*
2217 * Ensure that the local references table can hold at least this many
2218 * references.
2219 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002220static jint EnsureLocalCapacity(JNIEnv* env, jint capacity)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002221{
2222 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002223 bool okay = ensureLocalCapacity(env, capacity);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002224 if (!okay) {
2225 dvmThrowException("Ljava/lang/OutOfMemoryError;",
2226 "can't ensure local reference capacity");
2227 }
2228 JNI_EXIT();
2229 if (okay)
2230 return 0;
2231 else
2232 return -1;
2233}
2234
2235
2236/*
2237 * Determine whether two Object references refer to the same underlying object.
2238 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002239static jboolean IsSameObject(JNIEnv* env, jobject jref1, jobject jref2)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002240{
2241 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002242 Object* obj1 = dvmDecodeIndirectRef(env, jref1);
2243 Object* obj2 = dvmDecodeIndirectRef(env, jref2);
2244 jboolean result = (obj1 == obj2);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002245 JNI_EXIT();
2246 return result;
2247}
2248
2249/*
2250 * Allocate a new object without invoking any constructors.
2251 */
2252static jobject AllocObject(JNIEnv* env, jclass jclazz)
2253{
2254 JNI_ENTER();
2255
Andy McFaddenab00d452009-08-19 07:21:41 -07002256 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2257 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002258
2259 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2260 assert(dvmCheckException(_self));
Andy McFaddenab00d452009-08-19 07:21:41 -07002261 result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002262 } else {
Andy McFaddenab00d452009-08-19 07:21:41 -07002263 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
2264 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002265 }
2266
2267 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002268 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002269}
2270
2271/*
Andy McFaddenab00d452009-08-19 07:21:41 -07002272 * Allocate a new object and invoke the supplied constructor.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002273 */
2274static jobject NewObject(JNIEnv* env, jclass jclazz, jmethodID methodID, ...)
2275{
2276 JNI_ENTER();
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 if (newObj != NULL) {
2287 JValue unused;
2288 va_list args;
2289 va_start(args, methodID);
Andy McFaddend5ab7262009-08-25 07:19:34 -07002290 dvmCallMethodV(_self, (Method*) methodID, newObj, true, &unused,
2291 args);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002292 va_end(args);
2293 }
2294 }
2295
2296 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002297 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002298}
Andy McFaddenab00d452009-08-19 07:21:41 -07002299static jobject NewObjectV(JNIEnv* env, jclass jclazz, jmethodID methodID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002300 va_list args)
2301{
2302 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002303 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2304 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002305
Andy McFaddenab00d452009-08-19 07:21:41 -07002306 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
2307 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002308 if (newObj != NULL) {
2309 JValue unused;
Andy McFaddend5ab7262009-08-25 07:19:34 -07002310 dvmCallMethodV(_self, (Method*) methodID, newObj, true, &unused, args);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002311 }
2312
2313 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002314 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002315}
Andy McFaddenab00d452009-08-19 07:21:41 -07002316static jobject NewObjectA(JNIEnv* env, jclass jclazz, jmethodID methodID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002317 jvalue* args)
2318{
2319 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002320 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2321 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002322
Andy McFaddenab00d452009-08-19 07:21:41 -07002323 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
2324 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002325 if (newObj != NULL) {
2326 JValue unused;
Andy McFaddend5ab7262009-08-25 07:19:34 -07002327 dvmCallMethodA(_self, (Method*) methodID, newObj, true, &unused, args);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002328 }
2329
2330 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002331 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002332}
2333
2334/*
2335 * Returns the class of an object.
2336 *
2337 * JNI spec says: obj must not be NULL.
2338 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002339static jclass GetObjectClass(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002340{
2341 JNI_ENTER();
2342
Andy McFaddenab00d452009-08-19 07:21:41 -07002343 assert(jobj != NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002344
Andy McFaddenab00d452009-08-19 07:21:41 -07002345 Object* obj = dvmDecodeIndirectRef(env, jobj);
2346 jclass jclazz = addLocalReference(env, (Object*) obj->clazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002347
2348 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002349 return jclazz;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002350}
2351
2352/*
2353 * Determine whether "obj" is an instance of "clazz".
2354 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002355static jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass jclazz)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002356{
2357 JNI_ENTER();
2358
Andy McFaddenab00d452009-08-19 07:21:41 -07002359 assert(jclazz != NULL);
2360
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002361 jboolean result;
2362
Andy McFaddenab00d452009-08-19 07:21:41 -07002363 if (jobj == NULL) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002364 result = true;
Andy McFaddenab00d452009-08-19 07:21:41 -07002365 } else {
2366 Object* obj = dvmDecodeIndirectRef(env, jobj);
2367 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2368 result = dvmInstanceof(obj->clazz, clazz);
2369 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002370
2371 JNI_EXIT();
2372 return result;
2373}
2374
2375/*
2376 * Get a method ID for an instance method.
2377 *
2378 * JNI defines <init> as an instance method, but Dalvik considers it a
2379 * "direct" method, so we have to special-case it here.
2380 *
2381 * Dalvik also puts all private methods into the "direct" list, so we
2382 * really need to just search both lists.
2383 */
2384static jmethodID GetMethodID(JNIEnv* env, jclass jclazz, const char* name,
2385 const char* sig)
2386{
2387 JNI_ENTER();
2388
Andy McFaddenab00d452009-08-19 07:21:41 -07002389 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002390 jmethodID id = NULL;
2391
2392 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2393 assert(dvmCheckException(_self));
2394 } else {
2395 Method* meth;
2396
2397 meth = dvmFindVirtualMethodHierByDescriptor(clazz, name, sig);
2398 if (meth == NULL) {
2399 /* search private methods and constructors; non-hierarchical */
2400 meth = dvmFindDirectMethodByDescriptor(clazz, name, sig);
2401 }
2402 if (meth != NULL && dvmIsStaticMethod(meth)) {
2403 IF_LOGD() {
2404 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
2405 LOGD("GetMethodID: not returning static method %s.%s %s\n",
2406 clazz->descriptor, meth->name, desc);
2407 free(desc);
2408 }
2409 meth = NULL;
2410 }
2411 if (meth == NULL) {
Andy McFadden03bd0d52009-08-18 15:32:27 -07002412 LOGD("GetMethodID: method not found: %s.%s:%s\n",
2413 clazz->descriptor, name, sig);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002414 dvmThrowException("Ljava/lang/NoSuchMethodError;", name);
2415 }
2416
2417 /*
2418 * The method's class may not be the same as clazz, but if
2419 * it isn't this must be a virtual method and the class must
2420 * be a superclass (and, hence, already initialized).
2421 */
2422 if (meth != NULL) {
2423 assert(dvmIsClassInitialized(meth->clazz) ||
2424 dvmIsClassInitializing(meth->clazz));
2425 }
2426 id = (jmethodID) meth;
2427 }
2428 JNI_EXIT();
2429 return id;
2430}
2431
2432/*
2433 * Get a field ID (instance fields).
2434 */
2435static jfieldID GetFieldID(JNIEnv* env, jclass jclazz,
2436 const char* name, const char* sig)
2437{
2438 JNI_ENTER();
2439
Andy McFaddenab00d452009-08-19 07:21:41 -07002440 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002441 jfieldID id;
2442
2443 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2444 assert(dvmCheckException(_self));
2445 id = NULL;
2446 } else {
2447 id = (jfieldID) dvmFindInstanceFieldHier(clazz, name, sig);
Andy McFadden03bd0d52009-08-18 15:32:27 -07002448 if (id == NULL) {
2449 LOGD("GetFieldID: unable to find field %s.%s:%s\n",
2450 clazz->descriptor, name, sig);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002451 dvmThrowException("Ljava/lang/NoSuchFieldError;", name);
Andy McFadden03bd0d52009-08-18 15:32:27 -07002452 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002453 }
2454 JNI_EXIT();
2455 return id;
2456}
2457
2458/*
2459 * Get the method ID for a static method in a class.
2460 */
2461static jmethodID GetStaticMethodID(JNIEnv* env, jclass jclazz,
2462 const char* name, const char* sig)
2463{
2464 JNI_ENTER();
2465
Andy McFaddenab00d452009-08-19 07:21:41 -07002466 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002467 jmethodID id;
2468
2469 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2470 assert(dvmCheckException(_self));
2471 id = NULL;
2472 } else {
2473 Method* meth;
2474
2475 meth = dvmFindDirectMethodHierByDescriptor(clazz, name, sig);
2476
2477 /* make sure it's static, not virtual+private */
2478 if (meth != NULL && !dvmIsStaticMethod(meth)) {
2479 IF_LOGD() {
2480 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
2481 LOGD("GetStaticMethodID: "
2482 "not returning nonstatic method %s.%s %s\n",
2483 clazz->descriptor, meth->name, desc);
2484 free(desc);
2485 }
2486 meth = NULL;
2487 }
2488
2489 id = (jmethodID) meth;
2490 if (id == NULL)
2491 dvmThrowException("Ljava/lang/NoSuchMethodError;", name);
2492 }
2493
2494 JNI_EXIT();
2495 return id;
2496}
2497
2498/*
2499 * Get a field ID (static fields).
2500 */
2501static jfieldID GetStaticFieldID(JNIEnv* env, jclass jclazz,
2502 const char* name, const char* sig)
2503{
2504 JNI_ENTER();
2505
Andy McFaddenab00d452009-08-19 07:21:41 -07002506 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002507 jfieldID id;
2508
2509 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2510 assert(dvmCheckException(_self));
2511 id = NULL;
2512 } else {
2513 id = (jfieldID) dvmFindStaticField(clazz, name, sig);
2514 if (id == NULL)
2515 dvmThrowException("Ljava/lang/NoSuchFieldError;", name);
2516 }
2517 JNI_EXIT();
2518 return id;
2519}
2520
2521/*
2522 * Get a static field.
2523 *
2524 * If we get an object reference, add it to the local refs list.
2525 */
2526#define GET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002527 static _ctype GetStatic##_jname##Field(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002528 jfieldID fieldID) \
2529 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002530 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002531 JNI_ENTER(); \
2532 StaticField* sfield = (StaticField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002533 _ctype value; \
2534 if (_isref) { /* only when _ctype==jobject */ \
2535 Object* obj = dvmGetStaticFieldObject(sfield); \
2536 value = (_ctype)(u4)addLocalReference(env, obj); \
2537 } else { \
2538 value = dvmGetStaticField##_jname(sfield); \
2539 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002540 JNI_EXIT(); \
2541 return value; \
2542 }
2543GET_STATIC_TYPE_FIELD(jobject, Object, true);
2544GET_STATIC_TYPE_FIELD(jboolean, Boolean, false);
2545GET_STATIC_TYPE_FIELD(jbyte, Byte, false);
2546GET_STATIC_TYPE_FIELD(jchar, Char, false);
2547GET_STATIC_TYPE_FIELD(jshort, Short, false);
2548GET_STATIC_TYPE_FIELD(jint, Int, false);
2549GET_STATIC_TYPE_FIELD(jlong, Long, false);
2550GET_STATIC_TYPE_FIELD(jfloat, Float, false);
2551GET_STATIC_TYPE_FIELD(jdouble, Double, false);
2552
2553/*
2554 * Set a static field.
2555 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002556#define SET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) \
2557 static void SetStatic##_jname##Field(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002558 jfieldID fieldID, _ctype value) \
2559 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002560 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002561 JNI_ENTER(); \
2562 StaticField* sfield = (StaticField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002563 if (_isref) { /* only when _ctype==jobject */ \
2564 Object* valObj = dvmDecodeIndirectRef(env, (jobject)(u4)value); \
2565 dvmSetStaticFieldObject(sfield, valObj); \
2566 } else { \
2567 dvmSetStaticField##_jname(sfield, value); \
2568 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002569 JNI_EXIT(); \
2570 }
Andy McFaddenab00d452009-08-19 07:21:41 -07002571SET_STATIC_TYPE_FIELD(jobject, Object, true);
2572SET_STATIC_TYPE_FIELD(jboolean, Boolean, false);
2573SET_STATIC_TYPE_FIELD(jbyte, Byte, false);
2574SET_STATIC_TYPE_FIELD(jchar, Char, false);
2575SET_STATIC_TYPE_FIELD(jshort, Short, false);
2576SET_STATIC_TYPE_FIELD(jint, Int, false);
2577SET_STATIC_TYPE_FIELD(jlong, Long, false);
2578SET_STATIC_TYPE_FIELD(jfloat, Float, false);
2579SET_STATIC_TYPE_FIELD(jdouble, Double, false);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002580
2581/*
2582 * Get an instance field.
2583 *
2584 * If we get an object reference, add it to the local refs list.
2585 */
2586#define GET_TYPE_FIELD(_ctype, _jname, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002587 static _ctype Get##_jname##Field(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002588 jfieldID fieldID) \
2589 { \
2590 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002591 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002592 InstField* field = (InstField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002593 _ctype value; \
2594 if (_isref) { /* only when _ctype==jobject */ \
2595 Object* valObj = dvmGetFieldObject(obj, field->byteOffset); \
2596 value = (_ctype)(u4)addLocalReference(env, valObj); \
2597 } else { \
2598 value = dvmGetField##_jname(obj, field->byteOffset); \
2599 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002600 JNI_EXIT(); \
2601 return value; \
2602 }
2603GET_TYPE_FIELD(jobject, Object, true);
2604GET_TYPE_FIELD(jboolean, Boolean, false);
2605GET_TYPE_FIELD(jbyte, Byte, false);
2606GET_TYPE_FIELD(jchar, Char, false);
2607GET_TYPE_FIELD(jshort, Short, false);
2608GET_TYPE_FIELD(jint, Int, false);
2609GET_TYPE_FIELD(jlong, Long, false);
2610GET_TYPE_FIELD(jfloat, Float, false);
2611GET_TYPE_FIELD(jdouble, Double, false);
2612
2613/*
2614 * Set an instance field.
2615 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002616#define SET_TYPE_FIELD(_ctype, _jname, _isref) \
2617 static void Set##_jname##Field(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002618 jfieldID fieldID, _ctype value) \
2619 { \
2620 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002621 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002622 InstField* field = (InstField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002623 if (_isref) { /* only when _ctype==jobject */ \
2624 Object* valObj = dvmDecodeIndirectRef(env, (jobject)(u4)value); \
2625 dvmSetFieldObject(obj, field->byteOffset, valObj); \
2626 } else { \
2627 dvmSetField##_jname(obj, field->byteOffset, value); \
2628 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002629 JNI_EXIT(); \
2630 }
Andy McFaddenab00d452009-08-19 07:21:41 -07002631SET_TYPE_FIELD(jobject, Object, true);
2632SET_TYPE_FIELD(jboolean, Boolean, false);
2633SET_TYPE_FIELD(jbyte, Byte, false);
2634SET_TYPE_FIELD(jchar, Char, false);
2635SET_TYPE_FIELD(jshort, Short, false);
2636SET_TYPE_FIELD(jint, Int, false);
2637SET_TYPE_FIELD(jlong, Long, false);
2638SET_TYPE_FIELD(jfloat, Float, false);
2639SET_TYPE_FIELD(jdouble, Double, false);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002640
2641/*
2642 * Make a virtual method call.
2643 *
2644 * Three versions (..., va_list, jvalue[]) for each return type. If we're
2645 * returning an Object, we have to add it to the local references table.
2646 */
2647#define CALL_VIRTUAL(_ctype, _jname, _retfail, _retok, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002648 static _ctype Call##_jname##Method(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002649 jmethodID methodID, ...) \
2650 { \
2651 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002652 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002653 const Method* meth; \
2654 va_list args; \
2655 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002656 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002657 if (meth == NULL) { \
2658 JNI_EXIT(); \
2659 return _retfail; \
2660 } \
2661 va_start(args, methodID); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002662 dvmCallMethodV(_self, meth, obj, true, &result, args); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002663 va_end(args); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002664 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002665 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002666 JNI_EXIT(); \
2667 return _retok; \
2668 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002669 static _ctype Call##_jname##MethodV(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002670 jmethodID methodID, va_list args) \
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 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002676 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002677 if (meth == NULL) { \
2678 JNI_EXIT(); \
2679 return _retfail; \
2680 } \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002681 dvmCallMethodV(_self, meth, obj, true, &result, args); \
2682 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002683 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002684 JNI_EXIT(); \
2685 return _retok; \
2686 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002687 static _ctype Call##_jname##MethodA(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002688 jmethodID methodID, jvalue* args) \
2689 { \
2690 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002691 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002692 const Method* meth; \
2693 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002694 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002695 if (meth == NULL) { \
2696 JNI_EXIT(); \
2697 return _retfail; \
2698 } \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002699 dvmCallMethodA(_self, meth, obj, true, &result, args); \
2700 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002701 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002702 JNI_EXIT(); \
2703 return _retok; \
2704 }
2705CALL_VIRTUAL(jobject, Object, NULL, result.l, true);
2706CALL_VIRTUAL(jboolean, Boolean, 0, result.z, false);
2707CALL_VIRTUAL(jbyte, Byte, 0, result.b, false);
2708CALL_VIRTUAL(jchar, Char, 0, result.c, false);
2709CALL_VIRTUAL(jshort, Short, 0, result.s, false);
2710CALL_VIRTUAL(jint, Int, 0, result.i, false);
2711CALL_VIRTUAL(jlong, Long, 0, result.j, false);
2712CALL_VIRTUAL(jfloat, Float, 0.0f, result.f, false);
2713CALL_VIRTUAL(jdouble, Double, 0.0, result.d, false);
2714CALL_VIRTUAL(void, Void, , , false);
2715
2716/*
2717 * Make a "non-virtual" method call. We're still calling a virtual method,
2718 * but this time we're not doing an indirection through the object's vtable.
2719 * The "clazz" parameter defines which implementation of a method we want.
2720 *
2721 * Three versions (..., va_list, jvalue[]) for each return type.
2722 */
2723#define CALL_NONVIRTUAL(_ctype, _jname, _retfail, _retok, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002724 static _ctype CallNonvirtual##_jname##Method(JNIEnv* env, jobject jobj, \
2725 jclass jclazz, jmethodID methodID, ...) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002726 { \
2727 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002728 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2729 ClassObject* clazz = \
2730 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002731 const Method* meth; \
2732 va_list args; \
2733 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002734 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002735 if (meth == NULL) { \
2736 JNI_EXIT(); \
2737 return _retfail; \
2738 } \
2739 va_start(args, methodID); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002740 dvmCallMethodV(_self, meth, obj, true, &result, args); \
2741 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002742 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002743 va_end(args); \
2744 JNI_EXIT(); \
2745 return _retok; \
2746 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002747 static _ctype CallNonvirtual##_jname##MethodV(JNIEnv* env, jobject jobj,\
2748 jclass jclazz, jmethodID methodID, va_list args) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002749 { \
2750 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002751 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2752 ClassObject* clazz = \
2753 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002754 const Method* meth; \
2755 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002756 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002757 if (meth == NULL) { \
2758 JNI_EXIT(); \
2759 return _retfail; \
2760 } \
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 JNI_EXIT(); \
2765 return _retok; \
2766 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002767 static _ctype CallNonvirtual##_jname##MethodA(JNIEnv* env, jobject jobj,\
2768 jclass jclazz, jmethodID methodID, jvalue* args) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002769 { \
2770 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002771 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2772 ClassObject* clazz = \
2773 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002774 const Method* meth; \
2775 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002776 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002777 if (meth == NULL) { \
2778 JNI_EXIT(); \
2779 return _retfail; \
2780 } \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002781 dvmCallMethodA(_self, meth, obj, true, &result, args); \
2782 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002783 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002784 JNI_EXIT(); \
2785 return _retok; \
2786 }
2787CALL_NONVIRTUAL(jobject, Object, NULL, result.l, true);
2788CALL_NONVIRTUAL(jboolean, Boolean, 0, result.z, false);
2789CALL_NONVIRTUAL(jbyte, Byte, 0, result.b, false);
2790CALL_NONVIRTUAL(jchar, Char, 0, result.c, false);
2791CALL_NONVIRTUAL(jshort, Short, 0, result.s, false);
2792CALL_NONVIRTUAL(jint, Int, 0, result.i, false);
2793CALL_NONVIRTUAL(jlong, Long, 0, result.j, false);
2794CALL_NONVIRTUAL(jfloat, Float, 0.0f, result.f, false);
2795CALL_NONVIRTUAL(jdouble, Double, 0.0, result.d, false);
2796CALL_NONVIRTUAL(void, Void, , , false);
2797
2798
2799/*
2800 * Call a static method.
2801 */
2802#define CALL_STATIC(_ctype, _jname, _retfail, _retok, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002803 static _ctype CallStatic##_jname##Method(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002804 jmethodID methodID, ...) \
2805 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002806 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002807 JNI_ENTER(); \
2808 JValue result; \
2809 va_list args; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002810 va_start(args, methodID); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002811 dvmCallMethodV(_self, (Method*)methodID, NULL, true, &result, args);\
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002812 va_end(args); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002813 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002814 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002815 JNI_EXIT(); \
2816 return _retok; \
2817 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002818 static _ctype CallStatic##_jname##MethodV(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002819 jmethodID methodID, va_list args) \
2820 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002821 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002822 JNI_ENTER(); \
2823 JValue result; \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002824 dvmCallMethodV(_self, (Method*)methodID, NULL, true, &result, args);\
2825 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002826 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002827 JNI_EXIT(); \
2828 return _retok; \
2829 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002830 static _ctype CallStatic##_jname##MethodA(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002831 jmethodID methodID, jvalue* args) \
2832 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002833 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002834 JNI_ENTER(); \
2835 JValue result; \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002836 dvmCallMethodA(_self, (Method*)methodID, NULL, true, &result, args);\
2837 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002838 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002839 JNI_EXIT(); \
2840 return _retok; \
2841 }
2842CALL_STATIC(jobject, Object, NULL, result.l, true);
2843CALL_STATIC(jboolean, Boolean, 0, result.z, false);
2844CALL_STATIC(jbyte, Byte, 0, result.b, false);
2845CALL_STATIC(jchar, Char, 0, result.c, false);
2846CALL_STATIC(jshort, Short, 0, result.s, false);
2847CALL_STATIC(jint, Int, 0, result.i, false);
2848CALL_STATIC(jlong, Long, 0, result.j, false);
2849CALL_STATIC(jfloat, Float, 0.0f, result.f, false);
2850CALL_STATIC(jdouble, Double, 0.0, result.d, false);
2851CALL_STATIC(void, Void, , , false);
2852
2853/*
2854 * Create a new String from Unicode data.
2855 *
2856 * If "len" is zero, we will return an empty string even if "unicodeChars"
2857 * is NULL. (The JNI spec is vague here.)
2858 */
2859static jstring NewString(JNIEnv* env, const jchar* unicodeChars, jsize len)
2860{
2861 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002862 jobject retval;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002863
Andy McFaddenab00d452009-08-19 07:21:41 -07002864 StringObject* jstr = dvmCreateStringFromUnicode(unicodeChars, len);
2865 if (jstr == NULL) {
2866 retval = NULL;
2867 } else {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002868 dvmReleaseTrackedAlloc((Object*) jstr, NULL);
Andy McFaddenab00d452009-08-19 07:21:41 -07002869 retval = addLocalReference(env, (Object*) jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002870 }
2871
2872 JNI_EXIT();
Andy McFadden0423f0e2009-08-26 07:21:53 -07002873 return retval;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002874}
2875
2876/*
2877 * Return the length of a String in Unicode character units.
2878 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002879static jsize GetStringLength(JNIEnv* env, jstring jstr)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002880{
2881 JNI_ENTER();
2882
Andy McFaddenab00d452009-08-19 07:21:41 -07002883 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2884 jsize len = dvmStringLen(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002885
2886 JNI_EXIT();
2887 return len;
2888}
2889
Andy McFaddenab00d452009-08-19 07:21:41 -07002890
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002891/*
Andy McFaddenab00d452009-08-19 07:21:41 -07002892 * Get a string's character data.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002893 *
2894 * The result is guaranteed to be valid until ReleaseStringChars is
Andy McFaddenab00d452009-08-19 07:21:41 -07002895 * called, which means we have to pin it or return a copy.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002896 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002897static const jchar* GetStringChars(JNIEnv* env, jstring jstr, jboolean* isCopy)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002898{
2899 JNI_ENTER();
2900
Andy McFaddenab00d452009-08-19 07:21:41 -07002901 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
Andy McFaddend5ab7262009-08-25 07:19:34 -07002902 ArrayObject* strChars = dvmStringCharArray(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002903
Andy McFaddenab00d452009-08-19 07:21:41 -07002904 pinPrimitiveArray(strChars);
2905
2906 const u2* data = dvmStringChars(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002907 if (isCopy != NULL)
2908 *isCopy = JNI_FALSE;
2909
2910 JNI_EXIT();
2911 return (jchar*)data;
2912}
2913
2914/*
2915 * Release our grip on some characters from a string.
2916 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002917static void ReleaseStringChars(JNIEnv* env, jstring jstr, const jchar* chars)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002918{
2919 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002920 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
Andy McFaddend5ab7262009-08-25 07:19:34 -07002921 ArrayObject* strChars = dvmStringCharArray(strObj);
Andy McFaddenab00d452009-08-19 07:21:41 -07002922 unpinPrimitiveArray(strChars);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002923 JNI_EXIT();
2924}
2925
2926/*
2927 * Create a new java.lang.String object from chars in modified UTF-8 form.
2928 *
2929 * The spec doesn't say how to handle a NULL string. Popular desktop VMs
2930 * accept it and return a NULL pointer in response.
2931 */
2932static jstring NewStringUTF(JNIEnv* env, const char* bytes)
2933{
2934 JNI_ENTER();
2935
Andy McFaddenab00d452009-08-19 07:21:41 -07002936 jstring result;
2937
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002938 if (bytes == NULL) {
Andy McFaddenab00d452009-08-19 07:21:41 -07002939 result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002940 } else {
Andy McFaddenab00d452009-08-19 07:21:41 -07002941 /* note newStr could come back NULL on OOM */
2942 StringObject* newStr = dvmCreateStringFromCstr(bytes, ALLOC_DEFAULT);
2943 result = addLocalReference(env, (Object*) newStr);
2944 dvmReleaseTrackedAlloc((Object*)newStr, NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002945 }
2946
2947 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002948 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002949}
2950
2951/*
2952 * Return the length in bytes of the modified UTF-8 form of the string.
2953 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002954static jsize GetStringUTFLength(JNIEnv* env, jstring jstr)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002955{
2956 JNI_ENTER();
2957
Andy McFaddenab00d452009-08-19 07:21:41 -07002958 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2959 jsize len = dvmStringUtf8ByteLen(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002960
2961 JNI_EXIT();
2962 return len;
2963}
2964
2965/*
2966 * Convert "string" to modified UTF-8 and return a pointer. The returned
2967 * value must be released with ReleaseStringUTFChars.
2968 *
2969 * According to the JNI reference, "Returns a pointer to a UTF-8 string,
2970 * or NULL if the operation fails. Returns NULL if and only if an invocation
2971 * of this function has thrown an exception."
2972 *
2973 * The behavior here currently follows that of other open-source VMs, which
2974 * quietly return NULL if "string" is NULL. We should consider throwing an
2975 * NPE. (The CheckJNI code blows up if you try to pass in a NULL string,
2976 * which should catch this sort of thing during development.) Certain other
2977 * VMs will crash with a segmentation fault.
2978 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002979static const char* GetStringUTFChars(JNIEnv* env, jstring jstr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002980 jboolean* isCopy)
2981{
2982 JNI_ENTER();
2983 char* newStr;
2984
Andy McFaddenab00d452009-08-19 07:21:41 -07002985 if (jstr == NULL) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002986 /* this shouldn't happen; throw NPE? */
2987 newStr = NULL;
2988 } else {
2989 if (isCopy != NULL)
2990 *isCopy = JNI_TRUE;
2991
Andy McFaddenab00d452009-08-19 07:21:41 -07002992 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2993 newStr = dvmCreateCstrFromString(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002994 if (newStr == NULL) {
2995 /* assume memory failure */
2996 dvmThrowException("Ljava/lang/OutOfMemoryError;",
2997 "native heap string alloc failed");
2998 }
2999 }
3000
3001 JNI_EXIT();
3002 return newStr;
3003}
3004
3005/*
3006 * Release a string created by GetStringUTFChars().
3007 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003008static void ReleaseStringUTFChars(JNIEnv* env, jstring jstr, const char* utf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003009{
3010 JNI_ENTER();
3011 free((char*)utf);
3012 JNI_EXIT();
3013}
3014
3015/*
3016 * Return the capacity of the array.
3017 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003018static jsize GetArrayLength(JNIEnv* env, jarray jarr)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003019{
3020 JNI_ENTER();
3021
Andy McFaddenab00d452009-08-19 07:21:41 -07003022 ArrayObject* arrObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
3023 jsize length = arrObj->length;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003024
3025 JNI_EXIT();
3026 return length;
3027}
3028
3029/*
3030 * Construct a new array that holds objects from class "elementClass".
3031 */
3032static jobjectArray NewObjectArray(JNIEnv* env, jsize length,
Andy McFaddenab00d452009-08-19 07:21:41 -07003033 jclass jelementClass, jobject jinitialElement)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003034{
3035 JNI_ENTER();
3036
Andy McFaddenab00d452009-08-19 07:21:41 -07003037 jobjectArray newArray = NULL;
3038 ClassObject* elemClassObj =
3039 (ClassObject*) dvmDecodeIndirectRef(env, jelementClass);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003040
3041 if (elemClassObj == NULL) {
3042 dvmThrowException("Ljava/lang/NullPointerException;",
3043 "JNI NewObjectArray");
3044 goto bail;
3045 }
3046
Andy McFaddenab00d452009-08-19 07:21:41 -07003047 ArrayObject* newObj =
3048 dvmAllocObjectArray(elemClassObj, length, ALLOC_DEFAULT);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003049 if (newObj == NULL) {
3050 assert(dvmCheckException(_self));
3051 goto bail;
3052 }
Andy McFaddenab00d452009-08-19 07:21:41 -07003053 newArray = addLocalReference(env, (Object*) newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003054 dvmReleaseTrackedAlloc((Object*) newObj, NULL);
3055
3056 /*
3057 * Initialize the array. Trashes "length".
3058 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003059 if (jinitialElement != NULL) {
3060 Object* initialElement = dvmDecodeIndirectRef(env, jinitialElement);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003061 Object** arrayData = (Object**) newObj->contents;
3062
3063 while (length--)
Andy McFaddenab00d452009-08-19 07:21:41 -07003064 *arrayData++ = initialElement;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003065 }
3066
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003067
3068bail:
3069 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07003070 return newArray;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003071}
3072
3073/*
3074 * Get one element of an Object array.
3075 *
3076 * Add the object to the local references table in case the array goes away.
3077 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003078static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray jarr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003079 jsize index)
3080{
3081 JNI_ENTER();
3082
Andy McFaddenab00d452009-08-19 07:21:41 -07003083 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
3084 jobject retval = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003085
Andy McFaddenab00d452009-08-19 07:21:41 -07003086 assert(arrayObj != NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003087
3088 /* check the array bounds */
3089 if (index < 0 || index >= (int) arrayObj->length) {
3090 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
3091 arrayObj->obj.clazz->descriptor);
3092 goto bail;
3093 }
3094
Andy McFaddenab00d452009-08-19 07:21:41 -07003095 Object* value = ((Object**) arrayObj->contents)[index];
3096 retval = addLocalReference(env, value);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003097
3098bail:
3099 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07003100 return retval;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003101}
3102
3103/*
3104 * Set one element of an Object array.
3105 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003106static void SetObjectArrayElement(JNIEnv* env, jobjectArray jarr,
3107 jsize index, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003108{
3109 JNI_ENTER();
3110
Andy McFaddenab00d452009-08-19 07:21:41 -07003111 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003112
Andy McFaddenab00d452009-08-19 07:21:41 -07003113 assert(arrayObj != NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003114
3115 /* check the array bounds */
3116 if (index < 0 || index >= (int) arrayObj->length) {
3117 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
3118 arrayObj->obj.clazz->descriptor);
3119 goto bail;
3120 }
3121
3122 //LOGV("JNI: set element %d in array %p to %p\n", index, array, value);
3123
Andy McFadden0423f0e2009-08-26 07:21:53 -07003124 Object* obj = dvmDecodeIndirectRef(env, jobj);
Andy McFaddenab00d452009-08-19 07:21:41 -07003125 ((Object**) arrayObj->contents)[index] = obj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003126
3127bail:
3128 JNI_EXIT();
3129}
3130
3131/*
3132 * Create a new array of primitive elements.
3133 */
3134#define NEW_PRIMITIVE_ARRAY(_artype, _jname, _typechar) \
3135 static _artype New##_jname##Array(JNIEnv* env, jsize length) \
3136 { \
3137 JNI_ENTER(); \
3138 ArrayObject* arrayObj; \
3139 arrayObj = dvmAllocPrimitiveArray(_typechar, length, \
3140 ALLOC_DEFAULT); \
Andy McFaddenab00d452009-08-19 07:21:41 -07003141 jarray jarr = NULL; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003142 if (arrayObj != NULL) { \
Andy McFaddenab00d452009-08-19 07:21:41 -07003143 jarr = addLocalReference(env, (Object*) arrayObj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003144 dvmReleaseTrackedAlloc((Object*) arrayObj, NULL); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003145 } \
3146 JNI_EXIT(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07003147 return (_artype)jarr; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003148 }
3149NEW_PRIMITIVE_ARRAY(jbooleanArray, Boolean, 'Z');
3150NEW_PRIMITIVE_ARRAY(jbyteArray, Byte, 'B');
3151NEW_PRIMITIVE_ARRAY(jcharArray, Char, 'C');
3152NEW_PRIMITIVE_ARRAY(jshortArray, Short, 'S');
3153NEW_PRIMITIVE_ARRAY(jintArray, Int, 'I');
3154NEW_PRIMITIVE_ARRAY(jlongArray, Long, 'J');
3155NEW_PRIMITIVE_ARRAY(jfloatArray, Float, 'F');
3156NEW_PRIMITIVE_ARRAY(jdoubleArray, Double, 'D');
3157
3158/*
3159 * Get a pointer to a C array of primitive elements from an array object
3160 * of the matching type.
3161 *
Andy McFaddenab00d452009-08-19 07:21:41 -07003162 * In a compacting GC, we either need to return a copy of the elements or
3163 * "pin" the memory. Otherwise we run the risk of native code using the
3164 * buffer as the destination of e.g. a blocking read() call that wakes up
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003165 * during a GC.
3166 */
3167#define GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
3168 static _ctype* Get##_jname##ArrayElements(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07003169 _ctype##Array jarr, jboolean* isCopy) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003170 { \
3171 JNI_ENTER(); \
3172 _ctype* data; \
Andy McFaddenab00d452009-08-19 07:21:41 -07003173 ArrayObject* arrayObj = \
3174 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
3175 pinPrimitiveArray(arrayObj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003176 data = (_ctype*) arrayObj->contents; \
3177 if (isCopy != NULL) \
3178 *isCopy = JNI_FALSE; \
3179 JNI_EXIT(); \
3180 return data; \
3181 }
3182
3183/*
3184 * Release the storage locked down by the "get" function.
3185 *
Andy McFaddenab00d452009-08-19 07:21:41 -07003186 * 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 -08003187 * elements in 'array'." They apparently did not anticipate the need to
Andy McFaddenab00d452009-08-19 07:21:41 -07003188 * un-pin memory.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003189 */
3190#define RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
3191 static void Release##_jname##ArrayElements(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07003192 _ctype##Array jarr, _ctype* elems, jint mode) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003193 { \
3194 UNUSED_PARAMETER(elems); \
3195 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07003196 if (mode != JNI_COMMIT) { \
3197 ArrayObject* arrayObj = \
3198 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
3199 unpinPrimitiveArray(arrayObj); \
3200 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003201 JNI_EXIT(); \
3202 }
3203
3204/*
3205 * Copy a section of a primitive array to a buffer.
3206 */
3207#define GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
3208 static void Get##_jname##ArrayRegion(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07003209 _ctype##Array jarr, jsize start, jsize len, _ctype* buf) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003210 { \
3211 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07003212 ArrayObject* arrayObj = \
3213 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003214 _ctype* data = (_ctype*) arrayObj->contents; \
3215 if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
3216 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
3217 arrayObj->obj.clazz->descriptor); \
3218 } else { \
3219 memcpy(buf, data + start, len * sizeof(_ctype)); \
3220 } \
3221 JNI_EXIT(); \
3222 }
3223
3224/*
3225 * Copy a section of a primitive array to a buffer.
3226 */
3227#define SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
3228 static void Set##_jname##ArrayRegion(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07003229 _ctype##Array jarr, jsize start, jsize len, const _ctype* buf) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003230 { \
3231 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07003232 ArrayObject* arrayObj = \
3233 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003234 _ctype* data = (_ctype*) arrayObj->contents; \
3235 if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
3236 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
3237 arrayObj->obj.clazz->descriptor); \
3238 } else { \
3239 memcpy(data + start, buf, len * sizeof(_ctype)); \
3240 } \
3241 JNI_EXIT(); \
3242 }
3243
3244/*
3245 * 4-in-1:
3246 * Get<Type>ArrayElements
3247 * Release<Type>ArrayElements
3248 * Get<Type>ArrayRegion
3249 * Set<Type>ArrayRegion
3250 */
3251#define PRIMITIVE_ARRAY_FUNCTIONS(_ctype, _jname) \
3252 GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
3253 RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
3254 GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname); \
3255 SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname);
3256
3257PRIMITIVE_ARRAY_FUNCTIONS(jboolean, Boolean);
3258PRIMITIVE_ARRAY_FUNCTIONS(jbyte, Byte);
3259PRIMITIVE_ARRAY_FUNCTIONS(jchar, Char);
3260PRIMITIVE_ARRAY_FUNCTIONS(jshort, Short);
3261PRIMITIVE_ARRAY_FUNCTIONS(jint, Int);
3262PRIMITIVE_ARRAY_FUNCTIONS(jlong, Long);
3263PRIMITIVE_ARRAY_FUNCTIONS(jfloat, Float);
3264PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double);
3265
3266/*
3267 * Register one or more native functions in one class.
3268 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003269static jint RegisterNatives(JNIEnv* env, jclass jclazz,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003270 const JNINativeMethod* methods, jint nMethods)
3271{
3272 JNI_ENTER();
3273
Andy McFaddenab00d452009-08-19 07:21:41 -07003274 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
Elliott Hughes09239e32009-09-29 18:35:43 -07003275 jint retval = JNI_OK;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003276 int i;
3277
3278 if (gDvm.verboseJni) {
3279 LOGI("[Registering JNI native methods for class %s]\n",
Andy McFaddenab00d452009-08-19 07:21:41 -07003280 clazz->descriptor);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003281 }
3282
3283 for (i = 0; i < nMethods; i++) {
Andy McFaddenab00d452009-08-19 07:21:41 -07003284 if (!dvmRegisterJNIMethod(clazz, methods[i].name,
3285 methods[i].signature, methods[i].fnPtr))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003286 {
3287 retval = JNI_ERR;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003288 }
3289 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003290
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003291 JNI_EXIT();
3292 return retval;
3293}
3294
3295/*
3296 * Un-register a native function.
3297 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003298static jint UnregisterNatives(JNIEnv* env, jclass jclazz)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003299{
3300 JNI_ENTER();
3301 /*
3302 * The JNI docs refer to this as a way to reload/relink native libraries,
3303 * and say it "should not be used in normal native code".
3304 *
3305 * We can implement it if we decide we need it.
3306 */
3307 JNI_EXIT();
3308 return JNI_ERR;
3309}
3310
3311/*
3312 * Lock the monitor.
3313 *
3314 * We have to track all monitor enters and exits, so that we can undo any
3315 * outstanding synchronization before the thread exits.
3316 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003317static jint MonitorEnter(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003318{
3319 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003320 Object* obj = dvmDecodeIndirectRef(env, jobj);
3321 dvmLockObject(_self, obj);
3322 trackMonitorEnter(_self, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003323 JNI_EXIT();
3324 return JNI_OK;
3325}
3326
3327/*
3328 * Unlock the monitor.
3329 *
3330 * Throws an IllegalMonitorStateException if the current thread
Andy McFaddenab00d452009-08-19 07:21:41 -07003331 * doesn't own the monitor. (dvmUnlockObject() takes care of the throw.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003332 *
3333 * According to the 1.6 spec, it's legal to call here with an exception
3334 * pending. If this fails, we'll stomp the original exception.
3335 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003336static jint MonitorExit(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003337{
3338 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003339 Object* obj = dvmDecodeIndirectRef(env, jobj);
3340 bool success = dvmUnlockObject(_self, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003341 if (success)
Andy McFaddenab00d452009-08-19 07:21:41 -07003342 trackMonitorExit(_self, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003343 JNI_EXIT();
3344 return success ? JNI_OK : JNI_ERR;
3345}
3346
3347/*
3348 * Return the JavaVM interface associated with the current thread.
3349 */
3350static jint GetJavaVM(JNIEnv* env, JavaVM** vm)
3351{
3352 JNI_ENTER();
3353 //*vm = gDvm.vmList;
3354 *vm = (JavaVM*) ((JNIEnvExt*)env)->vm;
3355 JNI_EXIT();
3356 if (*vm == NULL)
3357 return JNI_ERR;
3358 else
3359 return JNI_OK;
3360}
3361
3362/*
3363 * Copies "len" Unicode characters, from offset "start".
3364 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003365static void GetStringRegion(JNIEnv* env, jstring jstr, jsize start, jsize len,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003366 jchar* buf)
3367{
3368 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003369 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003370 if (start + len > dvmStringLen(strObj))
3371 dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
3372 else
3373 memcpy(buf, dvmStringChars(strObj) + start, len * sizeof(u2));
3374 JNI_EXIT();
3375}
3376
3377/*
3378 * Translates "len" Unicode characters, from offset "start", into
3379 * modified UTF-8 encoding.
3380 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003381static void GetStringUTFRegion(JNIEnv* env, jstring jstr, jsize start,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003382 jsize len, char* buf)
3383{
3384 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003385 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003386 if (start + len > dvmStringLen(strObj))
3387 dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
3388 else
3389 dvmCreateCstrFromStringRegion(strObj, start, len, buf);
3390 JNI_EXIT();
3391}
3392
3393/*
3394 * Get a raw pointer to array data.
3395 *
3396 * The caller is expected to call "release" before doing any JNI calls
3397 * or blocking I/O operations.
3398 *
Andy McFaddenab00d452009-08-19 07:21:41 -07003399 * We need to pin the memory or block GC.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003400 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003401static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray jarr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003402 jboolean* isCopy)
3403{
3404 JNI_ENTER();
3405 void* data;
Andy McFaddenab00d452009-08-19 07:21:41 -07003406 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
3407 pinPrimitiveArray(arrayObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003408 data = arrayObj->contents;
3409 if (isCopy != NULL)
3410 *isCopy = JNI_FALSE;
3411 JNI_EXIT();
3412 return data;
3413}
3414
3415/*
3416 * Release an array obtained with GetPrimitiveArrayCritical.
3417 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003418static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray jarr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003419 void* carray, jint mode)
3420{
3421 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003422 if (mode != JNI_COMMIT) {
3423 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
3424 unpinPrimitiveArray(arrayObj);
3425 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003426 JNI_EXIT();
3427}
3428
3429/*
3430 * Like GetStringChars, but with restricted use.
3431 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003432static const jchar* GetStringCritical(JNIEnv* env, jstring jstr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003433 jboolean* isCopy)
3434{
3435 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003436 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
3437 ArrayObject* strChars = dvmStringCharArray(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003438
Andy McFaddenab00d452009-08-19 07:21:41 -07003439 pinPrimitiveArray(strChars);
3440
3441 const u2* data = dvmStringChars(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003442 if (isCopy != NULL)
3443 *isCopy = JNI_FALSE;
3444
3445 JNI_EXIT();
3446 return (jchar*)data;
3447}
3448
3449/*
3450 * Like ReleaseStringChars, but with restricted use.
3451 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003452static void ReleaseStringCritical(JNIEnv* env, jstring jstr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003453 const jchar* carray)
3454{
3455 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003456 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
Andy McFadden0423f0e2009-08-26 07:21:53 -07003457 ArrayObject* strChars = dvmStringCharArray(strObj);
Andy McFaddenab00d452009-08-19 07:21:41 -07003458 unpinPrimitiveArray(strChars);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003459 JNI_EXIT();
3460}
3461
3462/*
3463 * Create a new weak global reference.
3464 */
3465static jweak NewWeakGlobalRef(JNIEnv* env, jobject obj)
3466{
3467 JNI_ENTER();
Andy McFaddenb18992f2009-09-25 10:42:15 -07003468 jweak wref = createWeakGlobalRef(env, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003469 JNI_EXIT();
Andy McFaddenb18992f2009-09-25 10:42:15 -07003470 return wref;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003471}
3472
3473/*
3474 * Delete the specified weak global reference.
3475 */
Andy McFaddenb18992f2009-09-25 10:42:15 -07003476static void DeleteWeakGlobalRef(JNIEnv* env, jweak wref)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003477{
3478 JNI_ENTER();
Andy McFaddenb18992f2009-09-25 10:42:15 -07003479 deleteWeakGlobalRef(env, wref);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003480 JNI_EXIT();
3481}
3482
3483/*
3484 * Quick check for pending exceptions.
3485 *
3486 * TODO: we should be able to skip the enter/exit macros here.
3487 */
3488static jboolean ExceptionCheck(JNIEnv* env)
3489{
3490 JNI_ENTER();
3491 bool result = dvmCheckException(_self);
3492 JNI_EXIT();
3493 return result;
3494}
3495
3496/*
3497 * Returns the type of the object referred to by "obj". It can be local,
3498 * global, or weak global.
3499 *
3500 * In the current implementation, references can be global and local at
3501 * the same time, so while the return value is accurate it may not tell
3502 * the whole story.
3503 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003504static jobjectRefType GetObjectRefType(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003505{
3506 JNI_ENTER();
Andy McFadden0423f0e2009-08-26 07:21:53 -07003507 jobjectRefType type = dvmGetJNIRefType(env, jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003508 JNI_EXIT();
3509 return type;
3510}
3511
3512/*
3513 * Allocate and return a new java.nio.ByteBuffer for this block of memory.
3514 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003515 * "address" may not be NULL, and "capacity" must be > 0. (These are only
3516 * verified when CheckJNI is enabled.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003517 */
Andy McFadden8e5c7842009-07-23 17:47:18 -07003518static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003519{
Andy McFadden8e5c7842009-07-23 17:47:18 -07003520 JNI_ENTER();
3521
3522 Thread* self = _self /*dvmThreadSelf()*/;
3523 Object* platformAddress = NULL;
3524 JValue callResult;
The Android Open Source Project99409882009-03-18 22:20:24 -07003525 jobject result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003526
Andy McFadden8e5c7842009-07-23 17:47:18 -07003527 /* get an instance of PlatformAddress that wraps the provided address */
3528 dvmCallMethod(self,
3529 gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on,
3530 NULL, &callResult, address);
3531 if (dvmGetException(self) != NULL || callResult.l == NULL)
The Android Open Source Project99409882009-03-18 22:20:24 -07003532 goto bail;
Andy McFadden8e5c7842009-07-23 17:47:18 -07003533
3534 /* don't let the GC discard it */
3535 platformAddress = (Object*) callResult.l;
3536 dvmAddTrackedAlloc(platformAddress, self);
3537 LOGV("tracking %p for address=%p\n", platformAddress, address);
3538
3539 /* create an instance of java.nio.ReadWriteDirectByteBuffer */
3540 ClassObject* clazz = gDvm.classJavaNioReadWriteDirectByteBuffer;
3541 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))
3542 goto bail;
3543 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
3544 if (newObj != NULL) {
3545 /* call the (PlatformAddress, int, int) constructor */
Andy McFaddenab00d452009-08-19 07:21:41 -07003546 result = addLocalReference(env, newObj);
Andy McFadden8e5c7842009-07-23 17:47:18 -07003547 dvmCallMethod(self, gDvm.methJavaNioReadWriteDirectByteBuffer_init,
3548 newObj, &callResult, platformAddress, (jint) capacity, (jint) 0);
Andy McFaddenab00d452009-08-19 07:21:41 -07003549 if (dvmGetException(self) != NULL) {
3550 deleteLocalReference(env, result);
3551 result = NULL;
Andy McFadden8e5c7842009-07-23 17:47:18 -07003552 goto bail;
Andy McFaddenab00d452009-08-19 07:21:41 -07003553 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003554 }
3555
The Android Open Source Project99409882009-03-18 22:20:24 -07003556bail:
Andy McFadden8e5c7842009-07-23 17:47:18 -07003557 if (platformAddress != NULL)
3558 dvmReleaseTrackedAlloc(platformAddress, self);
3559 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07003560 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003561}
3562
3563/*
3564 * Get the starting address of the buffer for the specified java.nio.Buffer.
3565 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003566 * If this is not a "direct" buffer, we return NULL.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003567 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003568static void* GetDirectBufferAddress(JNIEnv* env, jobject jbuf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003569{
Andy McFadden8e5c7842009-07-23 17:47:18 -07003570 JNI_ENTER();
3571
Andy McFaddenab00d452009-08-19 07:21:41 -07003572 Object* bufObj = dvmDecodeIndirectRef(env, jbuf);
Andy McFadden8e5c7842009-07-23 17:47:18 -07003573 Thread* self = _self /*dvmThreadSelf()*/;
Andy McFadden8e696dc2009-07-24 15:28:16 -07003574 void* result;
3575
3576 /*
3577 * All Buffer objects have an effectiveDirectAddress field. If it's
3578 * nonzero, we can just return that value. If not, we have to call
3579 * through DirectBuffer.getEffectiveAddress(), which as a side-effect
3580 * will set the effectiveDirectAddress field for direct buffers (and
3581 * things that wrap direct buffers).
3582 */
3583 result = (void*) dvmGetFieldInt(bufObj,
3584 gDvm.offJavaNioBuffer_effectiveDirectAddress);
3585 if (result != NULL) {
3586 //LOGI("fast path for %p\n", buf);
3587 goto bail;
3588 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003589
Andy McFadden72d61fb2009-06-24 16:56:06 -07003590 /*
3591 * Start by determining if the object supports the DirectBuffer
3592 * interfaces. Note this does not guarantee that it's a direct buffer.
3593 */
Andy McFadden8e5c7842009-07-23 17:47:18 -07003594 if (!dvmInstanceof(bufObj->clazz,
3595 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003596 {
The Android Open Source Project99409882009-03-18 22:20:24 -07003597 goto bail;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003598 }
3599
Andy McFadden72d61fb2009-06-24 16:56:06 -07003600 /*
Andy McFadden8e5c7842009-07-23 17:47:18 -07003601 * Get a PlatformAddress object with the effective address.
Andy McFadden5f612b82009-07-22 15:07:27 -07003602 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003603 * If this isn't a direct buffer, the result will be NULL and/or an
Andy McFadden72d61fb2009-06-24 16:56:06 -07003604 * exception will have been thrown.
3605 */
Andy McFadden8e696dc2009-07-24 15:28:16 -07003606 JValue callResult;
Andy McFadden8e5c7842009-07-23 17:47:18 -07003607 const Method* meth = dvmGetVirtualizedMethod(bufObj->clazz,
3608 gDvm.methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress);
Andy McFaddend5ab7262009-08-25 07:19:34 -07003609 dvmCallMethodA(self, meth, bufObj, false, &callResult, NULL);
Andy McFadden8e5c7842009-07-23 17:47:18 -07003610 if (dvmGetException(self) != NULL) {
3611 dvmClearException(self);
3612 callResult.l = NULL;
Andy McFadden72d61fb2009-06-24 16:56:06 -07003613 }
Andy McFadden8e5c7842009-07-23 17:47:18 -07003614
Andy McFadden8e696dc2009-07-24 15:28:16 -07003615 Object* platformAddr = callResult.l;
Andy McFadden72d61fb2009-06-24 16:56:06 -07003616 if (platformAddr == NULL) {
Andy McFadden8e696dc2009-07-24 15:28:16 -07003617 LOGV("Got request for address of non-direct buffer\n");
Andy McFadden72d61fb2009-06-24 16:56:06 -07003618 goto bail;
3619 }
3620
Andy McFadden8e5c7842009-07-23 17:47:18 -07003621 /*
3622 * Extract the address from the PlatformAddress object. Instead of
3623 * calling the toLong() method, just grab the field directly. This
3624 * is faster but more fragile.
3625 */
3626 result = (void*) dvmGetFieldInt(platformAddr,
3627 gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr);
The Android Open Source Project99409882009-03-18 22:20:24 -07003628
Andy McFadden8e696dc2009-07-24 15:28:16 -07003629 //LOGI("slow path for %p --> %p\n", buf, result);
3630
The Android Open Source Project99409882009-03-18 22:20:24 -07003631bail:
Andy McFadden8e5c7842009-07-23 17:47:18 -07003632 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07003633 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003634}
3635
3636/*
3637 * Get the capacity of the buffer for the specified java.nio.Buffer.
3638 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003639 * Returns -1 if the object is not a direct buffer. (We actually skip
3640 * this check, since it's expensive to determine, and just return the
3641 * capacity regardless.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003642 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003643static jlong GetDirectBufferCapacity(JNIEnv* env, jobject jbuf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003644{
Andy McFadden8e5c7842009-07-23 17:47:18 -07003645 JNI_ENTER();
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003646
Andy McFadden8e5c7842009-07-23 17:47:18 -07003647 /*
3648 * The capacity is always in the Buffer.capacity field.
3649 *
3650 * (The "check" version should verify that this is actually a Buffer,
3651 * but we're not required to do so here.)
3652 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003653 Object* buf = dvmDecodeIndirectRef(env, jbuf);
3654 jlong result = dvmGetFieldInt(buf, gDvm.offJavaNioBuffer_capacity);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003655
Andy McFadden8e5c7842009-07-23 17:47:18 -07003656 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07003657 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003658}
3659
3660
3661/*
3662 * ===========================================================================
3663 * JNI invocation functions
3664 * ===========================================================================
3665 */
3666
3667/*
3668 * Handle AttachCurrentThread{AsDaemon}.
3669 *
3670 * We need to make sure the VM is actually running. For example, if we start
3671 * up, issue an Attach, and the VM exits almost immediately, by the time the
3672 * attaching happens the VM could already be shutting down.
3673 *
3674 * It's hard to avoid a race condition here because we don't want to hold
3675 * a lock across the entire operation. What we can do is temporarily
3676 * increment the thread count to prevent a VM exit.
3677 *
3678 * This could potentially still have problems if a daemon thread calls here
3679 * while the VM is shutting down. dvmThreadSelf() will work, since it just
3680 * uses pthread TLS, but dereferencing "vm" could fail. Such is life when
3681 * you shut down a VM while threads are still running inside it.
3682 *
3683 * Remember that some code may call this as a way to find the per-thread
3684 * JNIEnv pointer. Don't do excess work for that case.
3685 */
3686static jint attachThread(JavaVM* vm, JNIEnv** p_env, void* thr_args,
3687 bool isDaemon)
3688{
3689 JavaVMAttachArgs* args = (JavaVMAttachArgs*) thr_args;
3690 Thread* self;
3691 bool result = false;
3692
3693 /*
3694 * Return immediately if we're already one with the VM.
3695 */
3696 self = dvmThreadSelf();
3697 if (self != NULL) {
3698 *p_env = self->jniEnv;
3699 return JNI_OK;
3700 }
3701
3702 /*
3703 * No threads allowed in zygote mode.
3704 */
3705 if (gDvm.zygote) {
3706 return JNI_ERR;
3707 }
3708
3709 /* increment the count to keep the VM from bailing while we run */
3710 dvmLockThreadList(NULL);
3711 if (gDvm.nonDaemonThreadCount == 0) {
3712 // dead or dying
3713 LOGV("Refusing to attach thread '%s' -- VM is shutting down\n",
3714 (thr_args == NULL) ? "(unknown)" : args->name);
3715 dvmUnlockThreadList();
3716 return JNI_ERR;
3717 }
3718 gDvm.nonDaemonThreadCount++;
3719 dvmUnlockThreadList();
3720
3721 /* tweak the JavaVMAttachArgs as needed */
3722 JavaVMAttachArgs argsCopy;
3723 if (args == NULL) {
3724 /* allow the v1.1 calling convention */
3725 argsCopy.version = JNI_VERSION_1_2;
3726 argsCopy.name = NULL;
3727 argsCopy.group = dvmGetMainThreadGroup();
3728 } else {
3729 assert(args->version >= JNI_VERSION_1_2);
3730
3731 argsCopy.version = args->version;
3732 argsCopy.name = args->name;
3733 if (args->group != NULL)
3734 argsCopy.group = args->group;
3735 else
3736 argsCopy.group = dvmGetMainThreadGroup();
3737 }
3738
3739 result = dvmAttachCurrentThread(&argsCopy, isDaemon);
3740
3741 /* restore the count */
3742 dvmLockThreadList(NULL);
3743 gDvm.nonDaemonThreadCount--;
3744 dvmUnlockThreadList();
3745
3746 /*
3747 * Change the status to indicate that we're out in native code. This
3748 * call is not guarded with state-change macros, so we have to do it
3749 * by hand.
3750 */
3751 if (result) {
3752 self = dvmThreadSelf();
3753 assert(self != NULL);
3754 dvmChangeStatus(self, THREAD_NATIVE);
3755 *p_env = self->jniEnv;
3756 return JNI_OK;
3757 } else {
3758 return JNI_ERR;
3759 }
3760}
3761
3762/*
3763 * Attach the current thread to the VM. If the thread is already attached,
3764 * this is a no-op.
3765 */
3766static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args)
3767{
3768 return attachThread(vm, p_env, thr_args, false);
3769}
3770
3771/*
3772 * Like AttachCurrentThread, but set the "daemon" flag.
3773 */
3774static jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env,
3775 void* thr_args)
3776{
3777 return attachThread(vm, p_env, thr_args, true);
3778}
3779
3780/*
3781 * Dissociate the current thread from the VM.
3782 */
3783static jint DetachCurrentThread(JavaVM* vm)
3784{
3785 Thread* self = dvmThreadSelf();
3786
3787 if (self == NULL) /* not attached, can't do anything */
3788 return JNI_ERR;
3789
3790 /* switch to "running" to check for suspension */
3791 dvmChangeStatus(self, THREAD_RUNNING);
3792
3793 /* detach the thread */
3794 dvmDetachCurrentThread();
3795
3796 /* (no need to change status back -- we have no status) */
3797 return JNI_OK;
3798}
3799
3800/*
3801 * If current thread is attached to VM, return the associated JNIEnv.
3802 * Otherwise, stuff NULL in and return JNI_EDETACHED.
3803 *
3804 * JVMTI overloads this by specifying a magic value for "version", so we
3805 * do want to check that here.
3806 */
3807static jint GetEnv(JavaVM* vm, void** env, jint version)
3808{
3809 Thread* self = dvmThreadSelf();
3810
3811 if (version < JNI_VERSION_1_1 || version > JNI_VERSION_1_6)
3812 return JNI_EVERSION;
3813
3814 if (self == NULL) {
3815 *env = NULL;
3816 } else {
3817 /* TODO: status change is probably unnecessary */
3818 dvmChangeStatus(self, THREAD_RUNNING);
3819 *env = (void*) dvmGetThreadJNIEnv(self);
3820 dvmChangeStatus(self, THREAD_NATIVE);
3821 }
3822 if (*env == NULL)
3823 return JNI_EDETACHED;
3824 else
3825 return JNI_OK;
3826}
3827
3828/*
3829 * Destroy the VM. This may be called from any thread.
3830 *
3831 * If the current thread is attached, wait until the current thread is
3832 * the only non-daemon user-level thread. If the current thread is not
3833 * attached, we attach it and do the processing as usual. (If the attach
3834 * fails, it's probably because all the non-daemon threads have already
3835 * exited and the VM doesn't want to let us back in.)
3836 *
3837 * TODO: we don't really deal with the situation where more than one thread
3838 * has called here. One thread wins, the other stays trapped waiting on
3839 * the condition variable forever. Not sure this situation is interesting
3840 * in real life.
3841 */
3842static jint DestroyJavaVM(JavaVM* vm)
3843{
3844 JavaVMExt* ext = (JavaVMExt*) vm;
3845 Thread* self;
3846
3847 if (ext == NULL)
3848 return JNI_ERR;
3849
3850 LOGD("DestroyJavaVM waiting for non-daemon threads to exit\n");
3851
3852 /*
3853 * Sleep on a condition variable until it's okay to exit.
3854 */
3855 self = dvmThreadSelf();
3856 if (self == NULL) {
3857 JNIEnv* tmpEnv;
3858 if (AttachCurrentThread(vm, &tmpEnv, NULL) != JNI_OK) {
3859 LOGV("Unable to reattach main for Destroy; assuming VM is "
3860 "shutting down (count=%d)\n",
3861 gDvm.nonDaemonThreadCount);
3862 goto shutdown;
3863 } else {
3864 LOGV("Attached to wait for shutdown in Destroy\n");
3865 }
3866 }
3867 dvmChangeStatus(self, THREAD_VMWAIT);
3868
3869 dvmLockThreadList(self);
3870 gDvm.nonDaemonThreadCount--; // remove current thread from count
3871
3872 while (gDvm.nonDaemonThreadCount > 0)
3873 pthread_cond_wait(&gDvm.vmExitCond, &gDvm.threadListLock);
3874
3875 dvmUnlockThreadList();
3876 self = NULL;
3877
3878shutdown:
3879 // TODO: call System.exit() to run any registered shutdown hooks
3880 // (this may not return -- figure out how this should work)
3881
3882 LOGD("DestroyJavaVM shutting VM down\n");
3883 dvmShutdown();
3884
3885 // TODO - free resources associated with JNI-attached daemon threads
3886 free(ext->envList);
3887 free(ext);
3888
3889 return JNI_OK;
3890}
3891
3892
3893/*
3894 * ===========================================================================
3895 * Function tables
3896 * ===========================================================================
3897 */
3898
3899static const struct JNINativeInterface gNativeInterface = {
3900 NULL,
3901 NULL,
3902 NULL,
3903 NULL,
3904
3905 GetVersion,
3906
3907 DefineClass,
3908 FindClass,
3909
3910 FromReflectedMethod,
3911 FromReflectedField,
3912 ToReflectedMethod,
3913
3914 GetSuperclass,
3915 IsAssignableFrom,
3916
3917 ToReflectedField,
3918
3919 Throw,
3920 ThrowNew,
3921 ExceptionOccurred,
3922 ExceptionDescribe,
3923 ExceptionClear,
3924 FatalError,
3925
3926 PushLocalFrame,
3927 PopLocalFrame,
3928
3929 NewGlobalRef,
3930 DeleteGlobalRef,
3931 DeleteLocalRef,
3932 IsSameObject,
3933 NewLocalRef,
3934 EnsureLocalCapacity,
3935
3936 AllocObject,
3937 NewObject,
3938 NewObjectV,
3939 NewObjectA,
3940
3941 GetObjectClass,
3942 IsInstanceOf,
3943
3944 GetMethodID,
3945
3946 CallObjectMethod,
3947 CallObjectMethodV,
3948 CallObjectMethodA,
3949 CallBooleanMethod,
3950 CallBooleanMethodV,
3951 CallBooleanMethodA,
3952 CallByteMethod,
3953 CallByteMethodV,
3954 CallByteMethodA,
3955 CallCharMethod,
3956 CallCharMethodV,
3957 CallCharMethodA,
3958 CallShortMethod,
3959 CallShortMethodV,
3960 CallShortMethodA,
3961 CallIntMethod,
3962 CallIntMethodV,
3963 CallIntMethodA,
3964 CallLongMethod,
3965 CallLongMethodV,
3966 CallLongMethodA,
3967 CallFloatMethod,
3968 CallFloatMethodV,
3969 CallFloatMethodA,
3970 CallDoubleMethod,
3971 CallDoubleMethodV,
3972 CallDoubleMethodA,
3973 CallVoidMethod,
3974 CallVoidMethodV,
3975 CallVoidMethodA,
3976
3977 CallNonvirtualObjectMethod,
3978 CallNonvirtualObjectMethodV,
3979 CallNonvirtualObjectMethodA,
3980 CallNonvirtualBooleanMethod,
3981 CallNonvirtualBooleanMethodV,
3982 CallNonvirtualBooleanMethodA,
3983 CallNonvirtualByteMethod,
3984 CallNonvirtualByteMethodV,
3985 CallNonvirtualByteMethodA,
3986 CallNonvirtualCharMethod,
3987 CallNonvirtualCharMethodV,
3988 CallNonvirtualCharMethodA,
3989 CallNonvirtualShortMethod,
3990 CallNonvirtualShortMethodV,
3991 CallNonvirtualShortMethodA,
3992 CallNonvirtualIntMethod,
3993 CallNonvirtualIntMethodV,
3994 CallNonvirtualIntMethodA,
3995 CallNonvirtualLongMethod,
3996 CallNonvirtualLongMethodV,
3997 CallNonvirtualLongMethodA,
3998 CallNonvirtualFloatMethod,
3999 CallNonvirtualFloatMethodV,
4000 CallNonvirtualFloatMethodA,
4001 CallNonvirtualDoubleMethod,
4002 CallNonvirtualDoubleMethodV,
4003 CallNonvirtualDoubleMethodA,
4004 CallNonvirtualVoidMethod,
4005 CallNonvirtualVoidMethodV,
4006 CallNonvirtualVoidMethodA,
4007
4008 GetFieldID,
4009
4010 GetObjectField,
4011 GetBooleanField,
4012 GetByteField,
4013 GetCharField,
4014 GetShortField,
4015 GetIntField,
4016 GetLongField,
4017 GetFloatField,
4018 GetDoubleField,
4019 SetObjectField,
4020 SetBooleanField,
4021 SetByteField,
4022 SetCharField,
4023 SetShortField,
4024 SetIntField,
4025 SetLongField,
4026 SetFloatField,
4027 SetDoubleField,
4028
4029 GetStaticMethodID,
4030
4031 CallStaticObjectMethod,
4032 CallStaticObjectMethodV,
4033 CallStaticObjectMethodA,
4034 CallStaticBooleanMethod,
4035 CallStaticBooleanMethodV,
4036 CallStaticBooleanMethodA,
4037 CallStaticByteMethod,
4038 CallStaticByteMethodV,
4039 CallStaticByteMethodA,
4040 CallStaticCharMethod,
4041 CallStaticCharMethodV,
4042 CallStaticCharMethodA,
4043 CallStaticShortMethod,
4044 CallStaticShortMethodV,
4045 CallStaticShortMethodA,
4046 CallStaticIntMethod,
4047 CallStaticIntMethodV,
4048 CallStaticIntMethodA,
4049 CallStaticLongMethod,
4050 CallStaticLongMethodV,
4051 CallStaticLongMethodA,
4052 CallStaticFloatMethod,
4053 CallStaticFloatMethodV,
4054 CallStaticFloatMethodA,
4055 CallStaticDoubleMethod,
4056 CallStaticDoubleMethodV,
4057 CallStaticDoubleMethodA,
4058 CallStaticVoidMethod,
4059 CallStaticVoidMethodV,
4060 CallStaticVoidMethodA,
4061
4062 GetStaticFieldID,
4063
4064 GetStaticObjectField,
4065 GetStaticBooleanField,
4066 GetStaticByteField,
4067 GetStaticCharField,
4068 GetStaticShortField,
4069 GetStaticIntField,
4070 GetStaticLongField,
4071 GetStaticFloatField,
4072 GetStaticDoubleField,
4073
4074 SetStaticObjectField,
4075 SetStaticBooleanField,
4076 SetStaticByteField,
4077 SetStaticCharField,
4078 SetStaticShortField,
4079 SetStaticIntField,
4080 SetStaticLongField,
4081 SetStaticFloatField,
4082 SetStaticDoubleField,
4083
4084 NewString,
4085
4086 GetStringLength,
4087 GetStringChars,
4088 ReleaseStringChars,
4089
4090 NewStringUTF,
4091 GetStringUTFLength,
4092 GetStringUTFChars,
4093 ReleaseStringUTFChars,
4094
4095 GetArrayLength,
4096 NewObjectArray,
4097 GetObjectArrayElement,
4098 SetObjectArrayElement,
4099
4100 NewBooleanArray,
4101 NewByteArray,
4102 NewCharArray,
4103 NewShortArray,
4104 NewIntArray,
4105 NewLongArray,
4106 NewFloatArray,
4107 NewDoubleArray,
4108
4109 GetBooleanArrayElements,
4110 GetByteArrayElements,
4111 GetCharArrayElements,
4112 GetShortArrayElements,
4113 GetIntArrayElements,
4114 GetLongArrayElements,
4115 GetFloatArrayElements,
4116 GetDoubleArrayElements,
4117
4118 ReleaseBooleanArrayElements,
4119 ReleaseByteArrayElements,
4120 ReleaseCharArrayElements,
4121 ReleaseShortArrayElements,
4122 ReleaseIntArrayElements,
4123 ReleaseLongArrayElements,
4124 ReleaseFloatArrayElements,
4125 ReleaseDoubleArrayElements,
4126
4127 GetBooleanArrayRegion,
4128 GetByteArrayRegion,
4129 GetCharArrayRegion,
4130 GetShortArrayRegion,
4131 GetIntArrayRegion,
4132 GetLongArrayRegion,
4133 GetFloatArrayRegion,
4134 GetDoubleArrayRegion,
4135 SetBooleanArrayRegion,
4136 SetByteArrayRegion,
4137 SetCharArrayRegion,
4138 SetShortArrayRegion,
4139 SetIntArrayRegion,
4140 SetLongArrayRegion,
4141 SetFloatArrayRegion,
4142 SetDoubleArrayRegion,
4143
4144 RegisterNatives,
4145 UnregisterNatives,
4146
4147 MonitorEnter,
4148 MonitorExit,
4149
4150 GetJavaVM,
4151
4152 GetStringRegion,
4153 GetStringUTFRegion,
4154
4155 GetPrimitiveArrayCritical,
4156 ReleasePrimitiveArrayCritical,
4157
4158 GetStringCritical,
4159 ReleaseStringCritical,
4160
4161 NewWeakGlobalRef,
4162 DeleteWeakGlobalRef,
4163
4164 ExceptionCheck,
4165
4166 NewDirectByteBuffer,
4167 GetDirectBufferAddress,
4168 GetDirectBufferCapacity,
4169
4170 GetObjectRefType
4171};
4172static const struct JNIInvokeInterface gInvokeInterface = {
4173 NULL,
4174 NULL,
4175 NULL,
4176
4177 DestroyJavaVM,
4178 AttachCurrentThread,
4179 DetachCurrentThread,
4180
4181 GetEnv,
4182
4183 AttachCurrentThreadAsDaemon,
4184};
4185
4186
4187/*
4188 * ===========================================================================
4189 * VM/Env creation
4190 * ===========================================================================
4191 */
4192
4193/*
4194 * Enable "checked JNI" after the VM has partially started. This must
4195 * only be called in "zygote" mode, when we have one thread running.
Andy McFadden59b61772009-05-13 16:44:34 -07004196 *
4197 * This doesn't attempt to rewrite the JNI call bridge associated with
4198 * native methods, so we won't get those checks for any methods that have
4199 * already been resolved.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08004200 */
4201void dvmLateEnableCheckedJni(void)
4202{
4203 JNIEnvExt* extEnv;
4204 JavaVMExt* extVm;
Andy McFaddenab00d452009-08-19 07:21:41 -07004205
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08004206 extEnv = dvmGetJNIEnvForThread();
4207 if (extEnv == NULL) {
4208 LOGE("dvmLateEnableCheckedJni: thread has no JNIEnv\n");
4209 return;
4210 }
4211 extVm = extEnv->vm;
4212 assert(extVm != NULL);
4213
4214 if (!extVm->useChecked) {
4215 LOGD("Late-enabling CheckJNI\n");
4216 dvmUseCheckedJniVm(extVm);
4217 extVm->useChecked = true;
4218 dvmUseCheckedJniEnv(extEnv);
4219
4220 /* currently no way to pick up jniopts features */
4221 } else {
4222 LOGD("Not late-enabling CheckJNI (already on)\n");
4223 }
4224}
4225
4226/*
4227 * Not supported.
4228 */
4229jint JNI_GetDefaultJavaVMInitArgs(void* vm_args)
4230{
4231 return JNI_ERR;
4232}
4233
4234/*
4235 * Return a buffer full of created VMs.
4236 *
4237 * We always have zero or one.
4238 */
4239jint JNI_GetCreatedJavaVMs(JavaVM** vmBuf, jsize bufLen, jsize* nVMs)
4240{
4241 if (gDvm.vmList != NULL) {
4242 *nVMs = 1;
4243
4244 if (bufLen > 0)
4245 *vmBuf++ = gDvm.vmList;
4246 } else {
4247 *nVMs = 0;
4248 }
4249
4250 return JNI_OK;
4251}
4252
4253
4254/*
4255 * Create a new VM instance.
4256 *
4257 * The current thread becomes the main VM thread. We return immediately,
4258 * which effectively means the caller is executing in a native method.
4259 */
4260jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args)
4261{
4262 const JavaVMInitArgs* args = (JavaVMInitArgs*) vm_args;
4263 JNIEnvExt* pEnv = NULL;
4264 JavaVMExt* pVM = NULL;
4265 const char** argv;
4266 int argc = 0;
4267 int i, curOpt;
4268 int result = JNI_ERR;
4269 bool checkJni = false;
4270 bool warnError = true;
4271 bool forceDataCopy = false;
4272
4273 if (args->version < JNI_VERSION_1_2)
4274 return JNI_EVERSION;
4275
4276 // TODO: don't allow creation of multiple VMs -- one per customer for now
4277
4278 /* zero globals; not strictly necessary the first time a VM is started */
4279 memset(&gDvm, 0, sizeof(gDvm));
4280
4281 /*
4282 * Set up structures for JNIEnv and VM.
4283 */
4284 //pEnv = (JNIEnvExt*) malloc(sizeof(JNIEnvExt));
4285 pVM = (JavaVMExt*) malloc(sizeof(JavaVMExt));
4286
4287 //memset(pEnv, 0, sizeof(JNIEnvExt));
4288 //pEnv->funcTable = &gNativeInterface;
4289 //pEnv->vm = pVM;
4290 memset(pVM, 0, sizeof(JavaVMExt));
4291 pVM->funcTable = &gInvokeInterface;
4292 pVM->envList = pEnv;
4293 dvmInitMutex(&pVM->envListLock);
4294
4295 argv = (const char**) malloc(sizeof(char*) * (args->nOptions));
4296 memset(argv, 0, sizeof(char*) * (args->nOptions));
4297
4298 curOpt = 0;
4299
4300 /*
4301 * Convert JNI args to argv.
4302 *
4303 * We have to pull out vfprintf/exit/abort, because they use the
4304 * "extraInfo" field to pass function pointer "hooks" in. We also
4305 * look for the -Xcheck:jni stuff here.
4306 */
4307 for (i = 0; i < args->nOptions; i++) {
4308 const char* optStr = args->options[i].optionString;
4309
4310 if (optStr == NULL) {
4311 fprintf(stderr, "ERROR: arg %d string was null\n", i);
4312 goto bail;
4313 } else if (strcmp(optStr, "vfprintf") == 0) {
4314 gDvm.vfprintfHook = args->options[i].extraInfo;
4315 } else if (strcmp(optStr, "exit") == 0) {
4316 gDvm.exitHook = args->options[i].extraInfo;
4317 } else if (strcmp(optStr, "abort") == 0) {
4318 gDvm.abortHook = args->options[i].extraInfo;
4319 } else if (strcmp(optStr, "-Xcheck:jni") == 0) {
4320 checkJni = true;
4321 } else if (strncmp(optStr, "-Xjniopts:", 10) == 0) {
4322 const char* jniOpts = optStr + 9;
4323 while (jniOpts != NULL) {
4324 jniOpts++; /* skip past ':' or ',' */
4325 if (strncmp(jniOpts, "warnonly", 8) == 0) {
4326 warnError = false;
4327 } else if (strncmp(jniOpts, "forcecopy", 9) == 0) {
4328 forceDataCopy = true;
4329 } else {
4330 LOGW("unknown jni opt starting at '%s'\n", jniOpts);
4331 }
4332 jniOpts = strchr(jniOpts, ',');
4333 }
4334 } else {
4335 /* regular option */
4336 argv[curOpt++] = optStr;
4337 }
4338 }
4339 argc = curOpt;
4340
4341 if (checkJni) {
4342 dvmUseCheckedJniVm(pVM);
4343 pVM->useChecked = true;
4344 }
4345 pVM->warnError = warnError;
4346 pVM->forceDataCopy = forceDataCopy;
4347
4348 /* set this up before initializing VM, so it can create some JNIEnvs */
4349 gDvm.vmList = (JavaVM*) pVM;
4350
4351 /*
4352 * Create an env for main thread. We need to have something set up
4353 * here because some of the class initialization we do when starting
4354 * up the VM will call into native code.
4355 */
4356 pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL);
4357
4358 /* initialize VM */
4359 gDvm.initializing = true;
4360 if (dvmStartup(argc, argv, args->ignoreUnrecognized, (JNIEnv*)pEnv) != 0) {
4361 free(pEnv);
4362 free(pVM);
4363 goto bail;
4364 }
4365
4366 /*
4367 * Success! Return stuff to caller.
4368 */
4369 dvmChangeStatus(NULL, THREAD_NATIVE);
4370 *p_env = (JNIEnv*) pEnv;
4371 *p_vm = (JavaVM*) pVM;
4372 result = JNI_OK;
4373
4374bail:
4375 gDvm.initializing = false;
4376 if (result == JNI_OK)
4377 LOGV("JNI_CreateJavaVM succeeded\n");
4378 else
4379 LOGW("JNI_CreateJavaVM failed\n");
4380 free(argv);
4381 return result;
4382}