blob: d7d1122400460a0221826f9a6e10b9b2f5ef2b89 [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);
Elliott Hughes09239e32009-09-29 18:35:43 -07003105 jint retval = JNI_OK;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003106 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;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003118 }
3119 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003120
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003121 JNI_EXIT();
3122 return retval;
3123}
3124
3125/*
3126 * Un-register a native function.
3127 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003128static jint UnregisterNatives(JNIEnv* env, jclass jclazz)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003129{
3130 JNI_ENTER();
3131 /*
3132 * The JNI docs refer to this as a way to reload/relink native libraries,
3133 * and say it "should not be used in normal native code".
3134 *
3135 * We can implement it if we decide we need it.
3136 */
3137 JNI_EXIT();
3138 return JNI_ERR;
3139}
3140
3141/*
3142 * Lock the monitor.
3143 *
3144 * We have to track all monitor enters and exits, so that we can undo any
3145 * outstanding synchronization before the thread exits.
3146 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003147static jint MonitorEnter(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003148{
3149 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003150 Object* obj = dvmDecodeIndirectRef(env, jobj);
3151 dvmLockObject(_self, obj);
3152 trackMonitorEnter(_self, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003153 JNI_EXIT();
3154 return JNI_OK;
3155}
3156
3157/*
3158 * Unlock the monitor.
3159 *
3160 * Throws an IllegalMonitorStateException if the current thread
Andy McFaddenab00d452009-08-19 07:21:41 -07003161 * doesn't own the monitor. (dvmUnlockObject() takes care of the throw.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003162 *
3163 * According to the 1.6 spec, it's legal to call here with an exception
3164 * pending. If this fails, we'll stomp the original exception.
3165 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003166static jint MonitorExit(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003167{
3168 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003169 Object* obj = dvmDecodeIndirectRef(env, jobj);
3170 bool success = dvmUnlockObject(_self, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003171 if (success)
Andy McFaddenab00d452009-08-19 07:21:41 -07003172 trackMonitorExit(_self, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003173 JNI_EXIT();
3174 return success ? JNI_OK : JNI_ERR;
3175}
3176
3177/*
3178 * Return the JavaVM interface associated with the current thread.
3179 */
3180static jint GetJavaVM(JNIEnv* env, JavaVM** vm)
3181{
3182 JNI_ENTER();
3183 //*vm = gDvm.vmList;
3184 *vm = (JavaVM*) ((JNIEnvExt*)env)->vm;
3185 JNI_EXIT();
3186 if (*vm == NULL)
3187 return JNI_ERR;
3188 else
3189 return JNI_OK;
3190}
3191
3192/*
3193 * Copies "len" Unicode characters, from offset "start".
3194 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003195static void GetStringRegion(JNIEnv* env, jstring jstr, jsize start, jsize len,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003196 jchar* buf)
3197{
3198 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003199 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003200 if (start + len > dvmStringLen(strObj))
3201 dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
3202 else
3203 memcpy(buf, dvmStringChars(strObj) + start, len * sizeof(u2));
3204 JNI_EXIT();
3205}
3206
3207/*
3208 * Translates "len" Unicode characters, from offset "start", into
3209 * modified UTF-8 encoding.
3210 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003211static void GetStringUTFRegion(JNIEnv* env, jstring jstr, jsize start,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003212 jsize len, char* buf)
3213{
3214 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003215 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003216 if (start + len > dvmStringLen(strObj))
3217 dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
3218 else
3219 dvmCreateCstrFromStringRegion(strObj, start, len, buf);
3220 JNI_EXIT();
3221}
3222
3223/*
3224 * Get a raw pointer to array data.
3225 *
3226 * The caller is expected to call "release" before doing any JNI calls
3227 * or blocking I/O operations.
3228 *
Andy McFaddenab00d452009-08-19 07:21:41 -07003229 * We need to pin the memory or block GC.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003230 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003231static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray jarr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003232 jboolean* isCopy)
3233{
3234 JNI_ENTER();
3235 void* data;
Andy McFaddenab00d452009-08-19 07:21:41 -07003236 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
3237 pinPrimitiveArray(arrayObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003238 data = arrayObj->contents;
3239 if (isCopy != NULL)
3240 *isCopy = JNI_FALSE;
3241 JNI_EXIT();
3242 return data;
3243}
3244
3245/*
3246 * Release an array obtained with GetPrimitiveArrayCritical.
3247 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003248static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray jarr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003249 void* carray, jint mode)
3250{
3251 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003252 if (mode != JNI_COMMIT) {
3253 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
3254 unpinPrimitiveArray(arrayObj);
3255 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003256 JNI_EXIT();
3257}
3258
3259/*
3260 * Like GetStringChars, but with restricted use.
3261 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003262static const jchar* GetStringCritical(JNIEnv* env, jstring jstr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003263 jboolean* isCopy)
3264{
3265 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003266 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
3267 ArrayObject* strChars = dvmStringCharArray(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003268
Andy McFaddenab00d452009-08-19 07:21:41 -07003269 pinPrimitiveArray(strChars);
3270
3271 const u2* data = dvmStringChars(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003272 if (isCopy != NULL)
3273 *isCopy = JNI_FALSE;
3274
3275 JNI_EXIT();
3276 return (jchar*)data;
3277}
3278
3279/*
3280 * Like ReleaseStringChars, but with restricted use.
3281 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003282static void ReleaseStringCritical(JNIEnv* env, jstring jstr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003283 const jchar* carray)
3284{
3285 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003286 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
Andy McFadden0423f0e2009-08-26 07:21:53 -07003287 ArrayObject* strChars = dvmStringCharArray(strObj);
Andy McFaddenab00d452009-08-19 07:21:41 -07003288 unpinPrimitiveArray(strChars);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003289 JNI_EXIT();
3290}
3291
3292/*
3293 * Create a new weak global reference.
3294 */
3295static jweak NewWeakGlobalRef(JNIEnv* env, jobject obj)
3296{
3297 JNI_ENTER();
3298 // TODO - implement
3299 jobject gref = NULL;
3300 LOGE("JNI ERROR: NewWeakGlobalRef not implemented\n");
3301 dvmAbort();
3302 JNI_EXIT();
3303 return gref;
3304}
3305
3306/*
3307 * Delete the specified weak global reference.
3308 */
3309static void DeleteWeakGlobalRef(JNIEnv* env, jweak obj)
3310{
3311 JNI_ENTER();
3312 // TODO - implement
3313 LOGE("JNI ERROR: DeleteWeakGlobalRef not implemented\n");
3314 dvmAbort();
3315 JNI_EXIT();
3316}
3317
3318/*
3319 * Quick check for pending exceptions.
3320 *
3321 * TODO: we should be able to skip the enter/exit macros here.
3322 */
3323static jboolean ExceptionCheck(JNIEnv* env)
3324{
3325 JNI_ENTER();
3326 bool result = dvmCheckException(_self);
3327 JNI_EXIT();
3328 return result;
3329}
3330
3331/*
3332 * Returns the type of the object referred to by "obj". It can be local,
3333 * global, or weak global.
3334 *
3335 * In the current implementation, references can be global and local at
3336 * the same time, so while the return value is accurate it may not tell
3337 * the whole story.
3338 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003339static jobjectRefType GetObjectRefType(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003340{
3341 JNI_ENTER();
Andy McFadden0423f0e2009-08-26 07:21:53 -07003342 jobjectRefType type = dvmGetJNIRefType(env, jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003343 JNI_EXIT();
3344 return type;
3345}
3346
3347/*
3348 * Allocate and return a new java.nio.ByteBuffer for this block of memory.
3349 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003350 * "address" may not be NULL, and "capacity" must be > 0. (These are only
3351 * verified when CheckJNI is enabled.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003352 */
Andy McFadden8e5c7842009-07-23 17:47:18 -07003353static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003354{
Andy McFadden8e5c7842009-07-23 17:47:18 -07003355 JNI_ENTER();
3356
3357 Thread* self = _self /*dvmThreadSelf()*/;
3358 Object* platformAddress = NULL;
3359 JValue callResult;
The Android Open Source Project99409882009-03-18 22:20:24 -07003360 jobject result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003361
Andy McFadden8e5c7842009-07-23 17:47:18 -07003362 /* get an instance of PlatformAddress that wraps the provided address */
3363 dvmCallMethod(self,
3364 gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on,
3365 NULL, &callResult, address);
3366 if (dvmGetException(self) != NULL || callResult.l == NULL)
The Android Open Source Project99409882009-03-18 22:20:24 -07003367 goto bail;
Andy McFadden8e5c7842009-07-23 17:47:18 -07003368
3369 /* don't let the GC discard it */
3370 platformAddress = (Object*) callResult.l;
3371 dvmAddTrackedAlloc(platformAddress, self);
3372 LOGV("tracking %p for address=%p\n", platformAddress, address);
3373
3374 /* create an instance of java.nio.ReadWriteDirectByteBuffer */
3375 ClassObject* clazz = gDvm.classJavaNioReadWriteDirectByteBuffer;
3376 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))
3377 goto bail;
3378 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
3379 if (newObj != NULL) {
3380 /* call the (PlatformAddress, int, int) constructor */
Andy McFaddenab00d452009-08-19 07:21:41 -07003381 result = addLocalReference(env, newObj);
Andy McFadden8e5c7842009-07-23 17:47:18 -07003382 dvmCallMethod(self, gDvm.methJavaNioReadWriteDirectByteBuffer_init,
3383 newObj, &callResult, platformAddress, (jint) capacity, (jint) 0);
Andy McFaddenab00d452009-08-19 07:21:41 -07003384 if (dvmGetException(self) != NULL) {
3385 deleteLocalReference(env, result);
3386 result = NULL;
Andy McFadden8e5c7842009-07-23 17:47:18 -07003387 goto bail;
Andy McFaddenab00d452009-08-19 07:21:41 -07003388 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003389 }
3390
The Android Open Source Project99409882009-03-18 22:20:24 -07003391bail:
Andy McFadden8e5c7842009-07-23 17:47:18 -07003392 if (platformAddress != NULL)
3393 dvmReleaseTrackedAlloc(platformAddress, self);
3394 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07003395 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003396}
3397
3398/*
3399 * Get the starting address of the buffer for the specified java.nio.Buffer.
3400 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003401 * If this is not a "direct" buffer, we return NULL.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003402 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003403static void* GetDirectBufferAddress(JNIEnv* env, jobject jbuf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003404{
Andy McFadden8e5c7842009-07-23 17:47:18 -07003405 JNI_ENTER();
3406
Andy McFaddenab00d452009-08-19 07:21:41 -07003407 Object* bufObj = dvmDecodeIndirectRef(env, jbuf);
Andy McFadden8e5c7842009-07-23 17:47:18 -07003408 Thread* self = _self /*dvmThreadSelf()*/;
Andy McFadden8e696dc2009-07-24 15:28:16 -07003409 void* result;
3410
3411 /*
3412 * All Buffer objects have an effectiveDirectAddress field. If it's
3413 * nonzero, we can just return that value. If not, we have to call
3414 * through DirectBuffer.getEffectiveAddress(), which as a side-effect
3415 * will set the effectiveDirectAddress field for direct buffers (and
3416 * things that wrap direct buffers).
3417 */
3418 result = (void*) dvmGetFieldInt(bufObj,
3419 gDvm.offJavaNioBuffer_effectiveDirectAddress);
3420 if (result != NULL) {
3421 //LOGI("fast path for %p\n", buf);
3422 goto bail;
3423 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003424
Andy McFadden72d61fb2009-06-24 16:56:06 -07003425 /*
3426 * Start by determining if the object supports the DirectBuffer
3427 * interfaces. Note this does not guarantee that it's a direct buffer.
3428 */
Andy McFadden8e5c7842009-07-23 17:47:18 -07003429 if (!dvmInstanceof(bufObj->clazz,
3430 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003431 {
The Android Open Source Project99409882009-03-18 22:20:24 -07003432 goto bail;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003433 }
3434
Andy McFadden72d61fb2009-06-24 16:56:06 -07003435 /*
Andy McFadden8e5c7842009-07-23 17:47:18 -07003436 * Get a PlatformAddress object with the effective address.
Andy McFadden5f612b82009-07-22 15:07:27 -07003437 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003438 * If this isn't a direct buffer, the result will be NULL and/or an
Andy McFadden72d61fb2009-06-24 16:56:06 -07003439 * exception will have been thrown.
3440 */
Andy McFadden8e696dc2009-07-24 15:28:16 -07003441 JValue callResult;
Andy McFadden8e5c7842009-07-23 17:47:18 -07003442 const Method* meth = dvmGetVirtualizedMethod(bufObj->clazz,
3443 gDvm.methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress);
Andy McFaddend5ab7262009-08-25 07:19:34 -07003444 dvmCallMethodA(self, meth, bufObj, false, &callResult, NULL);
Andy McFadden8e5c7842009-07-23 17:47:18 -07003445 if (dvmGetException(self) != NULL) {
3446 dvmClearException(self);
3447 callResult.l = NULL;
Andy McFadden72d61fb2009-06-24 16:56:06 -07003448 }
Andy McFadden8e5c7842009-07-23 17:47:18 -07003449
Andy McFadden8e696dc2009-07-24 15:28:16 -07003450 Object* platformAddr = callResult.l;
Andy McFadden72d61fb2009-06-24 16:56:06 -07003451 if (platformAddr == NULL) {
Andy McFadden8e696dc2009-07-24 15:28:16 -07003452 LOGV("Got request for address of non-direct buffer\n");
Andy McFadden72d61fb2009-06-24 16:56:06 -07003453 goto bail;
3454 }
3455
Andy McFadden8e5c7842009-07-23 17:47:18 -07003456 /*
3457 * Extract the address from the PlatformAddress object. Instead of
3458 * calling the toLong() method, just grab the field directly. This
3459 * is faster but more fragile.
3460 */
3461 result = (void*) dvmGetFieldInt(platformAddr,
3462 gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr);
The Android Open Source Project99409882009-03-18 22:20:24 -07003463
Andy McFadden8e696dc2009-07-24 15:28:16 -07003464 //LOGI("slow path for %p --> %p\n", buf, result);
3465
The Android Open Source Project99409882009-03-18 22:20:24 -07003466bail:
Andy McFadden8e5c7842009-07-23 17:47:18 -07003467 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07003468 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003469}
3470
3471/*
3472 * Get the capacity of the buffer for the specified java.nio.Buffer.
3473 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003474 * Returns -1 if the object is not a direct buffer. (We actually skip
3475 * this check, since it's expensive to determine, and just return the
3476 * capacity regardless.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003477 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003478static jlong GetDirectBufferCapacity(JNIEnv* env, jobject jbuf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003479{
Andy McFadden8e5c7842009-07-23 17:47:18 -07003480 JNI_ENTER();
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003481
Andy McFadden8e5c7842009-07-23 17:47:18 -07003482 /*
3483 * The capacity is always in the Buffer.capacity field.
3484 *
3485 * (The "check" version should verify that this is actually a Buffer,
3486 * but we're not required to do so here.)
3487 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003488 Object* buf = dvmDecodeIndirectRef(env, jbuf);
3489 jlong result = dvmGetFieldInt(buf, gDvm.offJavaNioBuffer_capacity);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003490
Andy McFadden8e5c7842009-07-23 17:47:18 -07003491 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07003492 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003493}
3494
3495
3496/*
3497 * ===========================================================================
3498 * JNI invocation functions
3499 * ===========================================================================
3500 */
3501
3502/*
3503 * Handle AttachCurrentThread{AsDaemon}.
3504 *
3505 * We need to make sure the VM is actually running. For example, if we start
3506 * up, issue an Attach, and the VM exits almost immediately, by the time the
3507 * attaching happens the VM could already be shutting down.
3508 *
3509 * It's hard to avoid a race condition here because we don't want to hold
3510 * a lock across the entire operation. What we can do is temporarily
3511 * increment the thread count to prevent a VM exit.
3512 *
3513 * This could potentially still have problems if a daemon thread calls here
3514 * while the VM is shutting down. dvmThreadSelf() will work, since it just
3515 * uses pthread TLS, but dereferencing "vm" could fail. Such is life when
3516 * you shut down a VM while threads are still running inside it.
3517 *
3518 * Remember that some code may call this as a way to find the per-thread
3519 * JNIEnv pointer. Don't do excess work for that case.
3520 */
3521static jint attachThread(JavaVM* vm, JNIEnv** p_env, void* thr_args,
3522 bool isDaemon)
3523{
3524 JavaVMAttachArgs* args = (JavaVMAttachArgs*) thr_args;
3525 Thread* self;
3526 bool result = false;
3527
3528 /*
3529 * Return immediately if we're already one with the VM.
3530 */
3531 self = dvmThreadSelf();
3532 if (self != NULL) {
3533 *p_env = self->jniEnv;
3534 return JNI_OK;
3535 }
3536
3537 /*
3538 * No threads allowed in zygote mode.
3539 */
3540 if (gDvm.zygote) {
3541 return JNI_ERR;
3542 }
3543
3544 /* increment the count to keep the VM from bailing while we run */
3545 dvmLockThreadList(NULL);
3546 if (gDvm.nonDaemonThreadCount == 0) {
3547 // dead or dying
3548 LOGV("Refusing to attach thread '%s' -- VM is shutting down\n",
3549 (thr_args == NULL) ? "(unknown)" : args->name);
3550 dvmUnlockThreadList();
3551 return JNI_ERR;
3552 }
3553 gDvm.nonDaemonThreadCount++;
3554 dvmUnlockThreadList();
3555
3556 /* tweak the JavaVMAttachArgs as needed */
3557 JavaVMAttachArgs argsCopy;
3558 if (args == NULL) {
3559 /* allow the v1.1 calling convention */
3560 argsCopy.version = JNI_VERSION_1_2;
3561 argsCopy.name = NULL;
3562 argsCopy.group = dvmGetMainThreadGroup();
3563 } else {
3564 assert(args->version >= JNI_VERSION_1_2);
3565
3566 argsCopy.version = args->version;
3567 argsCopy.name = args->name;
3568 if (args->group != NULL)
3569 argsCopy.group = args->group;
3570 else
3571 argsCopy.group = dvmGetMainThreadGroup();
3572 }
3573
3574 result = dvmAttachCurrentThread(&argsCopy, isDaemon);
3575
3576 /* restore the count */
3577 dvmLockThreadList(NULL);
3578 gDvm.nonDaemonThreadCount--;
3579 dvmUnlockThreadList();
3580
3581 /*
3582 * Change the status to indicate that we're out in native code. This
3583 * call is not guarded with state-change macros, so we have to do it
3584 * by hand.
3585 */
3586 if (result) {
3587 self = dvmThreadSelf();
3588 assert(self != NULL);
3589 dvmChangeStatus(self, THREAD_NATIVE);
3590 *p_env = self->jniEnv;
3591 return JNI_OK;
3592 } else {
3593 return JNI_ERR;
3594 }
3595}
3596
3597/*
3598 * Attach the current thread to the VM. If the thread is already attached,
3599 * this is a no-op.
3600 */
3601static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args)
3602{
3603 return attachThread(vm, p_env, thr_args, false);
3604}
3605
3606/*
3607 * Like AttachCurrentThread, but set the "daemon" flag.
3608 */
3609static jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env,
3610 void* thr_args)
3611{
3612 return attachThread(vm, p_env, thr_args, true);
3613}
3614
3615/*
3616 * Dissociate the current thread from the VM.
3617 */
3618static jint DetachCurrentThread(JavaVM* vm)
3619{
3620 Thread* self = dvmThreadSelf();
3621
3622 if (self == NULL) /* not attached, can't do anything */
3623 return JNI_ERR;
3624
3625 /* switch to "running" to check for suspension */
3626 dvmChangeStatus(self, THREAD_RUNNING);
3627
3628 /* detach the thread */
3629 dvmDetachCurrentThread();
3630
3631 /* (no need to change status back -- we have no status) */
3632 return JNI_OK;
3633}
3634
3635/*
3636 * If current thread is attached to VM, return the associated JNIEnv.
3637 * Otherwise, stuff NULL in and return JNI_EDETACHED.
3638 *
3639 * JVMTI overloads this by specifying a magic value for "version", so we
3640 * do want to check that here.
3641 */
3642static jint GetEnv(JavaVM* vm, void** env, jint version)
3643{
3644 Thread* self = dvmThreadSelf();
3645
3646 if (version < JNI_VERSION_1_1 || version > JNI_VERSION_1_6)
3647 return JNI_EVERSION;
3648
3649 if (self == NULL) {
3650 *env = NULL;
3651 } else {
3652 /* TODO: status change is probably unnecessary */
3653 dvmChangeStatus(self, THREAD_RUNNING);
3654 *env = (void*) dvmGetThreadJNIEnv(self);
3655 dvmChangeStatus(self, THREAD_NATIVE);
3656 }
3657 if (*env == NULL)
3658 return JNI_EDETACHED;
3659 else
3660 return JNI_OK;
3661}
3662
3663/*
3664 * Destroy the VM. This may be called from any thread.
3665 *
3666 * If the current thread is attached, wait until the current thread is
3667 * the only non-daemon user-level thread. If the current thread is not
3668 * attached, we attach it and do the processing as usual. (If the attach
3669 * fails, it's probably because all the non-daemon threads have already
3670 * exited and the VM doesn't want to let us back in.)
3671 *
3672 * TODO: we don't really deal with the situation where more than one thread
3673 * has called here. One thread wins, the other stays trapped waiting on
3674 * the condition variable forever. Not sure this situation is interesting
3675 * in real life.
3676 */
3677static jint DestroyJavaVM(JavaVM* vm)
3678{
3679 JavaVMExt* ext = (JavaVMExt*) vm;
3680 Thread* self;
3681
3682 if (ext == NULL)
3683 return JNI_ERR;
3684
3685 LOGD("DestroyJavaVM waiting for non-daemon threads to exit\n");
3686
3687 /*
3688 * Sleep on a condition variable until it's okay to exit.
3689 */
3690 self = dvmThreadSelf();
3691 if (self == NULL) {
3692 JNIEnv* tmpEnv;
3693 if (AttachCurrentThread(vm, &tmpEnv, NULL) != JNI_OK) {
3694 LOGV("Unable to reattach main for Destroy; assuming VM is "
3695 "shutting down (count=%d)\n",
3696 gDvm.nonDaemonThreadCount);
3697 goto shutdown;
3698 } else {
3699 LOGV("Attached to wait for shutdown in Destroy\n");
3700 }
3701 }
3702 dvmChangeStatus(self, THREAD_VMWAIT);
3703
3704 dvmLockThreadList(self);
3705 gDvm.nonDaemonThreadCount--; // remove current thread from count
3706
3707 while (gDvm.nonDaemonThreadCount > 0)
3708 pthread_cond_wait(&gDvm.vmExitCond, &gDvm.threadListLock);
3709
3710 dvmUnlockThreadList();
3711 self = NULL;
3712
3713shutdown:
3714 // TODO: call System.exit() to run any registered shutdown hooks
3715 // (this may not return -- figure out how this should work)
3716
3717 LOGD("DestroyJavaVM shutting VM down\n");
3718 dvmShutdown();
3719
3720 // TODO - free resources associated with JNI-attached daemon threads
3721 free(ext->envList);
3722 free(ext);
3723
3724 return JNI_OK;
3725}
3726
3727
3728/*
3729 * ===========================================================================
3730 * Function tables
3731 * ===========================================================================
3732 */
3733
3734static const struct JNINativeInterface gNativeInterface = {
3735 NULL,
3736 NULL,
3737 NULL,
3738 NULL,
3739
3740 GetVersion,
3741
3742 DefineClass,
3743 FindClass,
3744
3745 FromReflectedMethod,
3746 FromReflectedField,
3747 ToReflectedMethod,
3748
3749 GetSuperclass,
3750 IsAssignableFrom,
3751
3752 ToReflectedField,
3753
3754 Throw,
3755 ThrowNew,
3756 ExceptionOccurred,
3757 ExceptionDescribe,
3758 ExceptionClear,
3759 FatalError,
3760
3761 PushLocalFrame,
3762 PopLocalFrame,
3763
3764 NewGlobalRef,
3765 DeleteGlobalRef,
3766 DeleteLocalRef,
3767 IsSameObject,
3768 NewLocalRef,
3769 EnsureLocalCapacity,
3770
3771 AllocObject,
3772 NewObject,
3773 NewObjectV,
3774 NewObjectA,
3775
3776 GetObjectClass,
3777 IsInstanceOf,
3778
3779 GetMethodID,
3780
3781 CallObjectMethod,
3782 CallObjectMethodV,
3783 CallObjectMethodA,
3784 CallBooleanMethod,
3785 CallBooleanMethodV,
3786 CallBooleanMethodA,
3787 CallByteMethod,
3788 CallByteMethodV,
3789 CallByteMethodA,
3790 CallCharMethod,
3791 CallCharMethodV,
3792 CallCharMethodA,
3793 CallShortMethod,
3794 CallShortMethodV,
3795 CallShortMethodA,
3796 CallIntMethod,
3797 CallIntMethodV,
3798 CallIntMethodA,
3799 CallLongMethod,
3800 CallLongMethodV,
3801 CallLongMethodA,
3802 CallFloatMethod,
3803 CallFloatMethodV,
3804 CallFloatMethodA,
3805 CallDoubleMethod,
3806 CallDoubleMethodV,
3807 CallDoubleMethodA,
3808 CallVoidMethod,
3809 CallVoidMethodV,
3810 CallVoidMethodA,
3811
3812 CallNonvirtualObjectMethod,
3813 CallNonvirtualObjectMethodV,
3814 CallNonvirtualObjectMethodA,
3815 CallNonvirtualBooleanMethod,
3816 CallNonvirtualBooleanMethodV,
3817 CallNonvirtualBooleanMethodA,
3818 CallNonvirtualByteMethod,
3819 CallNonvirtualByteMethodV,
3820 CallNonvirtualByteMethodA,
3821 CallNonvirtualCharMethod,
3822 CallNonvirtualCharMethodV,
3823 CallNonvirtualCharMethodA,
3824 CallNonvirtualShortMethod,
3825 CallNonvirtualShortMethodV,
3826 CallNonvirtualShortMethodA,
3827 CallNonvirtualIntMethod,
3828 CallNonvirtualIntMethodV,
3829 CallNonvirtualIntMethodA,
3830 CallNonvirtualLongMethod,
3831 CallNonvirtualLongMethodV,
3832 CallNonvirtualLongMethodA,
3833 CallNonvirtualFloatMethod,
3834 CallNonvirtualFloatMethodV,
3835 CallNonvirtualFloatMethodA,
3836 CallNonvirtualDoubleMethod,
3837 CallNonvirtualDoubleMethodV,
3838 CallNonvirtualDoubleMethodA,
3839 CallNonvirtualVoidMethod,
3840 CallNonvirtualVoidMethodV,
3841 CallNonvirtualVoidMethodA,
3842
3843 GetFieldID,
3844
3845 GetObjectField,
3846 GetBooleanField,
3847 GetByteField,
3848 GetCharField,
3849 GetShortField,
3850 GetIntField,
3851 GetLongField,
3852 GetFloatField,
3853 GetDoubleField,
3854 SetObjectField,
3855 SetBooleanField,
3856 SetByteField,
3857 SetCharField,
3858 SetShortField,
3859 SetIntField,
3860 SetLongField,
3861 SetFloatField,
3862 SetDoubleField,
3863
3864 GetStaticMethodID,
3865
3866 CallStaticObjectMethod,
3867 CallStaticObjectMethodV,
3868 CallStaticObjectMethodA,
3869 CallStaticBooleanMethod,
3870 CallStaticBooleanMethodV,
3871 CallStaticBooleanMethodA,
3872 CallStaticByteMethod,
3873 CallStaticByteMethodV,
3874 CallStaticByteMethodA,
3875 CallStaticCharMethod,
3876 CallStaticCharMethodV,
3877 CallStaticCharMethodA,
3878 CallStaticShortMethod,
3879 CallStaticShortMethodV,
3880 CallStaticShortMethodA,
3881 CallStaticIntMethod,
3882 CallStaticIntMethodV,
3883 CallStaticIntMethodA,
3884 CallStaticLongMethod,
3885 CallStaticLongMethodV,
3886 CallStaticLongMethodA,
3887 CallStaticFloatMethod,
3888 CallStaticFloatMethodV,
3889 CallStaticFloatMethodA,
3890 CallStaticDoubleMethod,
3891 CallStaticDoubleMethodV,
3892 CallStaticDoubleMethodA,
3893 CallStaticVoidMethod,
3894 CallStaticVoidMethodV,
3895 CallStaticVoidMethodA,
3896
3897 GetStaticFieldID,
3898
3899 GetStaticObjectField,
3900 GetStaticBooleanField,
3901 GetStaticByteField,
3902 GetStaticCharField,
3903 GetStaticShortField,
3904 GetStaticIntField,
3905 GetStaticLongField,
3906 GetStaticFloatField,
3907 GetStaticDoubleField,
3908
3909 SetStaticObjectField,
3910 SetStaticBooleanField,
3911 SetStaticByteField,
3912 SetStaticCharField,
3913 SetStaticShortField,
3914 SetStaticIntField,
3915 SetStaticLongField,
3916 SetStaticFloatField,
3917 SetStaticDoubleField,
3918
3919 NewString,
3920
3921 GetStringLength,
3922 GetStringChars,
3923 ReleaseStringChars,
3924
3925 NewStringUTF,
3926 GetStringUTFLength,
3927 GetStringUTFChars,
3928 ReleaseStringUTFChars,
3929
3930 GetArrayLength,
3931 NewObjectArray,
3932 GetObjectArrayElement,
3933 SetObjectArrayElement,
3934
3935 NewBooleanArray,
3936 NewByteArray,
3937 NewCharArray,
3938 NewShortArray,
3939 NewIntArray,
3940 NewLongArray,
3941 NewFloatArray,
3942 NewDoubleArray,
3943
3944 GetBooleanArrayElements,
3945 GetByteArrayElements,
3946 GetCharArrayElements,
3947 GetShortArrayElements,
3948 GetIntArrayElements,
3949 GetLongArrayElements,
3950 GetFloatArrayElements,
3951 GetDoubleArrayElements,
3952
3953 ReleaseBooleanArrayElements,
3954 ReleaseByteArrayElements,
3955 ReleaseCharArrayElements,
3956 ReleaseShortArrayElements,
3957 ReleaseIntArrayElements,
3958 ReleaseLongArrayElements,
3959 ReleaseFloatArrayElements,
3960 ReleaseDoubleArrayElements,
3961
3962 GetBooleanArrayRegion,
3963 GetByteArrayRegion,
3964 GetCharArrayRegion,
3965 GetShortArrayRegion,
3966 GetIntArrayRegion,
3967 GetLongArrayRegion,
3968 GetFloatArrayRegion,
3969 GetDoubleArrayRegion,
3970 SetBooleanArrayRegion,
3971 SetByteArrayRegion,
3972 SetCharArrayRegion,
3973 SetShortArrayRegion,
3974 SetIntArrayRegion,
3975 SetLongArrayRegion,
3976 SetFloatArrayRegion,
3977 SetDoubleArrayRegion,
3978
3979 RegisterNatives,
3980 UnregisterNatives,
3981
3982 MonitorEnter,
3983 MonitorExit,
3984
3985 GetJavaVM,
3986
3987 GetStringRegion,
3988 GetStringUTFRegion,
3989
3990 GetPrimitiveArrayCritical,
3991 ReleasePrimitiveArrayCritical,
3992
3993 GetStringCritical,
3994 ReleaseStringCritical,
3995
3996 NewWeakGlobalRef,
3997 DeleteWeakGlobalRef,
3998
3999 ExceptionCheck,
4000
4001 NewDirectByteBuffer,
4002 GetDirectBufferAddress,
4003 GetDirectBufferCapacity,
4004
4005 GetObjectRefType
4006};
4007static const struct JNIInvokeInterface gInvokeInterface = {
4008 NULL,
4009 NULL,
4010 NULL,
4011
4012 DestroyJavaVM,
4013 AttachCurrentThread,
4014 DetachCurrentThread,
4015
4016 GetEnv,
4017
4018 AttachCurrentThreadAsDaemon,
4019};
4020
4021
4022/*
4023 * ===========================================================================
4024 * VM/Env creation
4025 * ===========================================================================
4026 */
4027
4028/*
4029 * Enable "checked JNI" after the VM has partially started. This must
4030 * only be called in "zygote" mode, when we have one thread running.
Andy McFadden59b61772009-05-13 16:44:34 -07004031 *
4032 * This doesn't attempt to rewrite the JNI call bridge associated with
4033 * native methods, so we won't get those checks for any methods that have
4034 * already been resolved.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08004035 */
4036void dvmLateEnableCheckedJni(void)
4037{
4038 JNIEnvExt* extEnv;
4039 JavaVMExt* extVm;
Andy McFaddenab00d452009-08-19 07:21:41 -07004040
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08004041 extEnv = dvmGetJNIEnvForThread();
4042 if (extEnv == NULL) {
4043 LOGE("dvmLateEnableCheckedJni: thread has no JNIEnv\n");
4044 return;
4045 }
4046 extVm = extEnv->vm;
4047 assert(extVm != NULL);
4048
4049 if (!extVm->useChecked) {
4050 LOGD("Late-enabling CheckJNI\n");
4051 dvmUseCheckedJniVm(extVm);
4052 extVm->useChecked = true;
4053 dvmUseCheckedJniEnv(extEnv);
4054
4055 /* currently no way to pick up jniopts features */
4056 } else {
4057 LOGD("Not late-enabling CheckJNI (already on)\n");
4058 }
4059}
4060
4061/*
4062 * Not supported.
4063 */
4064jint JNI_GetDefaultJavaVMInitArgs(void* vm_args)
4065{
4066 return JNI_ERR;
4067}
4068
4069/*
4070 * Return a buffer full of created VMs.
4071 *
4072 * We always have zero or one.
4073 */
4074jint JNI_GetCreatedJavaVMs(JavaVM** vmBuf, jsize bufLen, jsize* nVMs)
4075{
4076 if (gDvm.vmList != NULL) {
4077 *nVMs = 1;
4078
4079 if (bufLen > 0)
4080 *vmBuf++ = gDvm.vmList;
4081 } else {
4082 *nVMs = 0;
4083 }
4084
4085 return JNI_OK;
4086}
4087
4088
4089/*
4090 * Create a new VM instance.
4091 *
4092 * The current thread becomes the main VM thread. We return immediately,
4093 * which effectively means the caller is executing in a native method.
4094 */
4095jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args)
4096{
4097 const JavaVMInitArgs* args = (JavaVMInitArgs*) vm_args;
4098 JNIEnvExt* pEnv = NULL;
4099 JavaVMExt* pVM = NULL;
4100 const char** argv;
4101 int argc = 0;
4102 int i, curOpt;
4103 int result = JNI_ERR;
4104 bool checkJni = false;
4105 bool warnError = true;
4106 bool forceDataCopy = false;
4107
4108 if (args->version < JNI_VERSION_1_2)
4109 return JNI_EVERSION;
4110
4111 // TODO: don't allow creation of multiple VMs -- one per customer for now
4112
4113 /* zero globals; not strictly necessary the first time a VM is started */
4114 memset(&gDvm, 0, sizeof(gDvm));
4115
4116 /*
4117 * Set up structures for JNIEnv and VM.
4118 */
4119 //pEnv = (JNIEnvExt*) malloc(sizeof(JNIEnvExt));
4120 pVM = (JavaVMExt*) malloc(sizeof(JavaVMExt));
4121
4122 //memset(pEnv, 0, sizeof(JNIEnvExt));
4123 //pEnv->funcTable = &gNativeInterface;
4124 //pEnv->vm = pVM;
4125 memset(pVM, 0, sizeof(JavaVMExt));
4126 pVM->funcTable = &gInvokeInterface;
4127 pVM->envList = pEnv;
4128 dvmInitMutex(&pVM->envListLock);
4129
4130 argv = (const char**) malloc(sizeof(char*) * (args->nOptions));
4131 memset(argv, 0, sizeof(char*) * (args->nOptions));
4132
4133 curOpt = 0;
4134
4135 /*
4136 * Convert JNI args to argv.
4137 *
4138 * We have to pull out vfprintf/exit/abort, because they use the
4139 * "extraInfo" field to pass function pointer "hooks" in. We also
4140 * look for the -Xcheck:jni stuff here.
4141 */
4142 for (i = 0; i < args->nOptions; i++) {
4143 const char* optStr = args->options[i].optionString;
4144
4145 if (optStr == NULL) {
4146 fprintf(stderr, "ERROR: arg %d string was null\n", i);
4147 goto bail;
4148 } else if (strcmp(optStr, "vfprintf") == 0) {
4149 gDvm.vfprintfHook = args->options[i].extraInfo;
4150 } else if (strcmp(optStr, "exit") == 0) {
4151 gDvm.exitHook = args->options[i].extraInfo;
4152 } else if (strcmp(optStr, "abort") == 0) {
4153 gDvm.abortHook = args->options[i].extraInfo;
4154 } else if (strcmp(optStr, "-Xcheck:jni") == 0) {
4155 checkJni = true;
4156 } else if (strncmp(optStr, "-Xjniopts:", 10) == 0) {
4157 const char* jniOpts = optStr + 9;
4158 while (jniOpts != NULL) {
4159 jniOpts++; /* skip past ':' or ',' */
4160 if (strncmp(jniOpts, "warnonly", 8) == 0) {
4161 warnError = false;
4162 } else if (strncmp(jniOpts, "forcecopy", 9) == 0) {
4163 forceDataCopy = true;
4164 } else {
4165 LOGW("unknown jni opt starting at '%s'\n", jniOpts);
4166 }
4167 jniOpts = strchr(jniOpts, ',');
4168 }
4169 } else {
4170 /* regular option */
4171 argv[curOpt++] = optStr;
4172 }
4173 }
4174 argc = curOpt;
4175
4176 if (checkJni) {
4177 dvmUseCheckedJniVm(pVM);
4178 pVM->useChecked = true;
4179 }
4180 pVM->warnError = warnError;
4181 pVM->forceDataCopy = forceDataCopy;
4182
4183 /* set this up before initializing VM, so it can create some JNIEnvs */
4184 gDvm.vmList = (JavaVM*) pVM;
4185
4186 /*
4187 * Create an env for main thread. We need to have something set up
4188 * here because some of the class initialization we do when starting
4189 * up the VM will call into native code.
4190 */
4191 pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL);
4192
4193 /* initialize VM */
4194 gDvm.initializing = true;
4195 if (dvmStartup(argc, argv, args->ignoreUnrecognized, (JNIEnv*)pEnv) != 0) {
4196 free(pEnv);
4197 free(pVM);
4198 goto bail;
4199 }
4200
4201 /*
4202 * Success! Return stuff to caller.
4203 */
4204 dvmChangeStatus(NULL, THREAD_NATIVE);
4205 *p_env = (JNIEnv*) pEnv;
4206 *p_vm = (JavaVM*) pVM;
4207 result = JNI_OK;
4208
4209bail:
4210 gDvm.initializing = false;
4211 if (result == JNI_OK)
4212 LOGV("JNI_CreateJavaVM succeeded\n");
4213 else
4214 LOGW("JNI_CreateJavaVM failed\n");
4215 free(argv);
4216 return result;
4217}