blob: f93a3329f4b70c175e5e9f045bcf7a0c112576f3 [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)
536{
537 return &((JNIEnvExt*)env)->self->jniLocalRefTable;
538}
539#else
Andy McFaddenab00d452009-08-19 07:21:41 -0700540static inline ReferenceTable* getLocalRefTable(JNIEnv* env)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800541{
Andy McFaddenab00d452009-08-19 07:21:41 -0700542 //return &dvmThreadSelf()->jniLocalRefTable;
543 return &((JNIEnvExt*)env)->self->jniLocalRefTable;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800544}
Andy McFaddend5ab7262009-08-25 07:19:34 -0700545#endif
546
547/*
548 * Convert an indirect reference to an Object reference. The indirect
549 * reference may be local, global, or weak-global.
550 *
551 * If "jobj" is NULL or an invalid indirect reference, this returns NULL.
552 */
553Object* dvmDecodeIndirectRef(JNIEnv* env, jobject jobj)
554{
555#ifdef USE_INDIRECT_REF
556 if (jobj == NULL)
557 return NULL;
558
559 Object* result;
560
561 switch (dvmGetIndirectRefType(jobj)) {
562 case kIndirectKindLocal:
563 {
564 IndirectRefTable* pRefTable = getLocalRefTable(env);
565 result = dvmGetFromIndirectRefTable(pRefTable, jobj);
566 }
567 break;
568 case kIndirectKindGlobal:
569 {
570 // TODO: find a way to avoid the mutex activity here
571 IndirectRefTable* pRefTable = &gDvm.jniGlobalRefTable;
572 dvmLockMutex(&gDvm.jniGlobalRefLock);
573 result = dvmGetFromIndirectRefTable(pRefTable, jobj);
574 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
575 }
576 break;
577 case kIndirectKindWeakGlobal:
578 {
579 LOGE("weak-global not yet supported\n");
580 result = NULL;
581 dvmAbort();
582 }
583 break;
584 case kIndirectKindInvalid:
585 default:
586 LOGW("Invalid indirect reference %p in decodeIndirectRef\n", jobj);
587 dvmAbort();
588 result = NULL;
589 break;
590 }
591
592 return result;
593#else
594 return (Object*) jobj;
595#endif
596}
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800597
598/*
599 * Add a local reference for an object to the current stack frame. When
600 * the native function returns, the reference will be discarded.
601 *
602 * We need to allow the same reference to be added multiple times.
603 *
604 * This will be called on otherwise unreferenced objects. We cannot do
605 * GC allocations here, and it's best if we don't grab a mutex.
606 *
607 * Returns the local reference (currently just the same pointer that was
608 * passed in), or NULL on failure.
609 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700610static jobject addLocalReference(JNIEnv* env, Object* obj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800611{
612 if (obj == NULL)
613 return NULL;
614
Andy McFaddend5ab7262009-08-25 07:19:34 -0700615 jobject jobj;
616
617#ifdef USE_INDIRECT_REF
618 IndirectRefTable* pRefTable = getLocalRefTable(env);
619 void* curFrame = ((JNIEnvExt*)env)->self->curFrame;
620 u4 cookie = SAVEAREA_FROM_FP(curFrame)->xtra.localRefCookie;
621
622 jobj = (jobject) dvmAddToIndirectRefTable(pRefTable, cookie, obj);
623 if (jobj == NULL) {
624 dvmDumpIndirectRefTable(pRefTable, "JNI local");
625 LOGE("Failed adding to JNI local ref table (has %d entries)\n",
626 (int) dvmIndirectRefTableEntries(pRefTable));
627 dvmDumpThread(dvmThreadSelf(), false);
628 dvmAbort(); // spec says call FatalError; this is equivalent
629 } else {
630 LOGVV("LREF add %p (%s.%s) (ent=%d)\n", obj,
631 dvmGetCurrentJNIMethod()->clazz->descriptor,
632 dvmGetCurrentJNIMethod()->name,
633 (int) dvmReferenceTableEntries(pRefTable));
634 }
635#else
Andy McFaddenab00d452009-08-19 07:21:41 -0700636 ReferenceTable* pRefTable = getLocalRefTable(env);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800637
Andy McFaddenab00d452009-08-19 07:21:41 -0700638 if (!dvmAddToReferenceTable(pRefTable, obj)) {
639 dvmDumpReferenceTable(pRefTable, "JNI local");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800640 LOGE("Failed adding to JNI local ref table (has %d entries)\n",
Andy McFaddenab00d452009-08-19 07:21:41 -0700641 (int) dvmReferenceTableEntries(pRefTable));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800642 dvmDumpThread(dvmThreadSelf(), false);
643 dvmAbort(); // spec says call FatalError; this is equivalent
644 } else {
Andy McFadden0083d372009-08-21 14:44:04 -0700645 LOGVV("LREF add %p (%s.%s) (ent=%d)\n", obj,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800646 dvmGetCurrentJNIMethod()->clazz->descriptor,
Andy McFadden0083d372009-08-21 14:44:04 -0700647 dvmGetCurrentJNIMethod()->name,
648 (int) dvmReferenceTableEntries(pRefTable));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800649 }
650
Andy McFaddend5ab7262009-08-25 07:19:34 -0700651 jobj = (jobject) obj;
652#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800653
Andy McFaddend5ab7262009-08-25 07:19:34 -0700654 return jobj;
Andy McFaddenab00d452009-08-19 07:21:41 -0700655}
656
657/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800658 * Ensure that at least "capacity" references can be held in the local
659 * refs table of the current thread.
660 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700661static bool ensureLocalCapacity(JNIEnv* env, int capacity)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800662{
Andy McFaddend5ab7262009-08-25 07:19:34 -0700663#ifdef USE_INDIRECT_REF
664 IndirectRefTable* pRefTable = getLocalRefTable(env);
665 int numEntries = dvmIndirectRefTableEntries(pRefTable);
666 // TODO: this isn't quite right, since "numEntries" includes holes
667 return ((kJniLocalRefMax - numEntries) >= capacity);
668#else
Andy McFaddenab00d452009-08-19 07:21:41 -0700669 ReferenceTable* pRefTable = getLocalRefTable(env);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800670
Andy McFaddenab00d452009-08-19 07:21:41 -0700671 return (kJniLocalRefMax - (pRefTable->nextEntry - pRefTable->table) >= capacity);
Andy McFaddend5ab7262009-08-25 07:19:34 -0700672#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800673}
674
675/*
676 * Explicitly delete a reference from the local list.
677 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700678static void deleteLocalReference(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800679{
Andy McFaddenab00d452009-08-19 07:21:41 -0700680 if (jobj == NULL)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800681 return;
682
Andy McFaddend5ab7262009-08-25 07:19:34 -0700683#ifdef USE_INDIRECT_REF
684 IndirectRefTable* pRefTable = getLocalRefTable(env);
685 Thread* self = ((JNIEnvExt*)env)->self;
686 u4 cookie = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefCookie;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800687
Andy McFaddend5ab7262009-08-25 07:19:34 -0700688 if (!dvmRemoveFromIndirectRefTable(pRefTable, cookie, jobj)) {
689 /*
690 * Attempting to delete a local reference that is not in the
691 * topmost local reference frame is a no-op. DeleteLocalRef returns
692 * void and doesn't throw any exceptions, but we should probably
693 * complain about it so the user will notice that things aren't
694 * going quite the way they expect.
695 */
696 LOGW("JNI WARNING: DeleteLocalRef(%p) failed to find entry\n", jobj);
697 }
698#else
699 ReferenceTable* pRefTable = getLocalRefTable(env);
700 Thread* self = ((JNIEnvExt*)env)->self;
701 Object** bottom = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefCookie;
702
703 if (!dvmRemoveFromReferenceTable(pRefTable, bottom, (Object*) jobj)) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800704 /*
705 * Attempting to delete a local reference that is not in the
706 * topmost local reference frame is a no-op. DeleteLocalRef returns
707 * void and doesn't throw any exceptions, but we should probably
708 * complain about it so the user will notice that things aren't
709 * going quite the way they expect.
710 */
711 LOGW("JNI WARNING: DeleteLocalRef(%p) failed to find entry (valid=%d)\n",
Andy McFaddenab00d452009-08-19 07:21:41 -0700712 jobj, dvmIsValidObject((Object*) jobj));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800713 }
Andy McFaddend5ab7262009-08-25 07:19:34 -0700714#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800715}
716
717/*
718 * Add a global reference for an object.
719 *
720 * We may add the same object more than once. Add/remove calls are paired,
721 * so it needs to appear on the list multiple times.
722 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700723static jobject addGlobalReference(Object* obj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800724{
725 if (obj == NULL)
726 return NULL;
727
728 //LOGI("adding obj=%p\n", obj);
729 //dvmDumpThread(dvmThreadSelf(), false);
730
731 if (false && ((Object*)obj)->clazz == gDvm.classJavaLangClass) {
732 ClassObject* clazz = (ClassObject*) obj;
733 LOGI("-------\n");
734 LOGI("Adding global ref on class %s\n", clazz->descriptor);
735 dvmDumpThread(dvmThreadSelf(), false);
736 }
737 if (false && ((Object*)obj)->clazz == gDvm.classJavaLangString) {
738 StringObject* strObj = (StringObject*) obj;
739 char* str = dvmCreateCstrFromString(strObj);
740 if (strcmp(str, "sync-response") == 0) {
741 LOGI("-------\n");
742 LOGI("Adding global ref on string '%s'\n", str);
743 dvmDumpThread(dvmThreadSelf(), false);
744 //dvmAbort();
745 }
746 free(str);
747 }
748 if (false && ((Object*)obj)->clazz == gDvm.classArrayByte) {
749 ArrayObject* arrayObj = (ArrayObject*) obj;
Andy McFaddend5ab7262009-08-25 07:19:34 -0700750 if (arrayObj->length == 8192 /*&&
751 dvmReferenceTableEntries(&gDvm.jniGlobalRefTable) > 400*/)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800752 {
753 LOGI("Adding global ref on byte array %p (len=%d)\n",
754 arrayObj, arrayObj->length);
755 dvmDumpThread(dvmThreadSelf(), false);
756 }
757 }
758
Andy McFaddend5ab7262009-08-25 07:19:34 -0700759 jobject jobj;
760
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800761 dvmLockMutex(&gDvm.jniGlobalRefLock);
762
763 /*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800764 * Throwing an exception on failure is problematic, because JNI code
765 * may not be expecting an exception, and things sort of cascade. We
766 * want to have a hard limit to catch leaks during debugging, but this
767 * otherwise needs to expand until memory is consumed. As a practical
768 * matter, if we have many thousands of global references, chances are
769 * we're either leaking global ref table entries or we're going to
770 * run out of space in the GC heap.
771 */
Andy McFaddend5ab7262009-08-25 07:19:34 -0700772#ifdef USE_INDIRECT_REF
773 jobj = dvmAddToIndirectRefTable(&gDvm.jniGlobalRefTable, IRT_FIRST_SEGMENT,
774 obj);
775 if (jobj == NULL) {
776 dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable, "JNI global");
777 LOGE("Failed adding to JNI global ref table (%d entries)\n",
778 (int) dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable));
779 dvmAbort();
780 }
781
782 LOGVV("GREF add %p (%s.%s)\n", obj,
783 dvmGetCurrentJNIMethod()->clazz->descriptor,
784 dvmGetCurrentJNIMethod()->name);
785
786 /* GREF usage tracking; should probably be disabled for production env */
787 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
788 int count = dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable);
789 // TODO: adjust for "holes"
790 if (count > gDvm.jniGlobalRefHiMark) {
791 LOGD("GREF has increased to %d\n", count);
792 gDvm.jniGlobalRefHiMark += kGrefWaterInterval;
793 gDvm.jniGlobalRefLoMark += kGrefWaterInterval;
794
795 /* watch for "excessive" use; not generally appropriate */
796 if (count >= gDvm.jniGrefLimit) {
797 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
798 if (vm->warnError) {
799 dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable,
800 "JNI global");
801 LOGE("Excessive JNI global references (%d)\n", count);
802 dvmAbort();
803 } else {
804 LOGW("Excessive JNI global references (%d)\n", count);
805 }
806 }
807 }
808 }
809#else
810 if (!dvmAddToReferenceTable(&gDvm.jniGlobalRefTable, obj)) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800811 dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global");
812 LOGE("Failed adding to JNI global ref table (%d entries)\n",
813 (int) dvmReferenceTableEntries(&gDvm.jniGlobalRefTable));
814 dvmAbort();
815 }
Andy McFaddend5ab7262009-08-25 07:19:34 -0700816 jobj = (jobject) obj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800817
818 LOGVV("GREF add %p (%s.%s)\n", obj,
819 dvmGetCurrentJNIMethod()->clazz->descriptor,
820 dvmGetCurrentJNIMethod()->name);
821
822 /* GREF usage tracking; should probably be disabled for production env */
823 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
824 int count = dvmReferenceTableEntries(&gDvm.jniGlobalRefTable);
825 if (count > gDvm.jniGlobalRefHiMark) {
826 LOGD("GREF has increased to %d\n", count);
827 gDvm.jniGlobalRefHiMark += kGrefWaterInterval;
828 gDvm.jniGlobalRefLoMark += kGrefWaterInterval;
829
830 /* watch for "excessive" use; not generally appropriate */
831 if (count >= gDvm.jniGrefLimit) {
832 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
833 if (vm->warnError) {
834 dvmDumpReferenceTable(&gDvm.jniGlobalRefTable,"JNI global");
835 LOGE("Excessive JNI global references (%d)\n", count);
836 dvmAbort();
837 } else {
838 LOGW("Excessive JNI global references (%d)\n", count);
839 }
840 }
841 }
842 }
Andy McFaddend5ab7262009-08-25 07:19:34 -0700843#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800844
845bail:
846 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
Andy McFaddend5ab7262009-08-25 07:19:34 -0700847 return jobj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800848}
849
850/*
851 * Remove a global reference. In most cases it's the entry most recently
852 * added, which makes this pretty quick.
853 *
854 * Thought: if it's not the most recent entry, just null it out. When we
855 * fill up, do a compaction pass before we expand the list.
856 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700857static void deleteGlobalReference(jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800858{
Andy McFaddenab00d452009-08-19 07:21:41 -0700859 if (jobj == NULL)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800860 return;
861
862 dvmLockMutex(&gDvm.jniGlobalRefLock);
863
Andy McFaddend5ab7262009-08-25 07:19:34 -0700864#ifdef USE_INDIRECT_REF
865 if (!dvmRemoveFromIndirectRefTable(&gDvm.jniGlobalRefTable,
866 IRT_FIRST_SEGMENT, jobj))
867 {
868 LOGW("JNI: DeleteGlobalRef(%p) failed to find entry\n", jobj);
869 goto bail;
870 }
871
872 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
873 int count = dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable);
874 // TODO: not quite right, need to subtract holes
875 if (count < gDvm.jniGlobalRefLoMark) {
876 LOGD("GREF has decreased to %d\n", count);
877 gDvm.jniGlobalRefHiMark -= kGrefWaterInterval;
878 gDvm.jniGlobalRefLoMark -= kGrefWaterInterval;
879 }
880 }
881#else
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800882 if (!dvmRemoveFromReferenceTable(&gDvm.jniGlobalRefTable,
Andy McFaddenab00d452009-08-19 07:21:41 -0700883 gDvm.jniGlobalRefTable.table, jobj))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800884 {
885 LOGW("JNI: DeleteGlobalRef(%p) failed to find entry (valid=%d)\n",
Andy McFaddenab00d452009-08-19 07:21:41 -0700886 jobj, dvmIsValidObject((Object*) jobj));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800887 goto bail;
888 }
889
890 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
891 int count = dvmReferenceTableEntries(&gDvm.jniGlobalRefTable);
892 if (count < gDvm.jniGlobalRefLoMark) {
893 LOGD("GREF has decreased to %d\n", count);
894 gDvm.jniGlobalRefHiMark -= kGrefWaterInterval;
895 gDvm.jniGlobalRefLoMark -= kGrefWaterInterval;
896 }
897 }
Andy McFaddend5ab7262009-08-25 07:19:34 -0700898#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800899
900bail:
901 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
902}
903
904/*
Andy McFaddenab00d452009-08-19 07:21:41 -0700905 * Objects don't currently move, so we just need to create a reference
906 * that will ensure the array object isn't collected.
907 *
Andy McFaddenc26bb632009-08-21 12:01:31 -0700908 * We use a separate reference table, which is part of the GC root set.
Andy McFaddenab00d452009-08-19 07:21:41 -0700909 */
910static void pinPrimitiveArray(ArrayObject* arrayObj)
911{
Andy McFaddenc26bb632009-08-21 12:01:31 -0700912 if (arrayObj == NULL)
913 return;
914
915 dvmLockMutex(&gDvm.jniPinRefLock);
916 if (!dvmAddToReferenceTable(&gDvm.jniPinRefTable, (Object*)arrayObj)) {
917 dvmDumpReferenceTable(&gDvm.jniPinRefTable, "JNI pinned array");
918 LOGE("Failed adding to JNI pinned array ref table (%d entries)\n",
919 (int) dvmReferenceTableEntries(&gDvm.jniPinRefTable));
920 dvmDumpThread(dvmThreadSelf(), false);
921 dvmAbort();
922 }
923
924 /*
925 * If we're watching global ref usage, also keep an eye on these.
926 *
927 * The total number of pinned primitive arrays should be pretty small.
928 * A single array should not be pinned more than once or twice; any
929 * more than that is a strong indicator that a Release function is
930 * not being called.
931 */
932 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
933 int count = 0;
934 Object** ppObj = gDvm.jniPinRefTable.table;
935 while (ppObj < gDvm.jniPinRefTable.nextEntry) {
936 if (*ppObj++ == (Object*) arrayObj)
937 count++;
938 }
939
940 if (count > kPinComplainThreshold) {
941 LOGW("JNI: pin count on array %p (%s) is now %d\n",
942 arrayObj, arrayObj->obj.clazz->descriptor, count);
943 /* keep going */
944 }
945 }
946
947 dvmUnlockMutex(&gDvm.jniPinRefLock);
Andy McFaddenab00d452009-08-19 07:21:41 -0700948}
949
950/*
951 * Un-pin the array object. If an object was pinned twice, it must be
952 * unpinned twice before it's free to move.
Andy McFaddenab00d452009-08-19 07:21:41 -0700953 */
954static void unpinPrimitiveArray(ArrayObject* arrayObj)
955{
Andy McFaddenc26bb632009-08-21 12:01:31 -0700956 if (arrayObj == NULL)
957 return;
958
959 dvmLockMutex(&gDvm.jniPinRefLock);
960 if (!dvmRemoveFromReferenceTable(&gDvm.jniPinRefTable,
961 gDvm.jniPinRefTable.table, (Object*) arrayObj))
962 {
963 LOGW("JNI: unpinPrimitiveArray(%p) failed to find entry (valid=%d)\n",
964 arrayObj, dvmIsValidObject((Object*) arrayObj));
965 goto bail;
966 }
967
968bail:
969 dvmUnlockMutex(&gDvm.jniPinRefLock);
Andy McFaddenab00d452009-08-19 07:21:41 -0700970}
971
972/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800973 * GC helper function to mark all JNI global references.
Andy McFaddenc26bb632009-08-21 12:01:31 -0700974 *
975 * We're currently handling the "pin" table here too.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800976 */
977void dvmGcMarkJniGlobalRefs()
978{
Andy McFaddend5ab7262009-08-25 07:19:34 -0700979 Object** op;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800980
981 dvmLockMutex(&gDvm.jniGlobalRefLock);
982
Andy McFaddend5ab7262009-08-25 07:19:34 -0700983#ifdef USE_INDIRECT_REF
984 IndirectRefTable* pRefTable = &gDvm.jniGlobalRefTable;
985 op = pRefTable->table;
986 int numEntries = dvmIndirectRefTableEntries(pRefTable);
987 int i;
988
989 for (i = 0; i < numEntries; i++) {
990 Object* obj = *op;
991 if (obj != NULL)
992 dvmMarkObjectNonNull(obj);
993 op++;
994 }
995#else
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800996 op = gDvm.jniGlobalRefTable.table;
997 while ((uintptr_t)op < (uintptr_t)gDvm.jniGlobalRefTable.nextEntry) {
998 dvmMarkObjectNonNull(*(op++));
999 }
Andy McFaddend5ab7262009-08-25 07:19:34 -07001000#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001001
1002 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
Andy McFaddenc26bb632009-08-21 12:01:31 -07001003
1004
1005 dvmLockMutex(&gDvm.jniPinRefLock);
1006
1007 op = gDvm.jniPinRefTable.table;
1008 while ((uintptr_t)op < (uintptr_t)gDvm.jniPinRefTable.nextEntry) {
1009 dvmMarkObjectNonNull(*(op++));
1010 }
1011
1012 dvmUnlockMutex(&gDvm.jniPinRefLock);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001013}
1014
1015
Andy McFadden0083d372009-08-21 14:44:04 -07001016#if 0
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001017/*
1018 * Determine if "obj" appears in the argument list for the native method.
1019 *
1020 * We use the "shorty" signature to determine which argument slots hold
1021 * reference types.
1022 */
1023static bool findInArgList(Thread* self, Object* obj)
1024{
1025 const Method* meth;
1026 u4* fp;
1027 int i;
1028
1029 fp = self->curFrame;
1030 while (1) {
1031 /*
1032 * Back up over JNI PushLocalFrame frames. This works because the
1033 * previous frame on the interpreted stack is either a break frame
1034 * (if we called here via native code) or an interpreted method (if
1035 * we called here via the interpreter). In both cases the method
1036 * pointer won't match.
1037 */
1038 StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
1039 meth = saveArea->method;
1040 if (meth != SAVEAREA_FROM_FP(saveArea->prevFrame)->method)
1041 break;
1042 fp = saveArea->prevFrame;
1043 }
1044
1045 LOGVV("+++ scanning %d args in %s (%s)\n",
1046 meth->insSize, meth->name, meth->shorty);
1047 const char* shorty = meth->shorty +1; /* skip return type char */
1048 for (i = 0; i < meth->insSize; i++) {
1049 if (i == 0 && !dvmIsStaticMethod(meth)) {
1050 /* first arg is "this" ref, not represented in "shorty" */
1051 if (fp[i] == (u4) obj)
1052 return true;
1053 } else {
1054 /* if this is a reference type, see if it matches */
1055 switch (*shorty) {
1056 case 'L':
1057 if (fp[i] == (u4) obj)
1058 return true;
1059 break;
1060 case 'D':
1061 case 'J':
1062 i++;
1063 break;
1064 case '\0':
1065 LOGE("Whoops! ran off the end of %s (%d)\n",
1066 meth->shorty, meth->insSize);
1067 break;
1068 default:
1069 if (fp[i] == (u4) obj)
1070 LOGI("NOTE: ref %p match on arg type %c\n", obj, *shorty);
1071 break;
1072 }
1073 shorty++;
1074 }
1075 }
1076
1077 /*
1078 * For static methods, we also pass a class pointer in.
1079 */
1080 if (dvmIsStaticMethod(meth)) {
1081 //LOGI("+++ checking class pointer in %s\n", meth->name);
1082 if ((void*)obj == (void*)meth->clazz)
1083 return true;
1084 }
1085 return false;
1086}
Andy McFadden0083d372009-08-21 14:44:04 -07001087#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001088
1089/*
1090 * Verify that a reference passed in from native code is one that the
1091 * code is allowed to have.
1092 *
1093 * It's okay for native code to pass us a reference that:
Andy McFadden0083d372009-08-21 14:44:04 -07001094 * - was passed in as an argument when invoked by native code (and hence
1095 * is in the JNI local refs table)
1096 * - was returned to it from JNI (and is now in the local refs table)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001097 * - is present in the JNI global refs table
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001098 *
1099 * Used by -Xcheck:jni and GetObjectRefType.
1100 *
1101 * NOTE: in the current VM, global and local references are identical. If
1102 * something is both global and local, we can't tell them apart, and always
1103 * return "local".
1104 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001105jobjectRefType dvmGetJNIRefType(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001106{
Andy McFaddend5ab7262009-08-25 07:19:34 -07001107#ifdef USE_INDIRECT_REF
1108 /*
1109 * IndirectRefKind is currently defined as an exact match of
1110 * jobjectRefType, so this is easy.
1111 */
1112 return (jobjectRefType) dvmIndirectRefToIndex(jobj);
1113#else
Andy McFaddenab00d452009-08-19 07:21:41 -07001114 ReferenceTable* pRefTable = getLocalRefTable(env);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001115 Thread* self = dvmThreadSelf();
1116 //Object** top;
1117 Object** ptr;
1118
Andy McFadden0083d372009-08-21 14:44:04 -07001119#if 0
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001120 /* check args */
Andy McFaddenab00d452009-08-19 07:21:41 -07001121 if (findInArgList(self, jobj)) {
1122 //LOGI("--- REF found %p on stack\n", jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001123 return JNILocalRefType;
1124 }
Andy McFadden0083d372009-08-21 14:44:04 -07001125#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001126
1127 /* check locals */
Andy McFaddenab00d452009-08-19 07:21:41 -07001128 if (dvmFindInReferenceTable(pRefTable, pRefTable->table, jobj) != NULL) {
1129 //LOGI("--- REF found %p in locals\n", jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001130 return JNILocalRefType;
1131 }
1132
1133 /* check globals */
1134 dvmLockMutex(&gDvm.jniGlobalRefLock);
1135 if (dvmFindInReferenceTable(&gDvm.jniGlobalRefTable,
Andy McFaddenab00d452009-08-19 07:21:41 -07001136 gDvm.jniGlobalRefTable.table, jobj))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001137 {
Andy McFaddenab00d452009-08-19 07:21:41 -07001138 //LOGI("--- REF found %p in globals\n", jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001139 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
1140 return JNIGlobalRefType;
1141 }
1142 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
1143
1144 /* not found! */
1145 return JNIInvalidRefType;
Andy McFaddend5ab7262009-08-25 07:19:34 -07001146#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001147}
1148
1149/*
1150 * Register a method that uses JNI calling conventions.
1151 */
1152static bool dvmRegisterJNIMethod(ClassObject* clazz, const char* methodName,
1153 const char* signature, void* fnPtr)
1154{
1155 Method* method;
1156 bool result = false;
1157
1158 if (fnPtr == NULL)
1159 goto bail;
1160
1161 method = dvmFindDirectMethodByDescriptor(clazz, methodName, signature);
1162 if (method == NULL)
1163 method = dvmFindVirtualMethodByDescriptor(clazz, methodName, signature);
1164 if (method == NULL) {
1165 LOGW("ERROR: Unable to find decl for native %s.%s %s\n",
1166 clazz->descriptor, methodName, signature);
1167 goto bail;
1168 }
1169
1170 if (!dvmIsNativeMethod(method)) {
1171 LOGW("Unable to register: not native: %s.%s %s\n",
1172 clazz->descriptor, methodName, signature);
1173 goto bail;
1174 }
1175
1176 if (method->nativeFunc != dvmResolveNativeMethod) {
1177 LOGW("Warning: %s.%s %s was already registered/resolved?\n",
1178 clazz->descriptor, methodName, signature);
1179 /* keep going, I guess */
1180 }
1181
Andy McFadden59b61772009-05-13 16:44:34 -07001182 dvmUseJNIBridge(method, fnPtr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001183
1184 LOGV("JNI-registered %s.%s %s\n", clazz->descriptor, methodName,
1185 signature);
1186 result = true;
1187
1188bail:
1189 return result;
1190}
1191
1192/*
Andy McFadden59b61772009-05-13 16:44:34 -07001193 * Returns "true" if CheckJNI is enabled in the VM.
1194 */
1195static bool dvmIsCheckJNIEnabled(void)
1196{
1197 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
1198 return vm->useChecked;
1199}
1200
1201/*
1202 * Point "method->nativeFunc" at the JNI bridge, and overload "method->insns"
1203 * to point at the actual function.
1204 */
1205void dvmUseJNIBridge(Method* method, void* func)
1206{
Andy McFadden0083d372009-08-21 14:44:04 -07001207 enum {
1208 kJNIGeneral = 0,
1209 kJNISync = 1,
1210 kJNIVirtualNoRef = 2,
1211 kJNIStaticNoRef = 3,
1212 } kind;
1213 static const DalvikBridgeFunc stdFunc[] = {
1214 dvmCallJNIMethod_general,
1215 dvmCallJNIMethod_synchronized,
1216 dvmCallJNIMethod_virtualNoRef,
1217 dvmCallJNIMethod_staticNoRef
1218 };
1219 static const DalvikBridgeFunc checkFunc[] = {
1220 dvmCheckCallJNIMethod_general,
1221 dvmCheckCallJNIMethod_synchronized,
1222 dvmCheckCallJNIMethod_virtualNoRef,
1223 dvmCheckCallJNIMethod_staticNoRef
1224 };
1225
1226 bool hasRefArg = false;
1227
1228 if (dvmIsSynchronizedMethod(method)) {
1229 /* use version with synchronization; calls into general handler */
1230 kind = kJNISync;
Andy McFadden59b61772009-05-13 16:44:34 -07001231 } else {
Andy McFadden0083d372009-08-21 14:44:04 -07001232 /*
1233 * Do a quick scan through the "shorty" signature to see if the method
1234 * takes any reference arguments.
1235 */
1236 const char* cp = method->shorty;
1237 while (*++cp != '\0') { /* pre-incr to skip return type */
1238 if (*cp == 'L') {
1239 /* 'L' used for both object and array references */
1240 hasRefArg = true;
1241 break;
1242 }
1243 }
1244
1245 if (hasRefArg) {
1246 /* use general handler to slurp up reference args */
1247 kind = kJNIGeneral;
1248 } else {
1249 /* virtual methods have a ref in args[0] (not in signature) */
1250 if (dvmIsStaticMethod(method))
1251 kind = kJNIStaticNoRef;
1252 else
1253 kind = kJNIVirtualNoRef;
1254 }
1255 }
1256
1257 if (dvmIsCheckJNIEnabled()) {
1258 dvmSetNativeFunc(method, checkFunc[kind], func);
1259 } else {
1260 dvmSetNativeFunc(method, stdFunc[kind], func);
Andy McFadden59b61772009-05-13 16:44:34 -07001261 }
1262}
1263
1264/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001265 * Get the method currently being executed by examining the interp stack.
1266 */
1267const Method* dvmGetCurrentJNIMethod(void)
1268{
1269 assert(dvmThreadSelf() != NULL);
1270
1271 void* fp = dvmThreadSelf()->curFrame;
1272 const Method* meth = SAVEAREA_FROM_FP(fp)->method;
1273
1274 assert(meth != NULL);
1275 assert(dvmIsNativeMethod(meth));
1276 return meth;
1277}
1278
1279
1280/*
1281 * Track a JNI MonitorEnter in the current thread.
1282 *
1283 * The goal is to be able to "implicitly" release all JNI-held monitors
1284 * when the thread detaches.
1285 *
1286 * Monitors may be entered multiple times, so we add a new entry for each
1287 * enter call. It would be more efficient to keep a counter. At present
1288 * there's no real motivation to improve this however.
1289 */
1290static void trackMonitorEnter(Thread* self, Object* obj)
1291{
1292 static const int kInitialSize = 16;
1293 ReferenceTable* refTable = &self->jniMonitorRefTable;
1294
1295 /* init table on first use */
1296 if (refTable->table == NULL) {
1297 assert(refTable->maxEntries == 0);
1298
1299 if (!dvmInitReferenceTable(refTable, kInitialSize, INT_MAX)) {
1300 LOGE("Unable to initialize monitor tracking table\n");
1301 dvmAbort();
1302 }
1303 }
1304
1305 if (!dvmAddToReferenceTable(refTable, obj)) {
1306 /* ran out of memory? could throw exception instead */
1307 LOGE("Unable to add entry to monitor tracking table\n");
1308 dvmAbort();
1309 } else {
1310 LOGVV("--- added monitor %p\n", obj);
1311 }
1312}
1313
Andy McFaddenab00d452009-08-19 07:21:41 -07001314
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001315/*
1316 * Track a JNI MonitorExit in the current thread.
1317 */
1318static void trackMonitorExit(Thread* self, Object* obj)
1319{
Andy McFaddenab00d452009-08-19 07:21:41 -07001320 ReferenceTable* pRefTable = &self->jniMonitorRefTable;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001321
Andy McFaddenab00d452009-08-19 07:21:41 -07001322 if (!dvmRemoveFromReferenceTable(pRefTable, pRefTable->table, obj)) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001323 LOGE("JNI monitor %p not found in tracking list\n", obj);
1324 /* keep going? */
1325 } else {
1326 LOGVV("--- removed monitor %p\n", obj);
1327 }
1328}
1329
1330/*
1331 * Release all monitors held by the jniMonitorRefTable list.
1332 */
1333void dvmReleaseJniMonitors(Thread* self)
1334{
Andy McFaddenab00d452009-08-19 07:21:41 -07001335 ReferenceTable* pRefTable = &self->jniMonitorRefTable;
1336 Object** top = pRefTable->table;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001337
1338 if (top == NULL)
1339 return;
1340
Andy McFaddenab00d452009-08-19 07:21:41 -07001341 Object** ptr = pRefTable->nextEntry;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001342 while (--ptr >= top) {
1343 if (!dvmUnlockObject(self, *ptr)) {
1344 LOGW("Unable to unlock monitor %p at thread detach\n", *ptr);
1345 } else {
1346 LOGVV("--- detach-releasing monitor %p\n", *ptr);
1347 }
1348 }
1349
1350 /* zap it */
Andy McFaddenab00d452009-08-19 07:21:41 -07001351 pRefTable->nextEntry = pRefTable->table;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001352}
1353
1354#ifdef WITH_JNI_STACK_CHECK
1355/*
1356 * Compute a CRC on the entire interpreted stack.
1357 *
1358 * Would be nice to compute it on "self" as well, but there are parts of
1359 * the Thread that can be altered by other threads (e.g. prev/next pointers).
1360 */
1361static void computeStackSum(Thread* self)
1362{
1363 const u1* low = (const u1*)SAVEAREA_FROM_FP(self->curFrame);
1364 u4 crc = dvmInitCrc32();
1365 self->stackCrc = 0;
1366 crc = dvmComputeCrc32(crc, low, self->interpStackStart - low);
1367 self->stackCrc = crc;
1368}
1369
1370/*
1371 * Compute a CRC on the entire interpreted stack, and compare it to what
1372 * we previously computed.
1373 *
1374 * We can execute JNI directly from native code without calling in from
1375 * interpreted code during VM initialization and immediately after JNI
1376 * thread attachment. Another opportunity exists during JNI_OnLoad. Rather
1377 * than catching these cases we just ignore them here, which is marginally
1378 * less accurate but reduces the amount of code we have to touch with #ifdefs.
1379 */
1380static void checkStackSum(Thread* self)
1381{
1382 const u1* low = (const u1*)SAVEAREA_FROM_FP(self->curFrame);
1383 u4 stackCrc, crc;
1384
1385 stackCrc = self->stackCrc;
1386 self->stackCrc = 0;
1387 crc = dvmInitCrc32();
1388 crc = dvmComputeCrc32(crc, low, self->interpStackStart - low);
1389 if (crc != stackCrc) {
1390 const Method* meth = dvmGetCurrentJNIMethod();
1391 if (dvmComputeExactFrameDepth(self->curFrame) == 1) {
1392 LOGD("JNI: bad stack CRC (0x%08x) -- okay during init\n",
1393 stackCrc);
1394 } else if (strcmp(meth->name, "nativeLoad") == 0 &&
1395 (strcmp(meth->clazz->descriptor, "Ljava/lang/Runtime;") == 0))
1396 {
1397 LOGD("JNI: bad stack CRC (0x%08x) -- okay during JNI_OnLoad\n",
1398 stackCrc);
1399 } else {
1400 LOGW("JNI: bad stack CRC (%08x vs %08x)\n", crc, stackCrc);
1401 dvmAbort();
1402 }
1403 }
1404 self->stackCrc = (u4) -1; /* make logic errors more noticeable */
1405}
1406#endif
1407
1408
1409/*
1410 * ===========================================================================
Andy McFaddend5ab7262009-08-25 07:19:34 -07001411 * JNI call bridge
1412 * ===========================================================================
1413 */
1414
1415/*
1416 * The functions here form a bridge between interpreted code and JNI native
1417 * functions. The basic task is to convert an array of primitives and
1418 * references into C-style function arguments. This is architecture-specific
1419 * and usually requires help from assembly code.
1420 *
1421 * The bridge takes four arguments: the array of parameters, a place to
1422 * store the function result (if any), the method to call, and a pointer
1423 * to the current thread.
1424 *
1425 * These functions aren't called directly from elsewhere in the VM.
1426 * A pointer in the Method struct points to one of these, and when a native
1427 * method is invoked the interpreter jumps to it.
1428 *
1429 * (The "internal native" methods are invoked the same way, but instead
1430 * of calling through a bridge, the target method is called directly.)
1431 *
1432 * The "args" array should not be modified, but we do so anyway for
1433 * performance reasons. We know that it points to the "outs" area on
1434 * the current method's interpreted stack. This area is ignored by the
1435 * precise GC, because there is no register map for a native method (for
1436 * an interpreted method the args would be listed in the argument set).
1437 * We know all of the values exist elsewhere on the interpreted stack,
1438 * because the method call setup copies them right before making the call,
1439 * so we don't have to worry about concealing stuff from the GC.
1440 *
1441 * If we don't want to modify "args", we either have to create a local
1442 * copy and modify it before calling dvmPlatformInvoke, or we have to do
1443 * the local reference replacement within dvmPlatformInvoke. The latter
1444 * has some performance advantages, though if we can inline the local
1445 * reference adds we may win when there's a lot of reference args (unless
1446 * we want to code up some local ref table manipulation in assembly.
1447 */
1448
1449/*
1450 * General form, handles all cases.
1451 */
1452void dvmCallJNIMethod_general(const u4* args, JValue* pResult,
1453 const Method* method, Thread* self)
1454{
1455 int oldStatus;
1456 u4* modArgs = (u4*) args;
1457
1458 assert(method->insns != NULL);
1459
1460 //LOGI("JNI calling %p (%s.%s:%s):\n", method->insns,
1461 // method->clazz->descriptor, method->name, method->shorty);
1462
1463 /*
1464 * Walk the argument list, creating local references for appropriate
1465 * arguments.
1466 */
1467 JNIEnv* env = self->jniEnv;
1468 jclass staticMethodClass;
1469 int idx = 0;
1470 if (dvmIsStaticMethod(method)) {
1471 /* add the class object we pass in */
1472 staticMethodClass = addLocalReference(env, (Object*) method->clazz);
1473 if (staticMethodClass == NULL) {
1474 assert(dvmCheckException(self));
1475 return;
1476 }
1477 } else {
1478 /* add "this" */
1479 staticMethodClass = NULL;
1480 jobject thisObj = addLocalReference(env, (Object*) modArgs[0]);
1481 if (thisObj == NULL) {
1482 assert(dvmCheckException(self));
1483 return;
1484 }
1485 modArgs[idx] = (u4) thisObj;
1486 idx = 1;
1487 }
1488
1489 const char* shorty = &method->shorty[1]; /* skip return type */
1490 while (*shorty != '\0') {
1491 switch (*shorty++) {
1492 case 'L':
1493 //LOGI(" local %d: 0x%08x\n", idx, modArgs[idx]);
1494 if (modArgs[idx] != 0) {
1495 //if (!dvmIsValidObject((Object*) modArgs[idx]))
1496 // dvmAbort();
1497 jobject argObj = addLocalReference(env, (Object*) modArgs[idx]);
1498 if (argObj == NULL) {
1499 assert(dvmCheckException(self));
1500 return;
1501 }
1502 modArgs[idx] = (u4) argObj;
1503 }
1504 break;
1505 case 'D':
1506 case 'J':
1507 idx++;
1508 break;
1509 default:
1510 /* Z B C S I -- do nothing */
1511 break;
1512 }
1513
1514 idx++;
1515 }
1516
1517 oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
1518
1519 COMPUTE_STACK_SUM(self);
1520 dvmPlatformInvoke(self->jniEnv, staticMethodClass,
1521 method->jniArgInfo, method->insSize, modArgs, method->shorty,
1522 (void*)method->insns, pResult);
1523 CHECK_STACK_SUM(self);
1524
1525 dvmChangeStatus(self, oldStatus);
1526}
1527
1528/*
1529 * Handler for the unusual case of a synchronized native method.
1530 *
1531 * Lock the object, then call through the general function.
1532 */
1533void dvmCallJNIMethod_synchronized(const u4* args, JValue* pResult,
1534 const Method* method, Thread* self)
1535{
1536 Object* lockObj;
1537
1538 assert(dvmIsSynchronizedMethod(method));
1539
1540 if (dvmIsStaticMethod(method))
1541 lockObj = (Object*) method->clazz;
1542 else
1543 lockObj = (Object*) args[0];
1544
1545 LOGVV("Calling %s.%s: locking %p (%s)\n",
1546 method->clazz->descriptor, method->name,
1547 lockObj, lockObj->clazz->descriptor);
1548
1549 dvmLockObject(self, lockObj);
1550 dvmCallJNIMethod_general(args, pResult, method, self);
1551 dvmUnlockObject(self, lockObj);
1552}
1553
1554/*
1555 * Virtual method call, no reference arguments.
1556 *
1557 * We need to local-ref the "this" argument, found in args[0].
1558 */
1559void dvmCallJNIMethod_virtualNoRef(const u4* args, JValue* pResult,
1560 const Method* method, Thread* self)
1561{
1562 u4* modArgs = (u4*) args;
1563 int oldStatus;
1564
1565 jobject thisObj = addLocalReference(self->jniEnv, (Object*) args[0]);
1566 if (thisObj == NULL) {
1567 assert(dvmCheckException(self));
1568 return;
1569 }
1570 modArgs[0] = (u4) thisObj;
1571
1572 oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
1573
1574 COMPUTE_STACK_SUM(self);
1575 dvmPlatformInvoke(self->jniEnv, NULL,
1576 method->jniArgInfo, method->insSize, modArgs, method->shorty,
1577 (void*)method->insns, pResult);
1578 CHECK_STACK_SUM(self);
1579
1580 dvmChangeStatus(self, oldStatus);
1581}
1582
1583/*
1584 * Static method call, no reference arguments.
1585 *
1586 * We need to local-ref the class reference.
1587 */
1588void dvmCallJNIMethod_staticNoRef(const u4* args, JValue* pResult,
1589 const Method* method, Thread* self)
1590{
1591 jclass staticMethodClass;
1592 int oldStatus;
1593
1594 staticMethodClass = addLocalReference(self->jniEnv, (Object*)method->clazz);
1595 if (staticMethodClass == NULL) {
1596 assert(dvmCheckException(self));
1597 return;
1598 }
1599
1600 oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
1601
1602 COMPUTE_STACK_SUM(self);
1603 dvmPlatformInvoke(self->jniEnv, staticMethodClass,
1604 method->jniArgInfo, method->insSize, args, method->shorty,
1605 (void*)method->insns, pResult);
1606 CHECK_STACK_SUM(self);
1607
1608 dvmChangeStatus(self, oldStatus);
1609}
1610
1611/*
1612 * Extract the return type enum from the "jniArgInfo" field.
1613 */
1614DalvikJniReturnType dvmGetArgInfoReturnType(int jniArgInfo)
1615{
1616 return (jniArgInfo & DALVIK_JNI_RETURN_MASK) >> DALVIK_JNI_RETURN_SHIFT;
1617}
1618
1619
1620/*
1621 * ===========================================================================
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001622 * JNI implementation
1623 * ===========================================================================
1624 */
1625
1626/*
1627 * Return the version of the native method interface.
1628 */
1629static jint GetVersion(JNIEnv* env)
1630{
1631 JNI_ENTER();
1632 /*
1633 * There is absolutely no need to toggle the mode for correct behavior.
1634 * However, it does provide native code with a simple "suspend self
1635 * if necessary" call.
1636 */
1637 JNI_EXIT();
1638 return JNI_VERSION_1_6;
1639}
1640
1641/*
1642 * Create a new class from a bag of bytes.
1643 *
1644 * This is not currently supported within Dalvik.
1645 */
1646static jclass DefineClass(JNIEnv* env, const char *name, jobject loader,
1647 const jbyte* buf, jsize bufLen)
1648{
1649 UNUSED_PARAMETER(name);
1650 UNUSED_PARAMETER(loader);
1651 UNUSED_PARAMETER(buf);
1652 UNUSED_PARAMETER(bufLen);
1653
1654 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001655 LOGW("JNI DefineClass is not supported\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001656 JNI_EXIT();
1657 return NULL;
1658}
1659
1660/*
1661 * Find a class by name.
1662 *
1663 * We have to use the "no init" version of FindClass here, because we might
1664 * be getting the class prior to registering native methods that will be
1665 * used in <clinit>.
1666 *
1667 * We need to get the class loader associated with the current native
1668 * method. If there is no native method, e.g. we're calling this from native
1669 * code right after creating the VM, the spec says we need to use the class
1670 * loader returned by "ClassLoader.getBaseClassLoader". There is no such
1671 * method, but it's likely they meant ClassLoader.getSystemClassLoader.
1672 * We can't get that until after the VM has initialized though.
1673 */
1674static jclass FindClass(JNIEnv* env, const char* name)
1675{
1676 JNI_ENTER();
1677
1678 const Method* thisMethod;
1679 ClassObject* clazz;
Andy McFaddenab00d452009-08-19 07:21:41 -07001680 jclass jclazz = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001681 Object* loader;
1682 char* descriptor = NULL;
1683
1684 thisMethod = dvmGetCurrentJNIMethod();
1685 assert(thisMethod != NULL);
1686
1687 descriptor = dvmNameToDescriptor(name);
1688 if (descriptor == NULL) {
1689 clazz = NULL;
1690 goto bail;
1691 }
1692
1693 //Thread* self = dvmThreadSelf();
1694 if (_self->classLoaderOverride != NULL) {
1695 /* hack for JNI_OnLoad */
1696 assert(strcmp(thisMethod->name, "nativeLoad") == 0);
1697 loader = _self->classLoaderOverride;
1698 } else if (thisMethod == gDvm.methFakeNativeEntry) {
1699 /* start point of invocation interface */
1700 if (!gDvm.initializing)
1701 loader = dvmGetSystemClassLoader();
1702 else
1703 loader = NULL;
1704 } else {
1705 loader = thisMethod->clazz->classLoader;
1706 }
1707
1708 clazz = dvmFindClassNoInit(descriptor, loader);
Andy McFaddenab00d452009-08-19 07:21:41 -07001709 jclazz = addLocalReference(env, (Object*) clazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001710
1711bail:
1712 free(descriptor);
Andy McFaddenab00d452009-08-19 07:21:41 -07001713
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001714 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001715 return jclazz;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001716}
1717
1718/*
1719 * Return the superclass of a class.
1720 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001721static jclass GetSuperclass(JNIEnv* env, jclass jclazz)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001722{
1723 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001724 jclass jsuper;
1725
1726 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1727 if (clazz == NULL)
1728 jsuper = NULL;
1729 else
1730 jsuper = addLocalReference(env, (Object*)clazz->super);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001731 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001732 return jsuper;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001733}
1734
1735/*
1736 * Determine whether an object of clazz1 can be safely cast to clazz2.
1737 *
1738 * Like IsInstanceOf, but with a pair of class objects instead of obj+class.
1739 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001740static jboolean IsAssignableFrom(JNIEnv* env, jclass jclazz1, jclass jclazz2)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001741{
1742 JNI_ENTER();
1743
Andy McFaddenab00d452009-08-19 07:21:41 -07001744 ClassObject* clazz1 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz1);
1745 ClassObject* clazz2 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz2);
1746
1747 jboolean result = dvmInstanceof(clazz1, clazz2);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001748
1749 JNI_EXIT();
1750 return result;
1751}
1752
1753/*
1754 * Given a java.lang.reflect.Method or .Constructor, return a methodID.
1755 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001756static jmethodID FromReflectedMethod(JNIEnv* env, jobject jmethod)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001757{
1758 JNI_ENTER();
1759 jmethodID methodID;
Andy McFaddenab00d452009-08-19 07:21:41 -07001760 Object* method = dvmDecodeIndirectRef(env, jmethod);
1761 methodID = (jmethodID) dvmGetMethodFromReflectObj(method);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001762 JNI_EXIT();
1763 return methodID;
1764}
1765
1766/*
1767 * Given a java.lang.reflect.Field, return a fieldID.
1768 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001769static jfieldID FromReflectedField(JNIEnv* env, jobject jfield)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001770{
1771 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001772 jfieldID fieldID;
1773 Object* field = dvmDecodeIndirectRef(env, jfield);
1774 fieldID = (jfieldID) dvmGetFieldFromReflectObj(field);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001775 JNI_EXIT();
1776 return fieldID;
1777}
1778
1779/*
1780 * Convert a methodID to a java.lang.reflect.Method or .Constructor.
1781 *
1782 * (The "isStatic" field does not appear in the spec.)
1783 *
1784 * Throws OutOfMemory and returns NULL on failure.
1785 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001786static jobject ToReflectedMethod(JNIEnv* env, jclass jcls, jmethodID methodID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001787 jboolean isStatic)
1788{
1789 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001790 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls);
1791 Object* obj = dvmCreateReflectObjForMethod(clazz, (Method*) methodID);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001792 dvmReleaseTrackedAlloc(obj, NULL);
Andy McFaddenab00d452009-08-19 07:21:41 -07001793 jobject jobj = addLocalReference(env, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001794 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001795 return jobj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001796}
1797
1798/*
1799 * Convert a fieldID to a java.lang.reflect.Field.
1800 *
1801 * (The "isStatic" field does not appear in the spec.)
1802 *
1803 * Throws OutOfMemory and returns NULL on failure.
1804 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001805static jobject ToReflectedField(JNIEnv* env, jclass jcls, jfieldID fieldID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001806 jboolean isStatic)
1807{
1808 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001809 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls);
1810 Object* obj = dvmCreateReflectObjForField(jcls, (Field*) fieldID);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001811 dvmReleaseTrackedAlloc(obj, NULL);
Andy McFaddenab00d452009-08-19 07:21:41 -07001812 jobject jobj = addLocalReference(env, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001813 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001814 return jobj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001815}
1816
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001817/*
1818 * Take this exception and throw it.
1819 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001820static jint Throw(JNIEnv* env, jthrowable jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001821{
1822 JNI_ENTER();
1823
1824 jint retval;
1825
Andy McFaddenab00d452009-08-19 07:21:41 -07001826 if (jobj != NULL) {
1827 Object* obj = dvmDecodeIndirectRef(env, jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001828 dvmSetException(_self, obj);
1829 retval = JNI_OK;
Andy McFaddenab00d452009-08-19 07:21:41 -07001830 } else {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001831 retval = JNI_ERR;
Andy McFaddenab00d452009-08-19 07:21:41 -07001832 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001833
1834 JNI_EXIT();
1835 return retval;
1836}
1837
1838/*
Andy McFaddenab00d452009-08-19 07:21:41 -07001839 * Constructs an exception object from the specified class with the message
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001840 * specified by "message", and throws it.
1841 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001842static jint ThrowNew(JNIEnv* env, jclass jclazz, const char* message)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001843{
1844 JNI_ENTER();
1845
Andy McFaddenab00d452009-08-19 07:21:41 -07001846 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1847 dvmThrowExceptionByClass(clazz, message);
1848 // TODO: should return failure if this didn't work (e.g. OOM)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001849
1850 JNI_EXIT();
1851 return JNI_OK;
1852}
1853
1854/*
1855 * If an exception is being thrown, return the exception object. Otherwise,
1856 * return NULL.
1857 *
1858 * TODO: if there is no pending exception, we should be able to skip the
1859 * enter/exit checks. If we find one, we need to enter and then re-fetch
1860 * the exception (in case it got moved by a compacting GC).
1861 */
1862static jthrowable ExceptionOccurred(JNIEnv* env)
1863{
1864 JNI_ENTER();
1865
1866 Object* exception;
Andy McFaddenab00d452009-08-19 07:21:41 -07001867 jobject localException;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001868
Andy McFaddenab00d452009-08-19 07:21:41 -07001869 exception = dvmGetException(_self);
1870 localException = addLocalReference(env, exception);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001871 if (localException == NULL && exception != NULL) {
1872 /*
1873 * We were unable to add a new local reference, and threw a new
1874 * exception. We can't return "exception", because it's not a
1875 * local reference. So we have to return NULL, indicating that
1876 * there was no exception, even though it's pretty much raining
1877 * exceptions in here.
1878 */
1879 LOGW("JNI WARNING: addLocal/exception combo\n");
1880 }
1881
1882 JNI_EXIT();
1883 return localException;
1884}
1885
1886/*
1887 * Print an exception and stack trace to stderr.
1888 */
1889static void ExceptionDescribe(JNIEnv* env)
1890{
1891 JNI_ENTER();
1892
1893 Object* exception = dvmGetException(_self);
1894 if (exception != NULL) {
1895 dvmPrintExceptionStackTrace();
1896 } else {
1897 LOGI("Odd: ExceptionDescribe called, but no exception pending\n");
1898 }
1899
1900 JNI_EXIT();
1901}
1902
1903/*
1904 * Clear the exception currently being thrown.
1905 *
1906 * TODO: we should be able to skip the enter/exit stuff.
1907 */
1908static void ExceptionClear(JNIEnv* env)
1909{
1910 JNI_ENTER();
1911 dvmClearException(_self);
1912 JNI_EXIT();
1913}
1914
1915/*
1916 * Kill the VM. This function does not return.
1917 */
1918static void FatalError(JNIEnv* env, const char* msg)
1919{
1920 //dvmChangeStatus(NULL, THREAD_RUNNING);
1921 LOGE("JNI posting fatal error: %s\n", msg);
1922 dvmAbort();
1923}
1924
1925/*
1926 * Push a new JNI frame on the stack, with a new set of locals.
1927 *
1928 * The new frame must have the same method pointer. (If for no other
1929 * reason than FindClass needs it to get the appropriate class loader.)
1930 */
1931static jint PushLocalFrame(JNIEnv* env, jint capacity)
1932{
1933 JNI_ENTER();
1934 int result = JNI_OK;
Andy McFaddenab00d452009-08-19 07:21:41 -07001935 if (!ensureLocalCapacity(env, capacity) ||
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001936 !dvmPushLocalFrame(_self /*dvmThreadSelf()*/, dvmGetCurrentJNIMethod()))
1937 {
1938 /* yes, OutOfMemoryError, not StackOverflowError */
1939 dvmClearException(_self);
1940 dvmThrowException("Ljava/lang/OutOfMemoryError;",
1941 "out of stack in JNI PushLocalFrame");
1942 result = JNI_ERR;
1943 }
1944 JNI_EXIT();
1945 return result;
1946}
1947
1948/*
1949 * Pop the local frame off. If "result" is not null, add it as a
1950 * local reference on the now-current frame.
1951 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001952static jobject PopLocalFrame(JNIEnv* env, jobject jresult)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001953{
1954 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001955 Object* result = dvmDecodeIndirectRef(env, jresult);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001956 if (!dvmPopLocalFrame(_self /*dvmThreadSelf()*/)) {
1957 LOGW("JNI WARNING: too many PopLocalFrame calls\n");
1958 dvmClearException(_self);
1959 dvmThrowException("Ljava/lang/RuntimeException;",
1960 "too many PopLocalFrame calls");
1961 }
Andy McFaddenab00d452009-08-19 07:21:41 -07001962 jresult = addLocalReference(env, result);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001963 JNI_EXIT();
1964 return result;
1965}
1966
1967/*
1968 * Add a reference to the global list.
1969 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001970static jobject NewGlobalRef(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001971{
1972 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001973 Object* obj = dvmDecodeIndirectRef(env, jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001974 jobject retval = addGlobalReference(obj);
1975 JNI_EXIT();
1976 return retval;
1977}
1978
1979/*
1980 * Delete a reference from the global list.
1981 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001982static void DeleteGlobalRef(JNIEnv* env, jobject jglobalRef)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001983{
1984 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001985 deleteGlobalReference(jglobalRef);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001986 JNI_EXIT();
1987}
1988
1989
1990/*
1991 * Add a reference to the local list.
1992 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001993static jobject NewLocalRef(JNIEnv* env, jobject jref)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001994{
1995 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001996 Object* obj = dvmDecodeIndirectRef(env, jref);
1997 jobject retval = addLocalReference(env, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001998 JNI_EXIT();
1999 return retval;
2000}
2001
2002/*
2003 * Delete a reference from the local list.
2004 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002005static void DeleteLocalRef(JNIEnv* env, jobject jlocalRef)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002006{
2007 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002008 deleteLocalReference(env, jlocalRef);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002009 JNI_EXIT();
2010}
2011
2012/*
2013 * Ensure that the local references table can hold at least this many
2014 * references.
2015 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002016static jint EnsureLocalCapacity(JNIEnv* env, jint capacity)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002017{
2018 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002019 bool okay = ensureLocalCapacity(env, capacity);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002020 if (!okay) {
2021 dvmThrowException("Ljava/lang/OutOfMemoryError;",
2022 "can't ensure local reference capacity");
2023 }
2024 JNI_EXIT();
2025 if (okay)
2026 return 0;
2027 else
2028 return -1;
2029}
2030
2031
2032/*
2033 * Determine whether two Object references refer to the same underlying object.
2034 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002035static jboolean IsSameObject(JNIEnv* env, jobject jref1, jobject jref2)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002036{
2037 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002038 Object* obj1 = dvmDecodeIndirectRef(env, jref1);
2039 Object* obj2 = dvmDecodeIndirectRef(env, jref2);
2040 jboolean result = (obj1 == obj2);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002041 JNI_EXIT();
2042 return result;
2043}
2044
2045/*
2046 * Allocate a new object without invoking any constructors.
2047 */
2048static jobject AllocObject(JNIEnv* env, jclass jclazz)
2049{
2050 JNI_ENTER();
2051
Andy McFaddenab00d452009-08-19 07:21:41 -07002052 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2053 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002054
2055 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2056 assert(dvmCheckException(_self));
Andy McFaddenab00d452009-08-19 07:21:41 -07002057 result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002058 } else {
Andy McFaddenab00d452009-08-19 07:21:41 -07002059 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
2060 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002061 }
2062
2063 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002064 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002065}
2066
2067/*
Andy McFaddenab00d452009-08-19 07:21:41 -07002068 * Allocate a new object and invoke the supplied constructor.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002069 */
2070static jobject NewObject(JNIEnv* env, jclass jclazz, jmethodID methodID, ...)
2071{
2072 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002073 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2074 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002075
2076 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2077 assert(dvmCheckException(_self));
Andy McFaddenab00d452009-08-19 07:21:41 -07002078 result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002079 } else {
Andy McFaddenab00d452009-08-19 07:21:41 -07002080 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
2081 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002082 if (newObj != NULL) {
2083 JValue unused;
2084 va_list args;
2085 va_start(args, methodID);
Andy McFaddend5ab7262009-08-25 07:19:34 -07002086 dvmCallMethodV(_self, (Method*) methodID, newObj, true, &unused,
2087 args);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002088 va_end(args);
2089 }
2090 }
2091
2092 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002093 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002094}
Andy McFaddenab00d452009-08-19 07:21:41 -07002095static jobject NewObjectV(JNIEnv* env, jclass jclazz, jmethodID methodID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002096 va_list args)
2097{
2098 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002099 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2100 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002101
Andy McFaddenab00d452009-08-19 07:21:41 -07002102 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
2103 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002104 if (newObj != NULL) {
2105 JValue unused;
Andy McFaddend5ab7262009-08-25 07:19:34 -07002106 dvmCallMethodV(_self, (Method*) methodID, newObj, true, &unused, args);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002107 }
2108
2109 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002110 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002111}
Andy McFaddenab00d452009-08-19 07:21:41 -07002112static jobject NewObjectA(JNIEnv* env, jclass jclazz, jmethodID methodID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002113 jvalue* args)
2114{
2115 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002116 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2117 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002118
Andy McFaddenab00d452009-08-19 07:21:41 -07002119 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
2120 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002121 if (newObj != NULL) {
2122 JValue unused;
Andy McFaddend5ab7262009-08-25 07:19:34 -07002123 dvmCallMethodA(_self, (Method*) methodID, newObj, true, &unused, args);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002124 }
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}
2129
2130/*
2131 * Returns the class of an object.
2132 *
2133 * JNI spec says: obj must not be NULL.
2134 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002135static jclass GetObjectClass(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002136{
2137 JNI_ENTER();
2138
Andy McFaddenab00d452009-08-19 07:21:41 -07002139 assert(jobj != NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002140
Andy McFaddenab00d452009-08-19 07:21:41 -07002141 Object* obj = dvmDecodeIndirectRef(env, jobj);
2142 jclass jclazz = addLocalReference(env, (Object*) obj->clazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002143
2144 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002145 return jclazz;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002146}
2147
2148/*
2149 * Determine whether "obj" is an instance of "clazz".
2150 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002151static jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass jclazz)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002152{
2153 JNI_ENTER();
2154
Andy McFaddenab00d452009-08-19 07:21:41 -07002155 assert(jclazz != NULL);
2156
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002157 jboolean result;
2158
Andy McFaddenab00d452009-08-19 07:21:41 -07002159 if (jobj == NULL) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002160 result = true;
Andy McFaddenab00d452009-08-19 07:21:41 -07002161 } else {
2162 Object* obj = dvmDecodeIndirectRef(env, jobj);
2163 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2164 result = dvmInstanceof(obj->clazz, clazz);
2165 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002166
2167 JNI_EXIT();
2168 return result;
2169}
2170
2171/*
2172 * Get a method ID for an instance method.
2173 *
2174 * JNI defines <init> as an instance method, but Dalvik considers it a
2175 * "direct" method, so we have to special-case it here.
2176 *
2177 * Dalvik also puts all private methods into the "direct" list, so we
2178 * really need to just search both lists.
2179 */
2180static jmethodID GetMethodID(JNIEnv* env, jclass jclazz, const char* name,
2181 const char* sig)
2182{
2183 JNI_ENTER();
2184
Andy McFaddenab00d452009-08-19 07:21:41 -07002185 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002186 jmethodID id = NULL;
2187
2188 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2189 assert(dvmCheckException(_self));
2190 } else {
2191 Method* meth;
2192
2193 meth = dvmFindVirtualMethodHierByDescriptor(clazz, name, sig);
2194 if (meth == NULL) {
2195 /* search private methods and constructors; non-hierarchical */
2196 meth = dvmFindDirectMethodByDescriptor(clazz, name, sig);
2197 }
2198 if (meth != NULL && dvmIsStaticMethod(meth)) {
2199 IF_LOGD() {
2200 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
2201 LOGD("GetMethodID: not returning static method %s.%s %s\n",
2202 clazz->descriptor, meth->name, desc);
2203 free(desc);
2204 }
2205 meth = NULL;
2206 }
2207 if (meth == NULL) {
Andy McFadden03bd0d52009-08-18 15:32:27 -07002208 LOGD("GetMethodID: method not found: %s.%s:%s\n",
2209 clazz->descriptor, name, sig);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002210 dvmThrowException("Ljava/lang/NoSuchMethodError;", name);
2211 }
2212
2213 /*
2214 * The method's class may not be the same as clazz, but if
2215 * it isn't this must be a virtual method and the class must
2216 * be a superclass (and, hence, already initialized).
2217 */
2218 if (meth != NULL) {
2219 assert(dvmIsClassInitialized(meth->clazz) ||
2220 dvmIsClassInitializing(meth->clazz));
2221 }
2222 id = (jmethodID) meth;
2223 }
2224 JNI_EXIT();
2225 return id;
2226}
2227
2228/*
2229 * Get a field ID (instance fields).
2230 */
2231static jfieldID GetFieldID(JNIEnv* env, jclass jclazz,
2232 const char* name, const char* sig)
2233{
2234 JNI_ENTER();
2235
Andy McFaddenab00d452009-08-19 07:21:41 -07002236 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002237 jfieldID id;
2238
2239 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2240 assert(dvmCheckException(_self));
2241 id = NULL;
2242 } else {
2243 id = (jfieldID) dvmFindInstanceFieldHier(clazz, name, sig);
Andy McFadden03bd0d52009-08-18 15:32:27 -07002244 if (id == NULL) {
2245 LOGD("GetFieldID: unable to find field %s.%s:%s\n",
2246 clazz->descriptor, name, sig);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002247 dvmThrowException("Ljava/lang/NoSuchFieldError;", name);
Andy McFadden03bd0d52009-08-18 15:32:27 -07002248 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002249 }
2250 JNI_EXIT();
2251 return id;
2252}
2253
2254/*
2255 * Get the method ID for a static method in a class.
2256 */
2257static jmethodID GetStaticMethodID(JNIEnv* env, jclass jclazz,
2258 const char* name, const char* sig)
2259{
2260 JNI_ENTER();
2261
Andy McFaddenab00d452009-08-19 07:21:41 -07002262 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002263 jmethodID id;
2264
2265 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2266 assert(dvmCheckException(_self));
2267 id = NULL;
2268 } else {
2269 Method* meth;
2270
2271 meth = dvmFindDirectMethodHierByDescriptor(clazz, name, sig);
2272
2273 /* make sure it's static, not virtual+private */
2274 if (meth != NULL && !dvmIsStaticMethod(meth)) {
2275 IF_LOGD() {
2276 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
2277 LOGD("GetStaticMethodID: "
2278 "not returning nonstatic method %s.%s %s\n",
2279 clazz->descriptor, meth->name, desc);
2280 free(desc);
2281 }
2282 meth = NULL;
2283 }
2284
2285 id = (jmethodID) meth;
2286 if (id == NULL)
2287 dvmThrowException("Ljava/lang/NoSuchMethodError;", name);
2288 }
2289
2290 JNI_EXIT();
2291 return id;
2292}
2293
2294/*
2295 * Get a field ID (static fields).
2296 */
2297static jfieldID GetStaticFieldID(JNIEnv* env, jclass jclazz,
2298 const char* name, const char* sig)
2299{
2300 JNI_ENTER();
2301
Andy McFaddenab00d452009-08-19 07:21:41 -07002302 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002303 jfieldID id;
2304
2305 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2306 assert(dvmCheckException(_self));
2307 id = NULL;
2308 } else {
2309 id = (jfieldID) dvmFindStaticField(clazz, name, sig);
2310 if (id == NULL)
2311 dvmThrowException("Ljava/lang/NoSuchFieldError;", name);
2312 }
2313 JNI_EXIT();
2314 return id;
2315}
2316
2317/*
2318 * Get a static field.
2319 *
2320 * If we get an object reference, add it to the local refs list.
2321 */
2322#define GET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002323 static _ctype GetStatic##_jname##Field(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002324 jfieldID fieldID) \
2325 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002326 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002327 JNI_ENTER(); \
2328 StaticField* sfield = (StaticField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002329 _ctype value; \
2330 if (_isref) { /* only when _ctype==jobject */ \
2331 Object* obj = dvmGetStaticFieldObject(sfield); \
2332 value = (_ctype)(u4)addLocalReference(env, obj); \
2333 } else { \
2334 value = dvmGetStaticField##_jname(sfield); \
2335 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002336 JNI_EXIT(); \
2337 return value; \
2338 }
2339GET_STATIC_TYPE_FIELD(jobject, Object, true);
2340GET_STATIC_TYPE_FIELD(jboolean, Boolean, false);
2341GET_STATIC_TYPE_FIELD(jbyte, Byte, false);
2342GET_STATIC_TYPE_FIELD(jchar, Char, false);
2343GET_STATIC_TYPE_FIELD(jshort, Short, false);
2344GET_STATIC_TYPE_FIELD(jint, Int, false);
2345GET_STATIC_TYPE_FIELD(jlong, Long, false);
2346GET_STATIC_TYPE_FIELD(jfloat, Float, false);
2347GET_STATIC_TYPE_FIELD(jdouble, Double, false);
2348
2349/*
2350 * Set a static field.
2351 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002352#define SET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) \
2353 static void SetStatic##_jname##Field(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002354 jfieldID fieldID, _ctype value) \
2355 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002356 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002357 JNI_ENTER(); \
2358 StaticField* sfield = (StaticField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002359 if (_isref) { /* only when _ctype==jobject */ \
2360 Object* valObj = dvmDecodeIndirectRef(env, (jobject)(u4)value); \
2361 dvmSetStaticFieldObject(sfield, valObj); \
2362 } else { \
2363 dvmSetStaticField##_jname(sfield, value); \
2364 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002365 JNI_EXIT(); \
2366 }
Andy McFaddenab00d452009-08-19 07:21:41 -07002367SET_STATIC_TYPE_FIELD(jobject, Object, true);
2368SET_STATIC_TYPE_FIELD(jboolean, Boolean, false);
2369SET_STATIC_TYPE_FIELD(jbyte, Byte, false);
2370SET_STATIC_TYPE_FIELD(jchar, Char, false);
2371SET_STATIC_TYPE_FIELD(jshort, Short, false);
2372SET_STATIC_TYPE_FIELD(jint, Int, false);
2373SET_STATIC_TYPE_FIELD(jlong, Long, false);
2374SET_STATIC_TYPE_FIELD(jfloat, Float, false);
2375SET_STATIC_TYPE_FIELD(jdouble, Double, false);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002376
2377/*
2378 * Get an instance field.
2379 *
2380 * If we get an object reference, add it to the local refs list.
2381 */
2382#define GET_TYPE_FIELD(_ctype, _jname, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002383 static _ctype Get##_jname##Field(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002384 jfieldID fieldID) \
2385 { \
2386 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002387 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002388 InstField* field = (InstField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002389 _ctype value; \
2390 if (_isref) { /* only when _ctype==jobject */ \
2391 Object* valObj = dvmGetFieldObject(obj, field->byteOffset); \
2392 value = (_ctype)(u4)addLocalReference(env, valObj); \
2393 } else { \
2394 value = dvmGetField##_jname(obj, field->byteOffset); \
2395 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002396 JNI_EXIT(); \
2397 return value; \
2398 }
2399GET_TYPE_FIELD(jobject, Object, true);
2400GET_TYPE_FIELD(jboolean, Boolean, false);
2401GET_TYPE_FIELD(jbyte, Byte, false);
2402GET_TYPE_FIELD(jchar, Char, false);
2403GET_TYPE_FIELD(jshort, Short, false);
2404GET_TYPE_FIELD(jint, Int, false);
2405GET_TYPE_FIELD(jlong, Long, false);
2406GET_TYPE_FIELD(jfloat, Float, false);
2407GET_TYPE_FIELD(jdouble, Double, false);
2408
2409/*
2410 * Set an instance field.
2411 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002412#define SET_TYPE_FIELD(_ctype, _jname, _isref) \
2413 static void Set##_jname##Field(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002414 jfieldID fieldID, _ctype value) \
2415 { \
2416 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002417 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002418 InstField* field = (InstField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002419 if (_isref) { /* only when _ctype==jobject */ \
2420 Object* valObj = dvmDecodeIndirectRef(env, (jobject)(u4)value); \
2421 dvmSetFieldObject(obj, field->byteOffset, valObj); \
2422 } else { \
2423 dvmSetField##_jname(obj, field->byteOffset, value); \
2424 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002425 JNI_EXIT(); \
2426 }
Andy McFaddenab00d452009-08-19 07:21:41 -07002427SET_TYPE_FIELD(jobject, Object, true);
2428SET_TYPE_FIELD(jboolean, Boolean, false);
2429SET_TYPE_FIELD(jbyte, Byte, false);
2430SET_TYPE_FIELD(jchar, Char, false);
2431SET_TYPE_FIELD(jshort, Short, false);
2432SET_TYPE_FIELD(jint, Int, false);
2433SET_TYPE_FIELD(jlong, Long, false);
2434SET_TYPE_FIELD(jfloat, Float, false);
2435SET_TYPE_FIELD(jdouble, Double, false);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002436
2437/*
2438 * Make a virtual method call.
2439 *
2440 * Three versions (..., va_list, jvalue[]) for each return type. If we're
2441 * returning an Object, we have to add it to the local references table.
2442 */
2443#define CALL_VIRTUAL(_ctype, _jname, _retfail, _retok, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002444 static _ctype Call##_jname##Method(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002445 jmethodID methodID, ...) \
2446 { \
2447 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002448 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002449 const Method* meth; \
2450 va_list args; \
2451 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002452 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002453 if (meth == NULL) { \
2454 JNI_EXIT(); \
2455 return _retfail; \
2456 } \
2457 va_start(args, methodID); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002458 dvmCallMethodV(_self, meth, obj, true, &result, args); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002459 va_end(args); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002460 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002461 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002462 JNI_EXIT(); \
2463 return _retok; \
2464 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002465 static _ctype Call##_jname##MethodV(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002466 jmethodID methodID, va_list args) \
2467 { \
2468 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002469 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002470 const Method* meth; \
2471 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002472 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002473 if (meth == NULL) { \
2474 JNI_EXIT(); \
2475 return _retfail; \
2476 } \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002477 dvmCallMethodV(_self, meth, obj, true, &result, args); \
2478 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002479 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002480 JNI_EXIT(); \
2481 return _retok; \
2482 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002483 static _ctype Call##_jname##MethodA(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002484 jmethodID methodID, jvalue* args) \
2485 { \
2486 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002487 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002488 const Method* meth; \
2489 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002490 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002491 if (meth == NULL) { \
2492 JNI_EXIT(); \
2493 return _retfail; \
2494 } \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002495 dvmCallMethodA(_self, meth, obj, true, &result, args); \
2496 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002497 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002498 JNI_EXIT(); \
2499 return _retok; \
2500 }
2501CALL_VIRTUAL(jobject, Object, NULL, result.l, true);
2502CALL_VIRTUAL(jboolean, Boolean, 0, result.z, false);
2503CALL_VIRTUAL(jbyte, Byte, 0, result.b, false);
2504CALL_VIRTUAL(jchar, Char, 0, result.c, false);
2505CALL_VIRTUAL(jshort, Short, 0, result.s, false);
2506CALL_VIRTUAL(jint, Int, 0, result.i, false);
2507CALL_VIRTUAL(jlong, Long, 0, result.j, false);
2508CALL_VIRTUAL(jfloat, Float, 0.0f, result.f, false);
2509CALL_VIRTUAL(jdouble, Double, 0.0, result.d, false);
2510CALL_VIRTUAL(void, Void, , , false);
2511
2512/*
2513 * Make a "non-virtual" method call. We're still calling a virtual method,
2514 * but this time we're not doing an indirection through the object's vtable.
2515 * The "clazz" parameter defines which implementation of a method we want.
2516 *
2517 * Three versions (..., va_list, jvalue[]) for each return type.
2518 */
2519#define CALL_NONVIRTUAL(_ctype, _jname, _retfail, _retok, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002520 static _ctype CallNonvirtual##_jname##Method(JNIEnv* env, jobject jobj, \
2521 jclass jclazz, jmethodID methodID, ...) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002522 { \
2523 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002524 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2525 ClassObject* clazz = \
2526 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002527 const Method* meth; \
2528 va_list args; \
2529 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002530 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002531 if (meth == NULL) { \
2532 JNI_EXIT(); \
2533 return _retfail; \
2534 } \
2535 va_start(args, methodID); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002536 dvmCallMethodV(_self, meth, obj, true, &result, args); \
2537 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002538 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002539 va_end(args); \
2540 JNI_EXIT(); \
2541 return _retok; \
2542 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002543 static _ctype CallNonvirtual##_jname##MethodV(JNIEnv* env, jobject jobj,\
2544 jclass jclazz, jmethodID methodID, va_list args) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002545 { \
2546 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002547 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2548 ClassObject* clazz = \
2549 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002550 const Method* meth; \
2551 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002552 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002553 if (meth == NULL) { \
2554 JNI_EXIT(); \
2555 return _retfail; \
2556 } \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002557 dvmCallMethodV(_self, meth, obj, true, &result, args); \
2558 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002559 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002560 JNI_EXIT(); \
2561 return _retok; \
2562 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002563 static _ctype CallNonvirtual##_jname##MethodA(JNIEnv* env, jobject jobj,\
2564 jclass jclazz, jmethodID methodID, jvalue* args) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002565 { \
2566 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002567 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2568 ClassObject* clazz = \
2569 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002570 const Method* meth; \
2571 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002572 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002573 if (meth == NULL) { \
2574 JNI_EXIT(); \
2575 return _retfail; \
2576 } \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002577 dvmCallMethodA(_self, meth, obj, true, &result, args); \
2578 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002579 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002580 JNI_EXIT(); \
2581 return _retok; \
2582 }
2583CALL_NONVIRTUAL(jobject, Object, NULL, result.l, true);
2584CALL_NONVIRTUAL(jboolean, Boolean, 0, result.z, false);
2585CALL_NONVIRTUAL(jbyte, Byte, 0, result.b, false);
2586CALL_NONVIRTUAL(jchar, Char, 0, result.c, false);
2587CALL_NONVIRTUAL(jshort, Short, 0, result.s, false);
2588CALL_NONVIRTUAL(jint, Int, 0, result.i, false);
2589CALL_NONVIRTUAL(jlong, Long, 0, result.j, false);
2590CALL_NONVIRTUAL(jfloat, Float, 0.0f, result.f, false);
2591CALL_NONVIRTUAL(jdouble, Double, 0.0, result.d, false);
2592CALL_NONVIRTUAL(void, Void, , , false);
2593
2594
2595/*
2596 * Call a static method.
2597 */
2598#define CALL_STATIC(_ctype, _jname, _retfail, _retok, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002599 static _ctype CallStatic##_jname##Method(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002600 jmethodID methodID, ...) \
2601 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002602 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002603 JNI_ENTER(); \
2604 JValue result; \
2605 va_list args; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002606 va_start(args, methodID); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002607 dvmCallMethodV(_self, (Method*)methodID, NULL, true, &result, args);\
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002608 va_end(args); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002609 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002610 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002611 JNI_EXIT(); \
2612 return _retok; \
2613 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002614 static _ctype CallStatic##_jname##MethodV(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002615 jmethodID methodID, va_list args) \
2616 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002617 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002618 JNI_ENTER(); \
2619 JValue result; \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002620 dvmCallMethodV(_self, (Method*)methodID, NULL, true, &result, args);\
2621 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002622 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002623 JNI_EXIT(); \
2624 return _retok; \
2625 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002626 static _ctype CallStatic##_jname##MethodA(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002627 jmethodID methodID, jvalue* args) \
2628 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002629 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002630 JNI_ENTER(); \
2631 JValue result; \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002632 dvmCallMethodA(_self, (Method*)methodID, NULL, true, &result, args);\
2633 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002634 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002635 JNI_EXIT(); \
2636 return _retok; \
2637 }
2638CALL_STATIC(jobject, Object, NULL, result.l, true);
2639CALL_STATIC(jboolean, Boolean, 0, result.z, false);
2640CALL_STATIC(jbyte, Byte, 0, result.b, false);
2641CALL_STATIC(jchar, Char, 0, result.c, false);
2642CALL_STATIC(jshort, Short, 0, result.s, false);
2643CALL_STATIC(jint, Int, 0, result.i, false);
2644CALL_STATIC(jlong, Long, 0, result.j, false);
2645CALL_STATIC(jfloat, Float, 0.0f, result.f, false);
2646CALL_STATIC(jdouble, Double, 0.0, result.d, false);
2647CALL_STATIC(void, Void, , , false);
2648
2649/*
2650 * Create a new String from Unicode data.
2651 *
2652 * If "len" is zero, we will return an empty string even if "unicodeChars"
2653 * is NULL. (The JNI spec is vague here.)
2654 */
2655static jstring NewString(JNIEnv* env, const jchar* unicodeChars, jsize len)
2656{
2657 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002658 jobject retval;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002659
Andy McFaddenab00d452009-08-19 07:21:41 -07002660 StringObject* jstr = dvmCreateStringFromUnicode(unicodeChars, len);
2661 if (jstr == NULL) {
2662 retval = NULL;
2663 } else {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002664 dvmReleaseTrackedAlloc((Object*) jstr, NULL);
Andy McFaddenab00d452009-08-19 07:21:41 -07002665 retval = addLocalReference(env, (Object*) jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002666 }
2667
2668 JNI_EXIT();
2669 return jstr;
2670}
2671
2672/*
2673 * Return the length of a String in Unicode character units.
2674 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002675static jsize GetStringLength(JNIEnv* env, jstring jstr)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002676{
2677 JNI_ENTER();
2678
Andy McFaddenab00d452009-08-19 07:21:41 -07002679 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2680 jsize len = dvmStringLen(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002681
2682 JNI_EXIT();
2683 return len;
2684}
2685
Andy McFaddenab00d452009-08-19 07:21:41 -07002686
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002687/*
Andy McFaddenab00d452009-08-19 07:21:41 -07002688 * Get a string's character data.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002689 *
2690 * The result is guaranteed to be valid until ReleaseStringChars is
Andy McFaddenab00d452009-08-19 07:21:41 -07002691 * called, which means we have to pin it or return a copy.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002692 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002693static const jchar* GetStringChars(JNIEnv* env, jstring jstr, jboolean* isCopy)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002694{
2695 JNI_ENTER();
2696
Andy McFaddenab00d452009-08-19 07:21:41 -07002697 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
Andy McFaddend5ab7262009-08-25 07:19:34 -07002698 ArrayObject* strChars = dvmStringCharArray(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002699
Andy McFaddenab00d452009-08-19 07:21:41 -07002700 pinPrimitiveArray(strChars);
2701
2702 const u2* data = dvmStringChars(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002703 if (isCopy != NULL)
2704 *isCopy = JNI_FALSE;
2705
2706 JNI_EXIT();
2707 return (jchar*)data;
2708}
2709
2710/*
2711 * Release our grip on some characters from a string.
2712 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002713static void ReleaseStringChars(JNIEnv* env, jstring jstr, const jchar* chars)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002714{
2715 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002716 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
Andy McFaddend5ab7262009-08-25 07:19:34 -07002717 ArrayObject* strChars = dvmStringCharArray(strObj);
Andy McFaddenab00d452009-08-19 07:21:41 -07002718 unpinPrimitiveArray(strChars);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002719 JNI_EXIT();
2720}
2721
2722/*
2723 * Create a new java.lang.String object from chars in modified UTF-8 form.
2724 *
2725 * The spec doesn't say how to handle a NULL string. Popular desktop VMs
2726 * accept it and return a NULL pointer in response.
2727 */
2728static jstring NewStringUTF(JNIEnv* env, const char* bytes)
2729{
2730 JNI_ENTER();
2731
Andy McFaddenab00d452009-08-19 07:21:41 -07002732 jstring result;
2733
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002734 if (bytes == NULL) {
Andy McFaddenab00d452009-08-19 07:21:41 -07002735 result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002736 } else {
Andy McFaddenab00d452009-08-19 07:21:41 -07002737 /* note newStr could come back NULL on OOM */
2738 StringObject* newStr = dvmCreateStringFromCstr(bytes, ALLOC_DEFAULT);
2739 result = addLocalReference(env, (Object*) newStr);
2740 dvmReleaseTrackedAlloc((Object*)newStr, NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002741 }
2742
2743 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002744 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002745}
2746
2747/*
2748 * Return the length in bytes of the modified UTF-8 form of the string.
2749 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002750static jsize GetStringUTFLength(JNIEnv* env, jstring jstr)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002751{
2752 JNI_ENTER();
2753
Andy McFaddenab00d452009-08-19 07:21:41 -07002754 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2755 jsize len = dvmStringUtf8ByteLen(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002756
2757 JNI_EXIT();
2758 return len;
2759}
2760
2761/*
2762 * Convert "string" to modified UTF-8 and return a pointer. The returned
2763 * value must be released with ReleaseStringUTFChars.
2764 *
2765 * According to the JNI reference, "Returns a pointer to a UTF-8 string,
2766 * or NULL if the operation fails. Returns NULL if and only if an invocation
2767 * of this function has thrown an exception."
2768 *
2769 * The behavior here currently follows that of other open-source VMs, which
2770 * quietly return NULL if "string" is NULL. We should consider throwing an
2771 * NPE. (The CheckJNI code blows up if you try to pass in a NULL string,
2772 * which should catch this sort of thing during development.) Certain other
2773 * VMs will crash with a segmentation fault.
2774 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002775static const char* GetStringUTFChars(JNIEnv* env, jstring jstr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002776 jboolean* isCopy)
2777{
2778 JNI_ENTER();
2779 char* newStr;
2780
Andy McFaddenab00d452009-08-19 07:21:41 -07002781 if (jstr == NULL) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002782 /* this shouldn't happen; throw NPE? */
2783 newStr = NULL;
2784 } else {
2785 if (isCopy != NULL)
2786 *isCopy = JNI_TRUE;
2787
Andy McFaddenab00d452009-08-19 07:21:41 -07002788 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2789 newStr = dvmCreateCstrFromString(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002790 if (newStr == NULL) {
2791 /* assume memory failure */
2792 dvmThrowException("Ljava/lang/OutOfMemoryError;",
2793 "native heap string alloc failed");
2794 }
2795 }
2796
2797 JNI_EXIT();
2798 return newStr;
2799}
2800
2801/*
2802 * Release a string created by GetStringUTFChars().
2803 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002804static void ReleaseStringUTFChars(JNIEnv* env, jstring jstr, const char* utf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002805{
2806 JNI_ENTER();
2807 free((char*)utf);
2808 JNI_EXIT();
2809}
2810
2811/*
2812 * Return the capacity of the array.
2813 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002814static jsize GetArrayLength(JNIEnv* env, jarray jarr)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002815{
2816 JNI_ENTER();
2817
Andy McFaddenab00d452009-08-19 07:21:41 -07002818 ArrayObject* arrObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
2819 jsize length = arrObj->length;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002820
2821 JNI_EXIT();
2822 return length;
2823}
2824
2825/*
2826 * Construct a new array that holds objects from class "elementClass".
2827 */
2828static jobjectArray NewObjectArray(JNIEnv* env, jsize length,
Andy McFaddenab00d452009-08-19 07:21:41 -07002829 jclass jelementClass, jobject jinitialElement)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002830{
2831 JNI_ENTER();
2832
Andy McFaddenab00d452009-08-19 07:21:41 -07002833 jobjectArray newArray = NULL;
2834 ClassObject* elemClassObj =
2835 (ClassObject*) dvmDecodeIndirectRef(env, jelementClass);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002836
2837 if (elemClassObj == NULL) {
2838 dvmThrowException("Ljava/lang/NullPointerException;",
2839 "JNI NewObjectArray");
2840 goto bail;
2841 }
2842
Andy McFaddenab00d452009-08-19 07:21:41 -07002843 ArrayObject* newObj =
2844 dvmAllocObjectArray(elemClassObj, length, ALLOC_DEFAULT);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002845 if (newObj == NULL) {
2846 assert(dvmCheckException(_self));
2847 goto bail;
2848 }
Andy McFaddenab00d452009-08-19 07:21:41 -07002849 newArray = addLocalReference(env, (Object*) newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002850 dvmReleaseTrackedAlloc((Object*) newObj, NULL);
2851
2852 /*
2853 * Initialize the array. Trashes "length".
2854 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002855 if (jinitialElement != NULL) {
2856 Object* initialElement = dvmDecodeIndirectRef(env, jinitialElement);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002857 Object** arrayData = (Object**) newObj->contents;
2858
2859 while (length--)
Andy McFaddenab00d452009-08-19 07:21:41 -07002860 *arrayData++ = initialElement;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002861 }
2862
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002863
2864bail:
2865 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002866 return newArray;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002867}
2868
2869/*
2870 * Get one element of an Object array.
2871 *
2872 * Add the object to the local references table in case the array goes away.
2873 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002874static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray jarr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002875 jsize index)
2876{
2877 JNI_ENTER();
2878
Andy McFaddenab00d452009-08-19 07:21:41 -07002879 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
2880 jobject retval = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002881
Andy McFaddenab00d452009-08-19 07:21:41 -07002882 assert(arrayObj != NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002883
2884 /* check the array bounds */
2885 if (index < 0 || index >= (int) arrayObj->length) {
2886 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
2887 arrayObj->obj.clazz->descriptor);
2888 goto bail;
2889 }
2890
Andy McFaddenab00d452009-08-19 07:21:41 -07002891 Object* value = ((Object**) arrayObj->contents)[index];
2892 retval = addLocalReference(env, value);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002893
2894bail:
2895 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002896 return retval;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002897}
2898
2899/*
2900 * Set one element of an Object array.
2901 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002902static void SetObjectArrayElement(JNIEnv* env, jobjectArray jarr,
2903 jsize index, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002904{
2905 JNI_ENTER();
2906
Andy McFaddenab00d452009-08-19 07:21:41 -07002907 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002908
Andy McFaddenab00d452009-08-19 07:21:41 -07002909 assert(arrayObj != NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002910
2911 /* check the array bounds */
2912 if (index < 0 || index >= (int) arrayObj->length) {
2913 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
2914 arrayObj->obj.clazz->descriptor);
2915 goto bail;
2916 }
2917
2918 //LOGV("JNI: set element %d in array %p to %p\n", index, array, value);
2919
Andy McFaddenab00d452009-08-19 07:21:41 -07002920 Object* obj;
2921 if (jobj == NULL)
2922 obj = NULL;
2923 else
2924 obj = dvmDecodeIndirectRef(env, jobj);
2925 ((Object**) arrayObj->contents)[index] = obj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002926
2927bail:
2928 JNI_EXIT();
2929}
2930
2931/*
2932 * Create a new array of primitive elements.
2933 */
2934#define NEW_PRIMITIVE_ARRAY(_artype, _jname, _typechar) \
2935 static _artype New##_jname##Array(JNIEnv* env, jsize length) \
2936 { \
2937 JNI_ENTER(); \
2938 ArrayObject* arrayObj; \
2939 arrayObj = dvmAllocPrimitiveArray(_typechar, length, \
2940 ALLOC_DEFAULT); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002941 jarray jarr = NULL; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002942 if (arrayObj != NULL) { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002943 jarr = addLocalReference(env, (Object*) arrayObj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002944 dvmReleaseTrackedAlloc((Object*) arrayObj, NULL); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002945 } \
2946 JNI_EXIT(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002947 return (_artype)jarr; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002948 }
2949NEW_PRIMITIVE_ARRAY(jbooleanArray, Boolean, 'Z');
2950NEW_PRIMITIVE_ARRAY(jbyteArray, Byte, 'B');
2951NEW_PRIMITIVE_ARRAY(jcharArray, Char, 'C');
2952NEW_PRIMITIVE_ARRAY(jshortArray, Short, 'S');
2953NEW_PRIMITIVE_ARRAY(jintArray, Int, 'I');
2954NEW_PRIMITIVE_ARRAY(jlongArray, Long, 'J');
2955NEW_PRIMITIVE_ARRAY(jfloatArray, Float, 'F');
2956NEW_PRIMITIVE_ARRAY(jdoubleArray, Double, 'D');
2957
2958/*
2959 * Get a pointer to a C array of primitive elements from an array object
2960 * of the matching type.
2961 *
Andy McFaddenab00d452009-08-19 07:21:41 -07002962 * In a compacting GC, we either need to return a copy of the elements or
2963 * "pin" the memory. Otherwise we run the risk of native code using the
2964 * buffer as the destination of e.g. a blocking read() call that wakes up
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002965 * during a GC.
2966 */
2967#define GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
2968 static _ctype* Get##_jname##ArrayElements(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07002969 _ctype##Array jarr, jboolean* isCopy) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002970 { \
2971 JNI_ENTER(); \
2972 _ctype* data; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002973 ArrayObject* arrayObj = \
2974 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
2975 pinPrimitiveArray(arrayObj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002976 data = (_ctype*) arrayObj->contents; \
2977 if (isCopy != NULL) \
2978 *isCopy = JNI_FALSE; \
2979 JNI_EXIT(); \
2980 return data; \
2981 }
2982
2983/*
2984 * Release the storage locked down by the "get" function.
2985 *
Andy McFaddenab00d452009-08-19 07:21:41 -07002986 * 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 -08002987 * elements in 'array'." They apparently did not anticipate the need to
Andy McFaddenab00d452009-08-19 07:21:41 -07002988 * un-pin memory.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002989 */
2990#define RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
2991 static void Release##_jname##ArrayElements(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07002992 _ctype##Array jarr, _ctype* elems, jint mode) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002993 { \
2994 UNUSED_PARAMETER(elems); \
2995 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002996 if (mode != JNI_COMMIT) { \
2997 ArrayObject* arrayObj = \
2998 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
2999 unpinPrimitiveArray(arrayObj); \
3000 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003001 JNI_EXIT(); \
3002 }
3003
3004/*
3005 * Copy a section of a primitive array to a buffer.
3006 */
3007#define GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
3008 static void Get##_jname##ArrayRegion(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07003009 _ctype##Array jarr, jsize start, jsize len, _ctype* buf) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003010 { \
3011 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07003012 ArrayObject* arrayObj = \
3013 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003014 _ctype* data = (_ctype*) arrayObj->contents; \
3015 if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
3016 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
3017 arrayObj->obj.clazz->descriptor); \
3018 } else { \
3019 memcpy(buf, data + start, len * sizeof(_ctype)); \
3020 } \
3021 JNI_EXIT(); \
3022 }
3023
3024/*
3025 * Copy a section of a primitive array to a buffer.
3026 */
3027#define SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
3028 static void Set##_jname##ArrayRegion(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07003029 _ctype##Array jarr, jsize start, jsize len, const _ctype* buf) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003030 { \
3031 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07003032 ArrayObject* arrayObj = \
3033 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003034 _ctype* data = (_ctype*) arrayObj->contents; \
3035 if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
3036 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
3037 arrayObj->obj.clazz->descriptor); \
3038 } else { \
3039 memcpy(data + start, buf, len * sizeof(_ctype)); \
3040 } \
3041 JNI_EXIT(); \
3042 }
3043
3044/*
3045 * 4-in-1:
3046 * Get<Type>ArrayElements
3047 * Release<Type>ArrayElements
3048 * Get<Type>ArrayRegion
3049 * Set<Type>ArrayRegion
3050 */
3051#define PRIMITIVE_ARRAY_FUNCTIONS(_ctype, _jname) \
3052 GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
3053 RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
3054 GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname); \
3055 SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname);
3056
3057PRIMITIVE_ARRAY_FUNCTIONS(jboolean, Boolean);
3058PRIMITIVE_ARRAY_FUNCTIONS(jbyte, Byte);
3059PRIMITIVE_ARRAY_FUNCTIONS(jchar, Char);
3060PRIMITIVE_ARRAY_FUNCTIONS(jshort, Short);
3061PRIMITIVE_ARRAY_FUNCTIONS(jint, Int);
3062PRIMITIVE_ARRAY_FUNCTIONS(jlong, Long);
3063PRIMITIVE_ARRAY_FUNCTIONS(jfloat, Float);
3064PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double);
3065
3066/*
3067 * Register one or more native functions in one class.
3068 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003069static jint RegisterNatives(JNIEnv* env, jclass jclazz,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003070 const JNINativeMethod* methods, jint nMethods)
3071{
3072 JNI_ENTER();
3073
Andy McFaddenab00d452009-08-19 07:21:41 -07003074 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003075 jint retval;
3076 int i;
3077
3078 if (gDvm.verboseJni) {
3079 LOGI("[Registering JNI native methods for class %s]\n",
Andy McFaddenab00d452009-08-19 07:21:41 -07003080 clazz->descriptor);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003081 }
3082
3083 for (i = 0; i < nMethods; i++) {
Andy McFaddenab00d452009-08-19 07:21:41 -07003084 if (!dvmRegisterJNIMethod(clazz, methods[i].name,
3085 methods[i].signature, methods[i].fnPtr))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003086 {
3087 retval = JNI_ERR;
3088 goto bail;
3089 }
3090 }
3091 retval = JNI_OK;
3092
3093bail:
3094 JNI_EXIT();
3095 return retval;
3096}
3097
3098/*
3099 * Un-register a native function.
3100 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003101static jint UnregisterNatives(JNIEnv* env, jclass jclazz)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003102{
3103 JNI_ENTER();
3104 /*
3105 * The JNI docs refer to this as a way to reload/relink native libraries,
3106 * and say it "should not be used in normal native code".
3107 *
3108 * We can implement it if we decide we need it.
3109 */
3110 JNI_EXIT();
3111 return JNI_ERR;
3112}
3113
3114/*
3115 * Lock the monitor.
3116 *
3117 * We have to track all monitor enters and exits, so that we can undo any
3118 * outstanding synchronization before the thread exits.
3119 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003120static jint MonitorEnter(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003121{
3122 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003123 Object* obj = dvmDecodeIndirectRef(env, jobj);
3124 dvmLockObject(_self, obj);
3125 trackMonitorEnter(_self, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003126 JNI_EXIT();
3127 return JNI_OK;
3128}
3129
3130/*
3131 * Unlock the monitor.
3132 *
3133 * Throws an IllegalMonitorStateException if the current thread
Andy McFaddenab00d452009-08-19 07:21:41 -07003134 * doesn't own the monitor. (dvmUnlockObject() takes care of the throw.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003135 *
3136 * According to the 1.6 spec, it's legal to call here with an exception
3137 * pending. If this fails, we'll stomp the original exception.
3138 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003139static jint MonitorExit(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003140{
3141 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003142 Object* obj = dvmDecodeIndirectRef(env, jobj);
3143 bool success = dvmUnlockObject(_self, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003144 if (success)
Andy McFaddenab00d452009-08-19 07:21:41 -07003145 trackMonitorExit(_self, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003146 JNI_EXIT();
3147 return success ? JNI_OK : JNI_ERR;
3148}
3149
3150/*
3151 * Return the JavaVM interface associated with the current thread.
3152 */
3153static jint GetJavaVM(JNIEnv* env, JavaVM** vm)
3154{
3155 JNI_ENTER();
3156 //*vm = gDvm.vmList;
3157 *vm = (JavaVM*) ((JNIEnvExt*)env)->vm;
3158 JNI_EXIT();
3159 if (*vm == NULL)
3160 return JNI_ERR;
3161 else
3162 return JNI_OK;
3163}
3164
3165/*
3166 * Copies "len" Unicode characters, from offset "start".
3167 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003168static void GetStringRegion(JNIEnv* env, jstring jstr, jsize start, jsize len,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003169 jchar* buf)
3170{
3171 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003172 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003173 if (start + len > dvmStringLen(strObj))
3174 dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
3175 else
3176 memcpy(buf, dvmStringChars(strObj) + start, len * sizeof(u2));
3177 JNI_EXIT();
3178}
3179
3180/*
3181 * Translates "len" Unicode characters, from offset "start", into
3182 * modified UTF-8 encoding.
3183 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003184static void GetStringUTFRegion(JNIEnv* env, jstring jstr, jsize start,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003185 jsize len, char* buf)
3186{
3187 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003188 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003189 if (start + len > dvmStringLen(strObj))
3190 dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
3191 else
3192 dvmCreateCstrFromStringRegion(strObj, start, len, buf);
3193 JNI_EXIT();
3194}
3195
3196/*
3197 * Get a raw pointer to array data.
3198 *
3199 * The caller is expected to call "release" before doing any JNI calls
3200 * or blocking I/O operations.
3201 *
Andy McFaddenab00d452009-08-19 07:21:41 -07003202 * We need to pin the memory or block GC.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003203 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003204static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray jarr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003205 jboolean* isCopy)
3206{
3207 JNI_ENTER();
3208 void* data;
Andy McFaddenab00d452009-08-19 07:21:41 -07003209 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
3210 pinPrimitiveArray(arrayObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003211 data = arrayObj->contents;
3212 if (isCopy != NULL)
3213 *isCopy = JNI_FALSE;
3214 JNI_EXIT();
3215 return data;
3216}
3217
3218/*
3219 * Release an array obtained with GetPrimitiveArrayCritical.
3220 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003221static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray jarr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003222 void* carray, jint mode)
3223{
3224 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003225 if (mode != JNI_COMMIT) {
3226 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
3227 unpinPrimitiveArray(arrayObj);
3228 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003229 JNI_EXIT();
3230}
3231
3232/*
3233 * Like GetStringChars, but with restricted use.
3234 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003235static const jchar* GetStringCritical(JNIEnv* env, jstring jstr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003236 jboolean* isCopy)
3237{
3238 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003239 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
3240 ArrayObject* strChars = dvmStringCharArray(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003241
Andy McFaddenab00d452009-08-19 07:21:41 -07003242 pinPrimitiveArray(strChars);
3243
3244 const u2* data = dvmStringChars(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003245 if (isCopy != NULL)
3246 *isCopy = JNI_FALSE;
3247
3248 JNI_EXIT();
3249 return (jchar*)data;
3250}
3251
3252/*
3253 * Like ReleaseStringChars, but with restricted use.
3254 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003255static void ReleaseStringCritical(JNIEnv* env, jstring jstr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003256 const jchar* carray)
3257{
3258 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003259 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
3260 ArrayObject* strChars = dvmStringCharArray(jstr);
3261 unpinPrimitiveArray(strChars);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003262 JNI_EXIT();
3263}
3264
3265/*
3266 * Create a new weak global reference.
3267 */
3268static jweak NewWeakGlobalRef(JNIEnv* env, jobject obj)
3269{
3270 JNI_ENTER();
3271 // TODO - implement
3272 jobject gref = NULL;
3273 LOGE("JNI ERROR: NewWeakGlobalRef not implemented\n");
3274 dvmAbort();
3275 JNI_EXIT();
3276 return gref;
3277}
3278
3279/*
3280 * Delete the specified weak global reference.
3281 */
3282static void DeleteWeakGlobalRef(JNIEnv* env, jweak obj)
3283{
3284 JNI_ENTER();
3285 // TODO - implement
3286 LOGE("JNI ERROR: DeleteWeakGlobalRef not implemented\n");
3287 dvmAbort();
3288 JNI_EXIT();
3289}
3290
3291/*
3292 * Quick check for pending exceptions.
3293 *
3294 * TODO: we should be able to skip the enter/exit macros here.
3295 */
3296static jboolean ExceptionCheck(JNIEnv* env)
3297{
3298 JNI_ENTER();
3299 bool result = dvmCheckException(_self);
3300 JNI_EXIT();
3301 return result;
3302}
3303
3304/*
3305 * Returns the type of the object referred to by "obj". It can be local,
3306 * global, or weak global.
3307 *
3308 * In the current implementation, references can be global and local at
3309 * the same time, so while the return value is accurate it may not tell
3310 * the whole story.
3311 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003312static jobjectRefType GetObjectRefType(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003313{
3314 JNI_ENTER();
3315 jobjectRefType type;
The Android Open Source Project99409882009-03-18 22:20:24 -07003316
Andy McFaddenab00d452009-08-19 07:21:41 -07003317 if (jobj == NULL)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003318 type = JNIInvalidRefType;
3319 else
Andy McFaddenab00d452009-08-19 07:21:41 -07003320 type = dvmGetJNIRefType(env, jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003321 JNI_EXIT();
3322 return type;
3323}
3324
3325/*
3326 * Allocate and return a new java.nio.ByteBuffer for this block of memory.
3327 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003328 * "address" may not be NULL, and "capacity" must be > 0. (These are only
3329 * verified when CheckJNI is enabled.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003330 */
Andy McFadden8e5c7842009-07-23 17:47:18 -07003331static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003332{
Andy McFadden8e5c7842009-07-23 17:47:18 -07003333 JNI_ENTER();
3334
3335 Thread* self = _self /*dvmThreadSelf()*/;
3336 Object* platformAddress = NULL;
3337 JValue callResult;
The Android Open Source Project99409882009-03-18 22:20:24 -07003338 jobject result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003339
Andy McFadden8e5c7842009-07-23 17:47:18 -07003340 /* get an instance of PlatformAddress that wraps the provided address */
3341 dvmCallMethod(self,
3342 gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on,
3343 NULL, &callResult, address);
3344 if (dvmGetException(self) != NULL || callResult.l == NULL)
The Android Open Source Project99409882009-03-18 22:20:24 -07003345 goto bail;
Andy McFadden8e5c7842009-07-23 17:47:18 -07003346
3347 /* don't let the GC discard it */
3348 platformAddress = (Object*) callResult.l;
3349 dvmAddTrackedAlloc(platformAddress, self);
3350 LOGV("tracking %p for address=%p\n", platformAddress, address);
3351
3352 /* create an instance of java.nio.ReadWriteDirectByteBuffer */
3353 ClassObject* clazz = gDvm.classJavaNioReadWriteDirectByteBuffer;
3354 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))
3355 goto bail;
3356 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
3357 if (newObj != NULL) {
3358 /* call the (PlatformAddress, int, int) constructor */
Andy McFaddenab00d452009-08-19 07:21:41 -07003359 result = addLocalReference(env, newObj);
Andy McFadden8e5c7842009-07-23 17:47:18 -07003360 dvmCallMethod(self, gDvm.methJavaNioReadWriteDirectByteBuffer_init,
3361 newObj, &callResult, platformAddress, (jint) capacity, (jint) 0);
Andy McFaddenab00d452009-08-19 07:21:41 -07003362 if (dvmGetException(self) != NULL) {
3363 deleteLocalReference(env, result);
3364 result = NULL;
Andy McFadden8e5c7842009-07-23 17:47:18 -07003365 goto bail;
Andy McFaddenab00d452009-08-19 07:21:41 -07003366 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003367 }
3368
The Android Open Source Project99409882009-03-18 22:20:24 -07003369bail:
Andy McFadden8e5c7842009-07-23 17:47:18 -07003370 if (platformAddress != NULL)
3371 dvmReleaseTrackedAlloc(platformAddress, self);
3372 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07003373 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003374}
3375
3376/*
3377 * Get the starting address of the buffer for the specified java.nio.Buffer.
3378 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003379 * If this is not a "direct" buffer, we return NULL.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003380 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003381static void* GetDirectBufferAddress(JNIEnv* env, jobject jbuf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003382{
Andy McFadden8e5c7842009-07-23 17:47:18 -07003383 JNI_ENTER();
3384
Andy McFaddenab00d452009-08-19 07:21:41 -07003385 Object* bufObj = dvmDecodeIndirectRef(env, jbuf);
Andy McFadden8e5c7842009-07-23 17:47:18 -07003386 Thread* self = _self /*dvmThreadSelf()*/;
Andy McFadden8e696dc2009-07-24 15:28:16 -07003387 void* result;
3388
3389 /*
3390 * All Buffer objects have an effectiveDirectAddress field. If it's
3391 * nonzero, we can just return that value. If not, we have to call
3392 * through DirectBuffer.getEffectiveAddress(), which as a side-effect
3393 * will set the effectiveDirectAddress field for direct buffers (and
3394 * things that wrap direct buffers).
3395 */
3396 result = (void*) dvmGetFieldInt(bufObj,
3397 gDvm.offJavaNioBuffer_effectiveDirectAddress);
3398 if (result != NULL) {
3399 //LOGI("fast path for %p\n", buf);
3400 goto bail;
3401 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003402
Andy McFadden72d61fb2009-06-24 16:56:06 -07003403 /*
3404 * Start by determining if the object supports the DirectBuffer
3405 * interfaces. Note this does not guarantee that it's a direct buffer.
3406 */
Andy McFadden8e5c7842009-07-23 17:47:18 -07003407 if (!dvmInstanceof(bufObj->clazz,
3408 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003409 {
The Android Open Source Project99409882009-03-18 22:20:24 -07003410 goto bail;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003411 }
3412
Andy McFadden72d61fb2009-06-24 16:56:06 -07003413 /*
Andy McFadden8e5c7842009-07-23 17:47:18 -07003414 * Get a PlatformAddress object with the effective address.
Andy McFadden5f612b82009-07-22 15:07:27 -07003415 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003416 * If this isn't a direct buffer, the result will be NULL and/or an
Andy McFadden72d61fb2009-06-24 16:56:06 -07003417 * exception will have been thrown.
3418 */
Andy McFadden8e696dc2009-07-24 15:28:16 -07003419 JValue callResult;
Andy McFadden8e5c7842009-07-23 17:47:18 -07003420 const Method* meth = dvmGetVirtualizedMethod(bufObj->clazz,
3421 gDvm.methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress);
Andy McFaddend5ab7262009-08-25 07:19:34 -07003422 dvmCallMethodA(self, meth, bufObj, false, &callResult, NULL);
Andy McFadden8e5c7842009-07-23 17:47:18 -07003423 if (dvmGetException(self) != NULL) {
3424 dvmClearException(self);
3425 callResult.l = NULL;
Andy McFadden72d61fb2009-06-24 16:56:06 -07003426 }
Andy McFadden8e5c7842009-07-23 17:47:18 -07003427
Andy McFadden8e696dc2009-07-24 15:28:16 -07003428 Object* platformAddr = callResult.l;
Andy McFadden72d61fb2009-06-24 16:56:06 -07003429 if (platformAddr == NULL) {
Andy McFadden8e696dc2009-07-24 15:28:16 -07003430 LOGV("Got request for address of non-direct buffer\n");
Andy McFadden72d61fb2009-06-24 16:56:06 -07003431 goto bail;
3432 }
3433
Andy McFadden8e5c7842009-07-23 17:47:18 -07003434 /*
3435 * Extract the address from the PlatformAddress object. Instead of
3436 * calling the toLong() method, just grab the field directly. This
3437 * is faster but more fragile.
3438 */
3439 result = (void*) dvmGetFieldInt(platformAddr,
3440 gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr);
The Android Open Source Project99409882009-03-18 22:20:24 -07003441
Andy McFadden8e696dc2009-07-24 15:28:16 -07003442 //LOGI("slow path for %p --> %p\n", buf, result);
3443
The Android Open Source Project99409882009-03-18 22:20:24 -07003444bail:
Andy McFadden8e5c7842009-07-23 17:47:18 -07003445 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07003446 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003447}
3448
3449/*
3450 * Get the capacity of the buffer for the specified java.nio.Buffer.
3451 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003452 * Returns -1 if the object is not a direct buffer. (We actually skip
3453 * this check, since it's expensive to determine, and just return the
3454 * capacity regardless.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003455 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003456static jlong GetDirectBufferCapacity(JNIEnv* env, jobject jbuf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003457{
Andy McFadden8e5c7842009-07-23 17:47:18 -07003458 JNI_ENTER();
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003459
Andy McFadden8e5c7842009-07-23 17:47:18 -07003460 /*
3461 * The capacity is always in the Buffer.capacity field.
3462 *
3463 * (The "check" version should verify that this is actually a Buffer,
3464 * but we're not required to do so here.)
3465 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003466 Object* buf = dvmDecodeIndirectRef(env, jbuf);
3467 jlong result = dvmGetFieldInt(buf, gDvm.offJavaNioBuffer_capacity);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003468
Andy McFadden8e5c7842009-07-23 17:47:18 -07003469 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07003470 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003471}
3472
3473
3474/*
3475 * ===========================================================================
3476 * JNI invocation functions
3477 * ===========================================================================
3478 */
3479
3480/*
3481 * Handle AttachCurrentThread{AsDaemon}.
3482 *
3483 * We need to make sure the VM is actually running. For example, if we start
3484 * up, issue an Attach, and the VM exits almost immediately, by the time the
3485 * attaching happens the VM could already be shutting down.
3486 *
3487 * It's hard to avoid a race condition here because we don't want to hold
3488 * a lock across the entire operation. What we can do is temporarily
3489 * increment the thread count to prevent a VM exit.
3490 *
3491 * This could potentially still have problems if a daemon thread calls here
3492 * while the VM is shutting down. dvmThreadSelf() will work, since it just
3493 * uses pthread TLS, but dereferencing "vm" could fail. Such is life when
3494 * you shut down a VM while threads are still running inside it.
3495 *
3496 * Remember that some code may call this as a way to find the per-thread
3497 * JNIEnv pointer. Don't do excess work for that case.
3498 */
3499static jint attachThread(JavaVM* vm, JNIEnv** p_env, void* thr_args,
3500 bool isDaemon)
3501{
3502 JavaVMAttachArgs* args = (JavaVMAttachArgs*) thr_args;
3503 Thread* self;
3504 bool result = false;
3505
3506 /*
3507 * Return immediately if we're already one with the VM.
3508 */
3509 self = dvmThreadSelf();
3510 if (self != NULL) {
3511 *p_env = self->jniEnv;
3512 return JNI_OK;
3513 }
3514
3515 /*
3516 * No threads allowed in zygote mode.
3517 */
3518 if (gDvm.zygote) {
3519 return JNI_ERR;
3520 }
3521
3522 /* increment the count to keep the VM from bailing while we run */
3523 dvmLockThreadList(NULL);
3524 if (gDvm.nonDaemonThreadCount == 0) {
3525 // dead or dying
3526 LOGV("Refusing to attach thread '%s' -- VM is shutting down\n",
3527 (thr_args == NULL) ? "(unknown)" : args->name);
3528 dvmUnlockThreadList();
3529 return JNI_ERR;
3530 }
3531 gDvm.nonDaemonThreadCount++;
3532 dvmUnlockThreadList();
3533
3534 /* tweak the JavaVMAttachArgs as needed */
3535 JavaVMAttachArgs argsCopy;
3536 if (args == NULL) {
3537 /* allow the v1.1 calling convention */
3538 argsCopy.version = JNI_VERSION_1_2;
3539 argsCopy.name = NULL;
3540 argsCopy.group = dvmGetMainThreadGroup();
3541 } else {
3542 assert(args->version >= JNI_VERSION_1_2);
3543
3544 argsCopy.version = args->version;
3545 argsCopy.name = args->name;
3546 if (args->group != NULL)
3547 argsCopy.group = args->group;
3548 else
3549 argsCopy.group = dvmGetMainThreadGroup();
3550 }
3551
3552 result = dvmAttachCurrentThread(&argsCopy, isDaemon);
3553
3554 /* restore the count */
3555 dvmLockThreadList(NULL);
3556 gDvm.nonDaemonThreadCount--;
3557 dvmUnlockThreadList();
3558
3559 /*
3560 * Change the status to indicate that we're out in native code. This
3561 * call is not guarded with state-change macros, so we have to do it
3562 * by hand.
3563 */
3564 if (result) {
3565 self = dvmThreadSelf();
3566 assert(self != NULL);
3567 dvmChangeStatus(self, THREAD_NATIVE);
3568 *p_env = self->jniEnv;
3569 return JNI_OK;
3570 } else {
3571 return JNI_ERR;
3572 }
3573}
3574
3575/*
3576 * Attach the current thread to the VM. If the thread is already attached,
3577 * this is a no-op.
3578 */
3579static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args)
3580{
3581 return attachThread(vm, p_env, thr_args, false);
3582}
3583
3584/*
3585 * Like AttachCurrentThread, but set the "daemon" flag.
3586 */
3587static jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env,
3588 void* thr_args)
3589{
3590 return attachThread(vm, p_env, thr_args, true);
3591}
3592
3593/*
3594 * Dissociate the current thread from the VM.
3595 */
3596static jint DetachCurrentThread(JavaVM* vm)
3597{
3598 Thread* self = dvmThreadSelf();
3599
3600 if (self == NULL) /* not attached, can't do anything */
3601 return JNI_ERR;
3602
3603 /* switch to "running" to check for suspension */
3604 dvmChangeStatus(self, THREAD_RUNNING);
3605
3606 /* detach the thread */
3607 dvmDetachCurrentThread();
3608
3609 /* (no need to change status back -- we have no status) */
3610 return JNI_OK;
3611}
3612
3613/*
3614 * If current thread is attached to VM, return the associated JNIEnv.
3615 * Otherwise, stuff NULL in and return JNI_EDETACHED.
3616 *
3617 * JVMTI overloads this by specifying a magic value for "version", so we
3618 * do want to check that here.
3619 */
3620static jint GetEnv(JavaVM* vm, void** env, jint version)
3621{
3622 Thread* self = dvmThreadSelf();
3623
3624 if (version < JNI_VERSION_1_1 || version > JNI_VERSION_1_6)
3625 return JNI_EVERSION;
3626
3627 if (self == NULL) {
3628 *env = NULL;
3629 } else {
3630 /* TODO: status change is probably unnecessary */
3631 dvmChangeStatus(self, THREAD_RUNNING);
3632 *env = (void*) dvmGetThreadJNIEnv(self);
3633 dvmChangeStatus(self, THREAD_NATIVE);
3634 }
3635 if (*env == NULL)
3636 return JNI_EDETACHED;
3637 else
3638 return JNI_OK;
3639}
3640
3641/*
3642 * Destroy the VM. This may be called from any thread.
3643 *
3644 * If the current thread is attached, wait until the current thread is
3645 * the only non-daemon user-level thread. If the current thread is not
3646 * attached, we attach it and do the processing as usual. (If the attach
3647 * fails, it's probably because all the non-daemon threads have already
3648 * exited and the VM doesn't want to let us back in.)
3649 *
3650 * TODO: we don't really deal with the situation where more than one thread
3651 * has called here. One thread wins, the other stays trapped waiting on
3652 * the condition variable forever. Not sure this situation is interesting
3653 * in real life.
3654 */
3655static jint DestroyJavaVM(JavaVM* vm)
3656{
3657 JavaVMExt* ext = (JavaVMExt*) vm;
3658 Thread* self;
3659
3660 if (ext == NULL)
3661 return JNI_ERR;
3662
3663 LOGD("DestroyJavaVM waiting for non-daemon threads to exit\n");
3664
3665 /*
3666 * Sleep on a condition variable until it's okay to exit.
3667 */
3668 self = dvmThreadSelf();
3669 if (self == NULL) {
3670 JNIEnv* tmpEnv;
3671 if (AttachCurrentThread(vm, &tmpEnv, NULL) != JNI_OK) {
3672 LOGV("Unable to reattach main for Destroy; assuming VM is "
3673 "shutting down (count=%d)\n",
3674 gDvm.nonDaemonThreadCount);
3675 goto shutdown;
3676 } else {
3677 LOGV("Attached to wait for shutdown in Destroy\n");
3678 }
3679 }
3680 dvmChangeStatus(self, THREAD_VMWAIT);
3681
3682 dvmLockThreadList(self);
3683 gDvm.nonDaemonThreadCount--; // remove current thread from count
3684
3685 while (gDvm.nonDaemonThreadCount > 0)
3686 pthread_cond_wait(&gDvm.vmExitCond, &gDvm.threadListLock);
3687
3688 dvmUnlockThreadList();
3689 self = NULL;
3690
3691shutdown:
3692 // TODO: call System.exit() to run any registered shutdown hooks
3693 // (this may not return -- figure out how this should work)
3694
3695 LOGD("DestroyJavaVM shutting VM down\n");
3696 dvmShutdown();
3697
3698 // TODO - free resources associated with JNI-attached daemon threads
3699 free(ext->envList);
3700 free(ext);
3701
3702 return JNI_OK;
3703}
3704
3705
3706/*
3707 * ===========================================================================
3708 * Function tables
3709 * ===========================================================================
3710 */
3711
3712static const struct JNINativeInterface gNativeInterface = {
3713 NULL,
3714 NULL,
3715 NULL,
3716 NULL,
3717
3718 GetVersion,
3719
3720 DefineClass,
3721 FindClass,
3722
3723 FromReflectedMethod,
3724 FromReflectedField,
3725 ToReflectedMethod,
3726
3727 GetSuperclass,
3728 IsAssignableFrom,
3729
3730 ToReflectedField,
3731
3732 Throw,
3733 ThrowNew,
3734 ExceptionOccurred,
3735 ExceptionDescribe,
3736 ExceptionClear,
3737 FatalError,
3738
3739 PushLocalFrame,
3740 PopLocalFrame,
3741
3742 NewGlobalRef,
3743 DeleteGlobalRef,
3744 DeleteLocalRef,
3745 IsSameObject,
3746 NewLocalRef,
3747 EnsureLocalCapacity,
3748
3749 AllocObject,
3750 NewObject,
3751 NewObjectV,
3752 NewObjectA,
3753
3754 GetObjectClass,
3755 IsInstanceOf,
3756
3757 GetMethodID,
3758
3759 CallObjectMethod,
3760 CallObjectMethodV,
3761 CallObjectMethodA,
3762 CallBooleanMethod,
3763 CallBooleanMethodV,
3764 CallBooleanMethodA,
3765 CallByteMethod,
3766 CallByteMethodV,
3767 CallByteMethodA,
3768 CallCharMethod,
3769 CallCharMethodV,
3770 CallCharMethodA,
3771 CallShortMethod,
3772 CallShortMethodV,
3773 CallShortMethodA,
3774 CallIntMethod,
3775 CallIntMethodV,
3776 CallIntMethodA,
3777 CallLongMethod,
3778 CallLongMethodV,
3779 CallLongMethodA,
3780 CallFloatMethod,
3781 CallFloatMethodV,
3782 CallFloatMethodA,
3783 CallDoubleMethod,
3784 CallDoubleMethodV,
3785 CallDoubleMethodA,
3786 CallVoidMethod,
3787 CallVoidMethodV,
3788 CallVoidMethodA,
3789
3790 CallNonvirtualObjectMethod,
3791 CallNonvirtualObjectMethodV,
3792 CallNonvirtualObjectMethodA,
3793 CallNonvirtualBooleanMethod,
3794 CallNonvirtualBooleanMethodV,
3795 CallNonvirtualBooleanMethodA,
3796 CallNonvirtualByteMethod,
3797 CallNonvirtualByteMethodV,
3798 CallNonvirtualByteMethodA,
3799 CallNonvirtualCharMethod,
3800 CallNonvirtualCharMethodV,
3801 CallNonvirtualCharMethodA,
3802 CallNonvirtualShortMethod,
3803 CallNonvirtualShortMethodV,
3804 CallNonvirtualShortMethodA,
3805 CallNonvirtualIntMethod,
3806 CallNonvirtualIntMethodV,
3807 CallNonvirtualIntMethodA,
3808 CallNonvirtualLongMethod,
3809 CallNonvirtualLongMethodV,
3810 CallNonvirtualLongMethodA,
3811 CallNonvirtualFloatMethod,
3812 CallNonvirtualFloatMethodV,
3813 CallNonvirtualFloatMethodA,
3814 CallNonvirtualDoubleMethod,
3815 CallNonvirtualDoubleMethodV,
3816 CallNonvirtualDoubleMethodA,
3817 CallNonvirtualVoidMethod,
3818 CallNonvirtualVoidMethodV,
3819 CallNonvirtualVoidMethodA,
3820
3821 GetFieldID,
3822
3823 GetObjectField,
3824 GetBooleanField,
3825 GetByteField,
3826 GetCharField,
3827 GetShortField,
3828 GetIntField,
3829 GetLongField,
3830 GetFloatField,
3831 GetDoubleField,
3832 SetObjectField,
3833 SetBooleanField,
3834 SetByteField,
3835 SetCharField,
3836 SetShortField,
3837 SetIntField,
3838 SetLongField,
3839 SetFloatField,
3840 SetDoubleField,
3841
3842 GetStaticMethodID,
3843
3844 CallStaticObjectMethod,
3845 CallStaticObjectMethodV,
3846 CallStaticObjectMethodA,
3847 CallStaticBooleanMethod,
3848 CallStaticBooleanMethodV,
3849 CallStaticBooleanMethodA,
3850 CallStaticByteMethod,
3851 CallStaticByteMethodV,
3852 CallStaticByteMethodA,
3853 CallStaticCharMethod,
3854 CallStaticCharMethodV,
3855 CallStaticCharMethodA,
3856 CallStaticShortMethod,
3857 CallStaticShortMethodV,
3858 CallStaticShortMethodA,
3859 CallStaticIntMethod,
3860 CallStaticIntMethodV,
3861 CallStaticIntMethodA,
3862 CallStaticLongMethod,
3863 CallStaticLongMethodV,
3864 CallStaticLongMethodA,
3865 CallStaticFloatMethod,
3866 CallStaticFloatMethodV,
3867 CallStaticFloatMethodA,
3868 CallStaticDoubleMethod,
3869 CallStaticDoubleMethodV,
3870 CallStaticDoubleMethodA,
3871 CallStaticVoidMethod,
3872 CallStaticVoidMethodV,
3873 CallStaticVoidMethodA,
3874
3875 GetStaticFieldID,
3876
3877 GetStaticObjectField,
3878 GetStaticBooleanField,
3879 GetStaticByteField,
3880 GetStaticCharField,
3881 GetStaticShortField,
3882 GetStaticIntField,
3883 GetStaticLongField,
3884 GetStaticFloatField,
3885 GetStaticDoubleField,
3886
3887 SetStaticObjectField,
3888 SetStaticBooleanField,
3889 SetStaticByteField,
3890 SetStaticCharField,
3891 SetStaticShortField,
3892 SetStaticIntField,
3893 SetStaticLongField,
3894 SetStaticFloatField,
3895 SetStaticDoubleField,
3896
3897 NewString,
3898
3899 GetStringLength,
3900 GetStringChars,
3901 ReleaseStringChars,
3902
3903 NewStringUTF,
3904 GetStringUTFLength,
3905 GetStringUTFChars,
3906 ReleaseStringUTFChars,
3907
3908 GetArrayLength,
3909 NewObjectArray,
3910 GetObjectArrayElement,
3911 SetObjectArrayElement,
3912
3913 NewBooleanArray,
3914 NewByteArray,
3915 NewCharArray,
3916 NewShortArray,
3917 NewIntArray,
3918 NewLongArray,
3919 NewFloatArray,
3920 NewDoubleArray,
3921
3922 GetBooleanArrayElements,
3923 GetByteArrayElements,
3924 GetCharArrayElements,
3925 GetShortArrayElements,
3926 GetIntArrayElements,
3927 GetLongArrayElements,
3928 GetFloatArrayElements,
3929 GetDoubleArrayElements,
3930
3931 ReleaseBooleanArrayElements,
3932 ReleaseByteArrayElements,
3933 ReleaseCharArrayElements,
3934 ReleaseShortArrayElements,
3935 ReleaseIntArrayElements,
3936 ReleaseLongArrayElements,
3937 ReleaseFloatArrayElements,
3938 ReleaseDoubleArrayElements,
3939
3940 GetBooleanArrayRegion,
3941 GetByteArrayRegion,
3942 GetCharArrayRegion,
3943 GetShortArrayRegion,
3944 GetIntArrayRegion,
3945 GetLongArrayRegion,
3946 GetFloatArrayRegion,
3947 GetDoubleArrayRegion,
3948 SetBooleanArrayRegion,
3949 SetByteArrayRegion,
3950 SetCharArrayRegion,
3951 SetShortArrayRegion,
3952 SetIntArrayRegion,
3953 SetLongArrayRegion,
3954 SetFloatArrayRegion,
3955 SetDoubleArrayRegion,
3956
3957 RegisterNatives,
3958 UnregisterNatives,
3959
3960 MonitorEnter,
3961 MonitorExit,
3962
3963 GetJavaVM,
3964
3965 GetStringRegion,
3966 GetStringUTFRegion,
3967
3968 GetPrimitiveArrayCritical,
3969 ReleasePrimitiveArrayCritical,
3970
3971 GetStringCritical,
3972 ReleaseStringCritical,
3973
3974 NewWeakGlobalRef,
3975 DeleteWeakGlobalRef,
3976
3977 ExceptionCheck,
3978
3979 NewDirectByteBuffer,
3980 GetDirectBufferAddress,
3981 GetDirectBufferCapacity,
3982
3983 GetObjectRefType
3984};
3985static const struct JNIInvokeInterface gInvokeInterface = {
3986 NULL,
3987 NULL,
3988 NULL,
3989
3990 DestroyJavaVM,
3991 AttachCurrentThread,
3992 DetachCurrentThread,
3993
3994 GetEnv,
3995
3996 AttachCurrentThreadAsDaemon,
3997};
3998
3999
4000/*
4001 * ===========================================================================
4002 * VM/Env creation
4003 * ===========================================================================
4004 */
4005
4006/*
4007 * Enable "checked JNI" after the VM has partially started. This must
4008 * only be called in "zygote" mode, when we have one thread running.
Andy McFadden59b61772009-05-13 16:44:34 -07004009 *
4010 * This doesn't attempt to rewrite the JNI call bridge associated with
4011 * native methods, so we won't get those checks for any methods that have
4012 * already been resolved.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08004013 */
4014void dvmLateEnableCheckedJni(void)
4015{
4016 JNIEnvExt* extEnv;
4017 JavaVMExt* extVm;
Andy McFaddenab00d452009-08-19 07:21:41 -07004018
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08004019 extEnv = dvmGetJNIEnvForThread();
4020 if (extEnv == NULL) {
4021 LOGE("dvmLateEnableCheckedJni: thread has no JNIEnv\n");
4022 return;
4023 }
4024 extVm = extEnv->vm;
4025 assert(extVm != NULL);
4026
4027 if (!extVm->useChecked) {
4028 LOGD("Late-enabling CheckJNI\n");
4029 dvmUseCheckedJniVm(extVm);
4030 extVm->useChecked = true;
4031 dvmUseCheckedJniEnv(extEnv);
4032
4033 /* currently no way to pick up jniopts features */
4034 } else {
4035 LOGD("Not late-enabling CheckJNI (already on)\n");
4036 }
4037}
4038
4039/*
4040 * Not supported.
4041 */
4042jint JNI_GetDefaultJavaVMInitArgs(void* vm_args)
4043{
4044 return JNI_ERR;
4045}
4046
4047/*
4048 * Return a buffer full of created VMs.
4049 *
4050 * We always have zero or one.
4051 */
4052jint JNI_GetCreatedJavaVMs(JavaVM** vmBuf, jsize bufLen, jsize* nVMs)
4053{
4054 if (gDvm.vmList != NULL) {
4055 *nVMs = 1;
4056
4057 if (bufLen > 0)
4058 *vmBuf++ = gDvm.vmList;
4059 } else {
4060 *nVMs = 0;
4061 }
4062
4063 return JNI_OK;
4064}
4065
4066
4067/*
4068 * Create a new VM instance.
4069 *
4070 * The current thread becomes the main VM thread. We return immediately,
4071 * which effectively means the caller is executing in a native method.
4072 */
4073jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args)
4074{
4075 const JavaVMInitArgs* args = (JavaVMInitArgs*) vm_args;
4076 JNIEnvExt* pEnv = NULL;
4077 JavaVMExt* pVM = NULL;
4078 const char** argv;
4079 int argc = 0;
4080 int i, curOpt;
4081 int result = JNI_ERR;
4082 bool checkJni = false;
4083 bool warnError = true;
4084 bool forceDataCopy = false;
4085
4086 if (args->version < JNI_VERSION_1_2)
4087 return JNI_EVERSION;
4088
4089 // TODO: don't allow creation of multiple VMs -- one per customer for now
4090
4091 /* zero globals; not strictly necessary the first time a VM is started */
4092 memset(&gDvm, 0, sizeof(gDvm));
4093
4094 /*
4095 * Set up structures for JNIEnv and VM.
4096 */
4097 //pEnv = (JNIEnvExt*) malloc(sizeof(JNIEnvExt));
4098 pVM = (JavaVMExt*) malloc(sizeof(JavaVMExt));
4099
4100 //memset(pEnv, 0, sizeof(JNIEnvExt));
4101 //pEnv->funcTable = &gNativeInterface;
4102 //pEnv->vm = pVM;
4103 memset(pVM, 0, sizeof(JavaVMExt));
4104 pVM->funcTable = &gInvokeInterface;
4105 pVM->envList = pEnv;
4106 dvmInitMutex(&pVM->envListLock);
4107
4108 argv = (const char**) malloc(sizeof(char*) * (args->nOptions));
4109 memset(argv, 0, sizeof(char*) * (args->nOptions));
4110
4111 curOpt = 0;
4112
4113 /*
4114 * Convert JNI args to argv.
4115 *
4116 * We have to pull out vfprintf/exit/abort, because they use the
4117 * "extraInfo" field to pass function pointer "hooks" in. We also
4118 * look for the -Xcheck:jni stuff here.
4119 */
4120 for (i = 0; i < args->nOptions; i++) {
4121 const char* optStr = args->options[i].optionString;
4122
4123 if (optStr == NULL) {
4124 fprintf(stderr, "ERROR: arg %d string was null\n", i);
4125 goto bail;
4126 } else if (strcmp(optStr, "vfprintf") == 0) {
4127 gDvm.vfprintfHook = args->options[i].extraInfo;
4128 } else if (strcmp(optStr, "exit") == 0) {
4129 gDvm.exitHook = args->options[i].extraInfo;
4130 } else if (strcmp(optStr, "abort") == 0) {
4131 gDvm.abortHook = args->options[i].extraInfo;
4132 } else if (strcmp(optStr, "-Xcheck:jni") == 0) {
4133 checkJni = true;
4134 } else if (strncmp(optStr, "-Xjniopts:", 10) == 0) {
4135 const char* jniOpts = optStr + 9;
4136 while (jniOpts != NULL) {
4137 jniOpts++; /* skip past ':' or ',' */
4138 if (strncmp(jniOpts, "warnonly", 8) == 0) {
4139 warnError = false;
4140 } else if (strncmp(jniOpts, "forcecopy", 9) == 0) {
4141 forceDataCopy = true;
4142 } else {
4143 LOGW("unknown jni opt starting at '%s'\n", jniOpts);
4144 }
4145 jniOpts = strchr(jniOpts, ',');
4146 }
4147 } else {
4148 /* regular option */
4149 argv[curOpt++] = optStr;
4150 }
4151 }
4152 argc = curOpt;
4153
4154 if (checkJni) {
4155 dvmUseCheckedJniVm(pVM);
4156 pVM->useChecked = true;
4157 }
4158 pVM->warnError = warnError;
4159 pVM->forceDataCopy = forceDataCopy;
4160
4161 /* set this up before initializing VM, so it can create some JNIEnvs */
4162 gDvm.vmList = (JavaVM*) pVM;
4163
4164 /*
4165 * Create an env for main thread. We need to have something set up
4166 * here because some of the class initialization we do when starting
4167 * up the VM will call into native code.
4168 */
4169 pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL);
4170
4171 /* initialize VM */
4172 gDvm.initializing = true;
4173 if (dvmStartup(argc, argv, args->ignoreUnrecognized, (JNIEnv*)pEnv) != 0) {
4174 free(pEnv);
4175 free(pVM);
4176 goto bail;
4177 }
4178
4179 /*
4180 * Success! Return stuff to caller.
4181 */
4182 dvmChangeStatus(NULL, THREAD_NATIVE);
4183 *p_env = (JNIEnv*) pEnv;
4184 *p_vm = (JavaVM*) pVM;
4185 result = JNI_OK;
4186
4187bail:
4188 gDvm.initializing = false;
4189 if (result == JNI_OK)
4190 LOGV("JNI_CreateJavaVM succeeded\n");
4191 else
4192 LOGW("JNI_CreateJavaVM failed\n");
4193 free(argv);
4194 return result;
4195}
4196