blob: 10afcaa3314d4aaec7efb0fd679013e0cde90da3 [file] [log] [blame]
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Andy McFadden59b61772009-05-13 16:44:34 -070016
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080017/*
18 * Dalvik implementation of JNI interfaces.
19 */
20#include "Dalvik.h"
21#include "JniInternal.h"
22
23#include <stdlib.h>
24#include <stdarg.h>
25#include <limits.h>
26
27/*
28Native methods and interaction with the GC
29
30All JNI methods must start by changing their thread status to
31THREAD_RUNNING, and finish by changing it back to THREAD_NATIVE before
32returning to native code. The switch to "running" triggers a thread
33suspension check.
34
35With a rudimentary GC we should be able to skip the status change for
36simple functions, e.g. IsSameObject, GetJavaVM, GetStringLength, maybe
37even access to fields with primitive types. Our options are more limited
38with a compacting GC, so we should replace JNI_ENTER with JNI_ENTER_NCGC
39or somesuch on the "lite" functions if we want to try this optimization.
40
41For performance reasons we do as little error-checking as possible here.
42For example, we don't check to make sure the correct type of Object is
43passed in when setting a field, and we don't prevent you from storing
44new values in a "final" field. Such things are best handled in the
45"check" version. For actions that are common, dangerous, and must be
46checked at runtime, such as array bounds checks, we do the tests here.
47
48
49General notes on local/global reference tracking
50
Andy McFadden0083d372009-08-21 14:44:04 -070051JNI provides explicit control over natively-held references that the GC
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080052needs to know about. These can be local, in which case they're released
Andy McFadden0083d372009-08-21 14:44:04 -070053when the native method returns into the VM, or global, which are held
Andy McFaddend5ab7262009-08-25 07:19:34 -070054until explicitly released. (There are also weak-global references,
55which have the lifespan and visibility of global references, but the
56object they refer to may be collected.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080057
Andy McFadden0083d372009-08-21 14:44:04 -070058The references can be created with explicit JNI NewLocalRef / NewGlobalRef
59calls. The former is very unusual, the latter is reasonably common
60(e.g. for caching references to class objects).
61
62Local references are most often created as a side-effect of JNI functions.
63For example, the AllocObject/NewObject functions must create local
64references to the objects returned, because nothing else in the GC root
65set has a reference to the new objects.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080066
67The most common mode of operation is for a method to create zero or
68more local references and return. Explicit "local delete" operations
69are expected to be exceedingly rare, except when walking through an
70object array, and the Push/PopLocalFrame calls are expected to be used
71infrequently. For efficient operation, we want to add new local refs
72with a simple store/increment operation; to avoid infinite growth in
73pathological situations, we need to reclaim the space used by deleted
74entries.
75
Andy McFaddend5ab7262009-08-25 07:19:34 -070076If we just want to maintain a list for the GC root set, we can use an
77expanding append-only array that compacts when objects are deleted.
78In typical situations, e.g. running through an array of objects, we will
79be deleting one of the most recently added entries, so we can minimize
80the number of elements moved (or avoid having to move any).
81
82If we want to conceal the pointer values from native code, which is
83necessary to allow the GC to move JNI-referenced objects around, then we
84have to use a more complicated indirection mechanism.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080085
86The spec says, "Local references are only valid in the thread in which
87they are created. The native code must not pass local references from
Andy McFadden0083d372009-08-21 14:44:04 -070088one thread to another."
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080089
90
Andy McFaddend5ab7262009-08-25 07:19:34 -070091Pinned objects
92
93For some large chunks of data, notably primitive arrays and String data,
94JNI allows the VM to choose whether it wants to pin the array object or
95make a copy. We currently pin the memory for better execution performance.
96
97TODO: we're using simple root set references to pin primitive array data,
98because they have the property we need (i.e. the pointer we return is
99guaranteed valid until we explicitly release it). However, if we have a
100compacting GC and don't want to pin all memory held by all global refs,
101we need to treat these differently.
102
103
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800104Global reference tracking
105
106There should be a small "active" set centered around the most-recently
Andy McFaddend5ab7262009-08-25 07:19:34 -0700107added items.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800108
Andy McFaddend5ab7262009-08-25 07:19:34 -0700109Because it's global, access to it has to be synchronized. Additions and
110removals require grabbing a mutex. If the table serves as an indirection
111mechanism (i.e. it's not just a list for the benefit of the garbage
112collector), reference lookups may also require grabbing a mutex.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800113
114The JNI spec does not define any sort of limit, so the list must be able
Andy McFaddend5ab7262009-08-25 07:19:34 -0700115to expand to a reasonable size. It may be useful to log significant
116increases in usage to help identify resource leaks.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800117
Andy McFaddend5ab7262009-08-25 07:19:34 -0700118
119Weak-global reference tracking
120
121[TBD]
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800122
123
124Local reference tracking
125
126The table of local references can be stored on the interpreted stack or
127in a parallel data structure (one per thread).
128
129*** Approach #1: use the interpreted stack
130
131The easiest place to tuck it is between the frame ptr and the first saved
132register, which is always in0. (See the ASCII art in Stack.h.) We can
133shift the "VM-specific goop" and frame ptr down, effectively inserting
134the JNI local refs in the space normally occupied by local variables.
135
136(Three things are accessed from the frame pointer:
137 (1) framePtr[N] is register vN, used to get at "ins" and "locals".
138 (2) framePtr - sizeof(StackSaveArea) is the VM frame goop.
139 (3) framePtr - sizeof(StackSaveArea) - numOuts is where the "outs" go.
140The only thing that isn't determined by an offset from the current FP
141is the previous frame. However, tucking things below the previous frame
142can be problematic because the "outs" of the previous frame overlap with
143the "ins" of the current frame. If the "ins" are altered they must be
144restored before we return. For a native method call, the easiest and
145safest thing to disrupt is #1, because there are no locals and the "ins"
146are all copied to the native stack.)
147
148We can implement Push/PopLocalFrame with the existing stack frame calls,
149making sure we copy some goop from the previous frame (notably the method
150ptr, so that dvmGetCurrentJNIMethod() doesn't require extra effort).
151
152We can pre-allocate the storage at the time the stack frame is first
153set up, but we have to be careful. When calling from interpreted code
154the frame ptr points directly at the arguments we're passing, but we can
155offset the args pointer when calling the native bridge.
156
157To manage the local ref collection, we need to be able to find three
158things: (1) the start of the region, (2) the end of the region, and (3)
159the next available entry. The last is only required for quick adds.
160We currently have two easily-accessible pointers, the current FP and the
161previous frame's FP. (The "stack pointer" shown in the ASCII art doesn't
162actually exist in the interpreted world.)
163
164We can't use the current FP to find the first "in", because we want to
165insert the variable-sized local refs table between them. It's awkward
166to use the previous frame's FP because native methods invoked via
167dvmCallMethod() or dvmInvokeMethod() don't have "ins", but native methods
168invoked from interpreted code do. We can either track the local refs
169table size with a field in the stack frame, or insert unnecessary items
170so that all native stack frames have "ins".
171
172Assuming we can find the region bounds, we still need pointer #3
173for an efficient implementation. This can be stored in an otherwise
174unused-for-native field in the frame goop.
175
176When we run out of room we have to make more space. If we start allocating
177locals immediately below in0 and grow downward, we will detect end-of-space
178by running into the current frame's FP. We then memmove() the goop down
179(memcpy if we guarantee the additional size is larger than the frame).
180This is nice because we only have to move sizeof(StackSaveArea) bytes
181each time.
182
183Stack walking should be okay so long as nothing tries to access the
184"ins" by an offset from the FP. In theory the "ins" could be read by
185the debugger or SIGQUIT handler looking for "this" or other arguments,
186but in practice this behavior isn't expected to work for native methods,
187so we can simply disallow it.
188
189A conservative GC can just scan the entire stack from top to bottom to find
190all references. An exact GC will need to understand the actual layout.
191
192*** Approach #2: use a parallel stack
193
Andy McFaddend5ab7262009-08-25 07:19:34 -0700194Each Thread/JNIEnv points to a reference table. The struct has
195a system-heap-allocated array of references and a pointer to the
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800196next-available entry ("nextEntry").
197
Andy McFaddend5ab7262009-08-25 07:19:34 -0700198Each stack frame has a pointer to what it sees as the "bottom" element
199in the array (we can double-up the "currentPc" field). This is set to
200"nextEntry" when the frame is pushed on. As local references are added
201or removed, "nextEntry" is updated.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800202
203We implement Push/PopLocalFrame with actual stack frames. Before a JNI
204frame gets popped, we set "nextEntry" to the "top" pointer of the current
205frame, effectively releasing the references.
206
207The GC will scan all references from the start of the table to the
208"nextEntry" pointer.
209
210*** Comparison
211
212All approaches will return a failure result when they run out of local
213reference space. For #1 that means blowing out the stack, for #2 it's
214running out of room in the array.
215
216Compared to #1, approach #2:
217 - Needs only one pointer in the stack frame goop.
218 - Makes pre-allocating storage unnecessary.
219 - Doesn't contend with interpreted stack depth for space. In most
220 cases, if something blows out the local ref storage, it's because the
221 JNI code was misbehaving rather than called from way down.
222 - Allows the GC to do a linear scan per thread in a buffer that is 100%
223 references. The GC can be slightly less smart when scanning the stack.
224 - Will be easier to work with if we combine native and interpeted stacks.
225
226 - Isn't as clean, especially when popping frames, since we have to do
227 explicit work. Fortunately we only have to do it when popping native
228 method calls off, so it doesn't add overhead to interpreted code paths.
229 - Is awkward to expand dynamically. We'll want to pre-allocate the full
230 amount of space; this is fine, since something on the order of 1KB should
231 be plenty. The JNI spec allows us to limit this.
232 - Requires the GC to scan even more memory. With the references embedded
233 in the stack we get better locality of reference.
234
235*/
236
Andy McFadden5f612b82009-07-22 15:07:27 -0700237/* fwd */
238static const struct JNINativeInterface gNativeInterface;
Andy McFaddenab00d452009-08-19 07:21:41 -0700239static jobject addGlobalReference(Object* obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800240
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800241#ifdef WITH_JNI_STACK_CHECK
242# define COMPUTE_STACK_SUM(_self) computeStackSum(_self);
243# define CHECK_STACK_SUM(_self) checkStackSum(_self);
Andy McFaddend5ab7262009-08-25 07:19:34 -0700244//static void computeStackSum(Thread* self);
245//static void checkStackSum(Thread* self);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800246#else
247# define COMPUTE_STACK_SUM(_self) ((void)0)
248# define CHECK_STACK_SUM(_self) ((void)0)
249#endif
250
251
252/*
253 * ===========================================================================
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800254 * Utility functions
255 * ===========================================================================
256 */
257
258/*
259 * Entry/exit processing for all JNI calls.
260 *
261 * If TRUSTED_JNIENV is set, we get to skip the (curiously expensive)
262 * thread-local storage lookup on our Thread*. If the caller has passed
263 * the wrong JNIEnv in, we're going to be accessing unsynchronized
264 * structures from more than one thread, and things are going to fail
265 * in bizarre ways. This is only sensible if the native code has been
266 * fully exercised with CheckJNI enabled.
267 */
268#define TRUSTED_JNIENV
269#ifdef TRUSTED_JNIENV
270# define JNI_ENTER() \
271 Thread* _self = ((JNIEnvExt*)env)->self; \
272 CHECK_STACK_SUM(_self); \
273 dvmChangeStatus(_self, THREAD_RUNNING)
274#else
275# define JNI_ENTER() \
276 Thread* _self = dvmThreadSelf(); \
277 UNUSED_PARAMETER(env); \
278 CHECK_STACK_SUM(_self); \
279 dvmChangeStatus(_self, THREAD_RUNNING)
280#endif
281#define JNI_EXIT() \
282 dvmChangeStatus(_self, THREAD_NATIVE); \
283 COMPUTE_STACK_SUM(_self)
284
285#define kGlobalRefsTableInitialSize 512
Andy McFaddenab00d452009-08-19 07:21:41 -0700286#define kGlobalRefsTableMaxSize 51200 /* arbitrary, must be < 64K */
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800287#define kGrefWaterInterval 100
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800288#define kTrackGrefUsage true
289
Andy McFaddenc26bb632009-08-21 12:01:31 -0700290#define kPinTableInitialSize 16
291#define kPinTableMaxSize 1024
292#define kPinComplainThreshold 10
293
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800294/*
Andy McFadden5f612b82009-07-22 15:07:27 -0700295 * Allocate the global references table, and look up some classes for
296 * the benefit of direct buffer access.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800297 */
298bool dvmJniStartup(void)
299{
Andy McFaddend5ab7262009-08-25 07:19:34 -0700300#ifdef USE_INDIRECT_REF
301 if (!dvmInitIndirectRefTable(&gDvm.jniGlobalRefTable,
302 kGlobalRefsTableInitialSize, kGlobalRefsTableMaxSize,
303 kIndirectKindGlobal))
304 return false;
305#else
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800306 if (!dvmInitReferenceTable(&gDvm.jniGlobalRefTable,
307 kGlobalRefsTableInitialSize, kGlobalRefsTableMaxSize))
308 return false;
Andy McFaddend5ab7262009-08-25 07:19:34 -0700309#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800310
311 dvmInitMutex(&gDvm.jniGlobalRefLock);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800312 gDvm.jniGlobalRefLoMark = 0;
313 gDvm.jniGlobalRefHiMark = kGrefWaterInterval * 2;
314
Andy McFaddenc26bb632009-08-21 12:01:31 -0700315 if (!dvmInitReferenceTable(&gDvm.jniPinRefTable,
316 kPinTableInitialSize, kPinTableMaxSize))
317 return false;
318
319 dvmInitMutex(&gDvm.jniPinRefLock);
320
Andy McFadden8e5c7842009-07-23 17:47:18 -0700321 /*
322 * Look up and cache pointers to some direct buffer classes, fields,
323 * and methods.
324 */
325 Method* meth;
326
Andy McFadden5f612b82009-07-22 15:07:27 -0700327 ClassObject* platformAddressClass =
328 dvmFindSystemClassNoInit("Lorg/apache/harmony/luni/platform/PlatformAddress;");
Andy McFadden8e5c7842009-07-23 17:47:18 -0700329 ClassObject* platformAddressFactoryClass =
330 dvmFindSystemClassNoInit("Lorg/apache/harmony/luni/platform/PlatformAddressFactory;");
Andy McFadden5f612b82009-07-22 15:07:27 -0700331 ClassObject* directBufferClass =
332 dvmFindSystemClassNoInit("Lorg/apache/harmony/nio/internal/DirectBuffer;");
Andy McFadden8e5c7842009-07-23 17:47:18 -0700333 ClassObject* readWriteBufferClass =
334 dvmFindSystemClassNoInit("Ljava/nio/ReadWriteDirectByteBuffer;");
335 ClassObject* bufferClass =
336 dvmFindSystemClassNoInit("Ljava/nio/Buffer;");
337
338 if (platformAddressClass == NULL || platformAddressFactoryClass == NULL ||
339 directBufferClass == NULL || readWriteBufferClass == NULL ||
340 bufferClass == NULL)
341 {
Andy McFadden5f612b82009-07-22 15:07:27 -0700342 LOGE("Unable to find internal direct buffer classes\n");
343 return false;
344 }
Andy McFadden8e5c7842009-07-23 17:47:18 -0700345 gDvm.classJavaNioReadWriteDirectByteBuffer = readWriteBufferClass;
Andy McFaddend5ab7262009-08-25 07:19:34 -0700346 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer = directBufferClass;
347 /* need a global reference for extended CheckJNI tests */
348 gDvm.jclassOrgApacheHarmonyNioInternalDirectBuffer =
349 addGlobalReference((Object*) directBufferClass);
Andy McFadden5f612b82009-07-22 15:07:27 -0700350
Andy McFadden8e5c7842009-07-23 17:47:18 -0700351 /*
352 * We need a Method* here rather than a vtable offset, because
353 * DirectBuffer is an interface class.
354 */
Andy McFadden5f612b82009-07-22 15:07:27 -0700355 meth = dvmFindVirtualMethodByDescriptor(
356 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer,
357 "getEffectiveAddress",
358 "()Lorg/apache/harmony/luni/platform/PlatformAddress;");
359 if (meth == NULL) {
360 LOGE("Unable to find PlatformAddress.getEffectiveAddress\n");
361 return false;
362 }
363 gDvm.methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress = meth;
364
365 meth = dvmFindVirtualMethodByDescriptor(platformAddressClass,
366 "toLong", "()J");
367 if (meth == NULL) {
368 LOGE("Unable to find PlatformAddress.toLong\n");
369 return false;
370 }
Andy McFadden8e5c7842009-07-23 17:47:18 -0700371 gDvm.voffOrgApacheHarmonyLuniPlatformPlatformAddress_toLong =
372 meth->methodIndex;
373
374 meth = dvmFindDirectMethodByDescriptor(platformAddressFactoryClass,
375 "on",
376 "(I)Lorg/apache/harmony/luni/platform/PlatformAddress;");
377 if (meth == NULL) {
378 LOGE("Unable to find PlatformAddressFactory.on\n");
379 return false;
380 }
381 gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on = meth;
382
383 meth = dvmFindDirectMethodByDescriptor(readWriteBufferClass,
384 "<init>",
385 "(Lorg/apache/harmony/luni/platform/PlatformAddress;II)V");
386 if (meth == NULL) {
387 LOGE("Unable to find ReadWriteDirectByteBuffer.<init>\n");
388 return false;
389 }
390 gDvm.methJavaNioReadWriteDirectByteBuffer_init = meth;
391
392 gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr =
393 dvmFindFieldOffset(platformAddressClass, "osaddr", "I");
394 if (gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr < 0) {
395 LOGE("Unable to find PlatformAddress.osaddr\n");
396 return false;
397 }
398
399 gDvm.offJavaNioBuffer_capacity =
400 dvmFindFieldOffset(bufferClass, "capacity", "I");
401 if (gDvm.offJavaNioBuffer_capacity < 0) {
402 LOGE("Unable to find Buffer.capacity\n");
403 return false;
404 }
Andy McFadden5f612b82009-07-22 15:07:27 -0700405
Andy McFadden8e696dc2009-07-24 15:28:16 -0700406 gDvm.offJavaNioBuffer_effectiveDirectAddress =
407 dvmFindFieldOffset(bufferClass, "effectiveDirectAddress", "I");
408 if (gDvm.offJavaNioBuffer_effectiveDirectAddress < 0) {
409 LOGE("Unable to find Buffer.effectiveDirectAddress\n");
410 return false;
411 }
412
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800413 return true;
414}
415
416/*
417 * Free the global references table.
418 */
419void dvmJniShutdown(void)
420{
Andy McFaddend5ab7262009-08-25 07:19:34 -0700421#ifdef USE_INDIRECT_REF
422 dvmClearIndirectRefTable(&gDvm.jniGlobalRefTable);
423#else
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800424 dvmClearReferenceTable(&gDvm.jniGlobalRefTable);
Andy McFaddend5ab7262009-08-25 07:19:34 -0700425#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800426}
427
428
429/*
430 * Find the JNIEnv associated with the current thread.
431 *
432 * Currently stored in the Thread struct. Could also just drop this into
433 * thread-local storage.
434 */
435JNIEnvExt* dvmGetJNIEnvForThread(void)
436{
437 Thread* self = dvmThreadSelf();
438 if (self == NULL)
439 return NULL;
440 return (JNIEnvExt*) dvmGetThreadJNIEnv(self);
441}
442
443/*
444 * Create a new JNIEnv struct and add it to the VM's list.
445 *
446 * "self" will be NULL for the main thread, since the VM hasn't started
447 * yet; the value will be filled in later.
448 */
449JNIEnv* dvmCreateJNIEnv(Thread* self)
450{
451 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
452 JNIEnvExt* newEnv;
453
454 //if (self != NULL)
455 // LOGI("Ent CreateJNIEnv: threadid=%d %p\n", self->threadId, self);
456
457 assert(vm != NULL);
458
459 newEnv = (JNIEnvExt*) calloc(1, sizeof(JNIEnvExt));
460 newEnv->funcTable = &gNativeInterface;
461 newEnv->vm = vm;
462 newEnv->forceDataCopy = vm->forceDataCopy;
463 if (self != NULL) {
464 dvmSetJniEnvThreadId((JNIEnv*) newEnv, self);
465 assert(newEnv->envThreadId != 0);
466 } else {
467 /* make it obvious if we fail to initialize these later */
468 newEnv->envThreadId = 0x77777775;
469 newEnv->self = (Thread*) 0x77777779;
470 }
471 if (vm->useChecked)
472 dvmUseCheckedJniEnv(newEnv);
473
474 dvmLockMutex(&vm->envListLock);
475
476 /* insert at head of list */
477 newEnv->next = vm->envList;
478 assert(newEnv->prev == NULL);
479 if (vm->envList == NULL) // rare, but possible
480 vm->envList = newEnv;
481 else
482 vm->envList->prev = newEnv;
483 vm->envList = newEnv;
484
485 dvmUnlockMutex(&vm->envListLock);
486
487 //if (self != NULL)
488 // LOGI("Xit CreateJNIEnv: threadid=%d %p\n", self->threadId, self);
489 return (JNIEnv*) newEnv;
490}
491
492/*
493 * Remove a JNIEnv struct from the list and free it.
494 */
495void dvmDestroyJNIEnv(JNIEnv* env)
496{
497 JNIEnvExt* extEnv = (JNIEnvExt*) env;
498 JavaVMExt* vm = extEnv->vm;
499 Thread* self;
500
501 if (env == NULL)
502 return;
503
504 self = dvmThreadSelf();
505 assert(self != NULL);
506
507 //LOGI("Ent DestroyJNIEnv: threadid=%d %p\n", self->threadId, self);
508
509 dvmLockMutex(&vm->envListLock);
510
511 if (extEnv == vm->envList) {
512 assert(extEnv->prev == NULL);
513 vm->envList = extEnv->next;
514 } else {
515 assert(extEnv->prev != NULL);
516 extEnv->prev->next = extEnv->next;
517 }
518 if (extEnv->next != NULL)
519 extEnv->next->prev = extEnv->prev;
520
521 dvmUnlockMutex(&extEnv->vm->envListLock);
522
523 free(env);
524 //LOGI("Xit DestroyJNIEnv: threadid=%d %p\n", self->threadId, self);
525}
526
527
528/*
529 * Retrieve the ReferenceTable struct for the current thread.
530 *
Andy McFaddenab00d452009-08-19 07:21:41 -0700531 * Going through "env" rather than dvmThreadSelf() is faster but will
532 * get weird if the JNI code is passing the wrong JNIEnv around.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800533 */
Andy McFaddend5ab7262009-08-25 07:19:34 -0700534#ifdef USE_INDIRECT_REF
535static inline IndirectRefTable* getLocalRefTable(JNIEnv* env)
Andy McFaddend5ab7262009-08-25 07:19:34 -0700536#else
Andy McFaddenab00d452009-08-19 07:21:41 -0700537static inline ReferenceTable* getLocalRefTable(JNIEnv* env)
Andy McFaddeneb9cbc32009-08-28 14:45:12 -0700538#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800539{
Andy McFaddenab00d452009-08-19 07:21:41 -0700540 //return &dvmThreadSelf()->jniLocalRefTable;
541 return &((JNIEnvExt*)env)->self->jniLocalRefTable;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800542}
Andy McFaddend5ab7262009-08-25 07:19:34 -0700543
Andy McFaddeneb9cbc32009-08-28 14:45:12 -0700544#ifdef USE_INDIRECT_REF
Andy McFaddend5ab7262009-08-25 07:19:34 -0700545/*
546 * Convert an indirect reference to an Object reference. The indirect
547 * reference may be local, global, or weak-global.
548 *
549 * If "jobj" is NULL or an invalid indirect reference, this returns NULL.
550 */
551Object* dvmDecodeIndirectRef(JNIEnv* env, jobject jobj)
552{
Andy McFaddend5ab7262009-08-25 07:19:34 -0700553 if (jobj == NULL)
554 return NULL;
555
556 Object* result;
557
558 switch (dvmGetIndirectRefType(jobj)) {
559 case kIndirectKindLocal:
560 {
561 IndirectRefTable* pRefTable = getLocalRefTable(env);
562 result = dvmGetFromIndirectRefTable(pRefTable, jobj);
563 }
564 break;
565 case kIndirectKindGlobal:
566 {
567 // TODO: find a way to avoid the mutex activity here
568 IndirectRefTable* pRefTable = &gDvm.jniGlobalRefTable;
569 dvmLockMutex(&gDvm.jniGlobalRefLock);
570 result = dvmGetFromIndirectRefTable(pRefTable, jobj);
571 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
572 }
573 break;
574 case kIndirectKindWeakGlobal:
575 {
576 LOGE("weak-global not yet supported\n");
577 result = NULL;
578 dvmAbort();
579 }
580 break;
581 case kIndirectKindInvalid:
582 default:
583 LOGW("Invalid indirect reference %p in decodeIndirectRef\n", jobj);
584 dvmAbort();
585 result = NULL;
586 break;
587 }
588
589 return result;
Andy McFaddend5ab7262009-08-25 07:19:34 -0700590}
Andy McFaddeneb9cbc32009-08-28 14:45:12 -0700591#else
592 /* use trivial inline in JniInternal.h for performance */
593#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800594
595/*
596 * Add a local reference for an object to the current stack frame. When
597 * the native function returns, the reference will be discarded.
598 *
599 * We need to allow the same reference to be added multiple times.
600 *
601 * This will be called on otherwise unreferenced objects. We cannot do
602 * GC allocations here, and it's best if we don't grab a mutex.
603 *
604 * Returns the local reference (currently just the same pointer that was
605 * passed in), or NULL on failure.
606 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700607static jobject addLocalReference(JNIEnv* env, Object* obj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800608{
609 if (obj == NULL)
610 return NULL;
611
Andy McFaddend5ab7262009-08-25 07:19:34 -0700612 jobject jobj;
613
614#ifdef USE_INDIRECT_REF
615 IndirectRefTable* pRefTable = getLocalRefTable(env);
616 void* curFrame = ((JNIEnvExt*)env)->self->curFrame;
617 u4 cookie = SAVEAREA_FROM_FP(curFrame)->xtra.localRefCookie;
618
619 jobj = (jobject) dvmAddToIndirectRefTable(pRefTable, cookie, obj);
620 if (jobj == NULL) {
621 dvmDumpIndirectRefTable(pRefTable, "JNI local");
622 LOGE("Failed adding to JNI local ref table (has %d entries)\n",
623 (int) dvmIndirectRefTableEntries(pRefTable));
624 dvmDumpThread(dvmThreadSelf(), false);
625 dvmAbort(); // spec says call FatalError; this is equivalent
626 } else {
627 LOGVV("LREF add %p (%s.%s) (ent=%d)\n", obj,
628 dvmGetCurrentJNIMethod()->clazz->descriptor,
629 dvmGetCurrentJNIMethod()->name,
630 (int) dvmReferenceTableEntries(pRefTable));
631 }
632#else
Andy McFaddenab00d452009-08-19 07:21:41 -0700633 ReferenceTable* pRefTable = getLocalRefTable(env);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800634
Andy McFaddenab00d452009-08-19 07:21:41 -0700635 if (!dvmAddToReferenceTable(pRefTable, obj)) {
636 dvmDumpReferenceTable(pRefTable, "JNI local");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800637 LOGE("Failed adding to JNI local ref table (has %d entries)\n",
Andy McFaddenab00d452009-08-19 07:21:41 -0700638 (int) dvmReferenceTableEntries(pRefTable));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800639 dvmDumpThread(dvmThreadSelf(), false);
640 dvmAbort(); // spec says call FatalError; this is equivalent
641 } else {
Andy McFadden0083d372009-08-21 14:44:04 -0700642 LOGVV("LREF add %p (%s.%s) (ent=%d)\n", obj,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800643 dvmGetCurrentJNIMethod()->clazz->descriptor,
Andy McFadden0083d372009-08-21 14:44:04 -0700644 dvmGetCurrentJNIMethod()->name,
645 (int) dvmReferenceTableEntries(pRefTable));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800646 }
647
Andy McFaddend5ab7262009-08-25 07:19:34 -0700648 jobj = (jobject) obj;
649#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800650
Andy McFaddend5ab7262009-08-25 07:19:34 -0700651 return jobj;
Andy McFaddenab00d452009-08-19 07:21:41 -0700652}
653
654/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800655 * Ensure that at least "capacity" references can be held in the local
656 * refs table of the current thread.
657 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700658static bool ensureLocalCapacity(JNIEnv* env, int capacity)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800659{
Andy McFaddend5ab7262009-08-25 07:19:34 -0700660#ifdef USE_INDIRECT_REF
661 IndirectRefTable* pRefTable = getLocalRefTable(env);
662 int numEntries = dvmIndirectRefTableEntries(pRefTable);
663 // TODO: this isn't quite right, since "numEntries" includes holes
664 return ((kJniLocalRefMax - numEntries) >= capacity);
665#else
Andy McFaddenab00d452009-08-19 07:21:41 -0700666 ReferenceTable* pRefTable = getLocalRefTable(env);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800667
Andy McFaddenab00d452009-08-19 07:21:41 -0700668 return (kJniLocalRefMax - (pRefTable->nextEntry - pRefTable->table) >= capacity);
Andy McFaddend5ab7262009-08-25 07:19:34 -0700669#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800670}
671
672/*
673 * Explicitly delete a reference from the local list.
674 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700675static void deleteLocalReference(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800676{
Andy McFaddenab00d452009-08-19 07:21:41 -0700677 if (jobj == NULL)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800678 return;
679
Andy McFaddend5ab7262009-08-25 07:19:34 -0700680#ifdef USE_INDIRECT_REF
681 IndirectRefTable* pRefTable = getLocalRefTable(env);
682 Thread* self = ((JNIEnvExt*)env)->self;
683 u4 cookie = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefCookie;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800684
Andy McFaddend5ab7262009-08-25 07:19:34 -0700685 if (!dvmRemoveFromIndirectRefTable(pRefTable, cookie, jobj)) {
686 /*
687 * Attempting to delete a local reference that is not in the
688 * topmost local reference frame is a no-op. DeleteLocalRef returns
689 * void and doesn't throw any exceptions, but we should probably
690 * complain about it so the user will notice that things aren't
691 * going quite the way they expect.
692 */
693 LOGW("JNI WARNING: DeleteLocalRef(%p) failed to find entry\n", jobj);
694 }
695#else
696 ReferenceTable* pRefTable = getLocalRefTable(env);
697 Thread* self = ((JNIEnvExt*)env)->self;
698 Object** bottom = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefCookie;
699
700 if (!dvmRemoveFromReferenceTable(pRefTable, bottom, (Object*) jobj)) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800701 /*
702 * Attempting to delete a local reference that is not in the
703 * topmost local reference frame is a no-op. DeleteLocalRef returns
704 * void and doesn't throw any exceptions, but we should probably
705 * complain about it so the user will notice that things aren't
706 * going quite the way they expect.
707 */
708 LOGW("JNI WARNING: DeleteLocalRef(%p) failed to find entry (valid=%d)\n",
Andy McFaddenab00d452009-08-19 07:21:41 -0700709 jobj, dvmIsValidObject((Object*) jobj));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800710 }
Andy McFaddend5ab7262009-08-25 07:19:34 -0700711#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800712}
713
714/*
715 * Add a global reference for an object.
716 *
717 * We may add the same object more than once. Add/remove calls are paired,
718 * so it needs to appear on the list multiple times.
719 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700720static jobject addGlobalReference(Object* obj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800721{
722 if (obj == NULL)
723 return NULL;
724
725 //LOGI("adding obj=%p\n", obj);
726 //dvmDumpThread(dvmThreadSelf(), false);
727
728 if (false && ((Object*)obj)->clazz == gDvm.classJavaLangClass) {
729 ClassObject* clazz = (ClassObject*) obj;
730 LOGI("-------\n");
731 LOGI("Adding global ref on class %s\n", clazz->descriptor);
732 dvmDumpThread(dvmThreadSelf(), false);
733 }
734 if (false && ((Object*)obj)->clazz == gDvm.classJavaLangString) {
735 StringObject* strObj = (StringObject*) obj;
736 char* str = dvmCreateCstrFromString(strObj);
737 if (strcmp(str, "sync-response") == 0) {
738 LOGI("-------\n");
739 LOGI("Adding global ref on string '%s'\n", str);
740 dvmDumpThread(dvmThreadSelf(), false);
741 //dvmAbort();
742 }
743 free(str);
744 }
745 if (false && ((Object*)obj)->clazz == gDvm.classArrayByte) {
746 ArrayObject* arrayObj = (ArrayObject*) obj;
Andy McFaddend5ab7262009-08-25 07:19:34 -0700747 if (arrayObj->length == 8192 /*&&
748 dvmReferenceTableEntries(&gDvm.jniGlobalRefTable) > 400*/)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800749 {
750 LOGI("Adding global ref on byte array %p (len=%d)\n",
751 arrayObj, arrayObj->length);
752 dvmDumpThread(dvmThreadSelf(), false);
753 }
754 }
755
Andy McFaddend5ab7262009-08-25 07:19:34 -0700756 jobject jobj;
757
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800758 dvmLockMutex(&gDvm.jniGlobalRefLock);
759
760 /*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800761 * Throwing an exception on failure is problematic, because JNI code
762 * may not be expecting an exception, and things sort of cascade. We
763 * want to have a hard limit to catch leaks during debugging, but this
764 * otherwise needs to expand until memory is consumed. As a practical
765 * matter, if we have many thousands of global references, chances are
766 * we're either leaking global ref table entries or we're going to
767 * run out of space in the GC heap.
768 */
Andy McFaddend5ab7262009-08-25 07:19:34 -0700769#ifdef USE_INDIRECT_REF
770 jobj = dvmAddToIndirectRefTable(&gDvm.jniGlobalRefTable, IRT_FIRST_SEGMENT,
771 obj);
772 if (jobj == NULL) {
773 dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable, "JNI global");
774 LOGE("Failed adding to JNI global ref table (%d entries)\n",
775 (int) dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable));
776 dvmAbort();
777 }
778
779 LOGVV("GREF add %p (%s.%s)\n", obj,
780 dvmGetCurrentJNIMethod()->clazz->descriptor,
781 dvmGetCurrentJNIMethod()->name);
782
783 /* GREF usage tracking; should probably be disabled for production env */
784 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
785 int count = dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable);
786 // TODO: adjust for "holes"
787 if (count > gDvm.jniGlobalRefHiMark) {
788 LOGD("GREF has increased to %d\n", count);
789 gDvm.jniGlobalRefHiMark += kGrefWaterInterval;
790 gDvm.jniGlobalRefLoMark += kGrefWaterInterval;
791
792 /* watch for "excessive" use; not generally appropriate */
793 if (count >= gDvm.jniGrefLimit) {
794 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
795 if (vm->warnError) {
796 dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable,
797 "JNI global");
798 LOGE("Excessive JNI global references (%d)\n", count);
799 dvmAbort();
800 } else {
801 LOGW("Excessive JNI global references (%d)\n", count);
802 }
803 }
804 }
805 }
806#else
807 if (!dvmAddToReferenceTable(&gDvm.jniGlobalRefTable, obj)) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800808 dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global");
809 LOGE("Failed adding to JNI global ref table (%d entries)\n",
810 (int) dvmReferenceTableEntries(&gDvm.jniGlobalRefTable));
811 dvmAbort();
812 }
Andy McFaddend5ab7262009-08-25 07:19:34 -0700813 jobj = (jobject) obj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800814
815 LOGVV("GREF add %p (%s.%s)\n", obj,
816 dvmGetCurrentJNIMethod()->clazz->descriptor,
817 dvmGetCurrentJNIMethod()->name);
818
819 /* GREF usage tracking; should probably be disabled for production env */
820 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
821 int count = dvmReferenceTableEntries(&gDvm.jniGlobalRefTable);
822 if (count > gDvm.jniGlobalRefHiMark) {
823 LOGD("GREF has increased to %d\n", count);
824 gDvm.jniGlobalRefHiMark += kGrefWaterInterval;
825 gDvm.jniGlobalRefLoMark += kGrefWaterInterval;
826
827 /* watch for "excessive" use; not generally appropriate */
828 if (count >= gDvm.jniGrefLimit) {
829 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
830 if (vm->warnError) {
831 dvmDumpReferenceTable(&gDvm.jniGlobalRefTable,"JNI global");
832 LOGE("Excessive JNI global references (%d)\n", count);
833 dvmAbort();
834 } else {
835 LOGW("Excessive JNI global references (%d)\n", count);
836 }
837 }
838 }
839 }
Andy McFaddend5ab7262009-08-25 07:19:34 -0700840#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800841
842bail:
843 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
Andy McFaddend5ab7262009-08-25 07:19:34 -0700844 return jobj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800845}
846
847/*
848 * Remove a global reference. In most cases it's the entry most recently
849 * added, which makes this pretty quick.
850 *
851 * Thought: if it's not the most recent entry, just null it out. When we
852 * fill up, do a compaction pass before we expand the list.
853 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700854static void deleteGlobalReference(jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800855{
Andy McFaddenab00d452009-08-19 07:21:41 -0700856 if (jobj == NULL)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800857 return;
858
859 dvmLockMutex(&gDvm.jniGlobalRefLock);
860
Andy McFaddend5ab7262009-08-25 07:19:34 -0700861#ifdef USE_INDIRECT_REF
862 if (!dvmRemoveFromIndirectRefTable(&gDvm.jniGlobalRefTable,
863 IRT_FIRST_SEGMENT, jobj))
864 {
865 LOGW("JNI: DeleteGlobalRef(%p) failed to find entry\n", jobj);
866 goto bail;
867 }
868
869 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
870 int count = dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable);
871 // TODO: not quite right, need to subtract holes
872 if (count < gDvm.jniGlobalRefLoMark) {
873 LOGD("GREF has decreased to %d\n", count);
874 gDvm.jniGlobalRefHiMark -= kGrefWaterInterval;
875 gDvm.jniGlobalRefLoMark -= kGrefWaterInterval;
876 }
877 }
878#else
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800879 if (!dvmRemoveFromReferenceTable(&gDvm.jniGlobalRefTable,
Andy McFaddenab00d452009-08-19 07:21:41 -0700880 gDvm.jniGlobalRefTable.table, jobj))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800881 {
882 LOGW("JNI: DeleteGlobalRef(%p) failed to find entry (valid=%d)\n",
Andy McFaddenab00d452009-08-19 07:21:41 -0700883 jobj, dvmIsValidObject((Object*) jobj));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800884 goto bail;
885 }
886
887 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
888 int count = dvmReferenceTableEntries(&gDvm.jniGlobalRefTable);
889 if (count < gDvm.jniGlobalRefLoMark) {
890 LOGD("GREF has decreased to %d\n", count);
891 gDvm.jniGlobalRefHiMark -= kGrefWaterInterval;
892 gDvm.jniGlobalRefLoMark -= kGrefWaterInterval;
893 }
894 }
Andy McFaddend5ab7262009-08-25 07:19:34 -0700895#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800896
897bail:
898 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
899}
900
901/*
Andy McFaddenab00d452009-08-19 07:21:41 -0700902 * Objects don't currently move, so we just need to create a reference
903 * that will ensure the array object isn't collected.
904 *
Andy McFaddenc26bb632009-08-21 12:01:31 -0700905 * We use a separate reference table, which is part of the GC root set.
Andy McFaddenab00d452009-08-19 07:21:41 -0700906 */
907static void pinPrimitiveArray(ArrayObject* arrayObj)
908{
Andy McFaddenc26bb632009-08-21 12:01:31 -0700909 if (arrayObj == NULL)
910 return;
911
912 dvmLockMutex(&gDvm.jniPinRefLock);
913 if (!dvmAddToReferenceTable(&gDvm.jniPinRefTable, (Object*)arrayObj)) {
914 dvmDumpReferenceTable(&gDvm.jniPinRefTable, "JNI pinned array");
915 LOGE("Failed adding to JNI pinned array ref table (%d entries)\n",
916 (int) dvmReferenceTableEntries(&gDvm.jniPinRefTable));
917 dvmDumpThread(dvmThreadSelf(), false);
918 dvmAbort();
919 }
920
921 /*
922 * If we're watching global ref usage, also keep an eye on these.
923 *
924 * The total number of pinned primitive arrays should be pretty small.
925 * A single array should not be pinned more than once or twice; any
926 * more than that is a strong indicator that a Release function is
927 * not being called.
928 */
929 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
930 int count = 0;
931 Object** ppObj = gDvm.jniPinRefTable.table;
932 while (ppObj < gDvm.jniPinRefTable.nextEntry) {
933 if (*ppObj++ == (Object*) arrayObj)
934 count++;
935 }
936
937 if (count > kPinComplainThreshold) {
938 LOGW("JNI: pin count on array %p (%s) is now %d\n",
939 arrayObj, arrayObj->obj.clazz->descriptor, count);
940 /* keep going */
941 }
942 }
943
944 dvmUnlockMutex(&gDvm.jniPinRefLock);
Andy McFaddenab00d452009-08-19 07:21:41 -0700945}
946
947/*
948 * Un-pin the array object. If an object was pinned twice, it must be
949 * unpinned twice before it's free to move.
Andy McFaddenab00d452009-08-19 07:21:41 -0700950 */
951static void unpinPrimitiveArray(ArrayObject* arrayObj)
952{
Andy McFaddenc26bb632009-08-21 12:01:31 -0700953 if (arrayObj == NULL)
954 return;
955
956 dvmLockMutex(&gDvm.jniPinRefLock);
957 if (!dvmRemoveFromReferenceTable(&gDvm.jniPinRefTable,
958 gDvm.jniPinRefTable.table, (Object*) arrayObj))
959 {
960 LOGW("JNI: unpinPrimitiveArray(%p) failed to find entry (valid=%d)\n",
961 arrayObj, dvmIsValidObject((Object*) arrayObj));
962 goto bail;
963 }
964
965bail:
966 dvmUnlockMutex(&gDvm.jniPinRefLock);
Andy McFaddenab00d452009-08-19 07:21:41 -0700967}
968
969/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800970 * GC helper function to mark all JNI global references.
Andy McFaddenc26bb632009-08-21 12:01:31 -0700971 *
972 * We're currently handling the "pin" table here too.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800973 */
974void dvmGcMarkJniGlobalRefs()
975{
Andy McFaddend5ab7262009-08-25 07:19:34 -0700976 Object** op;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800977
978 dvmLockMutex(&gDvm.jniGlobalRefLock);
979
Andy McFaddend5ab7262009-08-25 07:19:34 -0700980#ifdef USE_INDIRECT_REF
981 IndirectRefTable* pRefTable = &gDvm.jniGlobalRefTable;
982 op = pRefTable->table;
983 int numEntries = dvmIndirectRefTableEntries(pRefTable);
984 int i;
985
986 for (i = 0; i < numEntries; i++) {
987 Object* obj = *op;
988 if (obj != NULL)
989 dvmMarkObjectNonNull(obj);
990 op++;
991 }
992#else
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800993 op = gDvm.jniGlobalRefTable.table;
994 while ((uintptr_t)op < (uintptr_t)gDvm.jniGlobalRefTable.nextEntry) {
995 dvmMarkObjectNonNull(*(op++));
996 }
Andy McFaddend5ab7262009-08-25 07:19:34 -0700997#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800998
999 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
Andy McFaddenc26bb632009-08-21 12:01:31 -07001000
1001
1002 dvmLockMutex(&gDvm.jniPinRefLock);
1003
1004 op = gDvm.jniPinRefTable.table;
1005 while ((uintptr_t)op < (uintptr_t)gDvm.jniPinRefTable.nextEntry) {
1006 dvmMarkObjectNonNull(*(op++));
1007 }
1008
1009 dvmUnlockMutex(&gDvm.jniPinRefLock);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001010}
1011
1012
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001013#ifndef USE_INDIRECT_REF
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001014/*
1015 * Determine if "obj" appears in the argument list for the native method.
1016 *
1017 * We use the "shorty" signature to determine which argument slots hold
1018 * reference types.
1019 */
1020static bool findInArgList(Thread* self, Object* obj)
1021{
1022 const Method* meth;
1023 u4* fp;
1024 int i;
1025
1026 fp = self->curFrame;
1027 while (1) {
1028 /*
1029 * Back up over JNI PushLocalFrame frames. This works because the
1030 * previous frame on the interpreted stack is either a break frame
1031 * (if we called here via native code) or an interpreted method (if
1032 * we called here via the interpreter). In both cases the method
1033 * pointer won't match.
1034 */
1035 StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
1036 meth = saveArea->method;
1037 if (meth != SAVEAREA_FROM_FP(saveArea->prevFrame)->method)
1038 break;
1039 fp = saveArea->prevFrame;
1040 }
1041
1042 LOGVV("+++ scanning %d args in %s (%s)\n",
1043 meth->insSize, meth->name, meth->shorty);
1044 const char* shorty = meth->shorty +1; /* skip return type char */
1045 for (i = 0; i < meth->insSize; i++) {
1046 if (i == 0 && !dvmIsStaticMethod(meth)) {
1047 /* first arg is "this" ref, not represented in "shorty" */
1048 if (fp[i] == (u4) obj)
1049 return true;
1050 } else {
1051 /* if this is a reference type, see if it matches */
1052 switch (*shorty) {
1053 case 'L':
1054 if (fp[i] == (u4) obj)
1055 return true;
1056 break;
1057 case 'D':
1058 case 'J':
1059 i++;
1060 break;
1061 case '\0':
1062 LOGE("Whoops! ran off the end of %s (%d)\n",
1063 meth->shorty, meth->insSize);
1064 break;
1065 default:
1066 if (fp[i] == (u4) obj)
1067 LOGI("NOTE: ref %p match on arg type %c\n", obj, *shorty);
1068 break;
1069 }
1070 shorty++;
1071 }
1072 }
1073
1074 /*
1075 * For static methods, we also pass a class pointer in.
1076 */
1077 if (dvmIsStaticMethod(meth)) {
1078 //LOGI("+++ checking class pointer in %s\n", meth->name);
1079 if ((void*)obj == (void*)meth->clazz)
1080 return true;
1081 }
1082 return false;
1083}
Andy McFadden0083d372009-08-21 14:44:04 -07001084#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001085
1086/*
1087 * Verify that a reference passed in from native code is one that the
1088 * code is allowed to have.
1089 *
1090 * It's okay for native code to pass us a reference that:
Andy McFadden0083d372009-08-21 14:44:04 -07001091 * - was passed in as an argument when invoked by native code (and hence
1092 * is in the JNI local refs table)
1093 * - was returned to it from JNI (and is now in the local refs table)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001094 * - is present in the JNI global refs table
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001095 *
1096 * Used by -Xcheck:jni and GetObjectRefType.
1097 *
1098 * NOTE: in the current VM, global and local references are identical. If
1099 * something is both global and local, we can't tell them apart, and always
1100 * return "local".
1101 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001102jobjectRefType dvmGetJNIRefType(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001103{
Andy McFaddend5ab7262009-08-25 07:19:34 -07001104#ifdef USE_INDIRECT_REF
1105 /*
1106 * IndirectRefKind is currently defined as an exact match of
Andy McFadden0423f0e2009-08-26 07:21:53 -07001107 * jobjectRefType, so this is easy. We have to decode it to determine
1108 * if it's a valid reference and not merely valid-looking.
Andy McFaddend5ab7262009-08-25 07:19:34 -07001109 */
Andy McFadden0423f0e2009-08-26 07:21:53 -07001110 Object* obj = dvmDecodeIndirectRef(env, jobj);
1111
1112 if (obj == NULL) {
1113 /* invalid ref, or jobj was NULL */
1114 return JNIInvalidRefType;
1115 } else {
1116 return (jobjectRefType) dvmGetIndirectRefType(jobj);
1117 }
Andy McFaddend5ab7262009-08-25 07:19:34 -07001118#else
Andy McFaddenab00d452009-08-19 07:21:41 -07001119 ReferenceTable* pRefTable = getLocalRefTable(env);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001120 Thread* self = dvmThreadSelf();
1121 //Object** top;
1122 Object** ptr;
1123
1124 /* check args */
Andy McFaddenab00d452009-08-19 07:21:41 -07001125 if (findInArgList(self, jobj)) {
1126 //LOGI("--- REF found %p on stack\n", jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001127 return JNILocalRefType;
1128 }
1129
1130 /* check locals */
Andy McFaddenab00d452009-08-19 07:21:41 -07001131 if (dvmFindInReferenceTable(pRefTable, pRefTable->table, jobj) != NULL) {
1132 //LOGI("--- REF found %p in locals\n", jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001133 return JNILocalRefType;
1134 }
1135
1136 /* check globals */
1137 dvmLockMutex(&gDvm.jniGlobalRefLock);
1138 if (dvmFindInReferenceTable(&gDvm.jniGlobalRefTable,
Andy McFaddenab00d452009-08-19 07:21:41 -07001139 gDvm.jniGlobalRefTable.table, jobj))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001140 {
Andy McFaddenab00d452009-08-19 07:21:41 -07001141 //LOGI("--- REF found %p in globals\n", jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001142 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
1143 return JNIGlobalRefType;
1144 }
1145 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
1146
1147 /* not found! */
1148 return JNIInvalidRefType;
Andy McFaddend5ab7262009-08-25 07:19:34 -07001149#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001150}
1151
1152/*
1153 * Register a method that uses JNI calling conventions.
1154 */
1155static bool dvmRegisterJNIMethod(ClassObject* clazz, const char* methodName,
1156 const char* signature, void* fnPtr)
1157{
1158 Method* method;
1159 bool result = false;
1160
1161 if (fnPtr == NULL)
1162 goto bail;
1163
1164 method = dvmFindDirectMethodByDescriptor(clazz, methodName, signature);
1165 if (method == NULL)
1166 method = dvmFindVirtualMethodByDescriptor(clazz, methodName, signature);
1167 if (method == NULL) {
1168 LOGW("ERROR: Unable to find decl for native %s.%s %s\n",
1169 clazz->descriptor, methodName, signature);
1170 goto bail;
1171 }
1172
1173 if (!dvmIsNativeMethod(method)) {
1174 LOGW("Unable to register: not native: %s.%s %s\n",
1175 clazz->descriptor, methodName, signature);
1176 goto bail;
1177 }
1178
1179 if (method->nativeFunc != dvmResolveNativeMethod) {
1180 LOGW("Warning: %s.%s %s was already registered/resolved?\n",
1181 clazz->descriptor, methodName, signature);
1182 /* keep going, I guess */
1183 }
1184
Andy McFadden59b61772009-05-13 16:44:34 -07001185 dvmUseJNIBridge(method, fnPtr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001186
1187 LOGV("JNI-registered %s.%s %s\n", clazz->descriptor, methodName,
1188 signature);
1189 result = true;
1190
1191bail:
1192 return result;
1193}
1194
1195/*
Andy McFadden59b61772009-05-13 16:44:34 -07001196 * Returns "true" if CheckJNI is enabled in the VM.
1197 */
1198static bool dvmIsCheckJNIEnabled(void)
1199{
1200 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
1201 return vm->useChecked;
1202}
1203
1204/*
1205 * Point "method->nativeFunc" at the JNI bridge, and overload "method->insns"
1206 * to point at the actual function.
1207 */
1208void dvmUseJNIBridge(Method* method, void* func)
1209{
Andy McFadden0083d372009-08-21 14:44:04 -07001210 enum {
1211 kJNIGeneral = 0,
1212 kJNISync = 1,
1213 kJNIVirtualNoRef = 2,
1214 kJNIStaticNoRef = 3,
1215 } kind;
1216 static const DalvikBridgeFunc stdFunc[] = {
1217 dvmCallJNIMethod_general,
1218 dvmCallJNIMethod_synchronized,
1219 dvmCallJNIMethod_virtualNoRef,
1220 dvmCallJNIMethod_staticNoRef
1221 };
1222 static const DalvikBridgeFunc checkFunc[] = {
1223 dvmCheckCallJNIMethod_general,
1224 dvmCheckCallJNIMethod_synchronized,
1225 dvmCheckCallJNIMethod_virtualNoRef,
1226 dvmCheckCallJNIMethod_staticNoRef
1227 };
1228
1229 bool hasRefArg = false;
1230
1231 if (dvmIsSynchronizedMethod(method)) {
1232 /* use version with synchronization; calls into general handler */
1233 kind = kJNISync;
Andy McFadden59b61772009-05-13 16:44:34 -07001234 } else {
Andy McFadden0083d372009-08-21 14:44:04 -07001235 /*
1236 * Do a quick scan through the "shorty" signature to see if the method
1237 * takes any reference arguments.
1238 */
1239 const char* cp = method->shorty;
1240 while (*++cp != '\0') { /* pre-incr to skip return type */
1241 if (*cp == 'L') {
1242 /* 'L' used for both object and array references */
1243 hasRefArg = true;
1244 break;
1245 }
1246 }
1247
1248 if (hasRefArg) {
1249 /* use general handler to slurp up reference args */
1250 kind = kJNIGeneral;
1251 } else {
1252 /* virtual methods have a ref in args[0] (not in signature) */
1253 if (dvmIsStaticMethod(method))
1254 kind = kJNIStaticNoRef;
1255 else
1256 kind = kJNIVirtualNoRef;
1257 }
1258 }
1259
1260 if (dvmIsCheckJNIEnabled()) {
1261 dvmSetNativeFunc(method, checkFunc[kind], func);
1262 } else {
1263 dvmSetNativeFunc(method, stdFunc[kind], func);
Andy McFadden59b61772009-05-13 16:44:34 -07001264 }
1265}
1266
1267/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001268 * Get the method currently being executed by examining the interp stack.
1269 */
1270const Method* dvmGetCurrentJNIMethod(void)
1271{
1272 assert(dvmThreadSelf() != NULL);
1273
1274 void* fp = dvmThreadSelf()->curFrame;
1275 const Method* meth = SAVEAREA_FROM_FP(fp)->method;
1276
1277 assert(meth != NULL);
1278 assert(dvmIsNativeMethod(meth));
1279 return meth;
1280}
1281
1282
1283/*
1284 * Track a JNI MonitorEnter in the current thread.
1285 *
1286 * The goal is to be able to "implicitly" release all JNI-held monitors
1287 * when the thread detaches.
1288 *
1289 * Monitors may be entered multiple times, so we add a new entry for each
1290 * enter call. It would be more efficient to keep a counter. At present
1291 * there's no real motivation to improve this however.
1292 */
1293static void trackMonitorEnter(Thread* self, Object* obj)
1294{
1295 static const int kInitialSize = 16;
1296 ReferenceTable* refTable = &self->jniMonitorRefTable;
1297
1298 /* init table on first use */
1299 if (refTable->table == NULL) {
1300 assert(refTable->maxEntries == 0);
1301
1302 if (!dvmInitReferenceTable(refTable, kInitialSize, INT_MAX)) {
1303 LOGE("Unable to initialize monitor tracking table\n");
1304 dvmAbort();
1305 }
1306 }
1307
1308 if (!dvmAddToReferenceTable(refTable, obj)) {
1309 /* ran out of memory? could throw exception instead */
1310 LOGE("Unable to add entry to monitor tracking table\n");
1311 dvmAbort();
1312 } else {
1313 LOGVV("--- added monitor %p\n", obj);
1314 }
1315}
1316
Andy McFaddenab00d452009-08-19 07:21:41 -07001317
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001318/*
1319 * Track a JNI MonitorExit in the current thread.
1320 */
1321static void trackMonitorExit(Thread* self, Object* obj)
1322{
Andy McFaddenab00d452009-08-19 07:21:41 -07001323 ReferenceTable* pRefTable = &self->jniMonitorRefTable;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001324
Andy McFaddenab00d452009-08-19 07:21:41 -07001325 if (!dvmRemoveFromReferenceTable(pRefTable, pRefTable->table, obj)) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001326 LOGE("JNI monitor %p not found in tracking list\n", obj);
1327 /* keep going? */
1328 } else {
1329 LOGVV("--- removed monitor %p\n", obj);
1330 }
1331}
1332
1333/*
1334 * Release all monitors held by the jniMonitorRefTable list.
1335 */
1336void dvmReleaseJniMonitors(Thread* self)
1337{
Andy McFaddenab00d452009-08-19 07:21:41 -07001338 ReferenceTable* pRefTable = &self->jniMonitorRefTable;
1339 Object** top = pRefTable->table;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001340
1341 if (top == NULL)
1342 return;
1343
Andy McFaddenab00d452009-08-19 07:21:41 -07001344 Object** ptr = pRefTable->nextEntry;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001345 while (--ptr >= top) {
1346 if (!dvmUnlockObject(self, *ptr)) {
1347 LOGW("Unable to unlock monitor %p at thread detach\n", *ptr);
1348 } else {
1349 LOGVV("--- detach-releasing monitor %p\n", *ptr);
1350 }
1351 }
1352
1353 /* zap it */
Andy McFaddenab00d452009-08-19 07:21:41 -07001354 pRefTable->nextEntry = pRefTable->table;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001355}
1356
1357#ifdef WITH_JNI_STACK_CHECK
1358/*
1359 * Compute a CRC on the entire interpreted stack.
1360 *
1361 * Would be nice to compute it on "self" as well, but there are parts of
1362 * the Thread that can be altered by other threads (e.g. prev/next pointers).
1363 */
1364static void computeStackSum(Thread* self)
1365{
1366 const u1* low = (const u1*)SAVEAREA_FROM_FP(self->curFrame);
1367 u4 crc = dvmInitCrc32();
1368 self->stackCrc = 0;
1369 crc = dvmComputeCrc32(crc, low, self->interpStackStart - low);
1370 self->stackCrc = crc;
1371}
1372
1373/*
1374 * Compute a CRC on the entire interpreted stack, and compare it to what
1375 * we previously computed.
1376 *
1377 * We can execute JNI directly from native code without calling in from
1378 * interpreted code during VM initialization and immediately after JNI
1379 * thread attachment. Another opportunity exists during JNI_OnLoad. Rather
1380 * than catching these cases we just ignore them here, which is marginally
1381 * less accurate but reduces the amount of code we have to touch with #ifdefs.
1382 */
1383static void checkStackSum(Thread* self)
1384{
1385 const u1* low = (const u1*)SAVEAREA_FROM_FP(self->curFrame);
1386 u4 stackCrc, crc;
1387
1388 stackCrc = self->stackCrc;
1389 self->stackCrc = 0;
1390 crc = dvmInitCrc32();
1391 crc = dvmComputeCrc32(crc, low, self->interpStackStart - low);
1392 if (crc != stackCrc) {
1393 const Method* meth = dvmGetCurrentJNIMethod();
1394 if (dvmComputeExactFrameDepth(self->curFrame) == 1) {
1395 LOGD("JNI: bad stack CRC (0x%08x) -- okay during init\n",
1396 stackCrc);
1397 } else if (strcmp(meth->name, "nativeLoad") == 0 &&
1398 (strcmp(meth->clazz->descriptor, "Ljava/lang/Runtime;") == 0))
1399 {
1400 LOGD("JNI: bad stack CRC (0x%08x) -- okay during JNI_OnLoad\n",
1401 stackCrc);
1402 } else {
1403 LOGW("JNI: bad stack CRC (%08x vs %08x)\n", crc, stackCrc);
1404 dvmAbort();
1405 }
1406 }
1407 self->stackCrc = (u4) -1; /* make logic errors more noticeable */
1408}
1409#endif
1410
1411
1412/*
1413 * ===========================================================================
Andy McFaddend5ab7262009-08-25 07:19:34 -07001414 * JNI call bridge
1415 * ===========================================================================
1416 */
1417
1418/*
1419 * The functions here form a bridge between interpreted code and JNI native
1420 * functions. The basic task is to convert an array of primitives and
1421 * references into C-style function arguments. This is architecture-specific
1422 * and usually requires help from assembly code.
1423 *
1424 * The bridge takes four arguments: the array of parameters, a place to
1425 * store the function result (if any), the method to call, and a pointer
1426 * to the current thread.
1427 *
1428 * These functions aren't called directly from elsewhere in the VM.
1429 * A pointer in the Method struct points to one of these, and when a native
1430 * method is invoked the interpreter jumps to it.
1431 *
1432 * (The "internal native" methods are invoked the same way, but instead
1433 * of calling through a bridge, the target method is called directly.)
1434 *
1435 * The "args" array should not be modified, but we do so anyway for
1436 * performance reasons. We know that it points to the "outs" area on
1437 * the current method's interpreted stack. This area is ignored by the
1438 * precise GC, because there is no register map for a native method (for
1439 * an interpreted method the args would be listed in the argument set).
1440 * We know all of the values exist elsewhere on the interpreted stack,
1441 * because the method call setup copies them right before making the call,
1442 * so we don't have to worry about concealing stuff from the GC.
1443 *
1444 * If we don't want to modify "args", we either have to create a local
1445 * copy and modify it before calling dvmPlatformInvoke, or we have to do
1446 * the local reference replacement within dvmPlatformInvoke. The latter
1447 * has some performance advantages, though if we can inline the local
1448 * reference adds we may win when there's a lot of reference args (unless
1449 * we want to code up some local ref table manipulation in assembly.
1450 */
1451
1452/*
Andy McFadden0423f0e2009-08-26 07:21:53 -07001453 * If necessary, convert the value in pResult from a local/global reference
1454 * to an object pointer.
1455 */
1456static inline void convertReferenceResult(JNIEnv* env, JValue* pResult,
1457 const Method* method, Thread* self)
1458{
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001459#ifdef USE_INDIRECT_REF
Andy McFadden0423f0e2009-08-26 07:21:53 -07001460 if (method->shorty[0] == 'L' && !dvmCheckException(self) &&
1461 pResult->l != NULL)
1462 {
1463 pResult->l = dvmDecodeIndirectRef(env, pResult->l);
1464 }
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001465#endif
Andy McFadden0423f0e2009-08-26 07:21:53 -07001466}
1467
1468/*
Andy McFaddend5ab7262009-08-25 07:19:34 -07001469 * General form, handles all cases.
1470 */
1471void dvmCallJNIMethod_general(const u4* args, JValue* pResult,
1472 const Method* method, Thread* self)
1473{
1474 int oldStatus;
1475 u4* modArgs = (u4*) args;
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001476 jclass staticMethodClass;
1477 JNIEnv* env = self->jniEnv;
Andy McFaddend5ab7262009-08-25 07:19:34 -07001478
1479 assert(method->insns != NULL);
1480
1481 //LOGI("JNI calling %p (%s.%s:%s):\n", method->insns,
1482 // method->clazz->descriptor, method->name, method->shorty);
1483
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001484#ifdef USE_INDIRECT_REF
Andy McFaddend5ab7262009-08-25 07:19:34 -07001485 /*
1486 * Walk the argument list, creating local references for appropriate
1487 * arguments.
1488 */
Andy McFaddend5ab7262009-08-25 07:19:34 -07001489 int idx = 0;
1490 if (dvmIsStaticMethod(method)) {
1491 /* add the class object we pass in */
1492 staticMethodClass = addLocalReference(env, (Object*) method->clazz);
1493 if (staticMethodClass == NULL) {
1494 assert(dvmCheckException(self));
1495 return;
1496 }
1497 } else {
1498 /* add "this" */
1499 staticMethodClass = NULL;
1500 jobject thisObj = addLocalReference(env, (Object*) modArgs[0]);
1501 if (thisObj == NULL) {
1502 assert(dvmCheckException(self));
1503 return;
1504 }
1505 modArgs[idx] = (u4) thisObj;
1506 idx = 1;
1507 }
1508
1509 const char* shorty = &method->shorty[1]; /* skip return type */
1510 while (*shorty != '\0') {
1511 switch (*shorty++) {
1512 case 'L':
1513 //LOGI(" local %d: 0x%08x\n", idx, modArgs[idx]);
1514 if (modArgs[idx] != 0) {
1515 //if (!dvmIsValidObject((Object*) modArgs[idx]))
1516 // dvmAbort();
1517 jobject argObj = addLocalReference(env, (Object*) modArgs[idx]);
1518 if (argObj == NULL) {
1519 assert(dvmCheckException(self));
1520 return;
1521 }
1522 modArgs[idx] = (u4) argObj;
1523 }
1524 break;
1525 case 'D':
1526 case 'J':
1527 idx++;
1528 break;
1529 default:
1530 /* Z B C S I -- do nothing */
1531 break;
1532 }
1533
1534 idx++;
1535 }
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001536#else
1537 staticMethodClass = dvmIsStaticMethod(method) ?
1538 (jclass) method->clazz : NULL;
1539#endif
Andy McFaddend5ab7262009-08-25 07:19:34 -07001540
1541 oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
1542
1543 COMPUTE_STACK_SUM(self);
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001544 dvmPlatformInvoke(env, staticMethodClass,
Andy McFaddend5ab7262009-08-25 07:19:34 -07001545 method->jniArgInfo, method->insSize, modArgs, method->shorty,
1546 (void*)method->insns, pResult);
1547 CHECK_STACK_SUM(self);
1548
1549 dvmChangeStatus(self, oldStatus);
Andy McFadden0423f0e2009-08-26 07:21:53 -07001550
1551 convertReferenceResult(env, pResult, method, self);
Andy McFaddend5ab7262009-08-25 07:19:34 -07001552}
1553
1554/*
1555 * Handler for the unusual case of a synchronized native method.
1556 *
1557 * Lock the object, then call through the general function.
1558 */
1559void dvmCallJNIMethod_synchronized(const u4* args, JValue* pResult,
1560 const Method* method, Thread* self)
1561{
1562 Object* lockObj;
1563
1564 assert(dvmIsSynchronizedMethod(method));
1565
1566 if (dvmIsStaticMethod(method))
1567 lockObj = (Object*) method->clazz;
1568 else
1569 lockObj = (Object*) args[0];
1570
1571 LOGVV("Calling %s.%s: locking %p (%s)\n",
1572 method->clazz->descriptor, method->name,
1573 lockObj, lockObj->clazz->descriptor);
1574
1575 dvmLockObject(self, lockObj);
1576 dvmCallJNIMethod_general(args, pResult, method, self);
1577 dvmUnlockObject(self, lockObj);
1578}
1579
1580/*
1581 * Virtual method call, no reference arguments.
1582 *
1583 * We need to local-ref the "this" argument, found in args[0].
1584 */
1585void dvmCallJNIMethod_virtualNoRef(const u4* args, JValue* pResult,
1586 const Method* method, Thread* self)
1587{
1588 u4* modArgs = (u4*) args;
1589 int oldStatus;
1590
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001591#ifdef USE_INDIRECT_REF
Andy McFaddend5ab7262009-08-25 07:19:34 -07001592 jobject thisObj = addLocalReference(self->jniEnv, (Object*) args[0]);
1593 if (thisObj == NULL) {
1594 assert(dvmCheckException(self));
1595 return;
1596 }
1597 modArgs[0] = (u4) thisObj;
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001598#endif
Andy McFaddend5ab7262009-08-25 07:19:34 -07001599
1600 oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
1601
1602 COMPUTE_STACK_SUM(self);
1603 dvmPlatformInvoke(self->jniEnv, NULL,
1604 method->jniArgInfo, method->insSize, modArgs, method->shorty,
1605 (void*)method->insns, pResult);
1606 CHECK_STACK_SUM(self);
1607
1608 dvmChangeStatus(self, oldStatus);
Andy McFadden0423f0e2009-08-26 07:21:53 -07001609
1610 convertReferenceResult(self->jniEnv, pResult, method, self);
Andy McFaddend5ab7262009-08-25 07:19:34 -07001611}
1612
1613/*
1614 * Static method call, no reference arguments.
1615 *
1616 * We need to local-ref the class reference.
1617 */
1618void dvmCallJNIMethod_staticNoRef(const u4* args, JValue* pResult,
1619 const Method* method, Thread* self)
1620{
1621 jclass staticMethodClass;
1622 int oldStatus;
1623
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001624#ifdef USE_INDIRECT_REF
Andy McFaddend5ab7262009-08-25 07:19:34 -07001625 staticMethodClass = addLocalReference(self->jniEnv, (Object*)method->clazz);
1626 if (staticMethodClass == NULL) {
1627 assert(dvmCheckException(self));
1628 return;
1629 }
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001630#else
1631 staticMethodClass = (jobject) method->clazz;
1632#endif
Andy McFaddend5ab7262009-08-25 07:19:34 -07001633
1634 oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
1635
1636 COMPUTE_STACK_SUM(self);
1637 dvmPlatformInvoke(self->jniEnv, staticMethodClass,
1638 method->jniArgInfo, method->insSize, args, method->shorty,
1639 (void*)method->insns, pResult);
1640 CHECK_STACK_SUM(self);
1641
1642 dvmChangeStatus(self, oldStatus);
Andy McFadden0423f0e2009-08-26 07:21:53 -07001643
1644 convertReferenceResult(self->jniEnv, pResult, method, self);
Andy McFaddend5ab7262009-08-25 07:19:34 -07001645}
1646
1647/*
1648 * Extract the return type enum from the "jniArgInfo" field.
1649 */
1650DalvikJniReturnType dvmGetArgInfoReturnType(int jniArgInfo)
1651{
1652 return (jniArgInfo & DALVIK_JNI_RETURN_MASK) >> DALVIK_JNI_RETURN_SHIFT;
1653}
1654
1655
1656/*
1657 * ===========================================================================
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001658 * JNI implementation
1659 * ===========================================================================
1660 */
1661
1662/*
1663 * Return the version of the native method interface.
1664 */
1665static jint GetVersion(JNIEnv* env)
1666{
1667 JNI_ENTER();
1668 /*
1669 * There is absolutely no need to toggle the mode for correct behavior.
1670 * However, it does provide native code with a simple "suspend self
1671 * if necessary" call.
1672 */
1673 JNI_EXIT();
1674 return JNI_VERSION_1_6;
1675}
1676
1677/*
1678 * Create a new class from a bag of bytes.
1679 *
1680 * This is not currently supported within Dalvik.
1681 */
1682static jclass DefineClass(JNIEnv* env, const char *name, jobject loader,
1683 const jbyte* buf, jsize bufLen)
1684{
1685 UNUSED_PARAMETER(name);
1686 UNUSED_PARAMETER(loader);
1687 UNUSED_PARAMETER(buf);
1688 UNUSED_PARAMETER(bufLen);
1689
1690 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001691 LOGW("JNI DefineClass is not supported\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001692 JNI_EXIT();
1693 return NULL;
1694}
1695
1696/*
1697 * Find a class by name.
1698 *
1699 * We have to use the "no init" version of FindClass here, because we might
1700 * be getting the class prior to registering native methods that will be
1701 * used in <clinit>.
1702 *
1703 * We need to get the class loader associated with the current native
1704 * method. If there is no native method, e.g. we're calling this from native
1705 * code right after creating the VM, the spec says we need to use the class
1706 * loader returned by "ClassLoader.getBaseClassLoader". There is no such
1707 * method, but it's likely they meant ClassLoader.getSystemClassLoader.
1708 * We can't get that until after the VM has initialized though.
1709 */
1710static jclass FindClass(JNIEnv* env, const char* name)
1711{
1712 JNI_ENTER();
1713
1714 const Method* thisMethod;
1715 ClassObject* clazz;
Andy McFaddenab00d452009-08-19 07:21:41 -07001716 jclass jclazz = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001717 Object* loader;
1718 char* descriptor = NULL;
1719
1720 thisMethod = dvmGetCurrentJNIMethod();
1721 assert(thisMethod != NULL);
1722
1723 descriptor = dvmNameToDescriptor(name);
1724 if (descriptor == NULL) {
1725 clazz = NULL;
1726 goto bail;
1727 }
1728
1729 //Thread* self = dvmThreadSelf();
1730 if (_self->classLoaderOverride != NULL) {
1731 /* hack for JNI_OnLoad */
1732 assert(strcmp(thisMethod->name, "nativeLoad") == 0);
1733 loader = _self->classLoaderOverride;
1734 } else if (thisMethod == gDvm.methFakeNativeEntry) {
1735 /* start point of invocation interface */
1736 if (!gDvm.initializing)
1737 loader = dvmGetSystemClassLoader();
1738 else
1739 loader = NULL;
1740 } else {
1741 loader = thisMethod->clazz->classLoader;
1742 }
1743
1744 clazz = dvmFindClassNoInit(descriptor, loader);
Andy McFaddenab00d452009-08-19 07:21:41 -07001745 jclazz = addLocalReference(env, (Object*) clazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001746
1747bail:
1748 free(descriptor);
Andy McFaddenab00d452009-08-19 07:21:41 -07001749
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001750 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001751 return jclazz;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001752}
1753
1754/*
1755 * Return the superclass of a class.
1756 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001757static jclass GetSuperclass(JNIEnv* env, jclass jclazz)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001758{
1759 JNI_ENTER();
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001760 jclass jsuper = NULL;
Andy McFaddenab00d452009-08-19 07:21:41 -07001761
1762 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001763 if (clazz != NULL)
Andy McFaddenab00d452009-08-19 07:21:41 -07001764 jsuper = addLocalReference(env, (Object*)clazz->super);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001765 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001766 return jsuper;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001767}
1768
1769/*
1770 * Determine whether an object of clazz1 can be safely cast to clazz2.
1771 *
1772 * Like IsInstanceOf, but with a pair of class objects instead of obj+class.
1773 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001774static jboolean IsAssignableFrom(JNIEnv* env, jclass jclazz1, jclass jclazz2)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001775{
1776 JNI_ENTER();
1777
Andy McFaddenab00d452009-08-19 07:21:41 -07001778 ClassObject* clazz1 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz1);
1779 ClassObject* clazz2 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz2);
1780
1781 jboolean result = dvmInstanceof(clazz1, clazz2);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001782
1783 JNI_EXIT();
1784 return result;
1785}
1786
1787/*
1788 * Given a java.lang.reflect.Method or .Constructor, return a methodID.
1789 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001790static jmethodID FromReflectedMethod(JNIEnv* env, jobject jmethod)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001791{
1792 JNI_ENTER();
1793 jmethodID methodID;
Andy McFaddenab00d452009-08-19 07:21:41 -07001794 Object* method = dvmDecodeIndirectRef(env, jmethod);
1795 methodID = (jmethodID) dvmGetMethodFromReflectObj(method);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001796 JNI_EXIT();
1797 return methodID;
1798}
1799
1800/*
1801 * Given a java.lang.reflect.Field, return a fieldID.
1802 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001803static jfieldID FromReflectedField(JNIEnv* env, jobject jfield)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001804{
1805 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001806 jfieldID fieldID;
1807 Object* field = dvmDecodeIndirectRef(env, jfield);
1808 fieldID = (jfieldID) dvmGetFieldFromReflectObj(field);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001809 JNI_EXIT();
1810 return fieldID;
1811}
1812
1813/*
1814 * Convert a methodID to a java.lang.reflect.Method or .Constructor.
1815 *
1816 * (The "isStatic" field does not appear in the spec.)
1817 *
1818 * Throws OutOfMemory and returns NULL on failure.
1819 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001820static jobject ToReflectedMethod(JNIEnv* env, jclass jcls, jmethodID methodID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001821 jboolean isStatic)
1822{
1823 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001824 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls);
1825 Object* obj = dvmCreateReflectObjForMethod(clazz, (Method*) methodID);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001826 dvmReleaseTrackedAlloc(obj, NULL);
Andy McFaddenab00d452009-08-19 07:21:41 -07001827 jobject jobj = addLocalReference(env, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001828 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001829 return jobj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001830}
1831
1832/*
1833 * Convert a fieldID to a java.lang.reflect.Field.
1834 *
1835 * (The "isStatic" field does not appear in the spec.)
1836 *
1837 * Throws OutOfMemory and returns NULL on failure.
1838 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001839static jobject ToReflectedField(JNIEnv* env, jclass jcls, jfieldID fieldID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001840 jboolean isStatic)
1841{
1842 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001843 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls);
1844 Object* obj = dvmCreateReflectObjForField(jcls, (Field*) fieldID);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001845 dvmReleaseTrackedAlloc(obj, NULL);
Andy McFaddenab00d452009-08-19 07:21:41 -07001846 jobject jobj = addLocalReference(env, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001847 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001848 return jobj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001849}
1850
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001851/*
1852 * Take this exception and throw it.
1853 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001854static jint Throw(JNIEnv* env, jthrowable jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001855{
1856 JNI_ENTER();
1857
1858 jint retval;
1859
Andy McFaddenab00d452009-08-19 07:21:41 -07001860 if (jobj != NULL) {
1861 Object* obj = dvmDecodeIndirectRef(env, jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001862 dvmSetException(_self, obj);
1863 retval = JNI_OK;
Andy McFaddenab00d452009-08-19 07:21:41 -07001864 } else {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001865 retval = JNI_ERR;
Andy McFaddenab00d452009-08-19 07:21:41 -07001866 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001867
1868 JNI_EXIT();
1869 return retval;
1870}
1871
1872/*
Andy McFaddenab00d452009-08-19 07:21:41 -07001873 * Constructs an exception object from the specified class with the message
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001874 * specified by "message", and throws it.
1875 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001876static jint ThrowNew(JNIEnv* env, jclass jclazz, const char* message)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001877{
1878 JNI_ENTER();
1879
Andy McFaddenab00d452009-08-19 07:21:41 -07001880 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1881 dvmThrowExceptionByClass(clazz, message);
1882 // TODO: should return failure if this didn't work (e.g. OOM)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001883
1884 JNI_EXIT();
1885 return JNI_OK;
1886}
1887
1888/*
1889 * If an exception is being thrown, return the exception object. Otherwise,
1890 * return NULL.
1891 *
1892 * TODO: if there is no pending exception, we should be able to skip the
1893 * enter/exit checks. If we find one, we need to enter and then re-fetch
1894 * the exception (in case it got moved by a compacting GC).
1895 */
1896static jthrowable ExceptionOccurred(JNIEnv* env)
1897{
1898 JNI_ENTER();
1899
1900 Object* exception;
Andy McFaddenab00d452009-08-19 07:21:41 -07001901 jobject localException;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001902
Andy McFaddenab00d452009-08-19 07:21:41 -07001903 exception = dvmGetException(_self);
1904 localException = addLocalReference(env, exception);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001905 if (localException == NULL && exception != NULL) {
1906 /*
1907 * We were unable to add a new local reference, and threw a new
1908 * exception. We can't return "exception", because it's not a
1909 * local reference. So we have to return NULL, indicating that
1910 * there was no exception, even though it's pretty much raining
1911 * exceptions in here.
1912 */
1913 LOGW("JNI WARNING: addLocal/exception combo\n");
1914 }
1915
1916 JNI_EXIT();
1917 return localException;
1918}
1919
1920/*
1921 * Print an exception and stack trace to stderr.
1922 */
1923static void ExceptionDescribe(JNIEnv* env)
1924{
1925 JNI_ENTER();
1926
1927 Object* exception = dvmGetException(_self);
1928 if (exception != NULL) {
1929 dvmPrintExceptionStackTrace();
1930 } else {
1931 LOGI("Odd: ExceptionDescribe called, but no exception pending\n");
1932 }
1933
1934 JNI_EXIT();
1935}
1936
1937/*
1938 * Clear the exception currently being thrown.
1939 *
1940 * TODO: we should be able to skip the enter/exit stuff.
1941 */
1942static void ExceptionClear(JNIEnv* env)
1943{
1944 JNI_ENTER();
1945 dvmClearException(_self);
1946 JNI_EXIT();
1947}
1948
1949/*
1950 * Kill the VM. This function does not return.
1951 */
1952static void FatalError(JNIEnv* env, const char* msg)
1953{
1954 //dvmChangeStatus(NULL, THREAD_RUNNING);
1955 LOGE("JNI posting fatal error: %s\n", msg);
1956 dvmAbort();
1957}
1958
1959/*
1960 * Push a new JNI frame on the stack, with a new set of locals.
1961 *
1962 * The new frame must have the same method pointer. (If for no other
1963 * reason than FindClass needs it to get the appropriate class loader.)
1964 */
1965static jint PushLocalFrame(JNIEnv* env, jint capacity)
1966{
1967 JNI_ENTER();
1968 int result = JNI_OK;
Andy McFaddenab00d452009-08-19 07:21:41 -07001969 if (!ensureLocalCapacity(env, capacity) ||
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001970 !dvmPushLocalFrame(_self /*dvmThreadSelf()*/, dvmGetCurrentJNIMethod()))
1971 {
1972 /* yes, OutOfMemoryError, not StackOverflowError */
1973 dvmClearException(_self);
1974 dvmThrowException("Ljava/lang/OutOfMemoryError;",
1975 "out of stack in JNI PushLocalFrame");
1976 result = JNI_ERR;
1977 }
1978 JNI_EXIT();
1979 return result;
1980}
1981
1982/*
1983 * Pop the local frame off. If "result" is not null, add it as a
1984 * local reference on the now-current frame.
1985 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001986static jobject PopLocalFrame(JNIEnv* env, jobject jresult)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001987{
1988 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001989 Object* result = dvmDecodeIndirectRef(env, jresult);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001990 if (!dvmPopLocalFrame(_self /*dvmThreadSelf()*/)) {
1991 LOGW("JNI WARNING: too many PopLocalFrame calls\n");
1992 dvmClearException(_self);
1993 dvmThrowException("Ljava/lang/RuntimeException;",
1994 "too many PopLocalFrame calls");
1995 }
Andy McFaddenab00d452009-08-19 07:21:41 -07001996 jresult = addLocalReference(env, result);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001997 JNI_EXIT();
1998 return result;
1999}
2000
2001/*
2002 * Add a reference to the global list.
2003 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002004static jobject NewGlobalRef(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002005{
2006 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002007 Object* obj = dvmDecodeIndirectRef(env, jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002008 jobject retval = addGlobalReference(obj);
2009 JNI_EXIT();
2010 return retval;
2011}
2012
2013/*
2014 * Delete a reference from the global list.
2015 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002016static void DeleteGlobalRef(JNIEnv* env, jobject jglobalRef)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002017{
2018 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002019 deleteGlobalReference(jglobalRef);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002020 JNI_EXIT();
2021}
2022
2023
2024/*
2025 * Add a reference to the local list.
2026 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002027static jobject NewLocalRef(JNIEnv* env, jobject jref)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002028{
2029 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002030 Object* obj = dvmDecodeIndirectRef(env, jref);
2031 jobject retval = addLocalReference(env, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002032 JNI_EXIT();
2033 return retval;
2034}
2035
2036/*
2037 * Delete a reference from the local list.
2038 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002039static void DeleteLocalRef(JNIEnv* env, jobject jlocalRef)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002040{
2041 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002042 deleteLocalReference(env, jlocalRef);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002043 JNI_EXIT();
2044}
2045
2046/*
2047 * Ensure that the local references table can hold at least this many
2048 * references.
2049 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002050static jint EnsureLocalCapacity(JNIEnv* env, jint capacity)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002051{
2052 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002053 bool okay = ensureLocalCapacity(env, capacity);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002054 if (!okay) {
2055 dvmThrowException("Ljava/lang/OutOfMemoryError;",
2056 "can't ensure local reference capacity");
2057 }
2058 JNI_EXIT();
2059 if (okay)
2060 return 0;
2061 else
2062 return -1;
2063}
2064
2065
2066/*
2067 * Determine whether two Object references refer to the same underlying object.
2068 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002069static jboolean IsSameObject(JNIEnv* env, jobject jref1, jobject jref2)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002070{
2071 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002072 Object* obj1 = dvmDecodeIndirectRef(env, jref1);
2073 Object* obj2 = dvmDecodeIndirectRef(env, jref2);
2074 jboolean result = (obj1 == obj2);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002075 JNI_EXIT();
2076 return result;
2077}
2078
2079/*
2080 * Allocate a new object without invoking any constructors.
2081 */
2082static jobject AllocObject(JNIEnv* env, jclass jclazz)
2083{
2084 JNI_ENTER();
2085
Andy McFaddenab00d452009-08-19 07:21:41 -07002086 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2087 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002088
2089 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2090 assert(dvmCheckException(_self));
Andy McFaddenab00d452009-08-19 07:21:41 -07002091 result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002092 } else {
Andy McFaddenab00d452009-08-19 07:21:41 -07002093 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
2094 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002095 }
2096
2097 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002098 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002099}
2100
2101/*
Andy McFaddenab00d452009-08-19 07:21:41 -07002102 * Allocate a new object and invoke the supplied constructor.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002103 */
2104static jobject NewObject(JNIEnv* env, jclass jclazz, jmethodID methodID, ...)
2105{
2106 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002107 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2108 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002109
2110 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2111 assert(dvmCheckException(_self));
Andy McFaddenab00d452009-08-19 07:21:41 -07002112 result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002113 } else {
Andy McFaddenab00d452009-08-19 07:21:41 -07002114 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
2115 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002116 if (newObj != NULL) {
2117 JValue unused;
2118 va_list args;
2119 va_start(args, methodID);
Andy McFaddend5ab7262009-08-25 07:19:34 -07002120 dvmCallMethodV(_self, (Method*) methodID, newObj, true, &unused,
2121 args);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002122 va_end(args);
2123 }
2124 }
2125
2126 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002127 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002128}
Andy McFaddenab00d452009-08-19 07:21:41 -07002129static jobject NewObjectV(JNIEnv* env, jclass jclazz, jmethodID methodID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002130 va_list args)
2131{
2132 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002133 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2134 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002135
Andy McFaddenab00d452009-08-19 07:21:41 -07002136 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
2137 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002138 if (newObj != NULL) {
2139 JValue unused;
Andy McFaddend5ab7262009-08-25 07:19:34 -07002140 dvmCallMethodV(_self, (Method*) methodID, newObj, true, &unused, args);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002141 }
2142
2143 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002144 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002145}
Andy McFaddenab00d452009-08-19 07:21:41 -07002146static jobject NewObjectA(JNIEnv* env, jclass jclazz, jmethodID methodID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002147 jvalue* args)
2148{
2149 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002150 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2151 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002152
Andy McFaddenab00d452009-08-19 07:21:41 -07002153 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
2154 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002155 if (newObj != NULL) {
2156 JValue unused;
Andy McFaddend5ab7262009-08-25 07:19:34 -07002157 dvmCallMethodA(_self, (Method*) methodID, newObj, true, &unused, args);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002158 }
2159
2160 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002161 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002162}
2163
2164/*
2165 * Returns the class of an object.
2166 *
2167 * JNI spec says: obj must not be NULL.
2168 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002169static jclass GetObjectClass(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002170{
2171 JNI_ENTER();
2172
Andy McFaddenab00d452009-08-19 07:21:41 -07002173 assert(jobj != NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002174
Andy McFaddenab00d452009-08-19 07:21:41 -07002175 Object* obj = dvmDecodeIndirectRef(env, jobj);
2176 jclass jclazz = addLocalReference(env, (Object*) obj->clazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002177
2178 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002179 return jclazz;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002180}
2181
2182/*
2183 * Determine whether "obj" is an instance of "clazz".
2184 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002185static jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass jclazz)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002186{
2187 JNI_ENTER();
2188
Andy McFaddenab00d452009-08-19 07:21:41 -07002189 assert(jclazz != NULL);
2190
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002191 jboolean result;
2192
Andy McFaddenab00d452009-08-19 07:21:41 -07002193 if (jobj == NULL) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002194 result = true;
Andy McFaddenab00d452009-08-19 07:21:41 -07002195 } else {
2196 Object* obj = dvmDecodeIndirectRef(env, jobj);
2197 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2198 result = dvmInstanceof(obj->clazz, clazz);
2199 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002200
2201 JNI_EXIT();
2202 return result;
2203}
2204
2205/*
2206 * Get a method ID for an instance method.
2207 *
2208 * JNI defines <init> as an instance method, but Dalvik considers it a
2209 * "direct" method, so we have to special-case it here.
2210 *
2211 * Dalvik also puts all private methods into the "direct" list, so we
2212 * really need to just search both lists.
2213 */
2214static jmethodID GetMethodID(JNIEnv* env, jclass jclazz, const char* name,
2215 const char* sig)
2216{
2217 JNI_ENTER();
2218
Andy McFaddenab00d452009-08-19 07:21:41 -07002219 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002220 jmethodID id = NULL;
2221
2222 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2223 assert(dvmCheckException(_self));
2224 } else {
2225 Method* meth;
2226
2227 meth = dvmFindVirtualMethodHierByDescriptor(clazz, name, sig);
2228 if (meth == NULL) {
2229 /* search private methods and constructors; non-hierarchical */
2230 meth = dvmFindDirectMethodByDescriptor(clazz, name, sig);
2231 }
2232 if (meth != NULL && dvmIsStaticMethod(meth)) {
2233 IF_LOGD() {
2234 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
2235 LOGD("GetMethodID: not returning static method %s.%s %s\n",
2236 clazz->descriptor, meth->name, desc);
2237 free(desc);
2238 }
2239 meth = NULL;
2240 }
2241 if (meth == NULL) {
Andy McFadden03bd0d52009-08-18 15:32:27 -07002242 LOGD("GetMethodID: method not found: %s.%s:%s\n",
2243 clazz->descriptor, name, sig);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002244 dvmThrowException("Ljava/lang/NoSuchMethodError;", name);
2245 }
2246
2247 /*
2248 * The method's class may not be the same as clazz, but if
2249 * it isn't this must be a virtual method and the class must
2250 * be a superclass (and, hence, already initialized).
2251 */
2252 if (meth != NULL) {
2253 assert(dvmIsClassInitialized(meth->clazz) ||
2254 dvmIsClassInitializing(meth->clazz));
2255 }
2256 id = (jmethodID) meth;
2257 }
2258 JNI_EXIT();
2259 return id;
2260}
2261
2262/*
2263 * Get a field ID (instance fields).
2264 */
2265static jfieldID GetFieldID(JNIEnv* env, jclass jclazz,
2266 const char* name, const char* sig)
2267{
2268 JNI_ENTER();
2269
Andy McFaddenab00d452009-08-19 07:21:41 -07002270 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002271 jfieldID id;
2272
2273 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2274 assert(dvmCheckException(_self));
2275 id = NULL;
2276 } else {
2277 id = (jfieldID) dvmFindInstanceFieldHier(clazz, name, sig);
Andy McFadden03bd0d52009-08-18 15:32:27 -07002278 if (id == NULL) {
2279 LOGD("GetFieldID: unable to find field %s.%s:%s\n",
2280 clazz->descriptor, name, sig);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002281 dvmThrowException("Ljava/lang/NoSuchFieldError;", name);
Andy McFadden03bd0d52009-08-18 15:32:27 -07002282 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002283 }
2284 JNI_EXIT();
2285 return id;
2286}
2287
2288/*
2289 * Get the method ID for a static method in a class.
2290 */
2291static jmethodID GetStaticMethodID(JNIEnv* env, jclass jclazz,
2292 const char* name, const char* sig)
2293{
2294 JNI_ENTER();
2295
Andy McFaddenab00d452009-08-19 07:21:41 -07002296 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002297 jmethodID id;
2298
2299 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2300 assert(dvmCheckException(_self));
2301 id = NULL;
2302 } else {
2303 Method* meth;
2304
2305 meth = dvmFindDirectMethodHierByDescriptor(clazz, name, sig);
2306
2307 /* make sure it's static, not virtual+private */
2308 if (meth != NULL && !dvmIsStaticMethod(meth)) {
2309 IF_LOGD() {
2310 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
2311 LOGD("GetStaticMethodID: "
2312 "not returning nonstatic method %s.%s %s\n",
2313 clazz->descriptor, meth->name, desc);
2314 free(desc);
2315 }
2316 meth = NULL;
2317 }
2318
2319 id = (jmethodID) meth;
2320 if (id == NULL)
2321 dvmThrowException("Ljava/lang/NoSuchMethodError;", name);
2322 }
2323
2324 JNI_EXIT();
2325 return id;
2326}
2327
2328/*
2329 * Get a field ID (static fields).
2330 */
2331static jfieldID GetStaticFieldID(JNIEnv* env, jclass jclazz,
2332 const char* name, const char* sig)
2333{
2334 JNI_ENTER();
2335
Andy McFaddenab00d452009-08-19 07:21:41 -07002336 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002337 jfieldID id;
2338
2339 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2340 assert(dvmCheckException(_self));
2341 id = NULL;
2342 } else {
2343 id = (jfieldID) dvmFindStaticField(clazz, name, sig);
2344 if (id == NULL)
2345 dvmThrowException("Ljava/lang/NoSuchFieldError;", name);
2346 }
2347 JNI_EXIT();
2348 return id;
2349}
2350
2351/*
2352 * Get a static field.
2353 *
2354 * If we get an object reference, add it to the local refs list.
2355 */
2356#define GET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002357 static _ctype GetStatic##_jname##Field(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002358 jfieldID fieldID) \
2359 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002360 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002361 JNI_ENTER(); \
2362 StaticField* sfield = (StaticField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002363 _ctype value; \
2364 if (_isref) { /* only when _ctype==jobject */ \
2365 Object* obj = dvmGetStaticFieldObject(sfield); \
2366 value = (_ctype)(u4)addLocalReference(env, obj); \
2367 } else { \
2368 value = dvmGetStaticField##_jname(sfield); \
2369 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002370 JNI_EXIT(); \
2371 return value; \
2372 }
2373GET_STATIC_TYPE_FIELD(jobject, Object, true);
2374GET_STATIC_TYPE_FIELD(jboolean, Boolean, false);
2375GET_STATIC_TYPE_FIELD(jbyte, Byte, false);
2376GET_STATIC_TYPE_FIELD(jchar, Char, false);
2377GET_STATIC_TYPE_FIELD(jshort, Short, false);
2378GET_STATIC_TYPE_FIELD(jint, Int, false);
2379GET_STATIC_TYPE_FIELD(jlong, Long, false);
2380GET_STATIC_TYPE_FIELD(jfloat, Float, false);
2381GET_STATIC_TYPE_FIELD(jdouble, Double, false);
2382
2383/*
2384 * Set a static field.
2385 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002386#define SET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) \
2387 static void SetStatic##_jname##Field(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002388 jfieldID fieldID, _ctype value) \
2389 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002390 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002391 JNI_ENTER(); \
2392 StaticField* sfield = (StaticField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002393 if (_isref) { /* only when _ctype==jobject */ \
2394 Object* valObj = dvmDecodeIndirectRef(env, (jobject)(u4)value); \
2395 dvmSetStaticFieldObject(sfield, valObj); \
2396 } else { \
2397 dvmSetStaticField##_jname(sfield, value); \
2398 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002399 JNI_EXIT(); \
2400 }
Andy McFaddenab00d452009-08-19 07:21:41 -07002401SET_STATIC_TYPE_FIELD(jobject, Object, true);
2402SET_STATIC_TYPE_FIELD(jboolean, Boolean, false);
2403SET_STATIC_TYPE_FIELD(jbyte, Byte, false);
2404SET_STATIC_TYPE_FIELD(jchar, Char, false);
2405SET_STATIC_TYPE_FIELD(jshort, Short, false);
2406SET_STATIC_TYPE_FIELD(jint, Int, false);
2407SET_STATIC_TYPE_FIELD(jlong, Long, false);
2408SET_STATIC_TYPE_FIELD(jfloat, Float, false);
2409SET_STATIC_TYPE_FIELD(jdouble, Double, false);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002410
2411/*
2412 * Get an instance field.
2413 *
2414 * If we get an object reference, add it to the local refs list.
2415 */
2416#define GET_TYPE_FIELD(_ctype, _jname, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002417 static _ctype Get##_jname##Field(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002418 jfieldID fieldID) \
2419 { \
2420 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002421 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002422 InstField* field = (InstField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002423 _ctype value; \
2424 if (_isref) { /* only when _ctype==jobject */ \
2425 Object* valObj = dvmGetFieldObject(obj, field->byteOffset); \
2426 value = (_ctype)(u4)addLocalReference(env, valObj); \
2427 } else { \
2428 value = dvmGetField##_jname(obj, field->byteOffset); \
2429 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002430 JNI_EXIT(); \
2431 return value; \
2432 }
2433GET_TYPE_FIELD(jobject, Object, true);
2434GET_TYPE_FIELD(jboolean, Boolean, false);
2435GET_TYPE_FIELD(jbyte, Byte, false);
2436GET_TYPE_FIELD(jchar, Char, false);
2437GET_TYPE_FIELD(jshort, Short, false);
2438GET_TYPE_FIELD(jint, Int, false);
2439GET_TYPE_FIELD(jlong, Long, false);
2440GET_TYPE_FIELD(jfloat, Float, false);
2441GET_TYPE_FIELD(jdouble, Double, false);
2442
2443/*
2444 * Set an instance field.
2445 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002446#define SET_TYPE_FIELD(_ctype, _jname, _isref) \
2447 static void Set##_jname##Field(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002448 jfieldID fieldID, _ctype value) \
2449 { \
2450 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002451 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002452 InstField* field = (InstField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002453 if (_isref) { /* only when _ctype==jobject */ \
2454 Object* valObj = dvmDecodeIndirectRef(env, (jobject)(u4)value); \
2455 dvmSetFieldObject(obj, field->byteOffset, valObj); \
2456 } else { \
2457 dvmSetField##_jname(obj, field->byteOffset, value); \
2458 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002459 JNI_EXIT(); \
2460 }
Andy McFaddenab00d452009-08-19 07:21:41 -07002461SET_TYPE_FIELD(jobject, Object, true);
2462SET_TYPE_FIELD(jboolean, Boolean, false);
2463SET_TYPE_FIELD(jbyte, Byte, false);
2464SET_TYPE_FIELD(jchar, Char, false);
2465SET_TYPE_FIELD(jshort, Short, false);
2466SET_TYPE_FIELD(jint, Int, false);
2467SET_TYPE_FIELD(jlong, Long, false);
2468SET_TYPE_FIELD(jfloat, Float, false);
2469SET_TYPE_FIELD(jdouble, Double, false);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002470
2471/*
2472 * Make a virtual method call.
2473 *
2474 * Three versions (..., va_list, jvalue[]) for each return type. If we're
2475 * returning an Object, we have to add it to the local references table.
2476 */
2477#define CALL_VIRTUAL(_ctype, _jname, _retfail, _retok, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002478 static _ctype Call##_jname##Method(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002479 jmethodID methodID, ...) \
2480 { \
2481 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002482 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002483 const Method* meth; \
2484 va_list args; \
2485 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002486 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002487 if (meth == NULL) { \
2488 JNI_EXIT(); \
2489 return _retfail; \
2490 } \
2491 va_start(args, methodID); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002492 dvmCallMethodV(_self, meth, obj, true, &result, args); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002493 va_end(args); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002494 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002495 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002496 JNI_EXIT(); \
2497 return _retok; \
2498 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002499 static _ctype Call##_jname##MethodV(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002500 jmethodID methodID, va_list args) \
2501 { \
2502 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002503 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002504 const Method* meth; \
2505 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002506 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002507 if (meth == NULL) { \
2508 JNI_EXIT(); \
2509 return _retfail; \
2510 } \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002511 dvmCallMethodV(_self, meth, obj, true, &result, args); \
2512 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002513 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002514 JNI_EXIT(); \
2515 return _retok; \
2516 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002517 static _ctype Call##_jname##MethodA(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002518 jmethodID methodID, jvalue* args) \
2519 { \
2520 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002521 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002522 const Method* meth; \
2523 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002524 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002525 if (meth == NULL) { \
2526 JNI_EXIT(); \
2527 return _retfail; \
2528 } \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002529 dvmCallMethodA(_self, meth, obj, true, &result, args); \
2530 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002531 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002532 JNI_EXIT(); \
2533 return _retok; \
2534 }
2535CALL_VIRTUAL(jobject, Object, NULL, result.l, true);
2536CALL_VIRTUAL(jboolean, Boolean, 0, result.z, false);
2537CALL_VIRTUAL(jbyte, Byte, 0, result.b, false);
2538CALL_VIRTUAL(jchar, Char, 0, result.c, false);
2539CALL_VIRTUAL(jshort, Short, 0, result.s, false);
2540CALL_VIRTUAL(jint, Int, 0, result.i, false);
2541CALL_VIRTUAL(jlong, Long, 0, result.j, false);
2542CALL_VIRTUAL(jfloat, Float, 0.0f, result.f, false);
2543CALL_VIRTUAL(jdouble, Double, 0.0, result.d, false);
2544CALL_VIRTUAL(void, Void, , , false);
2545
2546/*
2547 * Make a "non-virtual" method call. We're still calling a virtual method,
2548 * but this time we're not doing an indirection through the object's vtable.
2549 * The "clazz" parameter defines which implementation of a method we want.
2550 *
2551 * Three versions (..., va_list, jvalue[]) for each return type.
2552 */
2553#define CALL_NONVIRTUAL(_ctype, _jname, _retfail, _retok, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002554 static _ctype CallNonvirtual##_jname##Method(JNIEnv* env, jobject jobj, \
2555 jclass jclazz, jmethodID methodID, ...) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002556 { \
2557 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002558 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2559 ClassObject* clazz = \
2560 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002561 const Method* meth; \
2562 va_list args; \
2563 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002564 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002565 if (meth == NULL) { \
2566 JNI_EXIT(); \
2567 return _retfail; \
2568 } \
2569 va_start(args, methodID); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002570 dvmCallMethodV(_self, meth, obj, true, &result, args); \
2571 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002572 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002573 va_end(args); \
2574 JNI_EXIT(); \
2575 return _retok; \
2576 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002577 static _ctype CallNonvirtual##_jname##MethodV(JNIEnv* env, jobject jobj,\
2578 jclass jclazz, jmethodID methodID, va_list args) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002579 { \
2580 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002581 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2582 ClassObject* clazz = \
2583 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002584 const Method* meth; \
2585 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002586 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002587 if (meth == NULL) { \
2588 JNI_EXIT(); \
2589 return _retfail; \
2590 } \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002591 dvmCallMethodV(_self, meth, obj, true, &result, args); \
2592 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002593 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002594 JNI_EXIT(); \
2595 return _retok; \
2596 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002597 static _ctype CallNonvirtual##_jname##MethodA(JNIEnv* env, jobject jobj,\
2598 jclass jclazz, jmethodID methodID, jvalue* args) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002599 { \
2600 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002601 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2602 ClassObject* clazz = \
2603 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002604 const Method* meth; \
2605 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002606 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002607 if (meth == NULL) { \
2608 JNI_EXIT(); \
2609 return _retfail; \
2610 } \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002611 dvmCallMethodA(_self, meth, obj, true, &result, args); \
2612 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002613 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002614 JNI_EXIT(); \
2615 return _retok; \
2616 }
2617CALL_NONVIRTUAL(jobject, Object, NULL, result.l, true);
2618CALL_NONVIRTUAL(jboolean, Boolean, 0, result.z, false);
2619CALL_NONVIRTUAL(jbyte, Byte, 0, result.b, false);
2620CALL_NONVIRTUAL(jchar, Char, 0, result.c, false);
2621CALL_NONVIRTUAL(jshort, Short, 0, result.s, false);
2622CALL_NONVIRTUAL(jint, Int, 0, result.i, false);
2623CALL_NONVIRTUAL(jlong, Long, 0, result.j, false);
2624CALL_NONVIRTUAL(jfloat, Float, 0.0f, result.f, false);
2625CALL_NONVIRTUAL(jdouble, Double, 0.0, result.d, false);
2626CALL_NONVIRTUAL(void, Void, , , false);
2627
2628
2629/*
2630 * Call a static method.
2631 */
2632#define CALL_STATIC(_ctype, _jname, _retfail, _retok, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002633 static _ctype CallStatic##_jname##Method(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002634 jmethodID methodID, ...) \
2635 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002636 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002637 JNI_ENTER(); \
2638 JValue result; \
2639 va_list args; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002640 va_start(args, methodID); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002641 dvmCallMethodV(_self, (Method*)methodID, NULL, true, &result, args);\
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002642 va_end(args); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002643 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002644 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002645 JNI_EXIT(); \
2646 return _retok; \
2647 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002648 static _ctype CallStatic##_jname##MethodV(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002649 jmethodID methodID, va_list args) \
2650 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002651 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002652 JNI_ENTER(); \
2653 JValue result; \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002654 dvmCallMethodV(_self, (Method*)methodID, NULL, true, &result, args);\
2655 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002656 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002657 JNI_EXIT(); \
2658 return _retok; \
2659 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002660 static _ctype CallStatic##_jname##MethodA(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002661 jmethodID methodID, jvalue* args) \
2662 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002663 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002664 JNI_ENTER(); \
2665 JValue result; \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002666 dvmCallMethodA(_self, (Method*)methodID, NULL, true, &result, args);\
2667 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002668 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002669 JNI_EXIT(); \
2670 return _retok; \
2671 }
2672CALL_STATIC(jobject, Object, NULL, result.l, true);
2673CALL_STATIC(jboolean, Boolean, 0, result.z, false);
2674CALL_STATIC(jbyte, Byte, 0, result.b, false);
2675CALL_STATIC(jchar, Char, 0, result.c, false);
2676CALL_STATIC(jshort, Short, 0, result.s, false);
2677CALL_STATIC(jint, Int, 0, result.i, false);
2678CALL_STATIC(jlong, Long, 0, result.j, false);
2679CALL_STATIC(jfloat, Float, 0.0f, result.f, false);
2680CALL_STATIC(jdouble, Double, 0.0, result.d, false);
2681CALL_STATIC(void, Void, , , false);
2682
2683/*
2684 * Create a new String from Unicode data.
2685 *
2686 * If "len" is zero, we will return an empty string even if "unicodeChars"
2687 * is NULL. (The JNI spec is vague here.)
2688 */
2689static jstring NewString(JNIEnv* env, const jchar* unicodeChars, jsize len)
2690{
2691 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002692 jobject retval;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002693
Andy McFaddenab00d452009-08-19 07:21:41 -07002694 StringObject* jstr = dvmCreateStringFromUnicode(unicodeChars, len);
2695 if (jstr == NULL) {
2696 retval = NULL;
2697 } else {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002698 dvmReleaseTrackedAlloc((Object*) jstr, NULL);
Andy McFaddenab00d452009-08-19 07:21:41 -07002699 retval = addLocalReference(env, (Object*) jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002700 }
2701
2702 JNI_EXIT();
Andy McFadden0423f0e2009-08-26 07:21:53 -07002703 return retval;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002704}
2705
2706/*
2707 * Return the length of a String in Unicode character units.
2708 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002709static jsize GetStringLength(JNIEnv* env, jstring jstr)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002710{
2711 JNI_ENTER();
2712
Andy McFaddenab00d452009-08-19 07:21:41 -07002713 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2714 jsize len = dvmStringLen(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002715
2716 JNI_EXIT();
2717 return len;
2718}
2719
Andy McFaddenab00d452009-08-19 07:21:41 -07002720
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002721/*
Andy McFaddenab00d452009-08-19 07:21:41 -07002722 * Get a string's character data.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002723 *
2724 * The result is guaranteed to be valid until ReleaseStringChars is
Andy McFaddenab00d452009-08-19 07:21:41 -07002725 * called, which means we have to pin it or return a copy.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002726 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002727static const jchar* GetStringChars(JNIEnv* env, jstring jstr, jboolean* isCopy)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002728{
2729 JNI_ENTER();
2730
Andy McFaddenab00d452009-08-19 07:21:41 -07002731 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
Andy McFaddend5ab7262009-08-25 07:19:34 -07002732 ArrayObject* strChars = dvmStringCharArray(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002733
Andy McFaddenab00d452009-08-19 07:21:41 -07002734 pinPrimitiveArray(strChars);
2735
2736 const u2* data = dvmStringChars(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002737 if (isCopy != NULL)
2738 *isCopy = JNI_FALSE;
2739
2740 JNI_EXIT();
2741 return (jchar*)data;
2742}
2743
2744/*
2745 * Release our grip on some characters from a string.
2746 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002747static void ReleaseStringChars(JNIEnv* env, jstring jstr, const jchar* chars)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002748{
2749 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002750 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
Andy McFaddend5ab7262009-08-25 07:19:34 -07002751 ArrayObject* strChars = dvmStringCharArray(strObj);
Andy McFaddenab00d452009-08-19 07:21:41 -07002752 unpinPrimitiveArray(strChars);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002753 JNI_EXIT();
2754}
2755
2756/*
2757 * Create a new java.lang.String object from chars in modified UTF-8 form.
2758 *
2759 * The spec doesn't say how to handle a NULL string. Popular desktop VMs
2760 * accept it and return a NULL pointer in response.
2761 */
2762static jstring NewStringUTF(JNIEnv* env, const char* bytes)
2763{
2764 JNI_ENTER();
2765
Andy McFaddenab00d452009-08-19 07:21:41 -07002766 jstring result;
2767
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002768 if (bytes == NULL) {
Andy McFaddenab00d452009-08-19 07:21:41 -07002769 result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002770 } else {
Andy McFaddenab00d452009-08-19 07:21:41 -07002771 /* note newStr could come back NULL on OOM */
2772 StringObject* newStr = dvmCreateStringFromCstr(bytes, ALLOC_DEFAULT);
2773 result = addLocalReference(env, (Object*) newStr);
2774 dvmReleaseTrackedAlloc((Object*)newStr, NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002775 }
2776
2777 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002778 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002779}
2780
2781/*
2782 * Return the length in bytes of the modified UTF-8 form of the string.
2783 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002784static jsize GetStringUTFLength(JNIEnv* env, jstring jstr)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002785{
2786 JNI_ENTER();
2787
Andy McFaddenab00d452009-08-19 07:21:41 -07002788 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2789 jsize len = dvmStringUtf8ByteLen(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002790
2791 JNI_EXIT();
2792 return len;
2793}
2794
2795/*
2796 * Convert "string" to modified UTF-8 and return a pointer. The returned
2797 * value must be released with ReleaseStringUTFChars.
2798 *
2799 * According to the JNI reference, "Returns a pointer to a UTF-8 string,
2800 * or NULL if the operation fails. Returns NULL if and only if an invocation
2801 * of this function has thrown an exception."
2802 *
2803 * The behavior here currently follows that of other open-source VMs, which
2804 * quietly return NULL if "string" is NULL. We should consider throwing an
2805 * NPE. (The CheckJNI code blows up if you try to pass in a NULL string,
2806 * which should catch this sort of thing during development.) Certain other
2807 * VMs will crash with a segmentation fault.
2808 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002809static const char* GetStringUTFChars(JNIEnv* env, jstring jstr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002810 jboolean* isCopy)
2811{
2812 JNI_ENTER();
2813 char* newStr;
2814
Andy McFaddenab00d452009-08-19 07:21:41 -07002815 if (jstr == NULL) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002816 /* this shouldn't happen; throw NPE? */
2817 newStr = NULL;
2818 } else {
2819 if (isCopy != NULL)
2820 *isCopy = JNI_TRUE;
2821
Andy McFaddenab00d452009-08-19 07:21:41 -07002822 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2823 newStr = dvmCreateCstrFromString(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002824 if (newStr == NULL) {
2825 /* assume memory failure */
2826 dvmThrowException("Ljava/lang/OutOfMemoryError;",
2827 "native heap string alloc failed");
2828 }
2829 }
2830
2831 JNI_EXIT();
2832 return newStr;
2833}
2834
2835/*
2836 * Release a string created by GetStringUTFChars().
2837 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002838static void ReleaseStringUTFChars(JNIEnv* env, jstring jstr, const char* utf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002839{
2840 JNI_ENTER();
2841 free((char*)utf);
2842 JNI_EXIT();
2843}
2844
2845/*
2846 * Return the capacity of the array.
2847 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002848static jsize GetArrayLength(JNIEnv* env, jarray jarr)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002849{
2850 JNI_ENTER();
2851
Andy McFaddenab00d452009-08-19 07:21:41 -07002852 ArrayObject* arrObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
2853 jsize length = arrObj->length;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002854
2855 JNI_EXIT();
2856 return length;
2857}
2858
2859/*
2860 * Construct a new array that holds objects from class "elementClass".
2861 */
2862static jobjectArray NewObjectArray(JNIEnv* env, jsize length,
Andy McFaddenab00d452009-08-19 07:21:41 -07002863 jclass jelementClass, jobject jinitialElement)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002864{
2865 JNI_ENTER();
2866
Andy McFaddenab00d452009-08-19 07:21:41 -07002867 jobjectArray newArray = NULL;
2868 ClassObject* elemClassObj =
2869 (ClassObject*) dvmDecodeIndirectRef(env, jelementClass);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002870
2871 if (elemClassObj == NULL) {
2872 dvmThrowException("Ljava/lang/NullPointerException;",
2873 "JNI NewObjectArray");
2874 goto bail;
2875 }
2876
Andy McFaddenab00d452009-08-19 07:21:41 -07002877 ArrayObject* newObj =
2878 dvmAllocObjectArray(elemClassObj, length, ALLOC_DEFAULT);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002879 if (newObj == NULL) {
2880 assert(dvmCheckException(_self));
2881 goto bail;
2882 }
Andy McFaddenab00d452009-08-19 07:21:41 -07002883 newArray = addLocalReference(env, (Object*) newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002884 dvmReleaseTrackedAlloc((Object*) newObj, NULL);
2885
2886 /*
2887 * Initialize the array. Trashes "length".
2888 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002889 if (jinitialElement != NULL) {
2890 Object* initialElement = dvmDecodeIndirectRef(env, jinitialElement);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002891 Object** arrayData = (Object**) newObj->contents;
2892
2893 while (length--)
Andy McFaddenab00d452009-08-19 07:21:41 -07002894 *arrayData++ = initialElement;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002895 }
2896
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002897
2898bail:
2899 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002900 return newArray;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002901}
2902
2903/*
2904 * Get one element of an Object array.
2905 *
2906 * Add the object to the local references table in case the array goes away.
2907 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002908static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray jarr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002909 jsize index)
2910{
2911 JNI_ENTER();
2912
Andy McFaddenab00d452009-08-19 07:21:41 -07002913 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
2914 jobject retval = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002915
Andy McFaddenab00d452009-08-19 07:21:41 -07002916 assert(arrayObj != NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002917
2918 /* check the array bounds */
2919 if (index < 0 || index >= (int) arrayObj->length) {
2920 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
2921 arrayObj->obj.clazz->descriptor);
2922 goto bail;
2923 }
2924
Andy McFaddenab00d452009-08-19 07:21:41 -07002925 Object* value = ((Object**) arrayObj->contents)[index];
2926 retval = addLocalReference(env, value);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002927
2928bail:
2929 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002930 return retval;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002931}
2932
2933/*
2934 * Set one element of an Object array.
2935 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002936static void SetObjectArrayElement(JNIEnv* env, jobjectArray jarr,
2937 jsize index, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002938{
2939 JNI_ENTER();
2940
Andy McFaddenab00d452009-08-19 07:21:41 -07002941 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002942
Andy McFaddenab00d452009-08-19 07:21:41 -07002943 assert(arrayObj != NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002944
2945 /* check the array bounds */
2946 if (index < 0 || index >= (int) arrayObj->length) {
2947 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
2948 arrayObj->obj.clazz->descriptor);
2949 goto bail;
2950 }
2951
2952 //LOGV("JNI: set element %d in array %p to %p\n", index, array, value);
2953
Andy McFadden0423f0e2009-08-26 07:21:53 -07002954 Object* obj = dvmDecodeIndirectRef(env, jobj);
Andy McFaddenab00d452009-08-19 07:21:41 -07002955 ((Object**) arrayObj->contents)[index] = obj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002956
2957bail:
2958 JNI_EXIT();
2959}
2960
2961/*
2962 * Create a new array of primitive elements.
2963 */
2964#define NEW_PRIMITIVE_ARRAY(_artype, _jname, _typechar) \
2965 static _artype New##_jname##Array(JNIEnv* env, jsize length) \
2966 { \
2967 JNI_ENTER(); \
2968 ArrayObject* arrayObj; \
2969 arrayObj = dvmAllocPrimitiveArray(_typechar, length, \
2970 ALLOC_DEFAULT); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002971 jarray jarr = NULL; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002972 if (arrayObj != NULL) { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002973 jarr = addLocalReference(env, (Object*) arrayObj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002974 dvmReleaseTrackedAlloc((Object*) arrayObj, NULL); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002975 } \
2976 JNI_EXIT(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002977 return (_artype)jarr; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002978 }
2979NEW_PRIMITIVE_ARRAY(jbooleanArray, Boolean, 'Z');
2980NEW_PRIMITIVE_ARRAY(jbyteArray, Byte, 'B');
2981NEW_PRIMITIVE_ARRAY(jcharArray, Char, 'C');
2982NEW_PRIMITIVE_ARRAY(jshortArray, Short, 'S');
2983NEW_PRIMITIVE_ARRAY(jintArray, Int, 'I');
2984NEW_PRIMITIVE_ARRAY(jlongArray, Long, 'J');
2985NEW_PRIMITIVE_ARRAY(jfloatArray, Float, 'F');
2986NEW_PRIMITIVE_ARRAY(jdoubleArray, Double, 'D');
2987
2988/*
2989 * Get a pointer to a C array of primitive elements from an array object
2990 * of the matching type.
2991 *
Andy McFaddenab00d452009-08-19 07:21:41 -07002992 * In a compacting GC, we either need to return a copy of the elements or
2993 * "pin" the memory. Otherwise we run the risk of native code using the
2994 * buffer as the destination of e.g. a blocking read() call that wakes up
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002995 * during a GC.
2996 */
2997#define GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
2998 static _ctype* Get##_jname##ArrayElements(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07002999 _ctype##Array jarr, jboolean* isCopy) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003000 { \
3001 JNI_ENTER(); \
3002 _ctype* data; \
Andy McFaddenab00d452009-08-19 07:21:41 -07003003 ArrayObject* arrayObj = \
3004 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
3005 pinPrimitiveArray(arrayObj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003006 data = (_ctype*) arrayObj->contents; \
3007 if (isCopy != NULL) \
3008 *isCopy = JNI_FALSE; \
3009 JNI_EXIT(); \
3010 return data; \
3011 }
3012
3013/*
3014 * Release the storage locked down by the "get" function.
3015 *
Andy McFaddenab00d452009-08-19 07:21:41 -07003016 * 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 -08003017 * elements in 'array'." They apparently did not anticipate the need to
Andy McFaddenab00d452009-08-19 07:21:41 -07003018 * un-pin memory.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003019 */
3020#define RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
3021 static void Release##_jname##ArrayElements(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07003022 _ctype##Array jarr, _ctype* elems, jint mode) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003023 { \
3024 UNUSED_PARAMETER(elems); \
3025 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07003026 if (mode != JNI_COMMIT) { \
3027 ArrayObject* arrayObj = \
3028 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
3029 unpinPrimitiveArray(arrayObj); \
3030 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003031 JNI_EXIT(); \
3032 }
3033
3034/*
3035 * Copy a section of a primitive array to a buffer.
3036 */
3037#define GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
3038 static void Get##_jname##ArrayRegion(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07003039 _ctype##Array jarr, jsize start, jsize len, _ctype* buf) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003040 { \
3041 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07003042 ArrayObject* arrayObj = \
3043 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003044 _ctype* data = (_ctype*) arrayObj->contents; \
3045 if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
3046 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
3047 arrayObj->obj.clazz->descriptor); \
3048 } else { \
3049 memcpy(buf, data + start, len * sizeof(_ctype)); \
3050 } \
3051 JNI_EXIT(); \
3052 }
3053
3054/*
3055 * Copy a section of a primitive array to a buffer.
3056 */
3057#define SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
3058 static void Set##_jname##ArrayRegion(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07003059 _ctype##Array jarr, jsize start, jsize len, const _ctype* buf) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003060 { \
3061 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07003062 ArrayObject* arrayObj = \
3063 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003064 _ctype* data = (_ctype*) arrayObj->contents; \
3065 if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
3066 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
3067 arrayObj->obj.clazz->descriptor); \
3068 } else { \
3069 memcpy(data + start, buf, len * sizeof(_ctype)); \
3070 } \
3071 JNI_EXIT(); \
3072 }
3073
3074/*
3075 * 4-in-1:
3076 * Get<Type>ArrayElements
3077 * Release<Type>ArrayElements
3078 * Get<Type>ArrayRegion
3079 * Set<Type>ArrayRegion
3080 */
3081#define PRIMITIVE_ARRAY_FUNCTIONS(_ctype, _jname) \
3082 GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
3083 RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
3084 GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname); \
3085 SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname);
3086
3087PRIMITIVE_ARRAY_FUNCTIONS(jboolean, Boolean);
3088PRIMITIVE_ARRAY_FUNCTIONS(jbyte, Byte);
3089PRIMITIVE_ARRAY_FUNCTIONS(jchar, Char);
3090PRIMITIVE_ARRAY_FUNCTIONS(jshort, Short);
3091PRIMITIVE_ARRAY_FUNCTIONS(jint, Int);
3092PRIMITIVE_ARRAY_FUNCTIONS(jlong, Long);
3093PRIMITIVE_ARRAY_FUNCTIONS(jfloat, Float);
3094PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double);
3095
3096/*
3097 * Register one or more native functions in one class.
3098 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003099static jint RegisterNatives(JNIEnv* env, jclass jclazz,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003100 const JNINativeMethod* methods, jint nMethods)
3101{
3102 JNI_ENTER();
3103
Andy McFaddenab00d452009-08-19 07:21:41 -07003104 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003105 jint retval;
3106 int i;
3107
3108 if (gDvm.verboseJni) {
3109 LOGI("[Registering JNI native methods for class %s]\n",
Andy McFaddenab00d452009-08-19 07:21:41 -07003110 clazz->descriptor);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003111 }
3112
3113 for (i = 0; i < nMethods; i++) {
Andy McFaddenab00d452009-08-19 07:21:41 -07003114 if (!dvmRegisterJNIMethod(clazz, methods[i].name,
3115 methods[i].signature, methods[i].fnPtr))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003116 {
3117 retval = JNI_ERR;
3118 goto bail;
3119 }
3120 }
3121 retval = JNI_OK;
3122
3123bail:
3124 JNI_EXIT();
3125 return retval;
3126}
3127
3128/*
3129 * Un-register a native function.
3130 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003131static jint UnregisterNatives(JNIEnv* env, jclass jclazz)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003132{
3133 JNI_ENTER();
3134 /*
3135 * The JNI docs refer to this as a way to reload/relink native libraries,
3136 * and say it "should not be used in normal native code".
3137 *
3138 * We can implement it if we decide we need it.
3139 */
3140 JNI_EXIT();
3141 return JNI_ERR;
3142}
3143
3144/*
3145 * Lock the monitor.
3146 *
3147 * We have to track all monitor enters and exits, so that we can undo any
3148 * outstanding synchronization before the thread exits.
3149 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003150static jint MonitorEnter(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003151{
3152 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003153 Object* obj = dvmDecodeIndirectRef(env, jobj);
3154 dvmLockObject(_self, obj);
3155 trackMonitorEnter(_self, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003156 JNI_EXIT();
3157 return JNI_OK;
3158}
3159
3160/*
3161 * Unlock the monitor.
3162 *
3163 * Throws an IllegalMonitorStateException if the current thread
Andy McFaddenab00d452009-08-19 07:21:41 -07003164 * doesn't own the monitor. (dvmUnlockObject() takes care of the throw.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003165 *
3166 * According to the 1.6 spec, it's legal to call here with an exception
3167 * pending. If this fails, we'll stomp the original exception.
3168 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003169static jint MonitorExit(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003170{
3171 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003172 Object* obj = dvmDecodeIndirectRef(env, jobj);
3173 bool success = dvmUnlockObject(_self, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003174 if (success)
Andy McFaddenab00d452009-08-19 07:21:41 -07003175 trackMonitorExit(_self, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003176 JNI_EXIT();
3177 return success ? JNI_OK : JNI_ERR;
3178}
3179
3180/*
3181 * Return the JavaVM interface associated with the current thread.
3182 */
3183static jint GetJavaVM(JNIEnv* env, JavaVM** vm)
3184{
3185 JNI_ENTER();
3186 //*vm = gDvm.vmList;
3187 *vm = (JavaVM*) ((JNIEnvExt*)env)->vm;
3188 JNI_EXIT();
3189 if (*vm == NULL)
3190 return JNI_ERR;
3191 else
3192 return JNI_OK;
3193}
3194
3195/*
3196 * Copies "len" Unicode characters, from offset "start".
3197 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003198static void GetStringRegion(JNIEnv* env, jstring jstr, jsize start, jsize len,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003199 jchar* buf)
3200{
3201 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003202 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003203 if (start + len > dvmStringLen(strObj))
3204 dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
3205 else
3206 memcpy(buf, dvmStringChars(strObj) + start, len * sizeof(u2));
3207 JNI_EXIT();
3208}
3209
3210/*
3211 * Translates "len" Unicode characters, from offset "start", into
3212 * modified UTF-8 encoding.
3213 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003214static void GetStringUTFRegion(JNIEnv* env, jstring jstr, jsize start,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003215 jsize len, char* buf)
3216{
3217 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003218 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003219 if (start + len > dvmStringLen(strObj))
3220 dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
3221 else
3222 dvmCreateCstrFromStringRegion(strObj, start, len, buf);
3223 JNI_EXIT();
3224}
3225
3226/*
3227 * Get a raw pointer to array data.
3228 *
3229 * The caller is expected to call "release" before doing any JNI calls
3230 * or blocking I/O operations.
3231 *
Andy McFaddenab00d452009-08-19 07:21:41 -07003232 * We need to pin the memory or block GC.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003233 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003234static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray jarr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003235 jboolean* isCopy)
3236{
3237 JNI_ENTER();
3238 void* data;
Andy McFaddenab00d452009-08-19 07:21:41 -07003239 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
3240 pinPrimitiveArray(arrayObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003241 data = arrayObj->contents;
3242 if (isCopy != NULL)
3243 *isCopy = JNI_FALSE;
3244 JNI_EXIT();
3245 return data;
3246}
3247
3248/*
3249 * Release an array obtained with GetPrimitiveArrayCritical.
3250 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003251static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray jarr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003252 void* carray, jint mode)
3253{
3254 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003255 if (mode != JNI_COMMIT) {
3256 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
3257 unpinPrimitiveArray(arrayObj);
3258 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003259 JNI_EXIT();
3260}
3261
3262/*
3263 * Like GetStringChars, but with restricted use.
3264 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003265static const jchar* GetStringCritical(JNIEnv* env, jstring jstr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003266 jboolean* isCopy)
3267{
3268 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003269 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
3270 ArrayObject* strChars = dvmStringCharArray(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003271
Andy McFaddenab00d452009-08-19 07:21:41 -07003272 pinPrimitiveArray(strChars);
3273
3274 const u2* data = dvmStringChars(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003275 if (isCopy != NULL)
3276 *isCopy = JNI_FALSE;
3277
3278 JNI_EXIT();
3279 return (jchar*)data;
3280}
3281
3282/*
3283 * Like ReleaseStringChars, but with restricted use.
3284 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003285static void ReleaseStringCritical(JNIEnv* env, jstring jstr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003286 const jchar* carray)
3287{
3288 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003289 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
Andy McFadden0423f0e2009-08-26 07:21:53 -07003290 ArrayObject* strChars = dvmStringCharArray(strObj);
Andy McFaddenab00d452009-08-19 07:21:41 -07003291 unpinPrimitiveArray(strChars);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003292 JNI_EXIT();
3293}
3294
3295/*
3296 * Create a new weak global reference.
3297 */
3298static jweak NewWeakGlobalRef(JNIEnv* env, jobject obj)
3299{
3300 JNI_ENTER();
3301 // TODO - implement
3302 jobject gref = NULL;
3303 LOGE("JNI ERROR: NewWeakGlobalRef not implemented\n");
3304 dvmAbort();
3305 JNI_EXIT();
3306 return gref;
3307}
3308
3309/*
3310 * Delete the specified weak global reference.
3311 */
3312static void DeleteWeakGlobalRef(JNIEnv* env, jweak obj)
3313{
3314 JNI_ENTER();
3315 // TODO - implement
3316 LOGE("JNI ERROR: DeleteWeakGlobalRef not implemented\n");
3317 dvmAbort();
3318 JNI_EXIT();
3319}
3320
3321/*
3322 * Quick check for pending exceptions.
3323 *
3324 * TODO: we should be able to skip the enter/exit macros here.
3325 */
3326static jboolean ExceptionCheck(JNIEnv* env)
3327{
3328 JNI_ENTER();
3329 bool result = dvmCheckException(_self);
3330 JNI_EXIT();
3331 return result;
3332}
3333
3334/*
3335 * Returns the type of the object referred to by "obj". It can be local,
3336 * global, or weak global.
3337 *
3338 * In the current implementation, references can be global and local at
3339 * the same time, so while the return value is accurate it may not tell
3340 * the whole story.
3341 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003342static jobjectRefType GetObjectRefType(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003343{
3344 JNI_ENTER();
Andy McFadden0423f0e2009-08-26 07:21:53 -07003345 jobjectRefType type = dvmGetJNIRefType(env, jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003346 JNI_EXIT();
3347 return type;
3348}
3349
3350/*
3351 * Allocate and return a new java.nio.ByteBuffer for this block of memory.
3352 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003353 * "address" may not be NULL, and "capacity" must be > 0. (These are only
3354 * verified when CheckJNI is enabled.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003355 */
Andy McFadden8e5c7842009-07-23 17:47:18 -07003356static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003357{
Andy McFadden8e5c7842009-07-23 17:47:18 -07003358 JNI_ENTER();
3359
3360 Thread* self = _self /*dvmThreadSelf()*/;
3361 Object* platformAddress = NULL;
3362 JValue callResult;
The Android Open Source Project99409882009-03-18 22:20:24 -07003363 jobject result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003364
Andy McFadden8e5c7842009-07-23 17:47:18 -07003365 /* get an instance of PlatformAddress that wraps the provided address */
3366 dvmCallMethod(self,
3367 gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on,
3368 NULL, &callResult, address);
3369 if (dvmGetException(self) != NULL || callResult.l == NULL)
The Android Open Source Project99409882009-03-18 22:20:24 -07003370 goto bail;
Andy McFadden8e5c7842009-07-23 17:47:18 -07003371
3372 /* don't let the GC discard it */
3373 platformAddress = (Object*) callResult.l;
3374 dvmAddTrackedAlloc(platformAddress, self);
3375 LOGV("tracking %p for address=%p\n", platformAddress, address);
3376
3377 /* create an instance of java.nio.ReadWriteDirectByteBuffer */
3378 ClassObject* clazz = gDvm.classJavaNioReadWriteDirectByteBuffer;
3379 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))
3380 goto bail;
3381 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
3382 if (newObj != NULL) {
3383 /* call the (PlatformAddress, int, int) constructor */
Andy McFaddenab00d452009-08-19 07:21:41 -07003384 result = addLocalReference(env, newObj);
Andy McFadden8e5c7842009-07-23 17:47:18 -07003385 dvmCallMethod(self, gDvm.methJavaNioReadWriteDirectByteBuffer_init,
3386 newObj, &callResult, platformAddress, (jint) capacity, (jint) 0);
Andy McFaddenab00d452009-08-19 07:21:41 -07003387 if (dvmGetException(self) != NULL) {
3388 deleteLocalReference(env, result);
3389 result = NULL;
Andy McFadden8e5c7842009-07-23 17:47:18 -07003390 goto bail;
Andy McFaddenab00d452009-08-19 07:21:41 -07003391 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003392 }
3393
The Android Open Source Project99409882009-03-18 22:20:24 -07003394bail:
Andy McFadden8e5c7842009-07-23 17:47:18 -07003395 if (platformAddress != NULL)
3396 dvmReleaseTrackedAlloc(platformAddress, self);
3397 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07003398 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003399}
3400
3401/*
3402 * Get the starting address of the buffer for the specified java.nio.Buffer.
3403 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003404 * If this is not a "direct" buffer, we return NULL.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003405 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003406static void* GetDirectBufferAddress(JNIEnv* env, jobject jbuf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003407{
Andy McFadden8e5c7842009-07-23 17:47:18 -07003408 JNI_ENTER();
3409
Andy McFaddenab00d452009-08-19 07:21:41 -07003410 Object* bufObj = dvmDecodeIndirectRef(env, jbuf);
Andy McFadden8e5c7842009-07-23 17:47:18 -07003411 Thread* self = _self /*dvmThreadSelf()*/;
Andy McFadden8e696dc2009-07-24 15:28:16 -07003412 void* result;
3413
3414 /*
3415 * All Buffer objects have an effectiveDirectAddress field. If it's
3416 * nonzero, we can just return that value. If not, we have to call
3417 * through DirectBuffer.getEffectiveAddress(), which as a side-effect
3418 * will set the effectiveDirectAddress field for direct buffers (and
3419 * things that wrap direct buffers).
3420 */
3421 result = (void*) dvmGetFieldInt(bufObj,
3422 gDvm.offJavaNioBuffer_effectiveDirectAddress);
3423 if (result != NULL) {
3424 //LOGI("fast path for %p\n", buf);
3425 goto bail;
3426 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003427
Andy McFadden72d61fb2009-06-24 16:56:06 -07003428 /*
3429 * Start by determining if the object supports the DirectBuffer
3430 * interfaces. Note this does not guarantee that it's a direct buffer.
3431 */
Andy McFadden8e5c7842009-07-23 17:47:18 -07003432 if (!dvmInstanceof(bufObj->clazz,
3433 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003434 {
The Android Open Source Project99409882009-03-18 22:20:24 -07003435 goto bail;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003436 }
3437
Andy McFadden72d61fb2009-06-24 16:56:06 -07003438 /*
Andy McFadden8e5c7842009-07-23 17:47:18 -07003439 * Get a PlatformAddress object with the effective address.
Andy McFadden5f612b82009-07-22 15:07:27 -07003440 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003441 * If this isn't a direct buffer, the result will be NULL and/or an
Andy McFadden72d61fb2009-06-24 16:56:06 -07003442 * exception will have been thrown.
3443 */
Andy McFadden8e696dc2009-07-24 15:28:16 -07003444 JValue callResult;
Andy McFadden8e5c7842009-07-23 17:47:18 -07003445 const Method* meth = dvmGetVirtualizedMethod(bufObj->clazz,
3446 gDvm.methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress);
Andy McFaddend5ab7262009-08-25 07:19:34 -07003447 dvmCallMethodA(self, meth, bufObj, false, &callResult, NULL);
Andy McFadden8e5c7842009-07-23 17:47:18 -07003448 if (dvmGetException(self) != NULL) {
3449 dvmClearException(self);
3450 callResult.l = NULL;
Andy McFadden72d61fb2009-06-24 16:56:06 -07003451 }
Andy McFadden8e5c7842009-07-23 17:47:18 -07003452
Andy McFadden8e696dc2009-07-24 15:28:16 -07003453 Object* platformAddr = callResult.l;
Andy McFadden72d61fb2009-06-24 16:56:06 -07003454 if (platformAddr == NULL) {
Andy McFadden8e696dc2009-07-24 15:28:16 -07003455 LOGV("Got request for address of non-direct buffer\n");
Andy McFadden72d61fb2009-06-24 16:56:06 -07003456 goto bail;
3457 }
3458
Andy McFadden8e5c7842009-07-23 17:47:18 -07003459 /*
3460 * Extract the address from the PlatformAddress object. Instead of
3461 * calling the toLong() method, just grab the field directly. This
3462 * is faster but more fragile.
3463 */
3464 result = (void*) dvmGetFieldInt(platformAddr,
3465 gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr);
The Android Open Source Project99409882009-03-18 22:20:24 -07003466
Andy McFadden8e696dc2009-07-24 15:28:16 -07003467 //LOGI("slow path for %p --> %p\n", buf, result);
3468
The Android Open Source Project99409882009-03-18 22:20:24 -07003469bail:
Andy McFadden8e5c7842009-07-23 17:47:18 -07003470 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07003471 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003472}
3473
3474/*
3475 * Get the capacity of the buffer for the specified java.nio.Buffer.
3476 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003477 * Returns -1 if the object is not a direct buffer. (We actually skip
3478 * this check, since it's expensive to determine, and just return the
3479 * capacity regardless.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003480 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003481static jlong GetDirectBufferCapacity(JNIEnv* env, jobject jbuf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003482{
Andy McFadden8e5c7842009-07-23 17:47:18 -07003483 JNI_ENTER();
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003484
Andy McFadden8e5c7842009-07-23 17:47:18 -07003485 /*
3486 * The capacity is always in the Buffer.capacity field.
3487 *
3488 * (The "check" version should verify that this is actually a Buffer,
3489 * but we're not required to do so here.)
3490 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003491 Object* buf = dvmDecodeIndirectRef(env, jbuf);
3492 jlong result = dvmGetFieldInt(buf, gDvm.offJavaNioBuffer_capacity);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003493
Andy McFadden8e5c7842009-07-23 17:47:18 -07003494 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07003495 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003496}
3497
3498
3499/*
3500 * ===========================================================================
3501 * JNI invocation functions
3502 * ===========================================================================
3503 */
3504
3505/*
3506 * Handle AttachCurrentThread{AsDaemon}.
3507 *
3508 * We need to make sure the VM is actually running. For example, if we start
3509 * up, issue an Attach, and the VM exits almost immediately, by the time the
3510 * attaching happens the VM could already be shutting down.
3511 *
3512 * It's hard to avoid a race condition here because we don't want to hold
3513 * a lock across the entire operation. What we can do is temporarily
3514 * increment the thread count to prevent a VM exit.
3515 *
3516 * This could potentially still have problems if a daemon thread calls here
3517 * while the VM is shutting down. dvmThreadSelf() will work, since it just
3518 * uses pthread TLS, but dereferencing "vm" could fail. Such is life when
3519 * you shut down a VM while threads are still running inside it.
3520 *
3521 * Remember that some code may call this as a way to find the per-thread
3522 * JNIEnv pointer. Don't do excess work for that case.
3523 */
3524static jint attachThread(JavaVM* vm, JNIEnv** p_env, void* thr_args,
3525 bool isDaemon)
3526{
3527 JavaVMAttachArgs* args = (JavaVMAttachArgs*) thr_args;
3528 Thread* self;
3529 bool result = false;
3530
3531 /*
3532 * Return immediately if we're already one with the VM.
3533 */
3534 self = dvmThreadSelf();
3535 if (self != NULL) {
3536 *p_env = self->jniEnv;
3537 return JNI_OK;
3538 }
3539
3540 /*
3541 * No threads allowed in zygote mode.
3542 */
3543 if (gDvm.zygote) {
3544 return JNI_ERR;
3545 }
3546
3547 /* increment the count to keep the VM from bailing while we run */
3548 dvmLockThreadList(NULL);
3549 if (gDvm.nonDaemonThreadCount == 0) {
3550 // dead or dying
3551 LOGV("Refusing to attach thread '%s' -- VM is shutting down\n",
3552 (thr_args == NULL) ? "(unknown)" : args->name);
3553 dvmUnlockThreadList();
3554 return JNI_ERR;
3555 }
3556 gDvm.nonDaemonThreadCount++;
3557 dvmUnlockThreadList();
3558
3559 /* tweak the JavaVMAttachArgs as needed */
3560 JavaVMAttachArgs argsCopy;
3561 if (args == NULL) {
3562 /* allow the v1.1 calling convention */
3563 argsCopy.version = JNI_VERSION_1_2;
3564 argsCopy.name = NULL;
3565 argsCopy.group = dvmGetMainThreadGroup();
3566 } else {
3567 assert(args->version >= JNI_VERSION_1_2);
3568
3569 argsCopy.version = args->version;
3570 argsCopy.name = args->name;
3571 if (args->group != NULL)
3572 argsCopy.group = args->group;
3573 else
3574 argsCopy.group = dvmGetMainThreadGroup();
3575 }
3576
3577 result = dvmAttachCurrentThread(&argsCopy, isDaemon);
3578
3579 /* restore the count */
3580 dvmLockThreadList(NULL);
3581 gDvm.nonDaemonThreadCount--;
3582 dvmUnlockThreadList();
3583
3584 /*
3585 * Change the status to indicate that we're out in native code. This
3586 * call is not guarded with state-change macros, so we have to do it
3587 * by hand.
3588 */
3589 if (result) {
3590 self = dvmThreadSelf();
3591 assert(self != NULL);
3592 dvmChangeStatus(self, THREAD_NATIVE);
3593 *p_env = self->jniEnv;
3594 return JNI_OK;
3595 } else {
3596 return JNI_ERR;
3597 }
3598}
3599
3600/*
3601 * Attach the current thread to the VM. If the thread is already attached,
3602 * this is a no-op.
3603 */
3604static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args)
3605{
3606 return attachThread(vm, p_env, thr_args, false);
3607}
3608
3609/*
3610 * Like AttachCurrentThread, but set the "daemon" flag.
3611 */
3612static jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env,
3613 void* thr_args)
3614{
3615 return attachThread(vm, p_env, thr_args, true);
3616}
3617
3618/*
3619 * Dissociate the current thread from the VM.
3620 */
3621static jint DetachCurrentThread(JavaVM* vm)
3622{
3623 Thread* self = dvmThreadSelf();
3624
3625 if (self == NULL) /* not attached, can't do anything */
3626 return JNI_ERR;
3627
3628 /* switch to "running" to check for suspension */
3629 dvmChangeStatus(self, THREAD_RUNNING);
3630
3631 /* detach the thread */
3632 dvmDetachCurrentThread();
3633
3634 /* (no need to change status back -- we have no status) */
3635 return JNI_OK;
3636}
3637
3638/*
3639 * If current thread is attached to VM, return the associated JNIEnv.
3640 * Otherwise, stuff NULL in and return JNI_EDETACHED.
3641 *
3642 * JVMTI overloads this by specifying a magic value for "version", so we
3643 * do want to check that here.
3644 */
3645static jint GetEnv(JavaVM* vm, void** env, jint version)
3646{
3647 Thread* self = dvmThreadSelf();
3648
3649 if (version < JNI_VERSION_1_1 || version > JNI_VERSION_1_6)
3650 return JNI_EVERSION;
3651
3652 if (self == NULL) {
3653 *env = NULL;
3654 } else {
3655 /* TODO: status change is probably unnecessary */
3656 dvmChangeStatus(self, THREAD_RUNNING);
3657 *env = (void*) dvmGetThreadJNIEnv(self);
3658 dvmChangeStatus(self, THREAD_NATIVE);
3659 }
3660 if (*env == NULL)
3661 return JNI_EDETACHED;
3662 else
3663 return JNI_OK;
3664}
3665
3666/*
3667 * Destroy the VM. This may be called from any thread.
3668 *
3669 * If the current thread is attached, wait until the current thread is
3670 * the only non-daemon user-level thread. If the current thread is not
3671 * attached, we attach it and do the processing as usual. (If the attach
3672 * fails, it's probably because all the non-daemon threads have already
3673 * exited and the VM doesn't want to let us back in.)
3674 *
3675 * TODO: we don't really deal with the situation where more than one thread
3676 * has called here. One thread wins, the other stays trapped waiting on
3677 * the condition variable forever. Not sure this situation is interesting
3678 * in real life.
3679 */
3680static jint DestroyJavaVM(JavaVM* vm)
3681{
3682 JavaVMExt* ext = (JavaVMExt*) vm;
3683 Thread* self;
3684
3685 if (ext == NULL)
3686 return JNI_ERR;
3687
3688 LOGD("DestroyJavaVM waiting for non-daemon threads to exit\n");
3689
3690 /*
3691 * Sleep on a condition variable until it's okay to exit.
3692 */
3693 self = dvmThreadSelf();
3694 if (self == NULL) {
3695 JNIEnv* tmpEnv;
3696 if (AttachCurrentThread(vm, &tmpEnv, NULL) != JNI_OK) {
3697 LOGV("Unable to reattach main for Destroy; assuming VM is "
3698 "shutting down (count=%d)\n",
3699 gDvm.nonDaemonThreadCount);
3700 goto shutdown;
3701 } else {
3702 LOGV("Attached to wait for shutdown in Destroy\n");
3703 }
3704 }
3705 dvmChangeStatus(self, THREAD_VMWAIT);
3706
3707 dvmLockThreadList(self);
3708 gDvm.nonDaemonThreadCount--; // remove current thread from count
3709
3710 while (gDvm.nonDaemonThreadCount > 0)
3711 pthread_cond_wait(&gDvm.vmExitCond, &gDvm.threadListLock);
3712
3713 dvmUnlockThreadList();
3714 self = NULL;
3715
3716shutdown:
3717 // TODO: call System.exit() to run any registered shutdown hooks
3718 // (this may not return -- figure out how this should work)
3719
3720 LOGD("DestroyJavaVM shutting VM down\n");
3721 dvmShutdown();
3722
3723 // TODO - free resources associated with JNI-attached daemon threads
3724 free(ext->envList);
3725 free(ext);
3726
3727 return JNI_OK;
3728}
3729
3730
3731/*
3732 * ===========================================================================
3733 * Function tables
3734 * ===========================================================================
3735 */
3736
3737static const struct JNINativeInterface gNativeInterface = {
3738 NULL,
3739 NULL,
3740 NULL,
3741 NULL,
3742
3743 GetVersion,
3744
3745 DefineClass,
3746 FindClass,
3747
3748 FromReflectedMethod,
3749 FromReflectedField,
3750 ToReflectedMethod,
3751
3752 GetSuperclass,
3753 IsAssignableFrom,
3754
3755 ToReflectedField,
3756
3757 Throw,
3758 ThrowNew,
3759 ExceptionOccurred,
3760 ExceptionDescribe,
3761 ExceptionClear,
3762 FatalError,
3763
3764 PushLocalFrame,
3765 PopLocalFrame,
3766
3767 NewGlobalRef,
3768 DeleteGlobalRef,
3769 DeleteLocalRef,
3770 IsSameObject,
3771 NewLocalRef,
3772 EnsureLocalCapacity,
3773
3774 AllocObject,
3775 NewObject,
3776 NewObjectV,
3777 NewObjectA,
3778
3779 GetObjectClass,
3780 IsInstanceOf,
3781
3782 GetMethodID,
3783
3784 CallObjectMethod,
3785 CallObjectMethodV,
3786 CallObjectMethodA,
3787 CallBooleanMethod,
3788 CallBooleanMethodV,
3789 CallBooleanMethodA,
3790 CallByteMethod,
3791 CallByteMethodV,
3792 CallByteMethodA,
3793 CallCharMethod,
3794 CallCharMethodV,
3795 CallCharMethodA,
3796 CallShortMethod,
3797 CallShortMethodV,
3798 CallShortMethodA,
3799 CallIntMethod,
3800 CallIntMethodV,
3801 CallIntMethodA,
3802 CallLongMethod,
3803 CallLongMethodV,
3804 CallLongMethodA,
3805 CallFloatMethod,
3806 CallFloatMethodV,
3807 CallFloatMethodA,
3808 CallDoubleMethod,
3809 CallDoubleMethodV,
3810 CallDoubleMethodA,
3811 CallVoidMethod,
3812 CallVoidMethodV,
3813 CallVoidMethodA,
3814
3815 CallNonvirtualObjectMethod,
3816 CallNonvirtualObjectMethodV,
3817 CallNonvirtualObjectMethodA,
3818 CallNonvirtualBooleanMethod,
3819 CallNonvirtualBooleanMethodV,
3820 CallNonvirtualBooleanMethodA,
3821 CallNonvirtualByteMethod,
3822 CallNonvirtualByteMethodV,
3823 CallNonvirtualByteMethodA,
3824 CallNonvirtualCharMethod,
3825 CallNonvirtualCharMethodV,
3826 CallNonvirtualCharMethodA,
3827 CallNonvirtualShortMethod,
3828 CallNonvirtualShortMethodV,
3829 CallNonvirtualShortMethodA,
3830 CallNonvirtualIntMethod,
3831 CallNonvirtualIntMethodV,
3832 CallNonvirtualIntMethodA,
3833 CallNonvirtualLongMethod,
3834 CallNonvirtualLongMethodV,
3835 CallNonvirtualLongMethodA,
3836 CallNonvirtualFloatMethod,
3837 CallNonvirtualFloatMethodV,
3838 CallNonvirtualFloatMethodA,
3839 CallNonvirtualDoubleMethod,
3840 CallNonvirtualDoubleMethodV,
3841 CallNonvirtualDoubleMethodA,
3842 CallNonvirtualVoidMethod,
3843 CallNonvirtualVoidMethodV,
3844 CallNonvirtualVoidMethodA,
3845
3846 GetFieldID,
3847
3848 GetObjectField,
3849 GetBooleanField,
3850 GetByteField,
3851 GetCharField,
3852 GetShortField,
3853 GetIntField,
3854 GetLongField,
3855 GetFloatField,
3856 GetDoubleField,
3857 SetObjectField,
3858 SetBooleanField,
3859 SetByteField,
3860 SetCharField,
3861 SetShortField,
3862 SetIntField,
3863 SetLongField,
3864 SetFloatField,
3865 SetDoubleField,
3866
3867 GetStaticMethodID,
3868
3869 CallStaticObjectMethod,
3870 CallStaticObjectMethodV,
3871 CallStaticObjectMethodA,
3872 CallStaticBooleanMethod,
3873 CallStaticBooleanMethodV,
3874 CallStaticBooleanMethodA,
3875 CallStaticByteMethod,
3876 CallStaticByteMethodV,
3877 CallStaticByteMethodA,
3878 CallStaticCharMethod,
3879 CallStaticCharMethodV,
3880 CallStaticCharMethodA,
3881 CallStaticShortMethod,
3882 CallStaticShortMethodV,
3883 CallStaticShortMethodA,
3884 CallStaticIntMethod,
3885 CallStaticIntMethodV,
3886 CallStaticIntMethodA,
3887 CallStaticLongMethod,
3888 CallStaticLongMethodV,
3889 CallStaticLongMethodA,
3890 CallStaticFloatMethod,
3891 CallStaticFloatMethodV,
3892 CallStaticFloatMethodA,
3893 CallStaticDoubleMethod,
3894 CallStaticDoubleMethodV,
3895 CallStaticDoubleMethodA,
3896 CallStaticVoidMethod,
3897 CallStaticVoidMethodV,
3898 CallStaticVoidMethodA,
3899
3900 GetStaticFieldID,
3901
3902 GetStaticObjectField,
3903 GetStaticBooleanField,
3904 GetStaticByteField,
3905 GetStaticCharField,
3906 GetStaticShortField,
3907 GetStaticIntField,
3908 GetStaticLongField,
3909 GetStaticFloatField,
3910 GetStaticDoubleField,
3911
3912 SetStaticObjectField,
3913 SetStaticBooleanField,
3914 SetStaticByteField,
3915 SetStaticCharField,
3916 SetStaticShortField,
3917 SetStaticIntField,
3918 SetStaticLongField,
3919 SetStaticFloatField,
3920 SetStaticDoubleField,
3921
3922 NewString,
3923
3924 GetStringLength,
3925 GetStringChars,
3926 ReleaseStringChars,
3927
3928 NewStringUTF,
3929 GetStringUTFLength,
3930 GetStringUTFChars,
3931 ReleaseStringUTFChars,
3932
3933 GetArrayLength,
3934 NewObjectArray,
3935 GetObjectArrayElement,
3936 SetObjectArrayElement,
3937
3938 NewBooleanArray,
3939 NewByteArray,
3940 NewCharArray,
3941 NewShortArray,
3942 NewIntArray,
3943 NewLongArray,
3944 NewFloatArray,
3945 NewDoubleArray,
3946
3947 GetBooleanArrayElements,
3948 GetByteArrayElements,
3949 GetCharArrayElements,
3950 GetShortArrayElements,
3951 GetIntArrayElements,
3952 GetLongArrayElements,
3953 GetFloatArrayElements,
3954 GetDoubleArrayElements,
3955
3956 ReleaseBooleanArrayElements,
3957 ReleaseByteArrayElements,
3958 ReleaseCharArrayElements,
3959 ReleaseShortArrayElements,
3960 ReleaseIntArrayElements,
3961 ReleaseLongArrayElements,
3962 ReleaseFloatArrayElements,
3963 ReleaseDoubleArrayElements,
3964
3965 GetBooleanArrayRegion,
3966 GetByteArrayRegion,
3967 GetCharArrayRegion,
3968 GetShortArrayRegion,
3969 GetIntArrayRegion,
3970 GetLongArrayRegion,
3971 GetFloatArrayRegion,
3972 GetDoubleArrayRegion,
3973 SetBooleanArrayRegion,
3974 SetByteArrayRegion,
3975 SetCharArrayRegion,
3976 SetShortArrayRegion,
3977 SetIntArrayRegion,
3978 SetLongArrayRegion,
3979 SetFloatArrayRegion,
3980 SetDoubleArrayRegion,
3981
3982 RegisterNatives,
3983 UnregisterNatives,
3984
3985 MonitorEnter,
3986 MonitorExit,
3987
3988 GetJavaVM,
3989
3990 GetStringRegion,
3991 GetStringUTFRegion,
3992
3993 GetPrimitiveArrayCritical,
3994 ReleasePrimitiveArrayCritical,
3995
3996 GetStringCritical,
3997 ReleaseStringCritical,
3998
3999 NewWeakGlobalRef,
4000 DeleteWeakGlobalRef,
4001
4002 ExceptionCheck,
4003
4004 NewDirectByteBuffer,
4005 GetDirectBufferAddress,
4006 GetDirectBufferCapacity,
4007
4008 GetObjectRefType
4009};
4010static const struct JNIInvokeInterface gInvokeInterface = {
4011 NULL,
4012 NULL,
4013 NULL,
4014
4015 DestroyJavaVM,
4016 AttachCurrentThread,
4017 DetachCurrentThread,
4018
4019 GetEnv,
4020
4021 AttachCurrentThreadAsDaemon,
4022};
4023
4024
4025/*
4026 * ===========================================================================
4027 * VM/Env creation
4028 * ===========================================================================
4029 */
4030
4031/*
4032 * Enable "checked JNI" after the VM has partially started. This must
4033 * only be called in "zygote" mode, when we have one thread running.
Andy McFadden59b61772009-05-13 16:44:34 -07004034 *
4035 * This doesn't attempt to rewrite the JNI call bridge associated with
4036 * native methods, so we won't get those checks for any methods that have
4037 * already been resolved.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08004038 */
4039void dvmLateEnableCheckedJni(void)
4040{
4041 JNIEnvExt* extEnv;
4042 JavaVMExt* extVm;
Andy McFaddenab00d452009-08-19 07:21:41 -07004043
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08004044 extEnv = dvmGetJNIEnvForThread();
4045 if (extEnv == NULL) {
4046 LOGE("dvmLateEnableCheckedJni: thread has no JNIEnv\n");
4047 return;
4048 }
4049 extVm = extEnv->vm;
4050 assert(extVm != NULL);
4051
4052 if (!extVm->useChecked) {
4053 LOGD("Late-enabling CheckJNI\n");
4054 dvmUseCheckedJniVm(extVm);
4055 extVm->useChecked = true;
4056 dvmUseCheckedJniEnv(extEnv);
4057
4058 /* currently no way to pick up jniopts features */
4059 } else {
4060 LOGD("Not late-enabling CheckJNI (already on)\n");
4061 }
4062}
4063
4064/*
4065 * Not supported.
4066 */
4067jint JNI_GetDefaultJavaVMInitArgs(void* vm_args)
4068{
4069 return JNI_ERR;
4070}
4071
4072/*
4073 * Return a buffer full of created VMs.
4074 *
4075 * We always have zero or one.
4076 */
4077jint JNI_GetCreatedJavaVMs(JavaVM** vmBuf, jsize bufLen, jsize* nVMs)
4078{
4079 if (gDvm.vmList != NULL) {
4080 *nVMs = 1;
4081
4082 if (bufLen > 0)
4083 *vmBuf++ = gDvm.vmList;
4084 } else {
4085 *nVMs = 0;
4086 }
4087
4088 return JNI_OK;
4089}
4090
4091
4092/*
4093 * Create a new VM instance.
4094 *
4095 * The current thread becomes the main VM thread. We return immediately,
4096 * which effectively means the caller is executing in a native method.
4097 */
4098jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args)
4099{
4100 const JavaVMInitArgs* args = (JavaVMInitArgs*) vm_args;
4101 JNIEnvExt* pEnv = NULL;
4102 JavaVMExt* pVM = NULL;
4103 const char** argv;
4104 int argc = 0;
4105 int i, curOpt;
4106 int result = JNI_ERR;
4107 bool checkJni = false;
4108 bool warnError = true;
4109 bool forceDataCopy = false;
4110
4111 if (args->version < JNI_VERSION_1_2)
4112 return JNI_EVERSION;
4113
4114 // TODO: don't allow creation of multiple VMs -- one per customer for now
4115
4116 /* zero globals; not strictly necessary the first time a VM is started */
4117 memset(&gDvm, 0, sizeof(gDvm));
4118
4119 /*
4120 * Set up structures for JNIEnv and VM.
4121 */
4122 //pEnv = (JNIEnvExt*) malloc(sizeof(JNIEnvExt));
4123 pVM = (JavaVMExt*) malloc(sizeof(JavaVMExt));
4124
4125 //memset(pEnv, 0, sizeof(JNIEnvExt));
4126 //pEnv->funcTable = &gNativeInterface;
4127 //pEnv->vm = pVM;
4128 memset(pVM, 0, sizeof(JavaVMExt));
4129 pVM->funcTable = &gInvokeInterface;
4130 pVM->envList = pEnv;
4131 dvmInitMutex(&pVM->envListLock);
4132
4133 argv = (const char**) malloc(sizeof(char*) * (args->nOptions));
4134 memset(argv, 0, sizeof(char*) * (args->nOptions));
4135
4136 curOpt = 0;
4137
4138 /*
4139 * Convert JNI args to argv.
4140 *
4141 * We have to pull out vfprintf/exit/abort, because they use the
4142 * "extraInfo" field to pass function pointer "hooks" in. We also
4143 * look for the -Xcheck:jni stuff here.
4144 */
4145 for (i = 0; i < args->nOptions; i++) {
4146 const char* optStr = args->options[i].optionString;
4147
4148 if (optStr == NULL) {
4149 fprintf(stderr, "ERROR: arg %d string was null\n", i);
4150 goto bail;
4151 } else if (strcmp(optStr, "vfprintf") == 0) {
4152 gDvm.vfprintfHook = args->options[i].extraInfo;
4153 } else if (strcmp(optStr, "exit") == 0) {
4154 gDvm.exitHook = args->options[i].extraInfo;
4155 } else if (strcmp(optStr, "abort") == 0) {
4156 gDvm.abortHook = args->options[i].extraInfo;
4157 } else if (strcmp(optStr, "-Xcheck:jni") == 0) {
4158 checkJni = true;
4159 } else if (strncmp(optStr, "-Xjniopts:", 10) == 0) {
4160 const char* jniOpts = optStr + 9;
4161 while (jniOpts != NULL) {
4162 jniOpts++; /* skip past ':' or ',' */
4163 if (strncmp(jniOpts, "warnonly", 8) == 0) {
4164 warnError = false;
4165 } else if (strncmp(jniOpts, "forcecopy", 9) == 0) {
4166 forceDataCopy = true;
4167 } else {
4168 LOGW("unknown jni opt starting at '%s'\n", jniOpts);
4169 }
4170 jniOpts = strchr(jniOpts, ',');
4171 }
4172 } else {
4173 /* regular option */
4174 argv[curOpt++] = optStr;
4175 }
4176 }
4177 argc = curOpt;
4178
4179 if (checkJni) {
4180 dvmUseCheckedJniVm(pVM);
4181 pVM->useChecked = true;
4182 }
4183 pVM->warnError = warnError;
4184 pVM->forceDataCopy = forceDataCopy;
4185
4186 /* set this up before initializing VM, so it can create some JNIEnvs */
4187 gDvm.vmList = (JavaVM*) pVM;
4188
4189 /*
4190 * Create an env for main thread. We need to have something set up
4191 * here because some of the class initialization we do when starting
4192 * up the VM will call into native code.
4193 */
4194 pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL);
4195
4196 /* initialize VM */
4197 gDvm.initializing = true;
4198 if (dvmStartup(argc, argv, args->ignoreUnrecognized, (JNIEnv*)pEnv) != 0) {
4199 free(pEnv);
4200 free(pVM);
4201 goto bail;
4202 }
4203
4204 /*
4205 * Success! Return stuff to caller.
4206 */
4207 dvmChangeStatus(NULL, THREAD_NATIVE);
4208 *p_env = (JNIEnv*) pEnv;
4209 *p_vm = (JavaVM*) pVM;
4210 result = JNI_OK;
4211
4212bail:
4213 gDvm.initializing = false;
4214 if (result == JNI_OK)
4215 LOGV("JNI_CreateJavaVM succeeded\n");
4216 else
4217 LOGW("JNI_CreateJavaVM failed\n");
4218 free(argv);
4219 return result;
4220}
4221