blob: 77aeb69d43158e56ea2393e2717e005cf5fe7465 [file] [log] [blame]
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Andy McFadden59b61772009-05-13 16:44:34 -070016
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080017/*
18 * Dalvik implementation of JNI interfaces.
19 */
20#include "Dalvik.h"
21#include "JniInternal.h"
22
23#include <stdlib.h>
24#include <stdarg.h>
25#include <limits.h>
26
27/*
28Native methods and interaction with the GC
29
30All JNI methods must start by changing their thread status to
31THREAD_RUNNING, and finish by changing it back to THREAD_NATIVE before
32returning to native code. The switch to "running" triggers a thread
33suspension check.
34
35With a rudimentary GC we should be able to skip the status change for
36simple functions, e.g. IsSameObject, GetJavaVM, GetStringLength, maybe
37even access to fields with primitive types. Our options are more limited
38with a compacting GC, so we should replace JNI_ENTER with JNI_ENTER_NCGC
39or somesuch on the "lite" functions if we want to try this optimization.
40
41For performance reasons we do as little error-checking as possible here.
42For example, we don't check to make sure the correct type of Object is
43passed in when setting a field, and we don't prevent you from storing
44new values in a "final" field. Such things are best handled in the
45"check" version. For actions that are common, dangerous, and must be
46checked at runtime, such as array bounds checks, we do the tests here.
47
48
49General notes on local/global reference tracking
50
Andy McFadden0083d372009-08-21 14:44:04 -070051JNI provides explicit control over natively-held references that the GC
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080052needs to know about. These can be local, in which case they're released
Andy McFadden0083d372009-08-21 14:44:04 -070053when the native method returns into the VM, or global, which are held
Andy McFaddend5ab7262009-08-25 07:19:34 -070054until explicitly released. (There are also weak-global references,
55which have the lifespan and visibility of global references, but the
56object they refer to may be collected.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080057
Andy McFadden0083d372009-08-21 14:44:04 -070058The references can be created with explicit JNI NewLocalRef / NewGlobalRef
59calls. The former is very unusual, the latter is reasonably common
60(e.g. for caching references to class objects).
61
62Local references are most often created as a side-effect of JNI functions.
63For example, the AllocObject/NewObject functions must create local
64references to the objects returned, because nothing else in the GC root
65set has a reference to the new objects.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080066
67The most common mode of operation is for a method to create zero or
68more local references and return. Explicit "local delete" operations
69are expected to be exceedingly rare, except when walking through an
70object array, and the Push/PopLocalFrame calls are expected to be used
71infrequently. For efficient operation, we want to add new local refs
72with a simple store/increment operation; to avoid infinite growth in
73pathological situations, we need to reclaim the space used by deleted
74entries.
75
Andy McFaddend5ab7262009-08-25 07:19:34 -070076If we just want to maintain a list for the GC root set, we can use an
77expanding append-only array that compacts when objects are deleted.
78In typical situations, e.g. running through an array of objects, we will
79be deleting one of the most recently added entries, so we can minimize
80the number of elements moved (or avoid having to move any).
81
82If we want to conceal the pointer values from native code, which is
83necessary to allow the GC to move JNI-referenced objects around, then we
84have to use a more complicated indirection mechanism.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080085
86The spec says, "Local references are only valid in the thread in which
87they are created. The native code must not pass local references from
Andy McFadden0083d372009-08-21 14:44:04 -070088one thread to another."
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080089
90
Andy McFaddend5ab7262009-08-25 07:19:34 -070091Pinned objects
92
93For some large chunks of data, notably primitive arrays and String data,
94JNI allows the VM to choose whether it wants to pin the array object or
95make a copy. We currently pin the memory for better execution performance.
96
97TODO: we're using simple root set references to pin primitive array data,
98because they have the property we need (i.e. the pointer we return is
99guaranteed valid until we explicitly release it). However, if we have a
100compacting GC and don't want to pin all memory held by all global refs,
101we need to treat these differently.
102
103
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800104Global reference tracking
105
106There should be a small "active" set centered around the most-recently
Andy McFaddend5ab7262009-08-25 07:19:34 -0700107added items.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800108
Andy McFaddend5ab7262009-08-25 07:19:34 -0700109Because it's global, access to it has to be synchronized. Additions and
110removals require grabbing a mutex. If the table serves as an indirection
111mechanism (i.e. it's not just a list for the benefit of the garbage
112collector), reference lookups may also require grabbing a mutex.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800113
114The JNI spec does not define any sort of limit, so the list must be able
Andy McFaddend5ab7262009-08-25 07:19:34 -0700115to expand to a reasonable size. It may be useful to log significant
116increases in usage to help identify resource leaks.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800117
Andy McFaddend5ab7262009-08-25 07:19:34 -0700118
119Weak-global reference tracking
120
121[TBD]
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800122
123
124Local reference tracking
125
126The table of local references can be stored on the interpreted stack or
127in a parallel data structure (one per thread).
128
129*** Approach #1: use the interpreted stack
130
131The easiest place to tuck it is between the frame ptr and the first saved
132register, which is always in0. (See the ASCII art in Stack.h.) We can
133shift the "VM-specific goop" and frame ptr down, effectively inserting
134the JNI local refs in the space normally occupied by local variables.
135
136(Three things are accessed from the frame pointer:
137 (1) framePtr[N] is register vN, used to get at "ins" and "locals".
138 (2) framePtr - sizeof(StackSaveArea) is the VM frame goop.
139 (3) framePtr - sizeof(StackSaveArea) - numOuts is where the "outs" go.
140The only thing that isn't determined by an offset from the current FP
141is the previous frame. However, tucking things below the previous frame
142can be problematic because the "outs" of the previous frame overlap with
143the "ins" of the current frame. If the "ins" are altered they must be
144restored before we return. For a native method call, the easiest and
145safest thing to disrupt is #1, because there are no locals and the "ins"
146are all copied to the native stack.)
147
148We can implement Push/PopLocalFrame with the existing stack frame calls,
149making sure we copy some goop from the previous frame (notably the method
150ptr, so that dvmGetCurrentJNIMethod() doesn't require extra effort).
151
152We can pre-allocate the storage at the time the stack frame is first
153set up, but we have to be careful. When calling from interpreted code
154the frame ptr points directly at the arguments we're passing, but we can
155offset the args pointer when calling the native bridge.
156
157To manage the local ref collection, we need to be able to find three
158things: (1) the start of the region, (2) the end of the region, and (3)
159the next available entry. The last is only required for quick adds.
160We currently have two easily-accessible pointers, the current FP and the
161previous frame's FP. (The "stack pointer" shown in the ASCII art doesn't
162actually exist in the interpreted world.)
163
164We can't use the current FP to find the first "in", because we want to
165insert the variable-sized local refs table between them. It's awkward
166to use the previous frame's FP because native methods invoked via
167dvmCallMethod() or dvmInvokeMethod() don't have "ins", but native methods
168invoked from interpreted code do. We can either track the local refs
169table size with a field in the stack frame, or insert unnecessary items
170so that all native stack frames have "ins".
171
172Assuming we can find the region bounds, we still need pointer #3
173for an efficient implementation. This can be stored in an otherwise
174unused-for-native field in the frame goop.
175
176When we run out of room we have to make more space. If we start allocating
177locals immediately below in0 and grow downward, we will detect end-of-space
178by running into the current frame's FP. We then memmove() the goop down
179(memcpy if we guarantee the additional size is larger than the frame).
180This is nice because we only have to move sizeof(StackSaveArea) bytes
181each time.
182
183Stack walking should be okay so long as nothing tries to access the
184"ins" by an offset from the FP. In theory the "ins" could be read by
185the debugger or SIGQUIT handler looking for "this" or other arguments,
186but in practice this behavior isn't expected to work for native methods,
187so we can simply disallow it.
188
189A conservative GC can just scan the entire stack from top to bottom to find
190all references. An exact GC will need to understand the actual layout.
191
192*** Approach #2: use a parallel stack
193
Andy McFaddend5ab7262009-08-25 07:19:34 -0700194Each Thread/JNIEnv points to a reference table. The struct has
195a system-heap-allocated array of references and a pointer to the
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800196next-available entry ("nextEntry").
197
Andy McFaddend5ab7262009-08-25 07:19:34 -0700198Each stack frame has a pointer to what it sees as the "bottom" element
199in the array (we can double-up the "currentPc" field). This is set to
200"nextEntry" when the frame is pushed on. As local references are added
201or removed, "nextEntry" is updated.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800202
203We implement Push/PopLocalFrame with actual stack frames. Before a JNI
204frame gets popped, we set "nextEntry" to the "top" pointer of the current
205frame, effectively releasing the references.
206
207The GC will scan all references from the start of the table to the
208"nextEntry" pointer.
209
210*** Comparison
211
212All approaches will return a failure result when they run out of local
213reference space. For #1 that means blowing out the stack, for #2 it's
214running out of room in the array.
215
216Compared to #1, approach #2:
217 - Needs only one pointer in the stack frame goop.
218 - Makes pre-allocating storage unnecessary.
219 - Doesn't contend with interpreted stack depth for space. In most
220 cases, if something blows out the local ref storage, it's because the
221 JNI code was misbehaving rather than called from way down.
222 - Allows the GC to do a linear scan per thread in a buffer that is 100%
223 references. The GC can be slightly less smart when scanning the stack.
224 - Will be easier to work with if we combine native and interpeted stacks.
225
226 - Isn't as clean, especially when popping frames, since we have to do
227 explicit work. Fortunately we only have to do it when popping native
228 method calls off, so it doesn't add overhead to interpreted code paths.
229 - Is awkward to expand dynamically. We'll want to pre-allocate the full
230 amount of space; this is fine, since something on the order of 1KB should
231 be plenty. The JNI spec allows us to limit this.
232 - Requires the GC to scan even more memory. With the references embedded
233 in the stack we get better locality of reference.
234
235*/
236
Andy McFadden5f612b82009-07-22 15:07:27 -0700237/* fwd */
238static const struct JNINativeInterface gNativeInterface;
Andy McFaddenab00d452009-08-19 07:21:41 -0700239static jobject addGlobalReference(Object* obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800240
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800241#ifdef WITH_JNI_STACK_CHECK
242# define COMPUTE_STACK_SUM(_self) computeStackSum(_self);
243# define CHECK_STACK_SUM(_self) checkStackSum(_self);
Andy McFaddend5ab7262009-08-25 07:19:34 -0700244//static void computeStackSum(Thread* self);
245//static void checkStackSum(Thread* self);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800246#else
247# define COMPUTE_STACK_SUM(_self) ((void)0)
248# define CHECK_STACK_SUM(_self) ((void)0)
249#endif
250
251
252/*
253 * ===========================================================================
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800254 * Utility functions
255 * ===========================================================================
256 */
257
258/*
259 * Entry/exit processing for all JNI calls.
260 *
261 * If TRUSTED_JNIENV is set, we get to skip the (curiously expensive)
262 * thread-local storage lookup on our Thread*. If the caller has passed
263 * the wrong JNIEnv in, we're going to be accessing unsynchronized
264 * structures from more than one thread, and things are going to fail
265 * in bizarre ways. This is only sensible if the native code has been
266 * fully exercised with CheckJNI enabled.
267 */
268#define TRUSTED_JNIENV
269#ifdef TRUSTED_JNIENV
270# define JNI_ENTER() \
271 Thread* _self = ((JNIEnvExt*)env)->self; \
272 CHECK_STACK_SUM(_self); \
273 dvmChangeStatus(_self, THREAD_RUNNING)
274#else
275# define JNI_ENTER() \
276 Thread* _self = dvmThreadSelf(); \
277 UNUSED_PARAMETER(env); \
278 CHECK_STACK_SUM(_self); \
279 dvmChangeStatus(_self, THREAD_RUNNING)
280#endif
281#define JNI_EXIT() \
282 dvmChangeStatus(_self, THREAD_NATIVE); \
283 COMPUTE_STACK_SUM(_self)
284
285#define kGlobalRefsTableInitialSize 512
Andy McFaddenab00d452009-08-19 07:21:41 -0700286#define kGlobalRefsTableMaxSize 51200 /* arbitrary, must be < 64K */
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800287#define kGrefWaterInterval 100
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800288#define kTrackGrefUsage true
289
Andy McFaddenc26bb632009-08-21 12:01:31 -0700290#define kPinTableInitialSize 16
291#define kPinTableMaxSize 1024
292#define kPinComplainThreshold 10
293
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800294/*
Andy McFadden5f612b82009-07-22 15:07:27 -0700295 * Allocate the global references table, and look up some classes for
296 * the benefit of direct buffer access.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800297 */
298bool dvmJniStartup(void)
299{
Andy McFaddend5ab7262009-08-25 07:19:34 -0700300#ifdef USE_INDIRECT_REF
301 if (!dvmInitIndirectRefTable(&gDvm.jniGlobalRefTable,
302 kGlobalRefsTableInitialSize, kGlobalRefsTableMaxSize,
303 kIndirectKindGlobal))
304 return false;
305#else
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800306 if (!dvmInitReferenceTable(&gDvm.jniGlobalRefTable,
307 kGlobalRefsTableInitialSize, kGlobalRefsTableMaxSize))
308 return false;
Andy McFaddend5ab7262009-08-25 07:19:34 -0700309#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800310
311 dvmInitMutex(&gDvm.jniGlobalRefLock);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800312 gDvm.jniGlobalRefLoMark = 0;
313 gDvm.jniGlobalRefHiMark = kGrefWaterInterval * 2;
314
Andy McFaddenc26bb632009-08-21 12:01:31 -0700315 if (!dvmInitReferenceTable(&gDvm.jniPinRefTable,
316 kPinTableInitialSize, kPinTableMaxSize))
317 return false;
318
319 dvmInitMutex(&gDvm.jniPinRefLock);
320
Andy McFaddenb18992f2009-09-25 10:42:15 -0700321 Method* meth;
322
323 /*
324 * Grab the PhantomReference constructor.
325 */
326 gDvm.classJavaLangRefPhantomReference =
327 dvmFindSystemClassNoInit("Ljava/lang/ref/PhantomReference;");
328 if (gDvm.classJavaLangRefPhantomReference == NULL) {
329 LOGE("Unable to find PhantomReference class\n");
330 return false;
331 }
332 meth= dvmFindDirectMethodByDescriptor(gDvm.classJavaLangRefPhantomReference,
333 "<init>", "(Ljava/lang/Object;Ljava/lang/ref/ReferenceQueue;)V");
334 if (meth == NULL) {
335 LOGE("Unable to find constructor for PhantomReference\n");
336 return false;
337 }
338 gDvm.methJavaLangRefPhantomReference_init = meth;
339
340
Andy McFadden8e5c7842009-07-23 17:47:18 -0700341 /*
342 * Look up and cache pointers to some direct buffer classes, fields,
343 * and methods.
344 */
Andy McFadden5f612b82009-07-22 15:07:27 -0700345 ClassObject* platformAddressClass =
346 dvmFindSystemClassNoInit("Lorg/apache/harmony/luni/platform/PlatformAddress;");
Andy McFadden8e5c7842009-07-23 17:47:18 -0700347 ClassObject* platformAddressFactoryClass =
348 dvmFindSystemClassNoInit("Lorg/apache/harmony/luni/platform/PlatformAddressFactory;");
Andy McFadden5f612b82009-07-22 15:07:27 -0700349 ClassObject* directBufferClass =
350 dvmFindSystemClassNoInit("Lorg/apache/harmony/nio/internal/DirectBuffer;");
Andy McFadden8e5c7842009-07-23 17:47:18 -0700351 ClassObject* readWriteBufferClass =
352 dvmFindSystemClassNoInit("Ljava/nio/ReadWriteDirectByteBuffer;");
353 ClassObject* bufferClass =
354 dvmFindSystemClassNoInit("Ljava/nio/Buffer;");
355
356 if (platformAddressClass == NULL || platformAddressFactoryClass == NULL ||
357 directBufferClass == NULL || readWriteBufferClass == NULL ||
358 bufferClass == NULL)
359 {
Andy McFadden5f612b82009-07-22 15:07:27 -0700360 LOGE("Unable to find internal direct buffer classes\n");
361 return false;
362 }
Andy McFadden8e5c7842009-07-23 17:47:18 -0700363 gDvm.classJavaNioReadWriteDirectByteBuffer = readWriteBufferClass;
Andy McFaddend5ab7262009-08-25 07:19:34 -0700364 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer = directBufferClass;
365 /* need a global reference for extended CheckJNI tests */
366 gDvm.jclassOrgApacheHarmonyNioInternalDirectBuffer =
367 addGlobalReference((Object*) directBufferClass);
Andy McFadden5f612b82009-07-22 15:07:27 -0700368
Andy McFadden8e5c7842009-07-23 17:47:18 -0700369 /*
370 * We need a Method* here rather than a vtable offset, because
371 * DirectBuffer is an interface class.
372 */
Andy McFadden5f612b82009-07-22 15:07:27 -0700373 meth = dvmFindVirtualMethodByDescriptor(
374 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer,
375 "getEffectiveAddress",
376 "()Lorg/apache/harmony/luni/platform/PlatformAddress;");
377 if (meth == NULL) {
378 LOGE("Unable to find PlatformAddress.getEffectiveAddress\n");
379 return false;
380 }
381 gDvm.methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress = meth;
382
383 meth = dvmFindVirtualMethodByDescriptor(platformAddressClass,
384 "toLong", "()J");
385 if (meth == NULL) {
386 LOGE("Unable to find PlatformAddress.toLong\n");
387 return false;
388 }
Andy McFadden8e5c7842009-07-23 17:47:18 -0700389 gDvm.voffOrgApacheHarmonyLuniPlatformPlatformAddress_toLong =
390 meth->methodIndex;
391
392 meth = dvmFindDirectMethodByDescriptor(platformAddressFactoryClass,
393 "on",
394 "(I)Lorg/apache/harmony/luni/platform/PlatformAddress;");
395 if (meth == NULL) {
396 LOGE("Unable to find PlatformAddressFactory.on\n");
397 return false;
398 }
399 gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on = meth;
400
401 meth = dvmFindDirectMethodByDescriptor(readWriteBufferClass,
402 "<init>",
403 "(Lorg/apache/harmony/luni/platform/PlatformAddress;II)V");
404 if (meth == NULL) {
405 LOGE("Unable to find ReadWriteDirectByteBuffer.<init>\n");
406 return false;
407 }
408 gDvm.methJavaNioReadWriteDirectByteBuffer_init = meth;
409
410 gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr =
411 dvmFindFieldOffset(platformAddressClass, "osaddr", "I");
412 if (gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr < 0) {
413 LOGE("Unable to find PlatformAddress.osaddr\n");
414 return false;
415 }
416
417 gDvm.offJavaNioBuffer_capacity =
418 dvmFindFieldOffset(bufferClass, "capacity", "I");
419 if (gDvm.offJavaNioBuffer_capacity < 0) {
420 LOGE("Unable to find Buffer.capacity\n");
421 return false;
422 }
Andy McFadden5f612b82009-07-22 15:07:27 -0700423
Andy McFadden8e696dc2009-07-24 15:28:16 -0700424 gDvm.offJavaNioBuffer_effectiveDirectAddress =
425 dvmFindFieldOffset(bufferClass, "effectiveDirectAddress", "I");
426 if (gDvm.offJavaNioBuffer_effectiveDirectAddress < 0) {
427 LOGE("Unable to find Buffer.effectiveDirectAddress\n");
428 return false;
429 }
430
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800431 return true;
432}
433
434/*
435 * Free the global references table.
436 */
437void dvmJniShutdown(void)
438{
Andy McFaddend5ab7262009-08-25 07:19:34 -0700439#ifdef USE_INDIRECT_REF
440 dvmClearIndirectRefTable(&gDvm.jniGlobalRefTable);
441#else
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800442 dvmClearReferenceTable(&gDvm.jniGlobalRefTable);
Andy McFaddend5ab7262009-08-25 07:19:34 -0700443#endif
Andy McFadden96516932009-10-28 17:39:02 -0700444 dvmClearReferenceTable(&gDvm.jniPinRefTable);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800445}
446
447
448/*
449 * Find the JNIEnv associated with the current thread.
450 *
451 * Currently stored in the Thread struct. Could also just drop this into
452 * thread-local storage.
453 */
454JNIEnvExt* dvmGetJNIEnvForThread(void)
455{
456 Thread* self = dvmThreadSelf();
457 if (self == NULL)
458 return NULL;
459 return (JNIEnvExt*) dvmGetThreadJNIEnv(self);
460}
461
462/*
463 * Create a new JNIEnv struct and add it to the VM's list.
464 *
465 * "self" will be NULL for the main thread, since the VM hasn't started
466 * yet; the value will be filled in later.
467 */
468JNIEnv* dvmCreateJNIEnv(Thread* self)
469{
470 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
471 JNIEnvExt* newEnv;
472
473 //if (self != NULL)
474 // LOGI("Ent CreateJNIEnv: threadid=%d %p\n", self->threadId, self);
475
476 assert(vm != NULL);
477
478 newEnv = (JNIEnvExt*) calloc(1, sizeof(JNIEnvExt));
479 newEnv->funcTable = &gNativeInterface;
480 newEnv->vm = vm;
481 newEnv->forceDataCopy = vm->forceDataCopy;
482 if (self != NULL) {
483 dvmSetJniEnvThreadId((JNIEnv*) newEnv, self);
484 assert(newEnv->envThreadId != 0);
485 } else {
486 /* make it obvious if we fail to initialize these later */
487 newEnv->envThreadId = 0x77777775;
488 newEnv->self = (Thread*) 0x77777779;
489 }
490 if (vm->useChecked)
491 dvmUseCheckedJniEnv(newEnv);
492
493 dvmLockMutex(&vm->envListLock);
494
495 /* insert at head of list */
496 newEnv->next = vm->envList;
497 assert(newEnv->prev == NULL);
498 if (vm->envList == NULL) // rare, but possible
499 vm->envList = newEnv;
500 else
501 vm->envList->prev = newEnv;
502 vm->envList = newEnv;
503
504 dvmUnlockMutex(&vm->envListLock);
505
506 //if (self != NULL)
507 // LOGI("Xit CreateJNIEnv: threadid=%d %p\n", self->threadId, self);
508 return (JNIEnv*) newEnv;
509}
510
511/*
512 * Remove a JNIEnv struct from the list and free it.
513 */
514void dvmDestroyJNIEnv(JNIEnv* env)
515{
516 JNIEnvExt* extEnv = (JNIEnvExt*) env;
517 JavaVMExt* vm = extEnv->vm;
518 Thread* self;
519
520 if (env == NULL)
521 return;
522
523 self = dvmThreadSelf();
524 assert(self != NULL);
525
526 //LOGI("Ent DestroyJNIEnv: threadid=%d %p\n", self->threadId, self);
527
528 dvmLockMutex(&vm->envListLock);
529
530 if (extEnv == vm->envList) {
531 assert(extEnv->prev == NULL);
532 vm->envList = extEnv->next;
533 } else {
534 assert(extEnv->prev != NULL);
535 extEnv->prev->next = extEnv->next;
536 }
537 if (extEnv->next != NULL)
538 extEnv->next->prev = extEnv->prev;
539
540 dvmUnlockMutex(&extEnv->vm->envListLock);
541
542 free(env);
543 //LOGI("Xit DestroyJNIEnv: threadid=%d %p\n", self->threadId, self);
544}
545
546
547/*
548 * Retrieve the ReferenceTable struct for the current thread.
549 *
Andy McFaddenab00d452009-08-19 07:21:41 -0700550 * Going through "env" rather than dvmThreadSelf() is faster but will
551 * get weird if the JNI code is passing the wrong JNIEnv around.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800552 */
Andy McFaddend5ab7262009-08-25 07:19:34 -0700553#ifdef USE_INDIRECT_REF
554static inline IndirectRefTable* getLocalRefTable(JNIEnv* env)
Andy McFaddend5ab7262009-08-25 07:19:34 -0700555#else
Andy McFaddenab00d452009-08-19 07:21:41 -0700556static inline ReferenceTable* getLocalRefTable(JNIEnv* env)
Andy McFaddeneb9cbc32009-08-28 14:45:12 -0700557#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800558{
Andy McFaddenab00d452009-08-19 07:21:41 -0700559 //return &dvmThreadSelf()->jniLocalRefTable;
560 return &((JNIEnvExt*)env)->self->jniLocalRefTable;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800561}
Andy McFaddend5ab7262009-08-25 07:19:34 -0700562
Andy McFaddeneb9cbc32009-08-28 14:45:12 -0700563#ifdef USE_INDIRECT_REF
Andy McFaddend5ab7262009-08-25 07:19:34 -0700564/*
565 * Convert an indirect reference to an Object reference. The indirect
566 * reference may be local, global, or weak-global.
567 *
568 * If "jobj" is NULL or an invalid indirect reference, this returns NULL.
569 */
570Object* dvmDecodeIndirectRef(JNIEnv* env, jobject jobj)
571{
Andy McFaddend5ab7262009-08-25 07:19:34 -0700572 if (jobj == NULL)
573 return NULL;
574
575 Object* result;
576
577 switch (dvmGetIndirectRefType(jobj)) {
578 case kIndirectKindLocal:
579 {
580 IndirectRefTable* pRefTable = getLocalRefTable(env);
581 result = dvmGetFromIndirectRefTable(pRefTable, jobj);
582 }
583 break;
584 case kIndirectKindGlobal:
585 {
586 // TODO: find a way to avoid the mutex activity here
587 IndirectRefTable* pRefTable = &gDvm.jniGlobalRefTable;
588 dvmLockMutex(&gDvm.jniGlobalRefLock);
589 result = dvmGetFromIndirectRefTable(pRefTable, jobj);
590 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
591 }
592 break;
593 case kIndirectKindWeakGlobal:
594 {
Andy McFaddenb18992f2009-09-25 10:42:15 -0700595 // TODO: implement
Andy McFaddend5ab7262009-08-25 07:19:34 -0700596 LOGE("weak-global not yet supported\n");
597 result = NULL;
598 dvmAbort();
599 }
600 break;
601 case kIndirectKindInvalid:
602 default:
603 LOGW("Invalid indirect reference %p in decodeIndirectRef\n", jobj);
604 dvmAbort();
605 result = NULL;
606 break;
607 }
608
609 return result;
Andy McFaddend5ab7262009-08-25 07:19:34 -0700610}
Andy McFaddeneb9cbc32009-08-28 14:45:12 -0700611#else
612 /* use trivial inline in JniInternal.h for performance */
613#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800614
615/*
616 * Add a local reference for an object to the current stack frame. When
617 * the native function returns, the reference will be discarded.
618 *
619 * We need to allow the same reference to be added multiple times.
620 *
621 * This will be called on otherwise unreferenced objects. We cannot do
622 * GC allocations here, and it's best if we don't grab a mutex.
623 *
624 * Returns the local reference (currently just the same pointer that was
625 * passed in), or NULL on failure.
626 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700627static jobject addLocalReference(JNIEnv* env, Object* obj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800628{
629 if (obj == NULL)
630 return NULL;
631
Andy McFaddend5ab7262009-08-25 07:19:34 -0700632 jobject jobj;
633
634#ifdef USE_INDIRECT_REF
635 IndirectRefTable* pRefTable = getLocalRefTable(env);
636 void* curFrame = ((JNIEnvExt*)env)->self->curFrame;
637 u4 cookie = SAVEAREA_FROM_FP(curFrame)->xtra.localRefCookie;
638
639 jobj = (jobject) dvmAddToIndirectRefTable(pRefTable, cookie, obj);
640 if (jobj == NULL) {
641 dvmDumpIndirectRefTable(pRefTable, "JNI local");
642 LOGE("Failed adding to JNI local ref table (has %d entries)\n",
643 (int) dvmIndirectRefTableEntries(pRefTable));
644 dvmDumpThread(dvmThreadSelf(), false);
645 dvmAbort(); // spec says call FatalError; this is equivalent
646 } else {
647 LOGVV("LREF add %p (%s.%s) (ent=%d)\n", obj,
648 dvmGetCurrentJNIMethod()->clazz->descriptor,
649 dvmGetCurrentJNIMethod()->name,
650 (int) dvmReferenceTableEntries(pRefTable));
651 }
652#else
Andy McFaddenab00d452009-08-19 07:21:41 -0700653 ReferenceTable* pRefTable = getLocalRefTable(env);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800654
Andy McFaddenab00d452009-08-19 07:21:41 -0700655 if (!dvmAddToReferenceTable(pRefTable, obj)) {
656 dvmDumpReferenceTable(pRefTable, "JNI local");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800657 LOGE("Failed adding to JNI local ref table (has %d entries)\n",
Andy McFaddenab00d452009-08-19 07:21:41 -0700658 (int) dvmReferenceTableEntries(pRefTable));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800659 dvmDumpThread(dvmThreadSelf(), false);
660 dvmAbort(); // spec says call FatalError; this is equivalent
661 } else {
Andy McFadden0083d372009-08-21 14:44:04 -0700662 LOGVV("LREF add %p (%s.%s) (ent=%d)\n", obj,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800663 dvmGetCurrentJNIMethod()->clazz->descriptor,
Andy McFadden0083d372009-08-21 14:44:04 -0700664 dvmGetCurrentJNIMethod()->name,
665 (int) dvmReferenceTableEntries(pRefTable));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800666 }
667
Andy McFaddend5ab7262009-08-25 07:19:34 -0700668 jobj = (jobject) obj;
669#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800670
Andy McFaddend5ab7262009-08-25 07:19:34 -0700671 return jobj;
Andy McFaddenab00d452009-08-19 07:21:41 -0700672}
673
674/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800675 * Ensure that at least "capacity" references can be held in the local
676 * refs table of the current thread.
677 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700678static bool ensureLocalCapacity(JNIEnv* env, int capacity)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800679{
Andy McFaddend5ab7262009-08-25 07:19:34 -0700680#ifdef USE_INDIRECT_REF
681 IndirectRefTable* pRefTable = getLocalRefTable(env);
682 int numEntries = dvmIndirectRefTableEntries(pRefTable);
683 // TODO: this isn't quite right, since "numEntries" includes holes
684 return ((kJniLocalRefMax - numEntries) >= capacity);
685#else
Andy McFaddenab00d452009-08-19 07:21:41 -0700686 ReferenceTable* pRefTable = getLocalRefTable(env);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800687
Andy McFaddenab00d452009-08-19 07:21:41 -0700688 return (kJniLocalRefMax - (pRefTable->nextEntry - pRefTable->table) >= capacity);
Andy McFaddend5ab7262009-08-25 07:19:34 -0700689#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800690}
691
692/*
693 * Explicitly delete a reference from the local list.
694 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700695static void deleteLocalReference(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800696{
Andy McFaddenab00d452009-08-19 07:21:41 -0700697 if (jobj == NULL)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800698 return;
699
Andy McFaddend5ab7262009-08-25 07:19:34 -0700700#ifdef USE_INDIRECT_REF
701 IndirectRefTable* pRefTable = getLocalRefTable(env);
702 Thread* self = ((JNIEnvExt*)env)->self;
703 u4 cookie = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefCookie;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800704
Andy McFaddend5ab7262009-08-25 07:19:34 -0700705 if (!dvmRemoveFromIndirectRefTable(pRefTable, cookie, jobj)) {
706 /*
707 * Attempting to delete a local reference that is not in the
708 * topmost local reference frame is a no-op. DeleteLocalRef returns
709 * void and doesn't throw any exceptions, but we should probably
710 * complain about it so the user will notice that things aren't
711 * going quite the way they expect.
712 */
713 LOGW("JNI WARNING: DeleteLocalRef(%p) failed to find entry\n", jobj);
714 }
715#else
716 ReferenceTable* pRefTable = getLocalRefTable(env);
717 Thread* self = ((JNIEnvExt*)env)->self;
718 Object** bottom = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefCookie;
719
720 if (!dvmRemoveFromReferenceTable(pRefTable, bottom, (Object*) jobj)) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800721 /*
722 * Attempting to delete a local reference that is not in the
723 * topmost local reference frame is a no-op. DeleteLocalRef returns
724 * void and doesn't throw any exceptions, but we should probably
725 * complain about it so the user will notice that things aren't
726 * going quite the way they expect.
727 */
728 LOGW("JNI WARNING: DeleteLocalRef(%p) failed to find entry (valid=%d)\n",
Andy McFaddenab00d452009-08-19 07:21:41 -0700729 jobj, dvmIsValidObject((Object*) jobj));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800730 }
Andy McFaddend5ab7262009-08-25 07:19:34 -0700731#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800732}
733
734/*
735 * Add a global reference for an object.
736 *
737 * We may add the same object more than once. Add/remove calls are paired,
738 * so it needs to appear on the list multiple times.
739 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700740static jobject addGlobalReference(Object* obj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800741{
742 if (obj == NULL)
743 return NULL;
744
745 //LOGI("adding obj=%p\n", obj);
746 //dvmDumpThread(dvmThreadSelf(), false);
747
748 if (false && ((Object*)obj)->clazz == gDvm.classJavaLangClass) {
749 ClassObject* clazz = (ClassObject*) obj;
750 LOGI("-------\n");
751 LOGI("Adding global ref on class %s\n", clazz->descriptor);
752 dvmDumpThread(dvmThreadSelf(), false);
753 }
754 if (false && ((Object*)obj)->clazz == gDvm.classJavaLangString) {
755 StringObject* strObj = (StringObject*) obj;
756 char* str = dvmCreateCstrFromString(strObj);
757 if (strcmp(str, "sync-response") == 0) {
758 LOGI("-------\n");
759 LOGI("Adding global ref on string '%s'\n", str);
760 dvmDumpThread(dvmThreadSelf(), false);
761 //dvmAbort();
762 }
763 free(str);
764 }
765 if (false && ((Object*)obj)->clazz == gDvm.classArrayByte) {
766 ArrayObject* arrayObj = (ArrayObject*) obj;
Andy McFaddend5ab7262009-08-25 07:19:34 -0700767 if (arrayObj->length == 8192 /*&&
768 dvmReferenceTableEntries(&gDvm.jniGlobalRefTable) > 400*/)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800769 {
770 LOGI("Adding global ref on byte array %p (len=%d)\n",
771 arrayObj, arrayObj->length);
772 dvmDumpThread(dvmThreadSelf(), false);
773 }
774 }
775
Andy McFaddend5ab7262009-08-25 07:19:34 -0700776 jobject jobj;
777
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800778 dvmLockMutex(&gDvm.jniGlobalRefLock);
779
780 /*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800781 * Throwing an exception on failure is problematic, because JNI code
782 * may not be expecting an exception, and things sort of cascade. We
783 * want to have a hard limit to catch leaks during debugging, but this
784 * otherwise needs to expand until memory is consumed. As a practical
785 * matter, if we have many thousands of global references, chances are
786 * we're either leaking global ref table entries or we're going to
787 * run out of space in the GC heap.
788 */
Andy McFaddend5ab7262009-08-25 07:19:34 -0700789#ifdef USE_INDIRECT_REF
790 jobj = dvmAddToIndirectRefTable(&gDvm.jniGlobalRefTable, IRT_FIRST_SEGMENT,
791 obj);
792 if (jobj == NULL) {
793 dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable, "JNI global");
794 LOGE("Failed adding to JNI global ref table (%d entries)\n",
795 (int) dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable));
796 dvmAbort();
797 }
798
799 LOGVV("GREF add %p (%s.%s)\n", obj,
800 dvmGetCurrentJNIMethod()->clazz->descriptor,
801 dvmGetCurrentJNIMethod()->name);
802
803 /* GREF usage tracking; should probably be disabled for production env */
804 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
805 int count = dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable);
806 // TODO: adjust for "holes"
807 if (count > gDvm.jniGlobalRefHiMark) {
808 LOGD("GREF has increased to %d\n", count);
809 gDvm.jniGlobalRefHiMark += kGrefWaterInterval;
810 gDvm.jniGlobalRefLoMark += kGrefWaterInterval;
811
812 /* watch for "excessive" use; not generally appropriate */
813 if (count >= gDvm.jniGrefLimit) {
814 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
815 if (vm->warnError) {
816 dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable,
817 "JNI global");
818 LOGE("Excessive JNI global references (%d)\n", count);
819 dvmAbort();
820 } else {
821 LOGW("Excessive JNI global references (%d)\n", count);
822 }
823 }
824 }
825 }
826#else
827 if (!dvmAddToReferenceTable(&gDvm.jniGlobalRefTable, obj)) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800828 dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global");
829 LOGE("Failed adding to JNI global ref table (%d entries)\n",
830 (int) dvmReferenceTableEntries(&gDvm.jniGlobalRefTable));
831 dvmAbort();
832 }
Andy McFaddend5ab7262009-08-25 07:19:34 -0700833 jobj = (jobject) obj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800834
835 LOGVV("GREF add %p (%s.%s)\n", obj,
836 dvmGetCurrentJNIMethod()->clazz->descriptor,
837 dvmGetCurrentJNIMethod()->name);
838
839 /* GREF usage tracking; should probably be disabled for production env */
840 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
841 int count = dvmReferenceTableEntries(&gDvm.jniGlobalRefTable);
842 if (count > gDvm.jniGlobalRefHiMark) {
843 LOGD("GREF has increased to %d\n", count);
844 gDvm.jniGlobalRefHiMark += kGrefWaterInterval;
845 gDvm.jniGlobalRefLoMark += kGrefWaterInterval;
846
847 /* watch for "excessive" use; not generally appropriate */
848 if (count >= gDvm.jniGrefLimit) {
849 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
850 if (vm->warnError) {
851 dvmDumpReferenceTable(&gDvm.jniGlobalRefTable,"JNI global");
852 LOGE("Excessive JNI global references (%d)\n", count);
853 dvmAbort();
854 } else {
855 LOGW("Excessive JNI global references (%d)\n", count);
856 }
857 }
858 }
859 }
Andy McFaddend5ab7262009-08-25 07:19:34 -0700860#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800861
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800862 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
Andy McFaddend5ab7262009-08-25 07:19:34 -0700863 return jobj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800864}
865
866/*
867 * Remove a global reference. In most cases it's the entry most recently
868 * added, which makes this pretty quick.
869 *
870 * Thought: if it's not the most recent entry, just null it out. When we
871 * fill up, do a compaction pass before we expand the list.
872 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700873static void deleteGlobalReference(jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800874{
Andy McFaddenab00d452009-08-19 07:21:41 -0700875 if (jobj == NULL)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800876 return;
877
878 dvmLockMutex(&gDvm.jniGlobalRefLock);
879
Andy McFaddend5ab7262009-08-25 07:19:34 -0700880#ifdef USE_INDIRECT_REF
881 if (!dvmRemoveFromIndirectRefTable(&gDvm.jniGlobalRefTable,
882 IRT_FIRST_SEGMENT, jobj))
883 {
884 LOGW("JNI: DeleteGlobalRef(%p) failed to find entry\n", jobj);
885 goto bail;
886 }
887
888 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
889 int count = dvmIndirectRefTableEntries(&gDvm.jniGlobalRefTable);
890 // TODO: not quite right, need to subtract holes
891 if (count < gDvm.jniGlobalRefLoMark) {
892 LOGD("GREF has decreased to %d\n", count);
893 gDvm.jniGlobalRefHiMark -= kGrefWaterInterval;
894 gDvm.jniGlobalRefLoMark -= kGrefWaterInterval;
895 }
896 }
897#else
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800898 if (!dvmRemoveFromReferenceTable(&gDvm.jniGlobalRefTable,
Andy McFaddenab00d452009-08-19 07:21:41 -0700899 gDvm.jniGlobalRefTable.table, jobj))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800900 {
901 LOGW("JNI: DeleteGlobalRef(%p) failed to find entry (valid=%d)\n",
Andy McFaddenab00d452009-08-19 07:21:41 -0700902 jobj, dvmIsValidObject((Object*) jobj));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800903 goto bail;
904 }
905
906 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
907 int count = dvmReferenceTableEntries(&gDvm.jniGlobalRefTable);
908 if (count < gDvm.jniGlobalRefLoMark) {
909 LOGD("GREF has decreased to %d\n", count);
910 gDvm.jniGlobalRefHiMark -= kGrefWaterInterval;
911 gDvm.jniGlobalRefLoMark -= kGrefWaterInterval;
912 }
913 }
Andy McFaddend5ab7262009-08-25 07:19:34 -0700914#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800915
916bail:
917 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
918}
919
Andy McFaddenb18992f2009-09-25 10:42:15 -0700920/*
921 * We create a PhantomReference that references the object, add a
922 * global reference to it, and then flip some bits before returning it.
923 * The last step ensures that we detect it as special and that only
924 * appropriate calls will accept it.
925 *
926 * On failure, returns NULL with an exception pending.
927 */
928static jweak createWeakGlobalRef(JNIEnv* env, jobject jobj)
929{
930 if (jobj == NULL)
931 return NULL;
932
933 Thread* self = ((JNIEnvExt*)env)->self;
934 Object* obj = dvmDecodeIndirectRef(env, jobj);
Andy McFaddenb18992f2009-09-25 10:42:15 -0700935 Object* phantomObj;
936 jobject phantomRef;
937
938 /*
939 * Allocate a PhantomReference, then call the constructor to set
940 * the referent and the reference queue.
941 *
942 * We use a "magic" reference queue that the GC knows about; it behaves
943 * more like a queueless WeakReference, clearing the referent and
944 * not calling enqueue().
945 */
946 if (!dvmIsClassInitialized(gDvm.classJavaLangRefPhantomReference))
947 dvmInitClass(gDvm.classJavaLangRefPhantomReference);
948 phantomObj = dvmAllocObject(gDvm.classJavaLangRefPhantomReference,
949 ALLOC_DEFAULT);
950 if (phantomObj == NULL) {
951 assert(dvmCheckException(self));
952 LOGW("Failed on WeakGlobalRef alloc\n");
953 return NULL;
954 }
955
956 JValue unused;
957 dvmCallMethod(self, gDvm.methJavaLangRefPhantomReference_init, phantomObj,
Carl Shapiroe3c01da2010-05-20 22:54:18 -0700958 &unused, obj, NULL);
Andy McFaddenb18992f2009-09-25 10:42:15 -0700959 dvmReleaseTrackedAlloc(phantomObj, self);
960
961 if (dvmCheckException(self)) {
962 LOGW("PhantomReference init failed\n");
963 return NULL;
964 }
965
966 LOGV("+++ WGR: created phantom ref %p for object %p\n", phantomObj, obj);
967
968 /*
969 * Add it to the global reference table, and mangle the pointer.
970 */
971 phantomRef = addGlobalReference(phantomObj);
972 return dvmObfuscateWeakGlobalRef(phantomRef);
973}
974
975/*
976 * Delete the global reference that's keeping the PhantomReference around.
977 * The PhantomReference will eventually be discarded by the GC.
978 */
979static void deleteWeakGlobalRef(JNIEnv* env, jweak wref)
980{
981 if (wref == NULL)
982 return;
983
984 jobject phantomRef = dvmNormalizeWeakGlobalRef(wref);
985 deleteGlobalReference(phantomRef);
986}
987
988/*
989 * Extract the referent from a PhantomReference. Used for weak global
990 * references.
991 *
992 * "jwobj" is a "mangled" WGR pointer.
993 */
994static Object* getPhantomReferent(JNIEnv* env, jweak jwobj)
995{
996 jobject jobj = dvmNormalizeWeakGlobalRef(jwobj);
997 Object* obj = dvmDecodeIndirectRef(env, jobj);
998
999 if (obj->clazz != gDvm.classJavaLangRefPhantomReference) {
1000 LOGE("%p is not a phantom reference (%s)\n",
1001 jwobj, obj->clazz->descriptor);
1002 return NULL;
1003 }
1004
1005 return dvmGetFieldObject(obj, gDvm.offJavaLangRefReference_referent);
1006}
1007
1008
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001009/*
Andy McFaddenab00d452009-08-19 07:21:41 -07001010 * Objects don't currently move, so we just need to create a reference
1011 * that will ensure the array object isn't collected.
1012 *
Andy McFaddenc26bb632009-08-21 12:01:31 -07001013 * We use a separate reference table, which is part of the GC root set.
Andy McFaddenab00d452009-08-19 07:21:41 -07001014 */
1015static void pinPrimitiveArray(ArrayObject* arrayObj)
1016{
Andy McFaddenc26bb632009-08-21 12:01:31 -07001017 if (arrayObj == NULL)
1018 return;
1019
1020 dvmLockMutex(&gDvm.jniPinRefLock);
1021 if (!dvmAddToReferenceTable(&gDvm.jniPinRefTable, (Object*)arrayObj)) {
1022 dvmDumpReferenceTable(&gDvm.jniPinRefTable, "JNI pinned array");
1023 LOGE("Failed adding to JNI pinned array ref table (%d entries)\n",
1024 (int) dvmReferenceTableEntries(&gDvm.jniPinRefTable));
1025 dvmDumpThread(dvmThreadSelf(), false);
1026 dvmAbort();
1027 }
1028
1029 /*
1030 * If we're watching global ref usage, also keep an eye on these.
1031 *
1032 * The total number of pinned primitive arrays should be pretty small.
1033 * A single array should not be pinned more than once or twice; any
1034 * more than that is a strong indicator that a Release function is
1035 * not being called.
1036 */
1037 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
1038 int count = 0;
1039 Object** ppObj = gDvm.jniPinRefTable.table;
1040 while (ppObj < gDvm.jniPinRefTable.nextEntry) {
1041 if (*ppObj++ == (Object*) arrayObj)
1042 count++;
1043 }
1044
1045 if (count > kPinComplainThreshold) {
1046 LOGW("JNI: pin count on array %p (%s) is now %d\n",
1047 arrayObj, arrayObj->obj.clazz->descriptor, count);
1048 /* keep going */
1049 }
1050 }
1051
1052 dvmUnlockMutex(&gDvm.jniPinRefLock);
Andy McFaddenab00d452009-08-19 07:21:41 -07001053}
1054
1055/*
1056 * Un-pin the array object. If an object was pinned twice, it must be
1057 * unpinned twice before it's free to move.
Andy McFaddenab00d452009-08-19 07:21:41 -07001058 */
1059static void unpinPrimitiveArray(ArrayObject* arrayObj)
1060{
Andy McFaddenc26bb632009-08-21 12:01:31 -07001061 if (arrayObj == NULL)
1062 return;
1063
1064 dvmLockMutex(&gDvm.jniPinRefLock);
1065 if (!dvmRemoveFromReferenceTable(&gDvm.jniPinRefTable,
1066 gDvm.jniPinRefTable.table, (Object*) arrayObj))
1067 {
1068 LOGW("JNI: unpinPrimitiveArray(%p) failed to find entry (valid=%d)\n",
1069 arrayObj, dvmIsValidObject((Object*) arrayObj));
1070 goto bail;
1071 }
1072
1073bail:
1074 dvmUnlockMutex(&gDvm.jniPinRefLock);
Andy McFaddenab00d452009-08-19 07:21:41 -07001075}
1076
1077/*
Andy McFadden92fa4762009-10-22 17:24:45 -07001078 * Dump the contents of the JNI reference tables to the log file.
1079 *
1080 * We only dump the local refs associated with the current thread.
1081 */
1082void dvmDumpJniReferenceTables(void)
1083{
1084 Thread* self = dvmThreadSelf();
1085 JNIEnv* env = self->jniEnv;
1086 ReferenceTable* pLocalRefs = getLocalRefTable(env);
1087
1088#ifdef USE_INDIRECT_REF
1089 dvmDumpIndirectRefTable(pLocalRefs, "JNI local");
1090 dvmDumpIndirectRefTable(&gDvm.jniGlobalRefTable, "JNI global");
1091#else
1092 dvmDumpReferenceTable(pLocalRefs, "JNI local");
1093 dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global");
1094#endif
1095 dvmDumpReferenceTable(&gDvm.jniPinRefTable, "JNI pinned array");
1096}
1097
1098/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001099 * GC helper function to mark all JNI global references.
Andy McFaddenc26bb632009-08-21 12:01:31 -07001100 *
1101 * We're currently handling the "pin" table here too.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001102 */
1103void dvmGcMarkJniGlobalRefs()
1104{
Andy McFaddend5ab7262009-08-25 07:19:34 -07001105 Object** op;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001106
1107 dvmLockMutex(&gDvm.jniGlobalRefLock);
1108
Andy McFaddend5ab7262009-08-25 07:19:34 -07001109#ifdef USE_INDIRECT_REF
1110 IndirectRefTable* pRefTable = &gDvm.jniGlobalRefTable;
1111 op = pRefTable->table;
1112 int numEntries = dvmIndirectRefTableEntries(pRefTable);
1113 int i;
1114
1115 for (i = 0; i < numEntries; i++) {
1116 Object* obj = *op;
1117 if (obj != NULL)
1118 dvmMarkObjectNonNull(obj);
1119 op++;
1120 }
1121#else
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001122 op = gDvm.jniGlobalRefTable.table;
1123 while ((uintptr_t)op < (uintptr_t)gDvm.jniGlobalRefTable.nextEntry) {
1124 dvmMarkObjectNonNull(*(op++));
1125 }
Andy McFaddend5ab7262009-08-25 07:19:34 -07001126#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001127
1128 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
Andy McFaddenc26bb632009-08-21 12:01:31 -07001129
1130
1131 dvmLockMutex(&gDvm.jniPinRefLock);
1132
1133 op = gDvm.jniPinRefTable.table;
1134 while ((uintptr_t)op < (uintptr_t)gDvm.jniPinRefTable.nextEntry) {
1135 dvmMarkObjectNonNull(*(op++));
1136 }
1137
1138 dvmUnlockMutex(&gDvm.jniPinRefLock);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001139}
1140
1141
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001142#ifndef USE_INDIRECT_REF
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001143/*
1144 * Determine if "obj" appears in the argument list for the native method.
1145 *
1146 * We use the "shorty" signature to determine which argument slots hold
1147 * reference types.
1148 */
1149static bool findInArgList(Thread* self, Object* obj)
1150{
1151 const Method* meth;
1152 u4* fp;
1153 int i;
1154
1155 fp = self->curFrame;
1156 while (1) {
1157 /*
1158 * Back up over JNI PushLocalFrame frames. This works because the
1159 * previous frame on the interpreted stack is either a break frame
1160 * (if we called here via native code) or an interpreted method (if
1161 * we called here via the interpreter). In both cases the method
1162 * pointer won't match.
1163 */
1164 StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
1165 meth = saveArea->method;
1166 if (meth != SAVEAREA_FROM_FP(saveArea->prevFrame)->method)
1167 break;
1168 fp = saveArea->prevFrame;
1169 }
1170
1171 LOGVV("+++ scanning %d args in %s (%s)\n",
1172 meth->insSize, meth->name, meth->shorty);
1173 const char* shorty = meth->shorty +1; /* skip return type char */
1174 for (i = 0; i < meth->insSize; i++) {
1175 if (i == 0 && !dvmIsStaticMethod(meth)) {
1176 /* first arg is "this" ref, not represented in "shorty" */
1177 if (fp[i] == (u4) obj)
1178 return true;
1179 } else {
1180 /* if this is a reference type, see if it matches */
1181 switch (*shorty) {
1182 case 'L':
1183 if (fp[i] == (u4) obj)
1184 return true;
1185 break;
1186 case 'D':
1187 case 'J':
1188 i++;
1189 break;
1190 case '\0':
1191 LOGE("Whoops! ran off the end of %s (%d)\n",
1192 meth->shorty, meth->insSize);
1193 break;
1194 default:
1195 if (fp[i] == (u4) obj)
1196 LOGI("NOTE: ref %p match on arg type %c\n", obj, *shorty);
1197 break;
1198 }
1199 shorty++;
1200 }
1201 }
1202
1203 /*
1204 * For static methods, we also pass a class pointer in.
1205 */
1206 if (dvmIsStaticMethod(meth)) {
1207 //LOGI("+++ checking class pointer in %s\n", meth->name);
1208 if ((void*)obj == (void*)meth->clazz)
1209 return true;
1210 }
1211 return false;
1212}
Andy McFadden0083d372009-08-21 14:44:04 -07001213#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001214
1215/*
1216 * Verify that a reference passed in from native code is one that the
1217 * code is allowed to have.
1218 *
1219 * It's okay for native code to pass us a reference that:
Andy McFadden0083d372009-08-21 14:44:04 -07001220 * - was passed in as an argument when invoked by native code (and hence
1221 * is in the JNI local refs table)
1222 * - was returned to it from JNI (and is now in the local refs table)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001223 * - is present in the JNI global refs table
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001224 *
1225 * Used by -Xcheck:jni and GetObjectRefType.
1226 *
1227 * NOTE: in the current VM, global and local references are identical. If
1228 * something is both global and local, we can't tell them apart, and always
1229 * return "local".
1230 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001231jobjectRefType dvmGetJNIRefType(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001232{
Andy McFaddend5ab7262009-08-25 07:19:34 -07001233#ifdef USE_INDIRECT_REF
1234 /*
1235 * IndirectRefKind is currently defined as an exact match of
Andy McFadden0423f0e2009-08-26 07:21:53 -07001236 * jobjectRefType, so this is easy. We have to decode it to determine
1237 * if it's a valid reference and not merely valid-looking.
Andy McFaddend5ab7262009-08-25 07:19:34 -07001238 */
Andy McFadden0423f0e2009-08-26 07:21:53 -07001239 Object* obj = dvmDecodeIndirectRef(env, jobj);
1240
1241 if (obj == NULL) {
1242 /* invalid ref, or jobj was NULL */
1243 return JNIInvalidRefType;
1244 } else {
1245 return (jobjectRefType) dvmGetIndirectRefType(jobj);
1246 }
Andy McFaddend5ab7262009-08-25 07:19:34 -07001247#else
Andy McFaddenab00d452009-08-19 07:21:41 -07001248 ReferenceTable* pRefTable = getLocalRefTable(env);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001249 Thread* self = dvmThreadSelf();
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001250
Andy McFaddenb18992f2009-09-25 10:42:15 -07001251 if (dvmIsWeakGlobalRef(jobj)) {
1252 return JNIWeakGlobalRefType;
1253 }
1254
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001255 /* check args */
Andy McFaddenab00d452009-08-19 07:21:41 -07001256 if (findInArgList(self, jobj)) {
1257 //LOGI("--- REF found %p on stack\n", jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001258 return JNILocalRefType;
1259 }
1260
1261 /* check locals */
Andy McFaddenab00d452009-08-19 07:21:41 -07001262 if (dvmFindInReferenceTable(pRefTable, pRefTable->table, jobj) != NULL) {
1263 //LOGI("--- REF found %p in locals\n", jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001264 return JNILocalRefType;
1265 }
1266
1267 /* check globals */
1268 dvmLockMutex(&gDvm.jniGlobalRefLock);
1269 if (dvmFindInReferenceTable(&gDvm.jniGlobalRefTable,
Andy McFaddenab00d452009-08-19 07:21:41 -07001270 gDvm.jniGlobalRefTable.table, jobj))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001271 {
Andy McFaddenab00d452009-08-19 07:21:41 -07001272 //LOGI("--- REF found %p in globals\n", jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001273 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
1274 return JNIGlobalRefType;
1275 }
1276 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
1277
1278 /* not found! */
1279 return JNIInvalidRefType;
Andy McFaddend5ab7262009-08-25 07:19:34 -07001280#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001281}
1282
1283/*
1284 * Register a method that uses JNI calling conventions.
1285 */
1286static bool dvmRegisterJNIMethod(ClassObject* clazz, const char* methodName,
1287 const char* signature, void* fnPtr)
1288{
1289 Method* method;
1290 bool result = false;
1291
1292 if (fnPtr == NULL)
1293 goto bail;
1294
1295 method = dvmFindDirectMethodByDescriptor(clazz, methodName, signature);
1296 if (method == NULL)
1297 method = dvmFindVirtualMethodByDescriptor(clazz, methodName, signature);
1298 if (method == NULL) {
Andy McFadden1e83b4d2010-07-15 17:20:24 -07001299 LOGW("ERROR: Unable to find decl for native %s.%s:%s\n",
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001300 clazz->descriptor, methodName, signature);
1301 goto bail;
1302 }
1303
1304 if (!dvmIsNativeMethod(method)) {
Andy McFadden1e83b4d2010-07-15 17:20:24 -07001305 LOGW("Unable to register: not native: %s.%s:%s\n",
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001306 clazz->descriptor, methodName, signature);
1307 goto bail;
1308 }
1309
1310 if (method->nativeFunc != dvmResolveNativeMethod) {
Andy McFadden1e83b4d2010-07-15 17:20:24 -07001311 /* this is allowed, but unusual */
1312 LOGV("Note: %s.%s:%s was already registered\n",
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001313 clazz->descriptor, methodName, signature);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001314 }
1315
Andy McFadden59b61772009-05-13 16:44:34 -07001316 dvmUseJNIBridge(method, fnPtr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001317
Andy McFadden1e83b4d2010-07-15 17:20:24 -07001318 LOGV("JNI-registered %s.%s:%s\n", clazz->descriptor, methodName,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001319 signature);
1320 result = true;
1321
1322bail:
1323 return result;
1324}
1325
1326/*
Andy McFadden59b61772009-05-13 16:44:34 -07001327 * Returns "true" if CheckJNI is enabled in the VM.
1328 */
1329static bool dvmIsCheckJNIEnabled(void)
1330{
1331 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
1332 return vm->useChecked;
1333}
1334
1335/*
1336 * Point "method->nativeFunc" at the JNI bridge, and overload "method->insns"
1337 * to point at the actual function.
1338 */
1339void dvmUseJNIBridge(Method* method, void* func)
1340{
Andy McFadden0083d372009-08-21 14:44:04 -07001341 enum {
1342 kJNIGeneral = 0,
1343 kJNISync = 1,
1344 kJNIVirtualNoRef = 2,
1345 kJNIStaticNoRef = 3,
1346 } kind;
1347 static const DalvikBridgeFunc stdFunc[] = {
1348 dvmCallJNIMethod_general,
1349 dvmCallJNIMethod_synchronized,
1350 dvmCallJNIMethod_virtualNoRef,
1351 dvmCallJNIMethod_staticNoRef
1352 };
1353 static const DalvikBridgeFunc checkFunc[] = {
1354 dvmCheckCallJNIMethod_general,
1355 dvmCheckCallJNIMethod_synchronized,
1356 dvmCheckCallJNIMethod_virtualNoRef,
1357 dvmCheckCallJNIMethod_staticNoRef
1358 };
1359
1360 bool hasRefArg = false;
1361
1362 if (dvmIsSynchronizedMethod(method)) {
1363 /* use version with synchronization; calls into general handler */
1364 kind = kJNISync;
Andy McFadden59b61772009-05-13 16:44:34 -07001365 } else {
Andy McFadden0083d372009-08-21 14:44:04 -07001366 /*
1367 * Do a quick scan through the "shorty" signature to see if the method
1368 * takes any reference arguments.
1369 */
1370 const char* cp = method->shorty;
1371 while (*++cp != '\0') { /* pre-incr to skip return type */
1372 if (*cp == 'L') {
1373 /* 'L' used for both object and array references */
1374 hasRefArg = true;
1375 break;
1376 }
1377 }
1378
1379 if (hasRefArg) {
1380 /* use general handler to slurp up reference args */
1381 kind = kJNIGeneral;
1382 } else {
1383 /* virtual methods have a ref in args[0] (not in signature) */
1384 if (dvmIsStaticMethod(method))
1385 kind = kJNIStaticNoRef;
1386 else
1387 kind = kJNIVirtualNoRef;
1388 }
1389 }
1390
1391 if (dvmIsCheckJNIEnabled()) {
1392 dvmSetNativeFunc(method, checkFunc[kind], func);
1393 } else {
1394 dvmSetNativeFunc(method, stdFunc[kind], func);
Andy McFadden59b61772009-05-13 16:44:34 -07001395 }
1396}
1397
1398/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001399 * Get the method currently being executed by examining the interp stack.
1400 */
1401const Method* dvmGetCurrentJNIMethod(void)
1402{
1403 assert(dvmThreadSelf() != NULL);
1404
1405 void* fp = dvmThreadSelf()->curFrame;
1406 const Method* meth = SAVEAREA_FROM_FP(fp)->method;
1407
1408 assert(meth != NULL);
1409 assert(dvmIsNativeMethod(meth));
1410 return meth;
1411}
1412
1413
1414/*
1415 * Track a JNI MonitorEnter in the current thread.
1416 *
1417 * The goal is to be able to "implicitly" release all JNI-held monitors
1418 * when the thread detaches.
1419 *
1420 * Monitors may be entered multiple times, so we add a new entry for each
1421 * enter call. It would be more efficient to keep a counter. At present
1422 * there's no real motivation to improve this however.
1423 */
1424static void trackMonitorEnter(Thread* self, Object* obj)
1425{
1426 static const int kInitialSize = 16;
1427 ReferenceTable* refTable = &self->jniMonitorRefTable;
1428
1429 /* init table on first use */
1430 if (refTable->table == NULL) {
1431 assert(refTable->maxEntries == 0);
1432
1433 if (!dvmInitReferenceTable(refTable, kInitialSize, INT_MAX)) {
1434 LOGE("Unable to initialize monitor tracking table\n");
1435 dvmAbort();
1436 }
1437 }
1438
1439 if (!dvmAddToReferenceTable(refTable, obj)) {
1440 /* ran out of memory? could throw exception instead */
1441 LOGE("Unable to add entry to monitor tracking table\n");
1442 dvmAbort();
1443 } else {
1444 LOGVV("--- added monitor %p\n", obj);
1445 }
1446}
1447
Andy McFaddenab00d452009-08-19 07:21:41 -07001448
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001449/*
1450 * Track a JNI MonitorExit in the current thread.
1451 */
1452static void trackMonitorExit(Thread* self, Object* obj)
1453{
Andy McFaddenab00d452009-08-19 07:21:41 -07001454 ReferenceTable* pRefTable = &self->jniMonitorRefTable;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001455
Andy McFaddenab00d452009-08-19 07:21:41 -07001456 if (!dvmRemoveFromReferenceTable(pRefTable, pRefTable->table, obj)) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001457 LOGE("JNI monitor %p not found in tracking list\n", obj);
1458 /* keep going? */
1459 } else {
1460 LOGVV("--- removed monitor %p\n", obj);
1461 }
1462}
1463
1464/*
1465 * Release all monitors held by the jniMonitorRefTable list.
1466 */
1467void dvmReleaseJniMonitors(Thread* self)
1468{
Andy McFaddenab00d452009-08-19 07:21:41 -07001469 ReferenceTable* pRefTable = &self->jniMonitorRefTable;
1470 Object** top = pRefTable->table;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001471
1472 if (top == NULL)
1473 return;
1474
Andy McFaddenab00d452009-08-19 07:21:41 -07001475 Object** ptr = pRefTable->nextEntry;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001476 while (--ptr >= top) {
1477 if (!dvmUnlockObject(self, *ptr)) {
1478 LOGW("Unable to unlock monitor %p at thread detach\n", *ptr);
1479 } else {
1480 LOGVV("--- detach-releasing monitor %p\n", *ptr);
1481 }
1482 }
1483
1484 /* zap it */
Andy McFaddenab00d452009-08-19 07:21:41 -07001485 pRefTable->nextEntry = pRefTable->table;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001486}
1487
Andy McFaddenb76594d2010-07-19 15:44:56 -07001488/*
1489 * Determine if the specified class can be instantiated from JNI. This
1490 * is used by AllocObject / NewObject, which are documented as throwing
1491 * an exception for abstract and interface classes, and not accepting
1492 * array classes. We also want to reject attempts to create new Class
1493 * objects, since only DefineClass should do that.
1494 */
1495static bool canAllocClass(ClassObject* clazz)
1496{
1497 if (dvmIsAbstractClass(clazz) || dvmIsInterfaceClass(clazz)) {
1498 /* JNI spec defines what this throws */
1499 dvmThrowExceptionFmt("Ljava/lang/InstantiationException;",
1500 "Can't instantiate %s (abstract or interface)", clazz->descriptor);
1501 return false;
1502 } else if (dvmIsArrayClass(clazz) || clazz == gDvm.classJavaLangClass) {
1503 /* spec says "must not" for arrays, ignores Class */
1504 dvmThrowExceptionFmt("Ljava/lang/IllegalArgumentException;",
1505 "Can't instantiate %s (array or Class) with this JNI function",
1506 clazz->descriptor);
1507 return false;
1508 }
1509
1510 return true;
1511}
1512
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001513#ifdef WITH_JNI_STACK_CHECK
1514/*
1515 * Compute a CRC on the entire interpreted stack.
1516 *
1517 * Would be nice to compute it on "self" as well, but there are parts of
1518 * the Thread that can be altered by other threads (e.g. prev/next pointers).
1519 */
1520static void computeStackSum(Thread* self)
1521{
1522 const u1* low = (const u1*)SAVEAREA_FROM_FP(self->curFrame);
1523 u4 crc = dvmInitCrc32();
1524 self->stackCrc = 0;
1525 crc = dvmComputeCrc32(crc, low, self->interpStackStart - low);
1526 self->stackCrc = crc;
1527}
1528
1529/*
1530 * Compute a CRC on the entire interpreted stack, and compare it to what
1531 * we previously computed.
1532 *
1533 * We can execute JNI directly from native code without calling in from
1534 * interpreted code during VM initialization and immediately after JNI
1535 * thread attachment. Another opportunity exists during JNI_OnLoad. Rather
1536 * than catching these cases we just ignore them here, which is marginally
1537 * less accurate but reduces the amount of code we have to touch with #ifdefs.
1538 */
1539static void checkStackSum(Thread* self)
1540{
1541 const u1* low = (const u1*)SAVEAREA_FROM_FP(self->curFrame);
1542 u4 stackCrc, crc;
1543
1544 stackCrc = self->stackCrc;
1545 self->stackCrc = 0;
1546 crc = dvmInitCrc32();
1547 crc = dvmComputeCrc32(crc, low, self->interpStackStart - low);
1548 if (crc != stackCrc) {
1549 const Method* meth = dvmGetCurrentJNIMethod();
1550 if (dvmComputeExactFrameDepth(self->curFrame) == 1) {
1551 LOGD("JNI: bad stack CRC (0x%08x) -- okay during init\n",
1552 stackCrc);
1553 } else if (strcmp(meth->name, "nativeLoad") == 0 &&
1554 (strcmp(meth->clazz->descriptor, "Ljava/lang/Runtime;") == 0))
1555 {
1556 LOGD("JNI: bad stack CRC (0x%08x) -- okay during JNI_OnLoad\n",
1557 stackCrc);
1558 } else {
1559 LOGW("JNI: bad stack CRC (%08x vs %08x)\n", crc, stackCrc);
1560 dvmAbort();
1561 }
1562 }
1563 self->stackCrc = (u4) -1; /* make logic errors more noticeable */
1564}
1565#endif
1566
1567
1568/*
1569 * ===========================================================================
Andy McFaddend5ab7262009-08-25 07:19:34 -07001570 * JNI call bridge
1571 * ===========================================================================
1572 */
1573
1574/*
1575 * The functions here form a bridge between interpreted code and JNI native
1576 * functions. The basic task is to convert an array of primitives and
1577 * references into C-style function arguments. This is architecture-specific
1578 * and usually requires help from assembly code.
1579 *
1580 * The bridge takes four arguments: the array of parameters, a place to
1581 * store the function result (if any), the method to call, and a pointer
1582 * to the current thread.
1583 *
1584 * These functions aren't called directly from elsewhere in the VM.
1585 * A pointer in the Method struct points to one of these, and when a native
1586 * method is invoked the interpreter jumps to it.
1587 *
1588 * (The "internal native" methods are invoked the same way, but instead
1589 * of calling through a bridge, the target method is called directly.)
1590 *
1591 * The "args" array should not be modified, but we do so anyway for
1592 * performance reasons. We know that it points to the "outs" area on
1593 * the current method's interpreted stack. This area is ignored by the
1594 * precise GC, because there is no register map for a native method (for
1595 * an interpreted method the args would be listed in the argument set).
1596 * We know all of the values exist elsewhere on the interpreted stack,
1597 * because the method call setup copies them right before making the call,
1598 * so we don't have to worry about concealing stuff from the GC.
1599 *
1600 * If we don't want to modify "args", we either have to create a local
1601 * copy and modify it before calling dvmPlatformInvoke, or we have to do
1602 * the local reference replacement within dvmPlatformInvoke. The latter
1603 * has some performance advantages, though if we can inline the local
1604 * reference adds we may win when there's a lot of reference args (unless
1605 * we want to code up some local ref table manipulation in assembly.
1606 */
1607
1608/*
Andy McFadden0423f0e2009-08-26 07:21:53 -07001609 * If necessary, convert the value in pResult from a local/global reference
1610 * to an object pointer.
1611 */
1612static inline void convertReferenceResult(JNIEnv* env, JValue* pResult,
1613 const Method* method, Thread* self)
1614{
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001615#ifdef USE_INDIRECT_REF
Andy McFadden0423f0e2009-08-26 07:21:53 -07001616 if (method->shorty[0] == 'L' && !dvmCheckException(self) &&
1617 pResult->l != NULL)
1618 {
1619 pResult->l = dvmDecodeIndirectRef(env, pResult->l);
1620 }
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001621#endif
Andy McFadden0423f0e2009-08-26 07:21:53 -07001622}
1623
1624/*
Andy McFaddend5ab7262009-08-25 07:19:34 -07001625 * General form, handles all cases.
1626 */
1627void dvmCallJNIMethod_general(const u4* args, JValue* pResult,
1628 const Method* method, Thread* self)
1629{
1630 int oldStatus;
1631 u4* modArgs = (u4*) args;
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001632 jclass staticMethodClass;
1633 JNIEnv* env = self->jniEnv;
Andy McFaddend5ab7262009-08-25 07:19:34 -07001634
Andy McFaddend5ab7262009-08-25 07:19:34 -07001635 //LOGI("JNI calling %p (%s.%s:%s):\n", method->insns,
1636 // method->clazz->descriptor, method->name, method->shorty);
1637
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001638#ifdef USE_INDIRECT_REF
Andy McFaddend5ab7262009-08-25 07:19:34 -07001639 /*
1640 * Walk the argument list, creating local references for appropriate
1641 * arguments.
1642 */
Andy McFaddend5ab7262009-08-25 07:19:34 -07001643 int idx = 0;
1644 if (dvmIsStaticMethod(method)) {
1645 /* add the class object we pass in */
1646 staticMethodClass = addLocalReference(env, (Object*) method->clazz);
1647 if (staticMethodClass == NULL) {
1648 assert(dvmCheckException(self));
1649 return;
1650 }
1651 } else {
1652 /* add "this" */
1653 staticMethodClass = NULL;
1654 jobject thisObj = addLocalReference(env, (Object*) modArgs[0]);
1655 if (thisObj == NULL) {
1656 assert(dvmCheckException(self));
1657 return;
1658 }
1659 modArgs[idx] = (u4) thisObj;
1660 idx = 1;
1661 }
1662
1663 const char* shorty = &method->shorty[1]; /* skip return type */
1664 while (*shorty != '\0') {
1665 switch (*shorty++) {
1666 case 'L':
1667 //LOGI(" local %d: 0x%08x\n", idx, modArgs[idx]);
1668 if (modArgs[idx] != 0) {
1669 //if (!dvmIsValidObject((Object*) modArgs[idx]))
1670 // dvmAbort();
1671 jobject argObj = addLocalReference(env, (Object*) modArgs[idx]);
1672 if (argObj == NULL) {
1673 assert(dvmCheckException(self));
1674 return;
1675 }
1676 modArgs[idx] = (u4) argObj;
1677 }
1678 break;
1679 case 'D':
1680 case 'J':
1681 idx++;
1682 break;
1683 default:
1684 /* Z B C S I -- do nothing */
1685 break;
1686 }
1687
1688 idx++;
1689 }
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001690#else
1691 staticMethodClass = dvmIsStaticMethod(method) ?
1692 (jclass) method->clazz : NULL;
1693#endif
Andy McFaddend5ab7262009-08-25 07:19:34 -07001694
1695 oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
1696
Andy McFadden1e83b4d2010-07-15 17:20:24 -07001697 ANDROID_MEMBAR_FULL(); /* guarantee ordering on method->insns */
1698 assert(method->insns != NULL);
1699
Andy McFaddend5ab7262009-08-25 07:19:34 -07001700 COMPUTE_STACK_SUM(self);
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001701 dvmPlatformInvoke(env, staticMethodClass,
Andy McFaddend5ab7262009-08-25 07:19:34 -07001702 method->jniArgInfo, method->insSize, modArgs, method->shorty,
1703 (void*)method->insns, pResult);
1704 CHECK_STACK_SUM(self);
1705
1706 dvmChangeStatus(self, oldStatus);
Andy McFadden0423f0e2009-08-26 07:21:53 -07001707
1708 convertReferenceResult(env, pResult, method, self);
Andy McFaddend5ab7262009-08-25 07:19:34 -07001709}
1710
1711/*
1712 * Handler for the unusual case of a synchronized native method.
1713 *
1714 * Lock the object, then call through the general function.
1715 */
1716void dvmCallJNIMethod_synchronized(const u4* args, JValue* pResult,
1717 const Method* method, Thread* self)
1718{
1719 Object* lockObj;
1720
1721 assert(dvmIsSynchronizedMethod(method));
1722
1723 if (dvmIsStaticMethod(method))
1724 lockObj = (Object*) method->clazz;
1725 else
1726 lockObj = (Object*) args[0];
1727
1728 LOGVV("Calling %s.%s: locking %p (%s)\n",
1729 method->clazz->descriptor, method->name,
1730 lockObj, lockObj->clazz->descriptor);
1731
1732 dvmLockObject(self, lockObj);
1733 dvmCallJNIMethod_general(args, pResult, method, self);
1734 dvmUnlockObject(self, lockObj);
1735}
1736
1737/*
1738 * Virtual method call, no reference arguments.
1739 *
1740 * We need to local-ref the "this" argument, found in args[0].
1741 */
1742void dvmCallJNIMethod_virtualNoRef(const u4* args, JValue* pResult,
1743 const Method* method, Thread* self)
1744{
1745 u4* modArgs = (u4*) args;
1746 int oldStatus;
1747
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001748#ifdef USE_INDIRECT_REF
Andy McFaddend5ab7262009-08-25 07:19:34 -07001749 jobject thisObj = addLocalReference(self->jniEnv, (Object*) args[0]);
1750 if (thisObj == NULL) {
1751 assert(dvmCheckException(self));
1752 return;
1753 }
1754 modArgs[0] = (u4) thisObj;
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001755#endif
Andy McFaddend5ab7262009-08-25 07:19:34 -07001756
1757 oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
1758
Andy McFadden1e83b4d2010-07-15 17:20:24 -07001759 ANDROID_MEMBAR_FULL(); /* guarantee ordering on method->insns */
1760
Andy McFaddend5ab7262009-08-25 07:19:34 -07001761 COMPUTE_STACK_SUM(self);
1762 dvmPlatformInvoke(self->jniEnv, NULL,
1763 method->jniArgInfo, method->insSize, modArgs, method->shorty,
1764 (void*)method->insns, pResult);
1765 CHECK_STACK_SUM(self);
1766
1767 dvmChangeStatus(self, oldStatus);
Andy McFadden0423f0e2009-08-26 07:21:53 -07001768
1769 convertReferenceResult(self->jniEnv, pResult, method, self);
Andy McFaddend5ab7262009-08-25 07:19:34 -07001770}
1771
1772/*
1773 * Static method call, no reference arguments.
1774 *
1775 * We need to local-ref the class reference.
1776 */
1777void dvmCallJNIMethod_staticNoRef(const u4* args, JValue* pResult,
1778 const Method* method, Thread* self)
1779{
1780 jclass staticMethodClass;
1781 int oldStatus;
1782
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001783#ifdef USE_INDIRECT_REF
Andy McFaddend5ab7262009-08-25 07:19:34 -07001784 staticMethodClass = addLocalReference(self->jniEnv, (Object*)method->clazz);
1785 if (staticMethodClass == NULL) {
1786 assert(dvmCheckException(self));
1787 return;
1788 }
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001789#else
1790 staticMethodClass = (jobject) method->clazz;
1791#endif
Andy McFaddend5ab7262009-08-25 07:19:34 -07001792
1793 oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
1794
Andy McFadden1e83b4d2010-07-15 17:20:24 -07001795 ANDROID_MEMBAR_FULL(); /* guarantee ordering on method->insns */
1796
Andy McFaddend5ab7262009-08-25 07:19:34 -07001797 COMPUTE_STACK_SUM(self);
1798 dvmPlatformInvoke(self->jniEnv, staticMethodClass,
1799 method->jniArgInfo, method->insSize, args, method->shorty,
1800 (void*)method->insns, pResult);
1801 CHECK_STACK_SUM(self);
1802
1803 dvmChangeStatus(self, oldStatus);
Andy McFadden0423f0e2009-08-26 07:21:53 -07001804
1805 convertReferenceResult(self->jniEnv, pResult, method, self);
Andy McFaddend5ab7262009-08-25 07:19:34 -07001806}
1807
1808/*
1809 * Extract the return type enum from the "jniArgInfo" field.
1810 */
1811DalvikJniReturnType dvmGetArgInfoReturnType(int jniArgInfo)
1812{
1813 return (jniArgInfo & DALVIK_JNI_RETURN_MASK) >> DALVIK_JNI_RETURN_SHIFT;
1814}
1815
1816
1817/*
1818 * ===========================================================================
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001819 * JNI implementation
1820 * ===========================================================================
1821 */
1822
1823/*
1824 * Return the version of the native method interface.
1825 */
1826static jint GetVersion(JNIEnv* env)
1827{
1828 JNI_ENTER();
1829 /*
1830 * There is absolutely no need to toggle the mode for correct behavior.
1831 * However, it does provide native code with a simple "suspend self
1832 * if necessary" call.
1833 */
1834 JNI_EXIT();
1835 return JNI_VERSION_1_6;
1836}
1837
1838/*
1839 * Create a new class from a bag of bytes.
1840 *
1841 * This is not currently supported within Dalvik.
1842 */
1843static jclass DefineClass(JNIEnv* env, const char *name, jobject loader,
1844 const jbyte* buf, jsize bufLen)
1845{
1846 UNUSED_PARAMETER(name);
1847 UNUSED_PARAMETER(loader);
1848 UNUSED_PARAMETER(buf);
1849 UNUSED_PARAMETER(bufLen);
1850
1851 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001852 LOGW("JNI DefineClass is not supported\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001853 JNI_EXIT();
1854 return NULL;
1855}
1856
1857/*
1858 * Find a class by name.
1859 *
1860 * We have to use the "no init" version of FindClass here, because we might
1861 * be getting the class prior to registering native methods that will be
1862 * used in <clinit>.
1863 *
1864 * We need to get the class loader associated with the current native
1865 * method. If there is no native method, e.g. we're calling this from native
1866 * code right after creating the VM, the spec says we need to use the class
1867 * loader returned by "ClassLoader.getBaseClassLoader". There is no such
1868 * method, but it's likely they meant ClassLoader.getSystemClassLoader.
1869 * We can't get that until after the VM has initialized though.
1870 */
1871static jclass FindClass(JNIEnv* env, const char* name)
1872{
1873 JNI_ENTER();
1874
1875 const Method* thisMethod;
1876 ClassObject* clazz;
Andy McFaddenab00d452009-08-19 07:21:41 -07001877 jclass jclazz = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001878 Object* loader;
1879 char* descriptor = NULL;
1880
1881 thisMethod = dvmGetCurrentJNIMethod();
1882 assert(thisMethod != NULL);
1883
1884 descriptor = dvmNameToDescriptor(name);
1885 if (descriptor == NULL) {
1886 clazz = NULL;
1887 goto bail;
1888 }
1889
1890 //Thread* self = dvmThreadSelf();
1891 if (_self->classLoaderOverride != NULL) {
1892 /* hack for JNI_OnLoad */
1893 assert(strcmp(thisMethod->name, "nativeLoad") == 0);
1894 loader = _self->classLoaderOverride;
1895 } else if (thisMethod == gDvm.methFakeNativeEntry) {
1896 /* start point of invocation interface */
1897 if (!gDvm.initializing)
1898 loader = dvmGetSystemClassLoader();
1899 else
1900 loader = NULL;
1901 } else {
1902 loader = thisMethod->clazz->classLoader;
1903 }
1904
1905 clazz = dvmFindClassNoInit(descriptor, loader);
Andy McFaddenab00d452009-08-19 07:21:41 -07001906 jclazz = addLocalReference(env, (Object*) clazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001907
1908bail:
1909 free(descriptor);
Andy McFaddenab00d452009-08-19 07:21:41 -07001910
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001911 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001912 return jclazz;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001913}
1914
1915/*
1916 * Return the superclass of a class.
1917 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001918static jclass GetSuperclass(JNIEnv* env, jclass jclazz)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001919{
1920 JNI_ENTER();
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001921 jclass jsuper = NULL;
Andy McFaddenab00d452009-08-19 07:21:41 -07001922
1923 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
Andy McFaddeneb9cbc32009-08-28 14:45:12 -07001924 if (clazz != NULL)
Andy McFaddenab00d452009-08-19 07:21:41 -07001925 jsuper = addLocalReference(env, (Object*)clazz->super);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001926 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001927 return jsuper;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001928}
1929
1930/*
1931 * Determine whether an object of clazz1 can be safely cast to clazz2.
1932 *
1933 * Like IsInstanceOf, but with a pair of class objects instead of obj+class.
1934 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001935static jboolean IsAssignableFrom(JNIEnv* env, jclass jclazz1, jclass jclazz2)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001936{
1937 JNI_ENTER();
1938
Andy McFaddenab00d452009-08-19 07:21:41 -07001939 ClassObject* clazz1 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz1);
1940 ClassObject* clazz2 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz2);
1941
1942 jboolean result = dvmInstanceof(clazz1, clazz2);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001943
1944 JNI_EXIT();
1945 return result;
1946}
1947
1948/*
1949 * Given a java.lang.reflect.Method or .Constructor, return a methodID.
1950 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001951static jmethodID FromReflectedMethod(JNIEnv* env, jobject jmethod)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001952{
1953 JNI_ENTER();
1954 jmethodID methodID;
Andy McFaddenab00d452009-08-19 07:21:41 -07001955 Object* method = dvmDecodeIndirectRef(env, jmethod);
1956 methodID = (jmethodID) dvmGetMethodFromReflectObj(method);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001957 JNI_EXIT();
1958 return methodID;
1959}
1960
1961/*
1962 * Given a java.lang.reflect.Field, return a fieldID.
1963 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001964static jfieldID FromReflectedField(JNIEnv* env, jobject jfield)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001965{
1966 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001967 jfieldID fieldID;
1968 Object* field = dvmDecodeIndirectRef(env, jfield);
1969 fieldID = (jfieldID) dvmGetFieldFromReflectObj(field);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001970 JNI_EXIT();
1971 return fieldID;
1972}
1973
1974/*
1975 * Convert a methodID to a java.lang.reflect.Method or .Constructor.
1976 *
1977 * (The "isStatic" field does not appear in the spec.)
1978 *
1979 * Throws OutOfMemory and returns NULL on failure.
1980 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001981static jobject ToReflectedMethod(JNIEnv* env, jclass jcls, jmethodID methodID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001982 jboolean isStatic)
1983{
1984 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001985 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls);
1986 Object* obj = dvmCreateReflectObjForMethod(clazz, (Method*) methodID);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001987 dvmReleaseTrackedAlloc(obj, NULL);
Andy McFaddenab00d452009-08-19 07:21:41 -07001988 jobject jobj = addLocalReference(env, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001989 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001990 return jobj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001991}
1992
1993/*
1994 * Convert a fieldID to a java.lang.reflect.Field.
1995 *
1996 * (The "isStatic" field does not appear in the spec.)
1997 *
1998 * Throws OutOfMemory and returns NULL on failure.
1999 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002000static jobject ToReflectedField(JNIEnv* env, jclass jcls, jfieldID fieldID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002001 jboolean isStatic)
2002{
2003 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002004 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls);
Carl Shapiroe3c01da2010-05-20 22:54:18 -07002005 Object* obj = dvmCreateReflectObjForField(clazz, (Field*) fieldID);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002006 dvmReleaseTrackedAlloc(obj, NULL);
Andy McFaddenab00d452009-08-19 07:21:41 -07002007 jobject jobj = addLocalReference(env, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002008 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002009 return jobj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002010}
2011
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002012/*
2013 * Take this exception and throw it.
2014 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002015static jint Throw(JNIEnv* env, jthrowable jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002016{
2017 JNI_ENTER();
2018
2019 jint retval;
2020
Andy McFaddenab00d452009-08-19 07:21:41 -07002021 if (jobj != NULL) {
2022 Object* obj = dvmDecodeIndirectRef(env, jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002023 dvmSetException(_self, obj);
2024 retval = JNI_OK;
Andy McFaddenab00d452009-08-19 07:21:41 -07002025 } else {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002026 retval = JNI_ERR;
Andy McFaddenab00d452009-08-19 07:21:41 -07002027 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002028
2029 JNI_EXIT();
2030 return retval;
2031}
2032
2033/*
Andy McFaddenab00d452009-08-19 07:21:41 -07002034 * Constructs an exception object from the specified class with the message
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002035 * specified by "message", and throws it.
2036 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002037static jint ThrowNew(JNIEnv* env, jclass jclazz, const char* message)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002038{
2039 JNI_ENTER();
2040
Andy McFaddenab00d452009-08-19 07:21:41 -07002041 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2042 dvmThrowExceptionByClass(clazz, message);
2043 // TODO: should return failure if this didn't work (e.g. OOM)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002044
2045 JNI_EXIT();
2046 return JNI_OK;
2047}
2048
2049/*
2050 * If an exception is being thrown, return the exception object. Otherwise,
2051 * return NULL.
2052 *
2053 * TODO: if there is no pending exception, we should be able to skip the
2054 * enter/exit checks. If we find one, we need to enter and then re-fetch
2055 * the exception (in case it got moved by a compacting GC).
2056 */
2057static jthrowable ExceptionOccurred(JNIEnv* env)
2058{
2059 JNI_ENTER();
2060
2061 Object* exception;
Andy McFaddenab00d452009-08-19 07:21:41 -07002062 jobject localException;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002063
Andy McFaddenab00d452009-08-19 07:21:41 -07002064 exception = dvmGetException(_self);
2065 localException = addLocalReference(env, exception);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002066 if (localException == NULL && exception != NULL) {
2067 /*
2068 * We were unable to add a new local reference, and threw a new
2069 * exception. We can't return "exception", because it's not a
2070 * local reference. So we have to return NULL, indicating that
2071 * there was no exception, even though it's pretty much raining
2072 * exceptions in here.
2073 */
2074 LOGW("JNI WARNING: addLocal/exception combo\n");
2075 }
2076
2077 JNI_EXIT();
2078 return localException;
2079}
2080
2081/*
2082 * Print an exception and stack trace to stderr.
2083 */
2084static void ExceptionDescribe(JNIEnv* env)
2085{
2086 JNI_ENTER();
2087
2088 Object* exception = dvmGetException(_self);
2089 if (exception != NULL) {
2090 dvmPrintExceptionStackTrace();
2091 } else {
2092 LOGI("Odd: ExceptionDescribe called, but no exception pending\n");
2093 }
2094
2095 JNI_EXIT();
2096}
2097
2098/*
2099 * Clear the exception currently being thrown.
2100 *
2101 * TODO: we should be able to skip the enter/exit stuff.
2102 */
2103static void ExceptionClear(JNIEnv* env)
2104{
2105 JNI_ENTER();
2106 dvmClearException(_self);
2107 JNI_EXIT();
2108}
2109
2110/*
2111 * Kill the VM. This function does not return.
2112 */
2113static void FatalError(JNIEnv* env, const char* msg)
2114{
2115 //dvmChangeStatus(NULL, THREAD_RUNNING);
2116 LOGE("JNI posting fatal error: %s\n", msg);
2117 dvmAbort();
2118}
2119
2120/*
2121 * Push a new JNI frame on the stack, with a new set of locals.
2122 *
2123 * The new frame must have the same method pointer. (If for no other
2124 * reason than FindClass needs it to get the appropriate class loader.)
2125 */
2126static jint PushLocalFrame(JNIEnv* env, jint capacity)
2127{
2128 JNI_ENTER();
2129 int result = JNI_OK;
Andy McFaddenab00d452009-08-19 07:21:41 -07002130 if (!ensureLocalCapacity(env, capacity) ||
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002131 !dvmPushLocalFrame(_self /*dvmThreadSelf()*/, dvmGetCurrentJNIMethod()))
2132 {
2133 /* yes, OutOfMemoryError, not StackOverflowError */
2134 dvmClearException(_self);
2135 dvmThrowException("Ljava/lang/OutOfMemoryError;",
2136 "out of stack in JNI PushLocalFrame");
2137 result = JNI_ERR;
2138 }
2139 JNI_EXIT();
2140 return result;
2141}
2142
2143/*
2144 * Pop the local frame off. If "result" is not null, add it as a
2145 * local reference on the now-current frame.
2146 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002147static jobject PopLocalFrame(JNIEnv* env, jobject jresult)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002148{
2149 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002150 Object* result = dvmDecodeIndirectRef(env, jresult);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002151 if (!dvmPopLocalFrame(_self /*dvmThreadSelf()*/)) {
2152 LOGW("JNI WARNING: too many PopLocalFrame calls\n");
2153 dvmClearException(_self);
2154 dvmThrowException("Ljava/lang/RuntimeException;",
2155 "too many PopLocalFrame calls");
2156 }
Andy McFaddenab00d452009-08-19 07:21:41 -07002157 jresult = addLocalReference(env, result);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002158 JNI_EXIT();
2159 return result;
2160}
2161
2162/*
2163 * Add a reference to the global list.
2164 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002165static jobject NewGlobalRef(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002166{
Andy McFaddenb18992f2009-09-25 10:42:15 -07002167 Object* obj;
2168
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002169 JNI_ENTER();
Andy McFaddenb18992f2009-09-25 10:42:15 -07002170 if (dvmIsWeakGlobalRef(jobj))
2171 obj = getPhantomReferent(env, (jweak) jobj);
2172 else
2173 obj = dvmDecodeIndirectRef(env, jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002174 jobject retval = addGlobalReference(obj);
2175 JNI_EXIT();
2176 return retval;
2177}
2178
2179/*
2180 * Delete a reference from the global list.
2181 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002182static void DeleteGlobalRef(JNIEnv* env, jobject jglobalRef)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002183{
2184 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002185 deleteGlobalReference(jglobalRef);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002186 JNI_EXIT();
2187}
2188
2189
2190/*
2191 * Add a reference to the local list.
2192 */
Andy McFaddenb18992f2009-09-25 10:42:15 -07002193static jobject NewLocalRef(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002194{
Andy McFaddenb18992f2009-09-25 10:42:15 -07002195 Object* obj;
2196
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002197 JNI_ENTER();
Andy McFaddenb18992f2009-09-25 10:42:15 -07002198 if (dvmIsWeakGlobalRef(jobj))
2199 obj = getPhantomReferent(env, (jweak) jobj);
2200 else
2201 obj = dvmDecodeIndirectRef(env, jobj);
Andy McFaddenab00d452009-08-19 07:21:41 -07002202 jobject retval = addLocalReference(env, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002203 JNI_EXIT();
2204 return retval;
2205}
2206
2207/*
2208 * Delete a reference from the local list.
2209 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002210static void DeleteLocalRef(JNIEnv* env, jobject jlocalRef)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002211{
2212 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002213 deleteLocalReference(env, jlocalRef);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002214 JNI_EXIT();
2215}
2216
2217/*
2218 * Ensure that the local references table can hold at least this many
2219 * references.
2220 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002221static jint EnsureLocalCapacity(JNIEnv* env, jint capacity)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002222{
2223 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002224 bool okay = ensureLocalCapacity(env, capacity);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002225 if (!okay) {
2226 dvmThrowException("Ljava/lang/OutOfMemoryError;",
2227 "can't ensure local reference capacity");
2228 }
2229 JNI_EXIT();
2230 if (okay)
2231 return 0;
2232 else
2233 return -1;
2234}
2235
2236
2237/*
2238 * Determine whether two Object references refer to the same underlying object.
2239 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002240static jboolean IsSameObject(JNIEnv* env, jobject jref1, jobject jref2)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002241{
2242 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002243 Object* obj1 = dvmDecodeIndirectRef(env, jref1);
2244 Object* obj2 = dvmDecodeIndirectRef(env, jref2);
2245 jboolean result = (obj1 == obj2);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002246 JNI_EXIT();
2247 return result;
2248}
2249
2250/*
2251 * Allocate a new object without invoking any constructors.
2252 */
2253static jobject AllocObject(JNIEnv* env, jclass jclazz)
2254{
2255 JNI_ENTER();
2256
Andy McFaddenab00d452009-08-19 07:21:41 -07002257 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2258 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002259
Andy McFaddenb76594d2010-07-19 15:44:56 -07002260 if (!canAllocClass(clazz) ||
2261 (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)))
2262 {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002263 assert(dvmCheckException(_self));
Andy McFaddenab00d452009-08-19 07:21:41 -07002264 result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002265 } else {
Andy McFaddenab00d452009-08-19 07:21:41 -07002266 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
2267 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002268 }
2269
2270 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002271 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002272}
2273
2274/*
Andy McFaddenab00d452009-08-19 07:21:41 -07002275 * Allocate a new object and invoke the supplied constructor.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002276 */
2277static jobject NewObject(JNIEnv* env, jclass jclazz, jmethodID methodID, ...)
2278{
2279 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002280 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2281 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002282
Andy McFaddenb76594d2010-07-19 15:44:56 -07002283 if (!canAllocClass(clazz) ||
2284 (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)))
2285 {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002286 assert(dvmCheckException(_self));
Andy McFaddenab00d452009-08-19 07:21:41 -07002287 result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002288 } else {
Andy McFaddenab00d452009-08-19 07:21:41 -07002289 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
2290 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002291 if (newObj != NULL) {
2292 JValue unused;
2293 va_list args;
2294 va_start(args, methodID);
Andy McFaddend5ab7262009-08-25 07:19:34 -07002295 dvmCallMethodV(_self, (Method*) methodID, newObj, true, &unused,
2296 args);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002297 va_end(args);
2298 }
2299 }
2300
2301 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002302 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002303}
Andy McFaddenab00d452009-08-19 07:21:41 -07002304static jobject NewObjectV(JNIEnv* env, jclass jclazz, jmethodID methodID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002305 va_list args)
2306{
2307 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002308 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2309 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002310
Andy McFaddenb76594d2010-07-19 15:44:56 -07002311 if (!canAllocClass(clazz) ||
2312 (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)))
2313 {
2314 assert(dvmCheckException(_self));
2315 result = NULL;
2316 } else {
2317 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
2318 result = addLocalReference(env, newObj);
2319 if (newObj != NULL) {
2320 JValue unused;
2321 dvmCallMethodV(_self, (Method*) methodID, newObj, true, &unused,
2322 args);
2323 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002324 }
2325
2326 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002327 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002328}
Andy McFaddenab00d452009-08-19 07:21:41 -07002329static jobject NewObjectA(JNIEnv* env, jclass jclazz, jmethodID methodID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002330 jvalue* args)
2331{
2332 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002333 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2334 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002335
Andy McFaddenb76594d2010-07-19 15:44:56 -07002336 if (!canAllocClass(clazz) ||
2337 (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)))
2338 {
2339 assert(dvmCheckException(_self));
2340 result = NULL;
2341 } else {
2342 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
2343 result = addLocalReference(env, newObj);
2344 if (newObj != NULL) {
2345 JValue unused;
2346 dvmCallMethodA(_self, (Method*) methodID, newObj, true, &unused,
2347 args);
2348 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002349 }
2350
2351 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002352 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002353}
2354
2355/*
2356 * Returns the class of an object.
2357 *
2358 * JNI spec says: obj must not be NULL.
2359 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002360static jclass GetObjectClass(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002361{
2362 JNI_ENTER();
2363
Andy McFaddenab00d452009-08-19 07:21:41 -07002364 assert(jobj != NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002365
Andy McFaddenab00d452009-08-19 07:21:41 -07002366 Object* obj = dvmDecodeIndirectRef(env, jobj);
2367 jclass jclazz = addLocalReference(env, (Object*) obj->clazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002368
2369 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002370 return jclazz;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002371}
2372
2373/*
2374 * Determine whether "obj" is an instance of "clazz".
2375 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002376static jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass jclazz)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002377{
2378 JNI_ENTER();
2379
Andy McFaddenab00d452009-08-19 07:21:41 -07002380 assert(jclazz != NULL);
2381
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002382 jboolean result;
2383
Andy McFaddenab00d452009-08-19 07:21:41 -07002384 if (jobj == NULL) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002385 result = true;
Andy McFaddenab00d452009-08-19 07:21:41 -07002386 } else {
2387 Object* obj = dvmDecodeIndirectRef(env, jobj);
2388 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
2389 result = dvmInstanceof(obj->clazz, clazz);
2390 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002391
2392 JNI_EXIT();
2393 return result;
2394}
2395
2396/*
2397 * Get a method ID for an instance method.
2398 *
2399 * JNI defines <init> as an instance method, but Dalvik considers it a
2400 * "direct" method, so we have to special-case it here.
2401 *
2402 * Dalvik also puts all private methods into the "direct" list, so we
2403 * really need to just search both lists.
2404 */
2405static jmethodID GetMethodID(JNIEnv* env, jclass jclazz, const char* name,
2406 const char* sig)
2407{
2408 JNI_ENTER();
2409
Andy McFaddenab00d452009-08-19 07:21:41 -07002410 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002411 jmethodID id = NULL;
2412
2413 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2414 assert(dvmCheckException(_self));
2415 } else {
2416 Method* meth;
2417
2418 meth = dvmFindVirtualMethodHierByDescriptor(clazz, name, sig);
2419 if (meth == NULL) {
2420 /* search private methods and constructors; non-hierarchical */
2421 meth = dvmFindDirectMethodByDescriptor(clazz, name, sig);
2422 }
2423 if (meth != NULL && dvmIsStaticMethod(meth)) {
2424 IF_LOGD() {
2425 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
2426 LOGD("GetMethodID: not returning static method %s.%s %s\n",
2427 clazz->descriptor, meth->name, desc);
2428 free(desc);
2429 }
2430 meth = NULL;
2431 }
2432 if (meth == NULL) {
Andy McFadden03bd0d52009-08-18 15:32:27 -07002433 LOGD("GetMethodID: method not found: %s.%s:%s\n",
2434 clazz->descriptor, name, sig);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002435 dvmThrowException("Ljava/lang/NoSuchMethodError;", name);
2436 }
2437
2438 /*
2439 * The method's class may not be the same as clazz, but if
2440 * it isn't this must be a virtual method and the class must
2441 * be a superclass (and, hence, already initialized).
2442 */
2443 if (meth != NULL) {
2444 assert(dvmIsClassInitialized(meth->clazz) ||
2445 dvmIsClassInitializing(meth->clazz));
2446 }
2447 id = (jmethodID) meth;
2448 }
2449 JNI_EXIT();
2450 return id;
2451}
2452
2453/*
2454 * Get a field ID (instance fields).
2455 */
2456static jfieldID GetFieldID(JNIEnv* env, jclass jclazz,
2457 const char* name, const char* sig)
2458{
2459 JNI_ENTER();
2460
Andy McFaddenab00d452009-08-19 07:21:41 -07002461 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002462 jfieldID id;
2463
2464 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2465 assert(dvmCheckException(_self));
2466 id = NULL;
2467 } else {
2468 id = (jfieldID) dvmFindInstanceFieldHier(clazz, name, sig);
Andy McFadden03bd0d52009-08-18 15:32:27 -07002469 if (id == NULL) {
2470 LOGD("GetFieldID: unable to find field %s.%s:%s\n",
2471 clazz->descriptor, name, sig);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002472 dvmThrowException("Ljava/lang/NoSuchFieldError;", name);
Andy McFadden03bd0d52009-08-18 15:32:27 -07002473 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002474 }
2475 JNI_EXIT();
2476 return id;
2477}
2478
2479/*
2480 * Get the method ID for a static method in a class.
2481 */
2482static jmethodID GetStaticMethodID(JNIEnv* env, jclass jclazz,
2483 const char* name, const char* sig)
2484{
2485 JNI_ENTER();
2486
Andy McFaddenab00d452009-08-19 07:21:41 -07002487 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002488 jmethodID id;
2489
2490 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2491 assert(dvmCheckException(_self));
2492 id = NULL;
2493 } else {
2494 Method* meth;
2495
2496 meth = dvmFindDirectMethodHierByDescriptor(clazz, name, sig);
2497
2498 /* make sure it's static, not virtual+private */
2499 if (meth != NULL && !dvmIsStaticMethod(meth)) {
2500 IF_LOGD() {
2501 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
2502 LOGD("GetStaticMethodID: "
2503 "not returning nonstatic method %s.%s %s\n",
2504 clazz->descriptor, meth->name, desc);
2505 free(desc);
2506 }
2507 meth = NULL;
2508 }
2509
2510 id = (jmethodID) meth;
2511 if (id == NULL)
2512 dvmThrowException("Ljava/lang/NoSuchMethodError;", name);
2513 }
2514
2515 JNI_EXIT();
2516 return id;
2517}
2518
2519/*
2520 * Get a field ID (static fields).
2521 */
2522static jfieldID GetStaticFieldID(JNIEnv* env, jclass jclazz,
2523 const char* name, const char* sig)
2524{
2525 JNI_ENTER();
2526
Andy McFaddenab00d452009-08-19 07:21:41 -07002527 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002528 jfieldID id;
2529
2530 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2531 assert(dvmCheckException(_self));
2532 id = NULL;
2533 } else {
2534 id = (jfieldID) dvmFindStaticField(clazz, name, sig);
2535 if (id == NULL)
2536 dvmThrowException("Ljava/lang/NoSuchFieldError;", name);
2537 }
2538 JNI_EXIT();
2539 return id;
2540}
2541
2542/*
2543 * Get a static field.
2544 *
2545 * If we get an object reference, add it to the local refs list.
2546 */
2547#define GET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002548 static _ctype GetStatic##_jname##Field(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002549 jfieldID fieldID) \
2550 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002551 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002552 JNI_ENTER(); \
2553 StaticField* sfield = (StaticField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002554 _ctype value; \
2555 if (_isref) { /* only when _ctype==jobject */ \
2556 Object* obj = dvmGetStaticFieldObject(sfield); \
2557 value = (_ctype)(u4)addLocalReference(env, obj); \
2558 } else { \
2559 value = dvmGetStaticField##_jname(sfield); \
2560 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002561 JNI_EXIT(); \
2562 return value; \
2563 }
2564GET_STATIC_TYPE_FIELD(jobject, Object, true);
2565GET_STATIC_TYPE_FIELD(jboolean, Boolean, false);
2566GET_STATIC_TYPE_FIELD(jbyte, Byte, false);
2567GET_STATIC_TYPE_FIELD(jchar, Char, false);
2568GET_STATIC_TYPE_FIELD(jshort, Short, false);
2569GET_STATIC_TYPE_FIELD(jint, Int, false);
2570GET_STATIC_TYPE_FIELD(jlong, Long, false);
2571GET_STATIC_TYPE_FIELD(jfloat, Float, false);
2572GET_STATIC_TYPE_FIELD(jdouble, Double, false);
2573
2574/*
2575 * Set a static field.
2576 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002577#define SET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) \
2578 static void SetStatic##_jname##Field(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002579 jfieldID fieldID, _ctype value) \
2580 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002581 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002582 JNI_ENTER(); \
2583 StaticField* sfield = (StaticField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002584 if (_isref) { /* only when _ctype==jobject */ \
2585 Object* valObj = dvmDecodeIndirectRef(env, (jobject)(u4)value); \
2586 dvmSetStaticFieldObject(sfield, valObj); \
2587 } else { \
2588 dvmSetStaticField##_jname(sfield, value); \
2589 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002590 JNI_EXIT(); \
2591 }
Andy McFaddenab00d452009-08-19 07:21:41 -07002592SET_STATIC_TYPE_FIELD(jobject, Object, true);
2593SET_STATIC_TYPE_FIELD(jboolean, Boolean, false);
2594SET_STATIC_TYPE_FIELD(jbyte, Byte, false);
2595SET_STATIC_TYPE_FIELD(jchar, Char, false);
2596SET_STATIC_TYPE_FIELD(jshort, Short, false);
2597SET_STATIC_TYPE_FIELD(jint, Int, false);
2598SET_STATIC_TYPE_FIELD(jlong, Long, false);
2599SET_STATIC_TYPE_FIELD(jfloat, Float, false);
2600SET_STATIC_TYPE_FIELD(jdouble, Double, false);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002601
2602/*
2603 * Get an instance field.
2604 *
2605 * If we get an object reference, add it to the local refs list.
2606 */
2607#define GET_TYPE_FIELD(_ctype, _jname, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002608 static _ctype Get##_jname##Field(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002609 jfieldID fieldID) \
2610 { \
2611 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002612 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002613 InstField* field = (InstField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002614 _ctype value; \
2615 if (_isref) { /* only when _ctype==jobject */ \
2616 Object* valObj = dvmGetFieldObject(obj, field->byteOffset); \
2617 value = (_ctype)(u4)addLocalReference(env, valObj); \
2618 } else { \
2619 value = dvmGetField##_jname(obj, field->byteOffset); \
2620 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002621 JNI_EXIT(); \
2622 return value; \
2623 }
2624GET_TYPE_FIELD(jobject, Object, true);
2625GET_TYPE_FIELD(jboolean, Boolean, false);
2626GET_TYPE_FIELD(jbyte, Byte, false);
2627GET_TYPE_FIELD(jchar, Char, false);
2628GET_TYPE_FIELD(jshort, Short, false);
2629GET_TYPE_FIELD(jint, Int, false);
2630GET_TYPE_FIELD(jlong, Long, false);
2631GET_TYPE_FIELD(jfloat, Float, false);
2632GET_TYPE_FIELD(jdouble, Double, false);
2633
2634/*
2635 * Set an instance field.
2636 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002637#define SET_TYPE_FIELD(_ctype, _jname, _isref) \
2638 static void Set##_jname##Field(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002639 jfieldID fieldID, _ctype value) \
2640 { \
2641 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002642 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002643 InstField* field = (InstField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002644 if (_isref) { /* only when _ctype==jobject */ \
2645 Object* valObj = dvmDecodeIndirectRef(env, (jobject)(u4)value); \
2646 dvmSetFieldObject(obj, field->byteOffset, valObj); \
2647 } else { \
2648 dvmSetField##_jname(obj, field->byteOffset, value); \
2649 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002650 JNI_EXIT(); \
2651 }
Andy McFaddenab00d452009-08-19 07:21:41 -07002652SET_TYPE_FIELD(jobject, Object, true);
2653SET_TYPE_FIELD(jboolean, Boolean, false);
2654SET_TYPE_FIELD(jbyte, Byte, false);
2655SET_TYPE_FIELD(jchar, Char, false);
2656SET_TYPE_FIELD(jshort, Short, false);
2657SET_TYPE_FIELD(jint, Int, false);
2658SET_TYPE_FIELD(jlong, Long, false);
2659SET_TYPE_FIELD(jfloat, Float, false);
2660SET_TYPE_FIELD(jdouble, Double, false);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002661
2662/*
2663 * Make a virtual method call.
2664 *
2665 * Three versions (..., va_list, jvalue[]) for each return type. If we're
2666 * returning an Object, we have to add it to the local references table.
2667 */
2668#define CALL_VIRTUAL(_ctype, _jname, _retfail, _retok, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002669 static _ctype Call##_jname##Method(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002670 jmethodID methodID, ...) \
2671 { \
2672 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002673 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002674 const Method* meth; \
2675 va_list args; \
2676 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002677 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002678 if (meth == NULL) { \
2679 JNI_EXIT(); \
2680 return _retfail; \
2681 } \
2682 va_start(args, methodID); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002683 dvmCallMethodV(_self, meth, obj, true, &result, args); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002684 va_end(args); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002685 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002686 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002687 JNI_EXIT(); \
2688 return _retok; \
2689 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002690 static _ctype Call##_jname##MethodV(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002691 jmethodID methodID, va_list args) \
2692 { \
2693 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002694 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002695 const Method* meth; \
2696 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002697 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002698 if (meth == NULL) { \
2699 JNI_EXIT(); \
2700 return _retfail; \
2701 } \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002702 dvmCallMethodV(_self, meth, obj, true, &result, args); \
2703 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002704 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002705 JNI_EXIT(); \
2706 return _retok; \
2707 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002708 static _ctype Call##_jname##MethodA(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002709 jmethodID methodID, jvalue* args) \
2710 { \
2711 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002712 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002713 const Method* meth; \
2714 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002715 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002716 if (meth == NULL) { \
2717 JNI_EXIT(); \
2718 return _retfail; \
2719 } \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002720 dvmCallMethodA(_self, meth, obj, true, &result, args); \
2721 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002722 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002723 JNI_EXIT(); \
2724 return _retok; \
2725 }
2726CALL_VIRTUAL(jobject, Object, NULL, result.l, true);
2727CALL_VIRTUAL(jboolean, Boolean, 0, result.z, false);
2728CALL_VIRTUAL(jbyte, Byte, 0, result.b, false);
2729CALL_VIRTUAL(jchar, Char, 0, result.c, false);
2730CALL_VIRTUAL(jshort, Short, 0, result.s, false);
2731CALL_VIRTUAL(jint, Int, 0, result.i, false);
2732CALL_VIRTUAL(jlong, Long, 0, result.j, false);
2733CALL_VIRTUAL(jfloat, Float, 0.0f, result.f, false);
2734CALL_VIRTUAL(jdouble, Double, 0.0, result.d, false);
2735CALL_VIRTUAL(void, Void, , , false);
2736
2737/*
2738 * Make a "non-virtual" method call. We're still calling a virtual method,
2739 * but this time we're not doing an indirection through the object's vtable.
2740 * The "clazz" parameter defines which implementation of a method we want.
2741 *
2742 * Three versions (..., va_list, jvalue[]) for each return type.
2743 */
2744#define CALL_NONVIRTUAL(_ctype, _jname, _retfail, _retok, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002745 static _ctype CallNonvirtual##_jname##Method(JNIEnv* env, jobject jobj, \
2746 jclass jclazz, jmethodID methodID, ...) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002747 { \
2748 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002749 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2750 ClassObject* clazz = \
2751 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002752 const Method* meth; \
2753 va_list args; \
2754 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002755 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002756 if (meth == NULL) { \
2757 JNI_EXIT(); \
2758 return _retfail; \
2759 } \
2760 va_start(args, methodID); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002761 dvmCallMethodV(_self, meth, obj, true, &result, args); \
2762 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002763 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002764 va_end(args); \
2765 JNI_EXIT(); \
2766 return _retok; \
2767 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002768 static _ctype CallNonvirtual##_jname##MethodV(JNIEnv* env, jobject jobj,\
2769 jclass jclazz, jmethodID methodID, va_list args) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002770 { \
2771 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002772 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2773 ClassObject* clazz = \
2774 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002775 const Method* meth; \
2776 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002777 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002778 if (meth == NULL) { \
2779 JNI_EXIT(); \
2780 return _retfail; \
2781 } \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002782 dvmCallMethodV(_self, meth, obj, true, &result, args); \
2783 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002784 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002785 JNI_EXIT(); \
2786 return _retok; \
2787 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002788 static _ctype CallNonvirtual##_jname##MethodA(JNIEnv* env, jobject jobj,\
2789 jclass jclazz, jmethodID methodID, jvalue* args) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002790 { \
2791 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002792 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2793 ClassObject* clazz = \
2794 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002795 const Method* meth; \
2796 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002797 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002798 if (meth == NULL) { \
2799 JNI_EXIT(); \
2800 return _retfail; \
2801 } \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002802 dvmCallMethodA(_self, meth, obj, true, &result, args); \
2803 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002804 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002805 JNI_EXIT(); \
2806 return _retok; \
2807 }
2808CALL_NONVIRTUAL(jobject, Object, NULL, result.l, true);
2809CALL_NONVIRTUAL(jboolean, Boolean, 0, result.z, false);
2810CALL_NONVIRTUAL(jbyte, Byte, 0, result.b, false);
2811CALL_NONVIRTUAL(jchar, Char, 0, result.c, false);
2812CALL_NONVIRTUAL(jshort, Short, 0, result.s, false);
2813CALL_NONVIRTUAL(jint, Int, 0, result.i, false);
2814CALL_NONVIRTUAL(jlong, Long, 0, result.j, false);
2815CALL_NONVIRTUAL(jfloat, Float, 0.0f, result.f, false);
2816CALL_NONVIRTUAL(jdouble, Double, 0.0, result.d, false);
2817CALL_NONVIRTUAL(void, Void, , , false);
2818
2819
2820/*
2821 * Call a static method.
2822 */
2823#define CALL_STATIC(_ctype, _jname, _retfail, _retok, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002824 static _ctype CallStatic##_jname##Method(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002825 jmethodID methodID, ...) \
2826 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002827 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002828 JNI_ENTER(); \
2829 JValue result; \
2830 va_list args; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002831 va_start(args, methodID); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002832 dvmCallMethodV(_self, (Method*)methodID, NULL, true, &result, args);\
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002833 va_end(args); \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002834 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002835 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002836 JNI_EXIT(); \
2837 return _retok; \
2838 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002839 static _ctype CallStatic##_jname##MethodV(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002840 jmethodID methodID, va_list args) \
2841 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002842 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002843 JNI_ENTER(); \
2844 JValue result; \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002845 dvmCallMethodV(_self, (Method*)methodID, NULL, true, &result, args);\
2846 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002847 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002848 JNI_EXIT(); \
2849 return _retok; \
2850 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002851 static _ctype CallStatic##_jname##MethodA(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002852 jmethodID methodID, jvalue* args) \
2853 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002854 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002855 JNI_ENTER(); \
2856 JValue result; \
Andy McFaddend5ab7262009-08-25 07:19:34 -07002857 dvmCallMethodA(_self, (Method*)methodID, NULL, true, &result, args);\
2858 if (_isref && !dvmCheckException(_self)) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002859 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002860 JNI_EXIT(); \
2861 return _retok; \
2862 }
2863CALL_STATIC(jobject, Object, NULL, result.l, true);
2864CALL_STATIC(jboolean, Boolean, 0, result.z, false);
2865CALL_STATIC(jbyte, Byte, 0, result.b, false);
2866CALL_STATIC(jchar, Char, 0, result.c, false);
2867CALL_STATIC(jshort, Short, 0, result.s, false);
2868CALL_STATIC(jint, Int, 0, result.i, false);
2869CALL_STATIC(jlong, Long, 0, result.j, false);
2870CALL_STATIC(jfloat, Float, 0.0f, result.f, false);
2871CALL_STATIC(jdouble, Double, 0.0, result.d, false);
2872CALL_STATIC(void, Void, , , false);
2873
2874/*
2875 * Create a new String from Unicode data.
2876 *
2877 * If "len" is zero, we will return an empty string even if "unicodeChars"
2878 * is NULL. (The JNI spec is vague here.)
2879 */
2880static jstring NewString(JNIEnv* env, const jchar* unicodeChars, jsize len)
2881{
2882 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002883 jobject retval;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002884
Andy McFaddenab00d452009-08-19 07:21:41 -07002885 StringObject* jstr = dvmCreateStringFromUnicode(unicodeChars, len);
2886 if (jstr == NULL) {
2887 retval = NULL;
2888 } else {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002889 dvmReleaseTrackedAlloc((Object*) jstr, NULL);
Andy McFaddenab00d452009-08-19 07:21:41 -07002890 retval = addLocalReference(env, (Object*) jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002891 }
2892
2893 JNI_EXIT();
Andy McFadden0423f0e2009-08-26 07:21:53 -07002894 return retval;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002895}
2896
2897/*
2898 * Return the length of a String in Unicode character units.
2899 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002900static jsize GetStringLength(JNIEnv* env, jstring jstr)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002901{
2902 JNI_ENTER();
2903
Andy McFaddenab00d452009-08-19 07:21:41 -07002904 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2905 jsize len = dvmStringLen(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002906
2907 JNI_EXIT();
2908 return len;
2909}
2910
Andy McFaddenab00d452009-08-19 07:21:41 -07002911
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002912/*
Andy McFaddenab00d452009-08-19 07:21:41 -07002913 * Get a string's character data.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002914 *
2915 * The result is guaranteed to be valid until ReleaseStringChars is
Andy McFaddenab00d452009-08-19 07:21:41 -07002916 * called, which means we have to pin it or return a copy.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002917 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002918static const jchar* GetStringChars(JNIEnv* env, jstring jstr, jboolean* isCopy)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002919{
2920 JNI_ENTER();
2921
Andy McFaddenab00d452009-08-19 07:21:41 -07002922 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
Andy McFaddend5ab7262009-08-25 07:19:34 -07002923 ArrayObject* strChars = dvmStringCharArray(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002924
Andy McFaddenab00d452009-08-19 07:21:41 -07002925 pinPrimitiveArray(strChars);
2926
2927 const u2* data = dvmStringChars(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002928 if (isCopy != NULL)
2929 *isCopy = JNI_FALSE;
2930
2931 JNI_EXIT();
2932 return (jchar*)data;
2933}
2934
2935/*
2936 * Release our grip on some characters from a string.
2937 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002938static void ReleaseStringChars(JNIEnv* env, jstring jstr, const jchar* chars)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002939{
2940 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002941 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
Andy McFaddend5ab7262009-08-25 07:19:34 -07002942 ArrayObject* strChars = dvmStringCharArray(strObj);
Andy McFaddenab00d452009-08-19 07:21:41 -07002943 unpinPrimitiveArray(strChars);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002944 JNI_EXIT();
2945}
2946
2947/*
2948 * Create a new java.lang.String object from chars in modified UTF-8 form.
2949 *
2950 * The spec doesn't say how to handle a NULL string. Popular desktop VMs
2951 * accept it and return a NULL pointer in response.
2952 */
2953static jstring NewStringUTF(JNIEnv* env, const char* bytes)
2954{
2955 JNI_ENTER();
2956
Andy McFaddenab00d452009-08-19 07:21:41 -07002957 jstring result;
2958
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002959 if (bytes == NULL) {
Andy McFaddenab00d452009-08-19 07:21:41 -07002960 result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002961 } else {
Andy McFaddenab00d452009-08-19 07:21:41 -07002962 /* note newStr could come back NULL on OOM */
Barry Hayes81f3ebe2010-06-15 16:17:37 -07002963 StringObject* newStr = dvmCreateStringFromCstr(bytes);
Andy McFaddenab00d452009-08-19 07:21:41 -07002964 result = addLocalReference(env, (Object*) newStr);
2965 dvmReleaseTrackedAlloc((Object*)newStr, NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002966 }
2967
2968 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002969 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002970}
2971
2972/*
2973 * Return the length in bytes of the modified UTF-8 form of the string.
2974 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002975static jsize GetStringUTFLength(JNIEnv* env, jstring jstr)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002976{
2977 JNI_ENTER();
2978
Andy McFaddenab00d452009-08-19 07:21:41 -07002979 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2980 jsize len = dvmStringUtf8ByteLen(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002981
2982 JNI_EXIT();
2983 return len;
2984}
2985
2986/*
2987 * Convert "string" to modified UTF-8 and return a pointer. The returned
2988 * value must be released with ReleaseStringUTFChars.
2989 *
2990 * According to the JNI reference, "Returns a pointer to a UTF-8 string,
2991 * or NULL if the operation fails. Returns NULL if and only if an invocation
2992 * of this function has thrown an exception."
2993 *
2994 * The behavior here currently follows that of other open-source VMs, which
2995 * quietly return NULL if "string" is NULL. We should consider throwing an
2996 * NPE. (The CheckJNI code blows up if you try to pass in a NULL string,
2997 * which should catch this sort of thing during development.) Certain other
2998 * VMs will crash with a segmentation fault.
2999 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003000static const char* GetStringUTFChars(JNIEnv* env, jstring jstr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003001 jboolean* isCopy)
3002{
3003 JNI_ENTER();
3004 char* newStr;
3005
Andy McFaddenab00d452009-08-19 07:21:41 -07003006 if (jstr == NULL) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003007 /* this shouldn't happen; throw NPE? */
3008 newStr = NULL;
3009 } else {
3010 if (isCopy != NULL)
3011 *isCopy = JNI_TRUE;
3012
Andy McFaddenab00d452009-08-19 07:21:41 -07003013 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
3014 newStr = dvmCreateCstrFromString(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003015 if (newStr == NULL) {
3016 /* assume memory failure */
3017 dvmThrowException("Ljava/lang/OutOfMemoryError;",
3018 "native heap string alloc failed");
3019 }
3020 }
3021
3022 JNI_EXIT();
3023 return newStr;
3024}
3025
3026/*
3027 * Release a string created by GetStringUTFChars().
3028 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003029static void ReleaseStringUTFChars(JNIEnv* env, jstring jstr, const char* utf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003030{
3031 JNI_ENTER();
3032 free((char*)utf);
3033 JNI_EXIT();
3034}
3035
3036/*
3037 * Return the capacity of the array.
3038 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003039static jsize GetArrayLength(JNIEnv* env, jarray jarr)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003040{
3041 JNI_ENTER();
3042
Andy McFaddenab00d452009-08-19 07:21:41 -07003043 ArrayObject* arrObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
3044 jsize length = arrObj->length;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003045
3046 JNI_EXIT();
3047 return length;
3048}
3049
3050/*
3051 * Construct a new array that holds objects from class "elementClass".
3052 */
3053static jobjectArray NewObjectArray(JNIEnv* env, jsize length,
Andy McFaddenab00d452009-08-19 07:21:41 -07003054 jclass jelementClass, jobject jinitialElement)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003055{
3056 JNI_ENTER();
3057
Andy McFaddenab00d452009-08-19 07:21:41 -07003058 jobjectArray newArray = NULL;
3059 ClassObject* elemClassObj =
3060 (ClassObject*) dvmDecodeIndirectRef(env, jelementClass);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003061
3062 if (elemClassObj == NULL) {
3063 dvmThrowException("Ljava/lang/NullPointerException;",
3064 "JNI NewObjectArray");
3065 goto bail;
3066 }
3067
Andy McFaddenab00d452009-08-19 07:21:41 -07003068 ArrayObject* newObj =
3069 dvmAllocObjectArray(elemClassObj, length, ALLOC_DEFAULT);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003070 if (newObj == NULL) {
3071 assert(dvmCheckException(_self));
3072 goto bail;
3073 }
Andy McFaddenab00d452009-08-19 07:21:41 -07003074 newArray = addLocalReference(env, (Object*) newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003075 dvmReleaseTrackedAlloc((Object*) newObj, NULL);
3076
3077 /*
3078 * Initialize the array. Trashes "length".
3079 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003080 if (jinitialElement != NULL) {
3081 Object* initialElement = dvmDecodeIndirectRef(env, jinitialElement);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003082 Object** arrayData = (Object**) newObj->contents;
3083
3084 while (length--)
Andy McFaddenab00d452009-08-19 07:21:41 -07003085 *arrayData++ = initialElement;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003086 }
3087
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003088
3089bail:
3090 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07003091 return newArray;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003092}
3093
3094/*
3095 * Get one element of an Object array.
3096 *
3097 * Add the object to the local references table in case the array goes away.
3098 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003099static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray jarr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003100 jsize index)
3101{
3102 JNI_ENTER();
3103
Andy McFaddenab00d452009-08-19 07:21:41 -07003104 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
3105 jobject retval = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003106
Andy McFaddenab00d452009-08-19 07:21:41 -07003107 assert(arrayObj != NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003108
3109 /* check the array bounds */
3110 if (index < 0 || index >= (int) arrayObj->length) {
3111 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
3112 arrayObj->obj.clazz->descriptor);
3113 goto bail;
3114 }
3115
Andy McFaddenab00d452009-08-19 07:21:41 -07003116 Object* value = ((Object**) arrayObj->contents)[index];
3117 retval = addLocalReference(env, value);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003118
3119bail:
3120 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07003121 return retval;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003122}
3123
3124/*
3125 * Set one element of an Object array.
3126 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003127static void SetObjectArrayElement(JNIEnv* env, jobjectArray jarr,
3128 jsize index, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003129{
3130 JNI_ENTER();
3131
Andy McFaddenab00d452009-08-19 07:21:41 -07003132 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003133
Andy McFaddenab00d452009-08-19 07:21:41 -07003134 assert(arrayObj != NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003135
3136 /* check the array bounds */
3137 if (index < 0 || index >= (int) arrayObj->length) {
3138 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
3139 arrayObj->obj.clazz->descriptor);
3140 goto bail;
3141 }
3142
3143 //LOGV("JNI: set element %d in array %p to %p\n", index, array, value);
3144
Andy McFadden0423f0e2009-08-26 07:21:53 -07003145 Object* obj = dvmDecodeIndirectRef(env, jobj);
Barry Hayes364f9d92010-06-11 16:12:47 -07003146 dvmSetObjectArrayElement(arrayObj, index, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003147
3148bail:
3149 JNI_EXIT();
3150}
3151
3152/*
3153 * Create a new array of primitive elements.
3154 */
3155#define NEW_PRIMITIVE_ARRAY(_artype, _jname, _typechar) \
3156 static _artype New##_jname##Array(JNIEnv* env, jsize length) \
3157 { \
3158 JNI_ENTER(); \
3159 ArrayObject* arrayObj; \
3160 arrayObj = dvmAllocPrimitiveArray(_typechar, length, \
3161 ALLOC_DEFAULT); \
Andy McFaddenab00d452009-08-19 07:21:41 -07003162 jarray jarr = NULL; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003163 if (arrayObj != NULL) { \
Andy McFaddenab00d452009-08-19 07:21:41 -07003164 jarr = addLocalReference(env, (Object*) arrayObj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003165 dvmReleaseTrackedAlloc((Object*) arrayObj, NULL); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003166 } \
3167 JNI_EXIT(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07003168 return (_artype)jarr; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003169 }
3170NEW_PRIMITIVE_ARRAY(jbooleanArray, Boolean, 'Z');
3171NEW_PRIMITIVE_ARRAY(jbyteArray, Byte, 'B');
3172NEW_PRIMITIVE_ARRAY(jcharArray, Char, 'C');
3173NEW_PRIMITIVE_ARRAY(jshortArray, Short, 'S');
3174NEW_PRIMITIVE_ARRAY(jintArray, Int, 'I');
3175NEW_PRIMITIVE_ARRAY(jlongArray, Long, 'J');
3176NEW_PRIMITIVE_ARRAY(jfloatArray, Float, 'F');
3177NEW_PRIMITIVE_ARRAY(jdoubleArray, Double, 'D');
3178
3179/*
3180 * Get a pointer to a C array of primitive elements from an array object
3181 * of the matching type.
3182 *
Andy McFaddenab00d452009-08-19 07:21:41 -07003183 * In a compacting GC, we either need to return a copy of the elements or
3184 * "pin" the memory. Otherwise we run the risk of native code using the
3185 * buffer as the destination of e.g. a blocking read() call that wakes up
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003186 * during a GC.
3187 */
3188#define GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
3189 static _ctype* Get##_jname##ArrayElements(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07003190 _ctype##Array jarr, jboolean* isCopy) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003191 { \
3192 JNI_ENTER(); \
3193 _ctype* data; \
Andy McFaddenab00d452009-08-19 07:21:41 -07003194 ArrayObject* arrayObj = \
3195 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
3196 pinPrimitiveArray(arrayObj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003197 data = (_ctype*) arrayObj->contents; \
3198 if (isCopy != NULL) \
3199 *isCopy = JNI_FALSE; \
3200 JNI_EXIT(); \
3201 return data; \
3202 }
3203
3204/*
3205 * Release the storage locked down by the "get" function.
3206 *
Andy McFaddenab00d452009-08-19 07:21:41 -07003207 * The spec says, "'mode' has no effect if 'elems' is not a copy of the
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003208 * elements in 'array'." They apparently did not anticipate the need to
Andy McFaddenab00d452009-08-19 07:21:41 -07003209 * un-pin memory.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003210 */
3211#define RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
3212 static void Release##_jname##ArrayElements(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07003213 _ctype##Array jarr, _ctype* elems, jint mode) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003214 { \
3215 UNUSED_PARAMETER(elems); \
3216 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07003217 if (mode != JNI_COMMIT) { \
3218 ArrayObject* arrayObj = \
3219 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
3220 unpinPrimitiveArray(arrayObj); \
3221 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003222 JNI_EXIT(); \
3223 }
3224
3225/*
3226 * Copy a section of a primitive array to a buffer.
3227 */
3228#define GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
3229 static void Get##_jname##ArrayRegion(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07003230 _ctype##Array jarr, jsize start, jsize len, _ctype* buf) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003231 { \
3232 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07003233 ArrayObject* arrayObj = \
3234 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003235 _ctype* data = (_ctype*) arrayObj->contents; \
3236 if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
3237 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
3238 arrayObj->obj.clazz->descriptor); \
3239 } else { \
3240 memcpy(buf, data + start, len * sizeof(_ctype)); \
3241 } \
3242 JNI_EXIT(); \
3243 }
3244
3245/*
3246 * Copy a section of a primitive array to a buffer.
3247 */
3248#define SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
3249 static void Set##_jname##ArrayRegion(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07003250 _ctype##Array jarr, jsize start, jsize len, const _ctype* buf) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003251 { \
3252 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07003253 ArrayObject* arrayObj = \
3254 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003255 _ctype* data = (_ctype*) arrayObj->contents; \
3256 if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
3257 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
3258 arrayObj->obj.clazz->descriptor); \
3259 } else { \
3260 memcpy(data + start, buf, len * sizeof(_ctype)); \
3261 } \
3262 JNI_EXIT(); \
3263 }
3264
3265/*
3266 * 4-in-1:
3267 * Get<Type>ArrayElements
3268 * Release<Type>ArrayElements
3269 * Get<Type>ArrayRegion
3270 * Set<Type>ArrayRegion
3271 */
3272#define PRIMITIVE_ARRAY_FUNCTIONS(_ctype, _jname) \
3273 GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
3274 RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
3275 GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname); \
3276 SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname);
3277
3278PRIMITIVE_ARRAY_FUNCTIONS(jboolean, Boolean);
3279PRIMITIVE_ARRAY_FUNCTIONS(jbyte, Byte);
3280PRIMITIVE_ARRAY_FUNCTIONS(jchar, Char);
3281PRIMITIVE_ARRAY_FUNCTIONS(jshort, Short);
3282PRIMITIVE_ARRAY_FUNCTIONS(jint, Int);
3283PRIMITIVE_ARRAY_FUNCTIONS(jlong, Long);
3284PRIMITIVE_ARRAY_FUNCTIONS(jfloat, Float);
3285PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double);
3286
3287/*
3288 * Register one or more native functions in one class.
Andy McFadden1e83b4d2010-07-15 17:20:24 -07003289 *
3290 * This can be called multiple times on the same method, allowing the
3291 * caller to redefine the method implementation at will.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003292 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003293static jint RegisterNatives(JNIEnv* env, jclass jclazz,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003294 const JNINativeMethod* methods, jint nMethods)
3295{
3296 JNI_ENTER();
3297
Andy McFaddenab00d452009-08-19 07:21:41 -07003298 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
Elliott Hughes09239e32009-09-29 18:35:43 -07003299 jint retval = JNI_OK;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003300 int i;
3301
3302 if (gDvm.verboseJni) {
3303 LOGI("[Registering JNI native methods for class %s]\n",
Andy McFaddenab00d452009-08-19 07:21:41 -07003304 clazz->descriptor);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003305 }
3306
3307 for (i = 0; i < nMethods; i++) {
Andy McFaddenab00d452009-08-19 07:21:41 -07003308 if (!dvmRegisterJNIMethod(clazz, methods[i].name,
3309 methods[i].signature, methods[i].fnPtr))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003310 {
3311 retval = JNI_ERR;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003312 }
3313 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003314
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003315 JNI_EXIT();
3316 return retval;
3317}
3318
3319/*
Andy McFadden1e83b4d2010-07-15 17:20:24 -07003320 * Un-register all native methods associated with the class.
3321 *
3322 * The JNI docs refer to this as a way to reload/relink native libraries,
3323 * and say it "should not be used in normal native code". In particular,
3324 * there is no need to do this during shutdown, and you do not need to do
3325 * this before redefining a method implementation with RegisterNatives.
3326 *
3327 * It's chiefly useful for a native "plugin"-style library that wasn't
3328 * loaded with System.loadLibrary() (since there's no way to unload those).
3329 * For example, the library could upgrade itself by:
3330 *
3331 * 1. call UnregisterNatives to unbind the old methods
3332 * 2. ensure that no code is still executing inside it (somehow)
3333 * 3. dlclose() the library
3334 * 4. dlopen() the new library
3335 * 5. use RegisterNatives to bind the methods from the new library
3336 *
3337 * The above can work correctly without the UnregisterNatives call, but
3338 * creates a window of opportunity in which somebody might try to call a
3339 * method that is pointing at unmapped memory, crashing the VM. In theory
3340 * the same guards that prevent dlclose() from unmapping executing code could
3341 * prevent that anyway, but with this we can be more thorough and also deal
3342 * with methods that only exist in the old or new form of the library (maybe
3343 * the lib wants to try the call and catch the UnsatisfiedLinkError).
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003344 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003345static jint UnregisterNatives(JNIEnv* env, jclass jclazz)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003346{
3347 JNI_ENTER();
Andy McFadden1e83b4d2010-07-15 17:20:24 -07003348
3349 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
3350 if (gDvm.verboseJni) {
3351 LOGI("[Unregistering JNI native methods for class %s]\n",
3352 clazz->descriptor);
3353 }
3354 dvmUnregisterJNINativeMethods(clazz);
3355
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003356 JNI_EXIT();
Andy McFadden1e83b4d2010-07-15 17:20:24 -07003357 return JNI_OK;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003358}
3359
3360/*
3361 * Lock the monitor.
3362 *
3363 * We have to track all monitor enters and exits, so that we can undo any
3364 * outstanding synchronization before the thread exits.
3365 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003366static jint MonitorEnter(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003367{
3368 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003369 Object* obj = dvmDecodeIndirectRef(env, jobj);
3370 dvmLockObject(_self, obj);
3371 trackMonitorEnter(_self, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003372 JNI_EXIT();
3373 return JNI_OK;
3374}
3375
3376/*
3377 * Unlock the monitor.
3378 *
3379 * Throws an IllegalMonitorStateException if the current thread
Andy McFaddenab00d452009-08-19 07:21:41 -07003380 * doesn't own the monitor. (dvmUnlockObject() takes care of the throw.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003381 *
3382 * According to the 1.6 spec, it's legal to call here with an exception
3383 * pending. If this fails, we'll stomp the original exception.
3384 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003385static jint MonitorExit(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003386{
3387 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003388 Object* obj = dvmDecodeIndirectRef(env, jobj);
3389 bool success = dvmUnlockObject(_self, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003390 if (success)
Andy McFaddenab00d452009-08-19 07:21:41 -07003391 trackMonitorExit(_self, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003392 JNI_EXIT();
3393 return success ? JNI_OK : JNI_ERR;
3394}
3395
3396/*
3397 * Return the JavaVM interface associated with the current thread.
3398 */
3399static jint GetJavaVM(JNIEnv* env, JavaVM** vm)
3400{
3401 JNI_ENTER();
3402 //*vm = gDvm.vmList;
3403 *vm = (JavaVM*) ((JNIEnvExt*)env)->vm;
3404 JNI_EXIT();
3405 if (*vm == NULL)
3406 return JNI_ERR;
3407 else
3408 return JNI_OK;
3409}
3410
3411/*
3412 * Copies "len" Unicode characters, from offset "start".
3413 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003414static void GetStringRegion(JNIEnv* env, jstring jstr, jsize start, jsize len,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003415 jchar* buf)
3416{
3417 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003418 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003419 if (start + len > dvmStringLen(strObj))
3420 dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
3421 else
3422 memcpy(buf, dvmStringChars(strObj) + start, len * sizeof(u2));
3423 JNI_EXIT();
3424}
3425
3426/*
3427 * Translates "len" Unicode characters, from offset "start", into
3428 * modified UTF-8 encoding.
3429 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003430static void GetStringUTFRegion(JNIEnv* env, jstring jstr, jsize start,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003431 jsize len, char* buf)
3432{
3433 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003434 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003435 if (start + len > dvmStringLen(strObj))
3436 dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
3437 else
3438 dvmCreateCstrFromStringRegion(strObj, start, len, buf);
3439 JNI_EXIT();
3440}
3441
3442/*
3443 * Get a raw pointer to array data.
3444 *
3445 * The caller is expected to call "release" before doing any JNI calls
3446 * or blocking I/O operations.
3447 *
Andy McFaddenab00d452009-08-19 07:21:41 -07003448 * We need to pin the memory or block GC.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003449 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003450static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray jarr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003451 jboolean* isCopy)
3452{
3453 JNI_ENTER();
3454 void* data;
Andy McFaddenab00d452009-08-19 07:21:41 -07003455 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
3456 pinPrimitiveArray(arrayObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003457 data = arrayObj->contents;
3458 if (isCopy != NULL)
3459 *isCopy = JNI_FALSE;
3460 JNI_EXIT();
3461 return data;
3462}
3463
3464/*
3465 * Release an array obtained with GetPrimitiveArrayCritical.
3466 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003467static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray jarr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003468 void* carray, jint mode)
3469{
3470 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003471 if (mode != JNI_COMMIT) {
3472 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
3473 unpinPrimitiveArray(arrayObj);
3474 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003475 JNI_EXIT();
3476}
3477
3478/*
3479 * Like GetStringChars, but with restricted use.
3480 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003481static const jchar* GetStringCritical(JNIEnv* env, jstring jstr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003482 jboolean* isCopy)
3483{
3484 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003485 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
3486 ArrayObject* strChars = dvmStringCharArray(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003487
Andy McFaddenab00d452009-08-19 07:21:41 -07003488 pinPrimitiveArray(strChars);
3489
3490 const u2* data = dvmStringChars(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003491 if (isCopy != NULL)
3492 *isCopy = JNI_FALSE;
3493
3494 JNI_EXIT();
3495 return (jchar*)data;
3496}
3497
3498/*
3499 * Like ReleaseStringChars, but with restricted use.
3500 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003501static void ReleaseStringCritical(JNIEnv* env, jstring jstr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003502 const jchar* carray)
3503{
3504 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003505 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
Andy McFadden0423f0e2009-08-26 07:21:53 -07003506 ArrayObject* strChars = dvmStringCharArray(strObj);
Andy McFaddenab00d452009-08-19 07:21:41 -07003507 unpinPrimitiveArray(strChars);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003508 JNI_EXIT();
3509}
3510
3511/*
3512 * Create a new weak global reference.
3513 */
3514static jweak NewWeakGlobalRef(JNIEnv* env, jobject obj)
3515{
3516 JNI_ENTER();
Andy McFaddenb18992f2009-09-25 10:42:15 -07003517 jweak wref = createWeakGlobalRef(env, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003518 JNI_EXIT();
Andy McFaddenb18992f2009-09-25 10:42:15 -07003519 return wref;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003520}
3521
3522/*
3523 * Delete the specified weak global reference.
3524 */
Andy McFaddenb18992f2009-09-25 10:42:15 -07003525static void DeleteWeakGlobalRef(JNIEnv* env, jweak wref)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003526{
3527 JNI_ENTER();
Andy McFaddenb18992f2009-09-25 10:42:15 -07003528 deleteWeakGlobalRef(env, wref);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003529 JNI_EXIT();
3530}
3531
3532/*
3533 * Quick check for pending exceptions.
3534 *
3535 * TODO: we should be able to skip the enter/exit macros here.
3536 */
3537static jboolean ExceptionCheck(JNIEnv* env)
3538{
3539 JNI_ENTER();
3540 bool result = dvmCheckException(_self);
3541 JNI_EXIT();
3542 return result;
3543}
3544
3545/*
3546 * Returns the type of the object referred to by "obj". It can be local,
3547 * global, or weak global.
3548 *
3549 * In the current implementation, references can be global and local at
3550 * the same time, so while the return value is accurate it may not tell
3551 * the whole story.
3552 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003553static jobjectRefType GetObjectRefType(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003554{
3555 JNI_ENTER();
Andy McFadden0423f0e2009-08-26 07:21:53 -07003556 jobjectRefType type = dvmGetJNIRefType(env, jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003557 JNI_EXIT();
3558 return type;
3559}
3560
3561/*
3562 * Allocate and return a new java.nio.ByteBuffer for this block of memory.
3563 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003564 * "address" may not be NULL, and "capacity" must be > 0. (These are only
3565 * verified when CheckJNI is enabled.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003566 */
Andy McFadden8e5c7842009-07-23 17:47:18 -07003567static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003568{
Andy McFadden8e5c7842009-07-23 17:47:18 -07003569 JNI_ENTER();
3570
3571 Thread* self = _self /*dvmThreadSelf()*/;
3572 Object* platformAddress = NULL;
3573 JValue callResult;
The Android Open Source Project99409882009-03-18 22:20:24 -07003574 jobject result = NULL;
Andy McFaddenac175b42009-12-17 11:21:09 -08003575 ClassObject* tmpClazz;
3576
3577 tmpClazz = gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on->clazz;
3578 if (!dvmIsClassInitialized(tmpClazz) && !dvmInitClass(tmpClazz))
3579 goto bail;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003580
Andy McFadden8e5c7842009-07-23 17:47:18 -07003581 /* get an instance of PlatformAddress that wraps the provided address */
3582 dvmCallMethod(self,
3583 gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on,
3584 NULL, &callResult, address);
3585 if (dvmGetException(self) != NULL || callResult.l == NULL)
The Android Open Source Project99409882009-03-18 22:20:24 -07003586 goto bail;
Andy McFadden8e5c7842009-07-23 17:47:18 -07003587
3588 /* don't let the GC discard it */
3589 platformAddress = (Object*) callResult.l;
3590 dvmAddTrackedAlloc(platformAddress, self);
3591 LOGV("tracking %p for address=%p\n", platformAddress, address);
3592
3593 /* create an instance of java.nio.ReadWriteDirectByteBuffer */
Andy McFaddenac175b42009-12-17 11:21:09 -08003594 tmpClazz = gDvm.classJavaNioReadWriteDirectByteBuffer;
3595 if (!dvmIsClassInitialized(tmpClazz) && !dvmInitClass(tmpClazz))
Andy McFadden8e5c7842009-07-23 17:47:18 -07003596 goto bail;
Andy McFaddenac175b42009-12-17 11:21:09 -08003597 Object* newObj = dvmAllocObject(tmpClazz, ALLOC_DONT_TRACK);
Andy McFadden8e5c7842009-07-23 17:47:18 -07003598 if (newObj != NULL) {
3599 /* call the (PlatformAddress, int, int) constructor */
Andy McFaddenab00d452009-08-19 07:21:41 -07003600 result = addLocalReference(env, newObj);
Andy McFadden8e5c7842009-07-23 17:47:18 -07003601 dvmCallMethod(self, gDvm.methJavaNioReadWriteDirectByteBuffer_init,
3602 newObj, &callResult, platformAddress, (jint) capacity, (jint) 0);
Andy McFaddenab00d452009-08-19 07:21:41 -07003603 if (dvmGetException(self) != NULL) {
3604 deleteLocalReference(env, result);
3605 result = NULL;
Andy McFadden8e5c7842009-07-23 17:47:18 -07003606 goto bail;
Andy McFaddenab00d452009-08-19 07:21:41 -07003607 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003608 }
3609
The Android Open Source Project99409882009-03-18 22:20:24 -07003610bail:
Andy McFadden8e5c7842009-07-23 17:47:18 -07003611 if (platformAddress != NULL)
3612 dvmReleaseTrackedAlloc(platformAddress, self);
3613 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07003614 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003615}
3616
3617/*
3618 * Get the starting address of the buffer for the specified java.nio.Buffer.
3619 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003620 * If this is not a "direct" buffer, we return NULL.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003621 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003622static void* GetDirectBufferAddress(JNIEnv* env, jobject jbuf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003623{
Andy McFadden8e5c7842009-07-23 17:47:18 -07003624 JNI_ENTER();
3625
Andy McFaddenab00d452009-08-19 07:21:41 -07003626 Object* bufObj = dvmDecodeIndirectRef(env, jbuf);
Andy McFadden8e5c7842009-07-23 17:47:18 -07003627 Thread* self = _self /*dvmThreadSelf()*/;
Andy McFadden8e696dc2009-07-24 15:28:16 -07003628 void* result;
3629
3630 /*
3631 * All Buffer objects have an effectiveDirectAddress field. If it's
3632 * nonzero, we can just return that value. If not, we have to call
3633 * through DirectBuffer.getEffectiveAddress(), which as a side-effect
3634 * will set the effectiveDirectAddress field for direct buffers (and
3635 * things that wrap direct buffers).
3636 */
3637 result = (void*) dvmGetFieldInt(bufObj,
3638 gDvm.offJavaNioBuffer_effectiveDirectAddress);
3639 if (result != NULL) {
3640 //LOGI("fast path for %p\n", buf);
3641 goto bail;
3642 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003643
Andy McFadden72d61fb2009-06-24 16:56:06 -07003644 /*
3645 * Start by determining if the object supports the DirectBuffer
3646 * interfaces. Note this does not guarantee that it's a direct buffer.
3647 */
Andy McFadden8e5c7842009-07-23 17:47:18 -07003648 if (!dvmInstanceof(bufObj->clazz,
3649 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003650 {
The Android Open Source Project99409882009-03-18 22:20:24 -07003651 goto bail;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003652 }
3653
Andy McFadden72d61fb2009-06-24 16:56:06 -07003654 /*
Andy McFadden8e5c7842009-07-23 17:47:18 -07003655 * Get a PlatformAddress object with the effective address.
Andy McFadden5f612b82009-07-22 15:07:27 -07003656 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003657 * If this isn't a direct buffer, the result will be NULL and/or an
Andy McFadden72d61fb2009-06-24 16:56:06 -07003658 * exception will have been thrown.
3659 */
Andy McFadden8e696dc2009-07-24 15:28:16 -07003660 JValue callResult;
Andy McFadden8e5c7842009-07-23 17:47:18 -07003661 const Method* meth = dvmGetVirtualizedMethod(bufObj->clazz,
3662 gDvm.methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress);
Andy McFaddend5ab7262009-08-25 07:19:34 -07003663 dvmCallMethodA(self, meth, bufObj, false, &callResult, NULL);
Andy McFadden8e5c7842009-07-23 17:47:18 -07003664 if (dvmGetException(self) != NULL) {
3665 dvmClearException(self);
3666 callResult.l = NULL;
Andy McFadden72d61fb2009-06-24 16:56:06 -07003667 }
Andy McFadden8e5c7842009-07-23 17:47:18 -07003668
Andy McFadden8e696dc2009-07-24 15:28:16 -07003669 Object* platformAddr = callResult.l;
Andy McFadden72d61fb2009-06-24 16:56:06 -07003670 if (platformAddr == NULL) {
Andy McFadden8e696dc2009-07-24 15:28:16 -07003671 LOGV("Got request for address of non-direct buffer\n");
Andy McFadden72d61fb2009-06-24 16:56:06 -07003672 goto bail;
3673 }
3674
Andy McFadden8e5c7842009-07-23 17:47:18 -07003675 /*
3676 * Extract the address from the PlatformAddress object. Instead of
3677 * calling the toLong() method, just grab the field directly. This
3678 * is faster but more fragile.
3679 */
3680 result = (void*) dvmGetFieldInt(platformAddr,
3681 gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr);
The Android Open Source Project99409882009-03-18 22:20:24 -07003682
Andy McFadden8e696dc2009-07-24 15:28:16 -07003683 //LOGI("slow path for %p --> %p\n", buf, result);
3684
The Android Open Source Project99409882009-03-18 22:20:24 -07003685bail:
Andy McFadden8e5c7842009-07-23 17:47:18 -07003686 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07003687 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003688}
3689
3690/*
3691 * Get the capacity of the buffer for the specified java.nio.Buffer.
3692 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003693 * Returns -1 if the object is not a direct buffer. (We actually skip
3694 * this check, since it's expensive to determine, and just return the
3695 * capacity regardless.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003696 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003697static jlong GetDirectBufferCapacity(JNIEnv* env, jobject jbuf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003698{
Andy McFadden8e5c7842009-07-23 17:47:18 -07003699 JNI_ENTER();
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003700
Andy McFadden8e5c7842009-07-23 17:47:18 -07003701 /*
3702 * The capacity is always in the Buffer.capacity field.
3703 *
3704 * (The "check" version should verify that this is actually a Buffer,
3705 * but we're not required to do so here.)
3706 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003707 Object* buf = dvmDecodeIndirectRef(env, jbuf);
3708 jlong result = dvmGetFieldInt(buf, gDvm.offJavaNioBuffer_capacity);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003709
Andy McFadden8e5c7842009-07-23 17:47:18 -07003710 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07003711 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003712}
3713
3714
3715/*
3716 * ===========================================================================
3717 * JNI invocation functions
3718 * ===========================================================================
3719 */
3720
3721/*
3722 * Handle AttachCurrentThread{AsDaemon}.
3723 *
3724 * We need to make sure the VM is actually running. For example, if we start
3725 * up, issue an Attach, and the VM exits almost immediately, by the time the
3726 * attaching happens the VM could already be shutting down.
3727 *
3728 * It's hard to avoid a race condition here because we don't want to hold
3729 * a lock across the entire operation. What we can do is temporarily
3730 * increment the thread count to prevent a VM exit.
3731 *
3732 * This could potentially still have problems if a daemon thread calls here
3733 * while the VM is shutting down. dvmThreadSelf() will work, since it just
3734 * uses pthread TLS, but dereferencing "vm" could fail. Such is life when
3735 * you shut down a VM while threads are still running inside it.
3736 *
3737 * Remember that some code may call this as a way to find the per-thread
3738 * JNIEnv pointer. Don't do excess work for that case.
3739 */
3740static jint attachThread(JavaVM* vm, JNIEnv** p_env, void* thr_args,
3741 bool isDaemon)
3742{
3743 JavaVMAttachArgs* args = (JavaVMAttachArgs*) thr_args;
3744 Thread* self;
3745 bool result = false;
3746
3747 /*
3748 * Return immediately if we're already one with the VM.
3749 */
3750 self = dvmThreadSelf();
3751 if (self != NULL) {
3752 *p_env = self->jniEnv;
3753 return JNI_OK;
3754 }
3755
3756 /*
3757 * No threads allowed in zygote mode.
3758 */
3759 if (gDvm.zygote) {
3760 return JNI_ERR;
3761 }
3762
3763 /* increment the count to keep the VM from bailing while we run */
3764 dvmLockThreadList(NULL);
3765 if (gDvm.nonDaemonThreadCount == 0) {
3766 // dead or dying
3767 LOGV("Refusing to attach thread '%s' -- VM is shutting down\n",
3768 (thr_args == NULL) ? "(unknown)" : args->name);
3769 dvmUnlockThreadList();
3770 return JNI_ERR;
3771 }
3772 gDvm.nonDaemonThreadCount++;
3773 dvmUnlockThreadList();
3774
3775 /* tweak the JavaVMAttachArgs as needed */
3776 JavaVMAttachArgs argsCopy;
3777 if (args == NULL) {
3778 /* allow the v1.1 calling convention */
3779 argsCopy.version = JNI_VERSION_1_2;
3780 argsCopy.name = NULL;
3781 argsCopy.group = dvmGetMainThreadGroup();
3782 } else {
3783 assert(args->version >= JNI_VERSION_1_2);
3784
3785 argsCopy.version = args->version;
3786 argsCopy.name = args->name;
3787 if (args->group != NULL)
3788 argsCopy.group = args->group;
3789 else
3790 argsCopy.group = dvmGetMainThreadGroup();
3791 }
3792
3793 result = dvmAttachCurrentThread(&argsCopy, isDaemon);
3794
3795 /* restore the count */
3796 dvmLockThreadList(NULL);
3797 gDvm.nonDaemonThreadCount--;
3798 dvmUnlockThreadList();
3799
3800 /*
3801 * Change the status to indicate that we're out in native code. This
3802 * call is not guarded with state-change macros, so we have to do it
3803 * by hand.
3804 */
3805 if (result) {
3806 self = dvmThreadSelf();
3807 assert(self != NULL);
3808 dvmChangeStatus(self, THREAD_NATIVE);
3809 *p_env = self->jniEnv;
3810 return JNI_OK;
3811 } else {
3812 return JNI_ERR;
3813 }
3814}
3815
3816/*
3817 * Attach the current thread to the VM. If the thread is already attached,
3818 * this is a no-op.
3819 */
3820static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args)
3821{
3822 return attachThread(vm, p_env, thr_args, false);
3823}
3824
3825/*
3826 * Like AttachCurrentThread, but set the "daemon" flag.
3827 */
3828static jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env,
3829 void* thr_args)
3830{
3831 return attachThread(vm, p_env, thr_args, true);
3832}
3833
3834/*
3835 * Dissociate the current thread from the VM.
3836 */
3837static jint DetachCurrentThread(JavaVM* vm)
3838{
3839 Thread* self = dvmThreadSelf();
3840
3841 if (self == NULL) /* not attached, can't do anything */
3842 return JNI_ERR;
3843
3844 /* switch to "running" to check for suspension */
3845 dvmChangeStatus(self, THREAD_RUNNING);
3846
3847 /* detach the thread */
3848 dvmDetachCurrentThread();
3849
3850 /* (no need to change status back -- we have no status) */
3851 return JNI_OK;
3852}
3853
3854/*
3855 * If current thread is attached to VM, return the associated JNIEnv.
3856 * Otherwise, stuff NULL in and return JNI_EDETACHED.
3857 *
3858 * JVMTI overloads this by specifying a magic value for "version", so we
3859 * do want to check that here.
3860 */
3861static jint GetEnv(JavaVM* vm, void** env, jint version)
3862{
3863 Thread* self = dvmThreadSelf();
3864
3865 if (version < JNI_VERSION_1_1 || version > JNI_VERSION_1_6)
3866 return JNI_EVERSION;
3867
3868 if (self == NULL) {
3869 *env = NULL;
3870 } else {
3871 /* TODO: status change is probably unnecessary */
3872 dvmChangeStatus(self, THREAD_RUNNING);
3873 *env = (void*) dvmGetThreadJNIEnv(self);
3874 dvmChangeStatus(self, THREAD_NATIVE);
3875 }
3876 if (*env == NULL)
3877 return JNI_EDETACHED;
3878 else
3879 return JNI_OK;
3880}
3881
3882/*
3883 * Destroy the VM. This may be called from any thread.
3884 *
3885 * If the current thread is attached, wait until the current thread is
3886 * the only non-daemon user-level thread. If the current thread is not
3887 * attached, we attach it and do the processing as usual. (If the attach
3888 * fails, it's probably because all the non-daemon threads have already
3889 * exited and the VM doesn't want to let us back in.)
3890 *
3891 * TODO: we don't really deal with the situation where more than one thread
3892 * has called here. One thread wins, the other stays trapped waiting on
3893 * the condition variable forever. Not sure this situation is interesting
3894 * in real life.
3895 */
3896static jint DestroyJavaVM(JavaVM* vm)
3897{
3898 JavaVMExt* ext = (JavaVMExt*) vm;
3899 Thread* self;
3900
3901 if (ext == NULL)
3902 return JNI_ERR;
3903
Andy McFadden43eb5012010-02-01 16:56:53 -08003904 if (gDvm.verboseShutdown)
3905 LOGD("DestroyJavaVM waiting for non-daemon threads to exit\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003906
3907 /*
3908 * Sleep on a condition variable until it's okay to exit.
3909 */
3910 self = dvmThreadSelf();
3911 if (self == NULL) {
3912 JNIEnv* tmpEnv;
3913 if (AttachCurrentThread(vm, &tmpEnv, NULL) != JNI_OK) {
3914 LOGV("Unable to reattach main for Destroy; assuming VM is "
3915 "shutting down (count=%d)\n",
3916 gDvm.nonDaemonThreadCount);
3917 goto shutdown;
3918 } else {
3919 LOGV("Attached to wait for shutdown in Destroy\n");
3920 }
3921 }
3922 dvmChangeStatus(self, THREAD_VMWAIT);
3923
3924 dvmLockThreadList(self);
3925 gDvm.nonDaemonThreadCount--; // remove current thread from count
3926
3927 while (gDvm.nonDaemonThreadCount > 0)
3928 pthread_cond_wait(&gDvm.vmExitCond, &gDvm.threadListLock);
3929
3930 dvmUnlockThreadList();
3931 self = NULL;
3932
3933shutdown:
3934 // TODO: call System.exit() to run any registered shutdown hooks
3935 // (this may not return -- figure out how this should work)
3936
Andy McFadden43eb5012010-02-01 16:56:53 -08003937 if (gDvm.verboseShutdown)
3938 LOGD("DestroyJavaVM shutting VM down\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003939 dvmShutdown();
3940
3941 // TODO - free resources associated with JNI-attached daemon threads
3942 free(ext->envList);
3943 free(ext);
3944
3945 return JNI_OK;
3946}
3947
3948
3949/*
3950 * ===========================================================================
3951 * Function tables
3952 * ===========================================================================
3953 */
3954
3955static const struct JNINativeInterface gNativeInterface = {
3956 NULL,
3957 NULL,
3958 NULL,
3959 NULL,
3960
3961 GetVersion,
3962
3963 DefineClass,
3964 FindClass,
3965
3966 FromReflectedMethod,
3967 FromReflectedField,
3968 ToReflectedMethod,
3969
3970 GetSuperclass,
3971 IsAssignableFrom,
3972
3973 ToReflectedField,
3974
3975 Throw,
3976 ThrowNew,
3977 ExceptionOccurred,
3978 ExceptionDescribe,
3979 ExceptionClear,
3980 FatalError,
3981
3982 PushLocalFrame,
3983 PopLocalFrame,
3984
3985 NewGlobalRef,
3986 DeleteGlobalRef,
3987 DeleteLocalRef,
3988 IsSameObject,
3989 NewLocalRef,
3990 EnsureLocalCapacity,
3991
3992 AllocObject,
3993 NewObject,
3994 NewObjectV,
3995 NewObjectA,
3996
3997 GetObjectClass,
3998 IsInstanceOf,
3999
4000 GetMethodID,
4001
4002 CallObjectMethod,
4003 CallObjectMethodV,
4004 CallObjectMethodA,
4005 CallBooleanMethod,
4006 CallBooleanMethodV,
4007 CallBooleanMethodA,
4008 CallByteMethod,
4009 CallByteMethodV,
4010 CallByteMethodA,
4011 CallCharMethod,
4012 CallCharMethodV,
4013 CallCharMethodA,
4014 CallShortMethod,
4015 CallShortMethodV,
4016 CallShortMethodA,
4017 CallIntMethod,
4018 CallIntMethodV,
4019 CallIntMethodA,
4020 CallLongMethod,
4021 CallLongMethodV,
4022 CallLongMethodA,
4023 CallFloatMethod,
4024 CallFloatMethodV,
4025 CallFloatMethodA,
4026 CallDoubleMethod,
4027 CallDoubleMethodV,
4028 CallDoubleMethodA,
4029 CallVoidMethod,
4030 CallVoidMethodV,
4031 CallVoidMethodA,
4032
4033 CallNonvirtualObjectMethod,
4034 CallNonvirtualObjectMethodV,
4035 CallNonvirtualObjectMethodA,
4036 CallNonvirtualBooleanMethod,
4037 CallNonvirtualBooleanMethodV,
4038 CallNonvirtualBooleanMethodA,
4039 CallNonvirtualByteMethod,
4040 CallNonvirtualByteMethodV,
4041 CallNonvirtualByteMethodA,
4042 CallNonvirtualCharMethod,
4043 CallNonvirtualCharMethodV,
4044 CallNonvirtualCharMethodA,
4045 CallNonvirtualShortMethod,
4046 CallNonvirtualShortMethodV,
4047 CallNonvirtualShortMethodA,
4048 CallNonvirtualIntMethod,
4049 CallNonvirtualIntMethodV,
4050 CallNonvirtualIntMethodA,
4051 CallNonvirtualLongMethod,
4052 CallNonvirtualLongMethodV,
4053 CallNonvirtualLongMethodA,
4054 CallNonvirtualFloatMethod,
4055 CallNonvirtualFloatMethodV,
4056 CallNonvirtualFloatMethodA,
4057 CallNonvirtualDoubleMethod,
4058 CallNonvirtualDoubleMethodV,
4059 CallNonvirtualDoubleMethodA,
4060 CallNonvirtualVoidMethod,
4061 CallNonvirtualVoidMethodV,
4062 CallNonvirtualVoidMethodA,
4063
4064 GetFieldID,
4065
4066 GetObjectField,
4067 GetBooleanField,
4068 GetByteField,
4069 GetCharField,
4070 GetShortField,
4071 GetIntField,
4072 GetLongField,
4073 GetFloatField,
4074 GetDoubleField,
4075 SetObjectField,
4076 SetBooleanField,
4077 SetByteField,
4078 SetCharField,
4079 SetShortField,
4080 SetIntField,
4081 SetLongField,
4082 SetFloatField,
4083 SetDoubleField,
4084
4085 GetStaticMethodID,
4086
4087 CallStaticObjectMethod,
4088 CallStaticObjectMethodV,
4089 CallStaticObjectMethodA,
4090 CallStaticBooleanMethod,
4091 CallStaticBooleanMethodV,
4092 CallStaticBooleanMethodA,
4093 CallStaticByteMethod,
4094 CallStaticByteMethodV,
4095 CallStaticByteMethodA,
4096 CallStaticCharMethod,
4097 CallStaticCharMethodV,
4098 CallStaticCharMethodA,
4099 CallStaticShortMethod,
4100 CallStaticShortMethodV,
4101 CallStaticShortMethodA,
4102 CallStaticIntMethod,
4103 CallStaticIntMethodV,
4104 CallStaticIntMethodA,
4105 CallStaticLongMethod,
4106 CallStaticLongMethodV,
4107 CallStaticLongMethodA,
4108 CallStaticFloatMethod,
4109 CallStaticFloatMethodV,
4110 CallStaticFloatMethodA,
4111 CallStaticDoubleMethod,
4112 CallStaticDoubleMethodV,
4113 CallStaticDoubleMethodA,
4114 CallStaticVoidMethod,
4115 CallStaticVoidMethodV,
4116 CallStaticVoidMethodA,
4117
4118 GetStaticFieldID,
4119
4120 GetStaticObjectField,
4121 GetStaticBooleanField,
4122 GetStaticByteField,
4123 GetStaticCharField,
4124 GetStaticShortField,
4125 GetStaticIntField,
4126 GetStaticLongField,
4127 GetStaticFloatField,
4128 GetStaticDoubleField,
4129
4130 SetStaticObjectField,
4131 SetStaticBooleanField,
4132 SetStaticByteField,
4133 SetStaticCharField,
4134 SetStaticShortField,
4135 SetStaticIntField,
4136 SetStaticLongField,
4137 SetStaticFloatField,
4138 SetStaticDoubleField,
4139
4140 NewString,
4141
4142 GetStringLength,
4143 GetStringChars,
4144 ReleaseStringChars,
4145
4146 NewStringUTF,
4147 GetStringUTFLength,
4148 GetStringUTFChars,
4149 ReleaseStringUTFChars,
4150
4151 GetArrayLength,
4152 NewObjectArray,
4153 GetObjectArrayElement,
4154 SetObjectArrayElement,
4155
4156 NewBooleanArray,
4157 NewByteArray,
4158 NewCharArray,
4159 NewShortArray,
4160 NewIntArray,
4161 NewLongArray,
4162 NewFloatArray,
4163 NewDoubleArray,
4164
4165 GetBooleanArrayElements,
4166 GetByteArrayElements,
4167 GetCharArrayElements,
4168 GetShortArrayElements,
4169 GetIntArrayElements,
4170 GetLongArrayElements,
4171 GetFloatArrayElements,
4172 GetDoubleArrayElements,
4173
4174 ReleaseBooleanArrayElements,
4175 ReleaseByteArrayElements,
4176 ReleaseCharArrayElements,
4177 ReleaseShortArrayElements,
4178 ReleaseIntArrayElements,
4179 ReleaseLongArrayElements,
4180 ReleaseFloatArrayElements,
4181 ReleaseDoubleArrayElements,
4182
4183 GetBooleanArrayRegion,
4184 GetByteArrayRegion,
4185 GetCharArrayRegion,
4186 GetShortArrayRegion,
4187 GetIntArrayRegion,
4188 GetLongArrayRegion,
4189 GetFloatArrayRegion,
4190 GetDoubleArrayRegion,
4191 SetBooleanArrayRegion,
4192 SetByteArrayRegion,
4193 SetCharArrayRegion,
4194 SetShortArrayRegion,
4195 SetIntArrayRegion,
4196 SetLongArrayRegion,
4197 SetFloatArrayRegion,
4198 SetDoubleArrayRegion,
4199
4200 RegisterNatives,
4201 UnregisterNatives,
4202
4203 MonitorEnter,
4204 MonitorExit,
4205
4206 GetJavaVM,
4207
4208 GetStringRegion,
4209 GetStringUTFRegion,
4210
4211 GetPrimitiveArrayCritical,
4212 ReleasePrimitiveArrayCritical,
4213
4214 GetStringCritical,
4215 ReleaseStringCritical,
4216
4217 NewWeakGlobalRef,
4218 DeleteWeakGlobalRef,
4219
4220 ExceptionCheck,
4221
4222 NewDirectByteBuffer,
4223 GetDirectBufferAddress,
4224 GetDirectBufferCapacity,
4225
4226 GetObjectRefType
4227};
4228static const struct JNIInvokeInterface gInvokeInterface = {
4229 NULL,
4230 NULL,
4231 NULL,
4232
4233 DestroyJavaVM,
4234 AttachCurrentThread,
4235 DetachCurrentThread,
4236
4237 GetEnv,
4238
4239 AttachCurrentThreadAsDaemon,
4240};
4241
4242
4243/*
4244 * ===========================================================================
4245 * VM/Env creation
4246 * ===========================================================================
4247 */
4248
4249/*
4250 * Enable "checked JNI" after the VM has partially started. This must
4251 * only be called in "zygote" mode, when we have one thread running.
Andy McFadden59b61772009-05-13 16:44:34 -07004252 *
4253 * This doesn't attempt to rewrite the JNI call bridge associated with
4254 * native methods, so we won't get those checks for any methods that have
4255 * already been resolved.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08004256 */
4257void dvmLateEnableCheckedJni(void)
4258{
4259 JNIEnvExt* extEnv;
4260 JavaVMExt* extVm;
Andy McFaddenab00d452009-08-19 07:21:41 -07004261
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08004262 extEnv = dvmGetJNIEnvForThread();
4263 if (extEnv == NULL) {
4264 LOGE("dvmLateEnableCheckedJni: thread has no JNIEnv\n");
4265 return;
4266 }
4267 extVm = extEnv->vm;
4268 assert(extVm != NULL);
4269
4270 if (!extVm->useChecked) {
4271 LOGD("Late-enabling CheckJNI\n");
4272 dvmUseCheckedJniVm(extVm);
4273 extVm->useChecked = true;
4274 dvmUseCheckedJniEnv(extEnv);
4275
4276 /* currently no way to pick up jniopts features */
4277 } else {
4278 LOGD("Not late-enabling CheckJNI (already on)\n");
4279 }
4280}
4281
4282/*
4283 * Not supported.
4284 */
4285jint JNI_GetDefaultJavaVMInitArgs(void* vm_args)
4286{
4287 return JNI_ERR;
4288}
4289
4290/*
4291 * Return a buffer full of created VMs.
4292 *
4293 * We always have zero or one.
4294 */
4295jint JNI_GetCreatedJavaVMs(JavaVM** vmBuf, jsize bufLen, jsize* nVMs)
4296{
4297 if (gDvm.vmList != NULL) {
4298 *nVMs = 1;
4299
4300 if (bufLen > 0)
4301 *vmBuf++ = gDvm.vmList;
4302 } else {
4303 *nVMs = 0;
4304 }
4305
4306 return JNI_OK;
4307}
4308
4309
4310/*
4311 * Create a new VM instance.
4312 *
4313 * The current thread becomes the main VM thread. We return immediately,
4314 * which effectively means the caller is executing in a native method.
4315 */
4316jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args)
4317{
4318 const JavaVMInitArgs* args = (JavaVMInitArgs*) vm_args;
4319 JNIEnvExt* pEnv = NULL;
4320 JavaVMExt* pVM = NULL;
4321 const char** argv;
4322 int argc = 0;
4323 int i, curOpt;
4324 int result = JNI_ERR;
4325 bool checkJni = false;
4326 bool warnError = true;
4327 bool forceDataCopy = false;
4328
4329 if (args->version < JNI_VERSION_1_2)
4330 return JNI_EVERSION;
4331
4332 // TODO: don't allow creation of multiple VMs -- one per customer for now
4333
4334 /* zero globals; not strictly necessary the first time a VM is started */
4335 memset(&gDvm, 0, sizeof(gDvm));
4336
4337 /*
4338 * Set up structures for JNIEnv and VM.
4339 */
4340 //pEnv = (JNIEnvExt*) malloc(sizeof(JNIEnvExt));
4341 pVM = (JavaVMExt*) malloc(sizeof(JavaVMExt));
4342
4343 //memset(pEnv, 0, sizeof(JNIEnvExt));
4344 //pEnv->funcTable = &gNativeInterface;
4345 //pEnv->vm = pVM;
4346 memset(pVM, 0, sizeof(JavaVMExt));
4347 pVM->funcTable = &gInvokeInterface;
4348 pVM->envList = pEnv;
4349 dvmInitMutex(&pVM->envListLock);
4350
4351 argv = (const char**) malloc(sizeof(char*) * (args->nOptions));
4352 memset(argv, 0, sizeof(char*) * (args->nOptions));
4353
4354 curOpt = 0;
4355
4356 /*
4357 * Convert JNI args to argv.
4358 *
4359 * We have to pull out vfprintf/exit/abort, because they use the
4360 * "extraInfo" field to pass function pointer "hooks" in. We also
4361 * look for the -Xcheck:jni stuff here.
4362 */
4363 for (i = 0; i < args->nOptions; i++) {
4364 const char* optStr = args->options[i].optionString;
4365
4366 if (optStr == NULL) {
4367 fprintf(stderr, "ERROR: arg %d string was null\n", i);
4368 goto bail;
4369 } else if (strcmp(optStr, "vfprintf") == 0) {
4370 gDvm.vfprintfHook = args->options[i].extraInfo;
4371 } else if (strcmp(optStr, "exit") == 0) {
4372 gDvm.exitHook = args->options[i].extraInfo;
4373 } else if (strcmp(optStr, "abort") == 0) {
4374 gDvm.abortHook = args->options[i].extraInfo;
4375 } else if (strcmp(optStr, "-Xcheck:jni") == 0) {
4376 checkJni = true;
4377 } else if (strncmp(optStr, "-Xjniopts:", 10) == 0) {
4378 const char* jniOpts = optStr + 9;
4379 while (jniOpts != NULL) {
4380 jniOpts++; /* skip past ':' or ',' */
4381 if (strncmp(jniOpts, "warnonly", 8) == 0) {
4382 warnError = false;
4383 } else if (strncmp(jniOpts, "forcecopy", 9) == 0) {
4384 forceDataCopy = true;
4385 } else {
4386 LOGW("unknown jni opt starting at '%s'\n", jniOpts);
4387 }
4388 jniOpts = strchr(jniOpts, ',');
4389 }
4390 } else {
4391 /* regular option */
4392 argv[curOpt++] = optStr;
4393 }
4394 }
4395 argc = curOpt;
4396
4397 if (checkJni) {
4398 dvmUseCheckedJniVm(pVM);
4399 pVM->useChecked = true;
4400 }
4401 pVM->warnError = warnError;
4402 pVM->forceDataCopy = forceDataCopy;
4403
4404 /* set this up before initializing VM, so it can create some JNIEnvs */
4405 gDvm.vmList = (JavaVM*) pVM;
4406
4407 /*
4408 * Create an env for main thread. We need to have something set up
4409 * here because some of the class initialization we do when starting
4410 * up the VM will call into native code.
4411 */
4412 pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL);
4413
4414 /* initialize VM */
4415 gDvm.initializing = true;
4416 if (dvmStartup(argc, argv, args->ignoreUnrecognized, (JNIEnv*)pEnv) != 0) {
4417 free(pEnv);
4418 free(pVM);
4419 goto bail;
4420 }
4421
4422 /*
4423 * Success! Return stuff to caller.
4424 */
4425 dvmChangeStatus(NULL, THREAD_NATIVE);
4426 *p_env = (JNIEnv*) pEnv;
4427 *p_vm = (JavaVM*) pVM;
4428 result = JNI_OK;
4429
4430bail:
4431 gDvm.initializing = false;
4432 if (result == JNI_OK)
4433 LOGV("JNI_CreateJavaVM succeeded\n");
4434 else
4435 LOGW("JNI_CreateJavaVM failed\n");
4436 free(argv);
4437 return result;
4438}