blob: 0248e8fc169a0929473c0933dc7e933f82ce2fb4 [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 */
16/*
17 * Dalvik implementation of JNI interfaces.
18 */
19#include "Dalvik.h"
20#include "JniInternal.h"
21
22#include <stdlib.h>
23#include <stdarg.h>
24#include <limits.h>
25
26/*
27Native methods and interaction with the GC
28
29All JNI methods must start by changing their thread status to
30THREAD_RUNNING, and finish by changing it back to THREAD_NATIVE before
31returning to native code. The switch to "running" triggers a thread
32suspension check.
33
34With a rudimentary GC we should be able to skip the status change for
35simple functions, e.g. IsSameObject, GetJavaVM, GetStringLength, maybe
36even access to fields with primitive types. Our options are more limited
37with a compacting GC, so we should replace JNI_ENTER with JNI_ENTER_NCGC
38or somesuch on the "lite" functions if we want to try this optimization.
39
40For performance reasons we do as little error-checking as possible here.
41For example, we don't check to make sure the correct type of Object is
42passed in when setting a field, and we don't prevent you from storing
43new values in a "final" field. Such things are best handled in the
44"check" version. For actions that are common, dangerous, and must be
45checked at runtime, such as array bounds checks, we do the tests here.
46
47
48General notes on local/global reference tracking
49
50JNI provides explicit control over natively-held references that the VM GC
51needs to know about. These can be local, in which case they're released
52when the native method returns, or global, which are held until explicitly
53released.
54
55The references can be created and deleted with JNI NewLocalRef /
56NewGlobalRef calls, but this is unusual except perhaps for holding on
57to a Class reference. Most often they are created transparently by the
58JNI functions. For example, the paired Get/Release calls guarantee that
59objects survive until explicitly released, so a simple way to implement
60this is to create a global reference on "Get" and delete it on "Release".
61The AllocObject/NewObject functions must create local references, because
62nothing else in the GC root set has a reference to the new objects.
63
64The most common mode of operation is for a method to create zero or
65more local references and return. Explicit "local delete" operations
66are expected to be exceedingly rare, except when walking through an
67object array, and the Push/PopLocalFrame calls are expected to be used
68infrequently. For efficient operation, we want to add new local refs
69with a simple store/increment operation; to avoid infinite growth in
70pathological situations, we need to reclaim the space used by deleted
71entries.
72
73The simplest implementation is an expanding append-only array that compacts
74when objects are deleted. In typical situations, e.g. running through
75an array of objects, we will be deleting one of the most recently added
76entries, so we can minimize the number of elements moved (or avoid having
77to move any).
78
79The spec says, "Local references are only valid in the thread in which
80they are created. The native code must not pass local references from
81one thread to another." It should also be noted that, while some calls
82will *create* global references as a side-effect, only the NewGlobalRef
83and NewWeakGlobalRef calls actually *return* global references.
84
85
86Global reference tracking
87
88There should be a small "active" set centered around the most-recently
89added items. We can use an append-only, compacting array like we do for
90local refs.
91
92Because it's global, access to it has to be synchronized.
93
94The JNI spec does not define any sort of limit, so the list must be able
95to expand. It may be useful to log significant increases in usage to
96help identify resource leaks.
97
98TODO: we currently use global references on strings and primitive array
99data, because they have the property we need (i.e. the pointer we return
100is guaranteed valid until we explicitly release it). However, if we have
101a compacting GC and don't want to pin all memory held by all global refs,
102we actually want to treat these differently. Either we need a way to
103tell the GC that specific global references are pinned, or we have to
104make a copy of the data and return that instead (something JNI supports).
105
106
107Local reference tracking
108
109The table of local references can be stored on the interpreted stack or
110in a parallel data structure (one per thread).
111
112*** Approach #1: use the interpreted stack
113
114The easiest place to tuck it is between the frame ptr and the first saved
115register, which is always in0. (See the ASCII art in Stack.h.) We can
116shift the "VM-specific goop" and frame ptr down, effectively inserting
117the JNI local refs in the space normally occupied by local variables.
118
119(Three things are accessed from the frame pointer:
120 (1) framePtr[N] is register vN, used to get at "ins" and "locals".
121 (2) framePtr - sizeof(StackSaveArea) is the VM frame goop.
122 (3) framePtr - sizeof(StackSaveArea) - numOuts is where the "outs" go.
123The only thing that isn't determined by an offset from the current FP
124is the previous frame. However, tucking things below the previous frame
125can be problematic because the "outs" of the previous frame overlap with
126the "ins" of the current frame. If the "ins" are altered they must be
127restored before we return. For a native method call, the easiest and
128safest thing to disrupt is #1, because there are no locals and the "ins"
129are all copied to the native stack.)
130
131We can implement Push/PopLocalFrame with the existing stack frame calls,
132making sure we copy some goop from the previous frame (notably the method
133ptr, so that dvmGetCurrentJNIMethod() doesn't require extra effort).
134
135We can pre-allocate the storage at the time the stack frame is first
136set up, but we have to be careful. When calling from interpreted code
137the frame ptr points directly at the arguments we're passing, but we can
138offset the args pointer when calling the native bridge.
139
140To manage the local ref collection, we need to be able to find three
141things: (1) the start of the region, (2) the end of the region, and (3)
142the next available entry. The last is only required for quick adds.
143We currently have two easily-accessible pointers, the current FP and the
144previous frame's FP. (The "stack pointer" shown in the ASCII art doesn't
145actually exist in the interpreted world.)
146
147We can't use the current FP to find the first "in", because we want to
148insert the variable-sized local refs table between them. It's awkward
149to use the previous frame's FP because native methods invoked via
150dvmCallMethod() or dvmInvokeMethod() don't have "ins", but native methods
151invoked from interpreted code do. We can either track the local refs
152table size with a field in the stack frame, or insert unnecessary items
153so that all native stack frames have "ins".
154
155Assuming we can find the region bounds, we still need pointer #3
156for an efficient implementation. This can be stored in an otherwise
157unused-for-native field in the frame goop.
158
159When we run out of room we have to make more space. If we start allocating
160locals immediately below in0 and grow downward, we will detect end-of-space
161by running into the current frame's FP. We then memmove() the goop down
162(memcpy if we guarantee the additional size is larger than the frame).
163This is nice because we only have to move sizeof(StackSaveArea) bytes
164each time.
165
166Stack walking should be okay so long as nothing tries to access the
167"ins" by an offset from the FP. In theory the "ins" could be read by
168the debugger or SIGQUIT handler looking for "this" or other arguments,
169but in practice this behavior isn't expected to work for native methods,
170so we can simply disallow it.
171
172A conservative GC can just scan the entire stack from top to bottom to find
173all references. An exact GC will need to understand the actual layout.
174
175*** Approach #2: use a parallel stack
176
177Each Thread/JNIEnv points to a ReferenceTable struct. The struct
178has a system-heap-allocated array of references and a pointer to the
179next-available entry ("nextEntry").
180
181Each stack frame has a pointer to what it sees as the "top" element in the
182array (we can double-up the "currentPc" field). This is set to "nextEntry"
183when the frame is pushed on. As local references are added or removed,
184"nextEntry" is updated.
185
186We implement Push/PopLocalFrame with actual stack frames. Before a JNI
187frame gets popped, we set "nextEntry" to the "top" pointer of the current
188frame, effectively releasing the references.
189
190The GC will scan all references from the start of the table to the
191"nextEntry" pointer.
192
193*** Comparison
194
195All approaches will return a failure result when they run out of local
196reference space. For #1 that means blowing out the stack, for #2 it's
197running out of room in the array.
198
199Compared to #1, approach #2:
200 - Needs only one pointer in the stack frame goop.
201 - Makes pre-allocating storage unnecessary.
202 - Doesn't contend with interpreted stack depth for space. In most
203 cases, if something blows out the local ref storage, it's because the
204 JNI code was misbehaving rather than called from way down.
205 - Allows the GC to do a linear scan per thread in a buffer that is 100%
206 references. The GC can be slightly less smart when scanning the stack.
207 - Will be easier to work with if we combine native and interpeted stacks.
208
209 - Isn't as clean, especially when popping frames, since we have to do
210 explicit work. Fortunately we only have to do it when popping native
211 method calls off, so it doesn't add overhead to interpreted code paths.
212 - Is awkward to expand dynamically. We'll want to pre-allocate the full
213 amount of space; this is fine, since something on the order of 1KB should
214 be plenty. The JNI spec allows us to limit this.
215 - Requires the GC to scan even more memory. With the references embedded
216 in the stack we get better locality of reference.
217
218*/
219
Andy McFadden5f612b82009-07-22 15:07:27 -0700220/* fwd */
221static const struct JNINativeInterface gNativeInterface;
222static jobject addGlobalReference(jobject obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800223
224
225#ifdef WITH_JNI_STACK_CHECK
226# define COMPUTE_STACK_SUM(_self) computeStackSum(_self);
227# define CHECK_STACK_SUM(_self) checkStackSum(_self);
228static void computeStackSum(Thread* self);
229static void checkStackSum(Thread* self);
230#else
231# define COMPUTE_STACK_SUM(_self) ((void)0)
232# define CHECK_STACK_SUM(_self) ((void)0)
233#endif
234
235
236/*
237 * ===========================================================================
238 * JNI call bridge
239 * ===========================================================================
240 */
241
242/*
243 * Bridge to calling a JNI function. This ideally gets some help from
244 * assembly language code in dvmPlatformInvoke, because the arguments
245 * must be pushed into the native stack as if we were calling a <stdarg.h>
246 * function.
247 *
248 * The number of values in "args" must match method->insSize.
249 *
250 * This is generally just set up by the resolver and then called through.
251 * We don't call here explicitly. This takes the same arguments as all
252 * of the "internal native" methods.
253 */
254void dvmCallJNIMethod(const u4* args, JValue* pResult, const Method* method,
255 Thread* self)
256{
257 int oldStatus;
258
259 assert(method->insns != NULL);
260
261 //int i;
262 //LOGI("JNI calling %p (%s.%s %s):\n", method->insns,
263 // method->clazz->descriptor, method->name, method->signature);
264 //for (i = 0; i < method->insSize; i++)
265 // LOGI(" %d: 0x%08x\n", i, args[i]);
266
267 oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
268
269 COMPUTE_STACK_SUM(self);
270 // TODO: should we be converting 'this' to a local ref?
271 dvmPlatformInvoke(self->jniEnv,
272 dvmIsStaticMethod(method) ? method->clazz : NULL,
273 method->jniArgInfo, method->insSize, args, method->shorty,
274 (void*)method->insns, pResult);
275 CHECK_STACK_SUM(self);
276
277 dvmChangeStatus(self, oldStatus);
278}
279
280/*
281 * Alternate call bridge for the unusual case of a synchronized native method.
282 *
283 * Lock the object, then call through the usual function.
284 */
285void dvmCallSynchronizedJNIMethod(const u4* args, JValue* pResult,
286 const Method* method, Thread* self)
287{
288 Object* lockObj;
289
290 assert(dvmIsSynchronizedMethod(method));
291
292 if (dvmIsStaticMethod(method))
293 lockObj = (Object*) method->clazz;
294 else
295 lockObj = (Object*) args[0];
296
297 LOGVV("Calling %s.%s: locking %p (%s)\n",
298 method->clazz->descriptor, method->name,
299 lockObj, lockObj->clazz->descriptor);
300
301 dvmLockObject(self, lockObj);
302 dvmCallJNIMethod(args, pResult, method, self);
303 dvmUnlockObject(self, lockObj);
304}
305
306/*
307 * Extract the return type enum from the "jniArgInfo" field.
308 */
309DalvikJniReturnType dvmGetArgInfoReturnType(int jniArgInfo)
310{
311 return (jniArgInfo & DALVIK_JNI_RETURN_MASK) >> DALVIK_JNI_RETURN_SHIFT;
312}
313
314
315/*
316 * ===========================================================================
317 * Utility functions
318 * ===========================================================================
319 */
320
321/*
322 * Entry/exit processing for all JNI calls.
323 *
324 * If TRUSTED_JNIENV is set, we get to skip the (curiously expensive)
325 * thread-local storage lookup on our Thread*. If the caller has passed
326 * the wrong JNIEnv in, we're going to be accessing unsynchronized
327 * structures from more than one thread, and things are going to fail
328 * in bizarre ways. This is only sensible if the native code has been
329 * fully exercised with CheckJNI enabled.
330 */
331#define TRUSTED_JNIENV
332#ifdef TRUSTED_JNIENV
333# define JNI_ENTER() \
334 Thread* _self = ((JNIEnvExt*)env)->self; \
335 CHECK_STACK_SUM(_self); \
336 dvmChangeStatus(_self, THREAD_RUNNING)
337#else
338# define JNI_ENTER() \
339 Thread* _self = dvmThreadSelf(); \
340 UNUSED_PARAMETER(env); \
341 CHECK_STACK_SUM(_self); \
342 dvmChangeStatus(_self, THREAD_RUNNING)
343#endif
344#define JNI_EXIT() \
345 dvmChangeStatus(_self, THREAD_NATIVE); \
346 COMPUTE_STACK_SUM(_self)
347
348#define kGlobalRefsTableInitialSize 512
349#define kGlobalRefsTableMaxSize 51200 /* arbitrary */
350#define kGrefWaterInterval 100
351
352#define kTrackGrefUsage true
353
354/*
Andy McFadden5f612b82009-07-22 15:07:27 -0700355 * Allocate the global references table, and look up some classes for
356 * the benefit of direct buffer access.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800357 */
358bool dvmJniStartup(void)
359{
360 if (!dvmInitReferenceTable(&gDvm.jniGlobalRefTable,
361 kGlobalRefsTableInitialSize, kGlobalRefsTableMaxSize))
362 return false;
363
364 dvmInitMutex(&gDvm.jniGlobalRefLock);
365
366 gDvm.jniGlobalRefLoMark = 0;
367 gDvm.jniGlobalRefHiMark = kGrefWaterInterval * 2;
368
Andy McFadden8e5c7842009-07-23 17:47:18 -0700369 /*
370 * Look up and cache pointers to some direct buffer classes, fields,
371 * and methods.
372 */
373 Method* meth;
374
Andy McFadden5f612b82009-07-22 15:07:27 -0700375 ClassObject* platformAddressClass =
376 dvmFindSystemClassNoInit("Lorg/apache/harmony/luni/platform/PlatformAddress;");
Andy McFadden8e5c7842009-07-23 17:47:18 -0700377 ClassObject* platformAddressFactoryClass =
378 dvmFindSystemClassNoInit("Lorg/apache/harmony/luni/platform/PlatformAddressFactory;");
Andy McFadden5f612b82009-07-22 15:07:27 -0700379 ClassObject* directBufferClass =
380 dvmFindSystemClassNoInit("Lorg/apache/harmony/nio/internal/DirectBuffer;");
Andy McFadden8e5c7842009-07-23 17:47:18 -0700381 ClassObject* readWriteBufferClass =
382 dvmFindSystemClassNoInit("Ljava/nio/ReadWriteDirectByteBuffer;");
383 ClassObject* bufferClass =
384 dvmFindSystemClassNoInit("Ljava/nio/Buffer;");
385
386 if (platformAddressClass == NULL || platformAddressFactoryClass == NULL ||
387 directBufferClass == NULL || readWriteBufferClass == NULL ||
388 bufferClass == NULL)
389 {
Andy McFadden5f612b82009-07-22 15:07:27 -0700390 LOGE("Unable to find internal direct buffer classes\n");
391 return false;
392 }
393 /* needs to be a global ref so CheckJNI thinks we're allowed to see it */
394 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer =
395 addGlobalReference((Object*) directBufferClass);
Andy McFadden8e5c7842009-07-23 17:47:18 -0700396 gDvm.classJavaNioReadWriteDirectByteBuffer = readWriteBufferClass;
Andy McFadden5f612b82009-07-22 15:07:27 -0700397
Andy McFadden8e5c7842009-07-23 17:47:18 -0700398 /*
399 * We need a Method* here rather than a vtable offset, because
400 * DirectBuffer is an interface class.
401 */
Andy McFadden5f612b82009-07-22 15:07:27 -0700402 meth = dvmFindVirtualMethodByDescriptor(
403 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer,
404 "getEffectiveAddress",
405 "()Lorg/apache/harmony/luni/platform/PlatformAddress;");
406 if (meth == NULL) {
407 LOGE("Unable to find PlatformAddress.getEffectiveAddress\n");
408 return false;
409 }
410 gDvm.methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress = meth;
411
412 meth = dvmFindVirtualMethodByDescriptor(platformAddressClass,
413 "toLong", "()J");
414 if (meth == NULL) {
415 LOGE("Unable to find PlatformAddress.toLong\n");
416 return false;
417 }
Andy McFadden8e5c7842009-07-23 17:47:18 -0700418 gDvm.voffOrgApacheHarmonyLuniPlatformPlatformAddress_toLong =
419 meth->methodIndex;
420
421 meth = dvmFindDirectMethodByDescriptor(platformAddressFactoryClass,
422 "on",
423 "(I)Lorg/apache/harmony/luni/platform/PlatformAddress;");
424 if (meth == NULL) {
425 LOGE("Unable to find PlatformAddressFactory.on\n");
426 return false;
427 }
428 gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on = meth;
429
430 meth = dvmFindDirectMethodByDescriptor(readWriteBufferClass,
431 "<init>",
432 "(Lorg/apache/harmony/luni/platform/PlatformAddress;II)V");
433 if (meth == NULL) {
434 LOGE("Unable to find ReadWriteDirectByteBuffer.<init>\n");
435 return false;
436 }
437 gDvm.methJavaNioReadWriteDirectByteBuffer_init = meth;
438
439 gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr =
440 dvmFindFieldOffset(platformAddressClass, "osaddr", "I");
441 if (gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr < 0) {
442 LOGE("Unable to find PlatformAddress.osaddr\n");
443 return false;
444 }
445
446 gDvm.offJavaNioBuffer_capacity =
447 dvmFindFieldOffset(bufferClass, "capacity", "I");
448 if (gDvm.offJavaNioBuffer_capacity < 0) {
449 LOGE("Unable to find Buffer.capacity\n");
450 return false;
451 }
Andy McFadden5f612b82009-07-22 15:07:27 -0700452
Andy McFadden8e696dc2009-07-24 15:28:16 -0700453 gDvm.offJavaNioBuffer_effectiveDirectAddress =
454 dvmFindFieldOffset(bufferClass, "effectiveDirectAddress", "I");
455 if (gDvm.offJavaNioBuffer_effectiveDirectAddress < 0) {
456 LOGE("Unable to find Buffer.effectiveDirectAddress\n");
457 return false;
458 }
459
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800460 return true;
461}
462
463/*
464 * Free the global references table.
465 */
466void dvmJniShutdown(void)
467{
468 dvmClearReferenceTable(&gDvm.jniGlobalRefTable);
469}
470
471
472/*
473 * Find the JNIEnv associated with the current thread.
474 *
475 * Currently stored in the Thread struct. Could also just drop this into
476 * thread-local storage.
477 */
478JNIEnvExt* dvmGetJNIEnvForThread(void)
479{
480 Thread* self = dvmThreadSelf();
481 if (self == NULL)
482 return NULL;
483 return (JNIEnvExt*) dvmGetThreadJNIEnv(self);
484}
485
486/*
487 * Create a new JNIEnv struct and add it to the VM's list.
488 *
489 * "self" will be NULL for the main thread, since the VM hasn't started
490 * yet; the value will be filled in later.
491 */
492JNIEnv* dvmCreateJNIEnv(Thread* self)
493{
494 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
495 JNIEnvExt* newEnv;
496
497 //if (self != NULL)
498 // LOGI("Ent CreateJNIEnv: threadid=%d %p\n", self->threadId, self);
499
500 assert(vm != NULL);
501
502 newEnv = (JNIEnvExt*) calloc(1, sizeof(JNIEnvExt));
503 newEnv->funcTable = &gNativeInterface;
504 newEnv->vm = vm;
505 newEnv->forceDataCopy = vm->forceDataCopy;
506 if (self != NULL) {
507 dvmSetJniEnvThreadId((JNIEnv*) newEnv, self);
508 assert(newEnv->envThreadId != 0);
509 } else {
510 /* make it obvious if we fail to initialize these later */
511 newEnv->envThreadId = 0x77777775;
512 newEnv->self = (Thread*) 0x77777779;
513 }
514 if (vm->useChecked)
515 dvmUseCheckedJniEnv(newEnv);
516
517 dvmLockMutex(&vm->envListLock);
518
519 /* insert at head of list */
520 newEnv->next = vm->envList;
521 assert(newEnv->prev == NULL);
522 if (vm->envList == NULL) // rare, but possible
523 vm->envList = newEnv;
524 else
525 vm->envList->prev = newEnv;
526 vm->envList = newEnv;
527
528 dvmUnlockMutex(&vm->envListLock);
529
530 //if (self != NULL)
531 // LOGI("Xit CreateJNIEnv: threadid=%d %p\n", self->threadId, self);
532 return (JNIEnv*) newEnv;
533}
534
535/*
536 * Remove a JNIEnv struct from the list and free it.
537 */
538void dvmDestroyJNIEnv(JNIEnv* env)
539{
540 JNIEnvExt* extEnv = (JNIEnvExt*) env;
541 JavaVMExt* vm = extEnv->vm;
542 Thread* self;
543
544 if (env == NULL)
545 return;
546
547 self = dvmThreadSelf();
548 assert(self != NULL);
549
550 //LOGI("Ent DestroyJNIEnv: threadid=%d %p\n", self->threadId, self);
551
552 dvmLockMutex(&vm->envListLock);
553
554 if (extEnv == vm->envList) {
555 assert(extEnv->prev == NULL);
556 vm->envList = extEnv->next;
557 } else {
558 assert(extEnv->prev != NULL);
559 extEnv->prev->next = extEnv->next;
560 }
561 if (extEnv->next != NULL)
562 extEnv->next->prev = extEnv->prev;
563
564 dvmUnlockMutex(&extEnv->vm->envListLock);
565
566 free(env);
567 //LOGI("Xit DestroyJNIEnv: threadid=%d %p\n", self->threadId, self);
568}
569
570
571/*
572 * Retrieve the ReferenceTable struct for the current thread.
573 *
574 * If we know the code isn't sharing JNIEnv pointers between threads, we
575 * could put this into env and skip the TLS lookup.
576 */
577static inline ReferenceTable* getLocalRefTable(void)
578{
579 return &dvmThreadSelf()->jniLocalRefTable;
580}
581
582/*
583 * Add a local reference for an object to the current stack frame. When
584 * the native function returns, the reference will be discarded.
585 *
586 * We need to allow the same reference to be added multiple times.
587 *
588 * This will be called on otherwise unreferenced objects. We cannot do
589 * GC allocations here, and it's best if we don't grab a mutex.
590 *
591 * Returns the local reference (currently just the same pointer that was
592 * passed in), or NULL on failure.
593 */
594static jobject addLocalReference(jobject obj)
595{
596 if (obj == NULL)
597 return NULL;
598
599 ReferenceTable* pRef = getLocalRefTable();
600
601 if (!dvmAddToReferenceTable(pRef, (Object*)obj)) {
602 dvmDumpReferenceTable(pRef, "JNI local");
603 LOGE("Failed adding to JNI local ref table (has %d entries)\n",
604 (int) dvmReferenceTableEntries(pRef));
605 dvmDumpThread(dvmThreadSelf(), false);
606 dvmAbort(); // spec says call FatalError; this is equivalent
607 } else {
608 LOGVV("LREF add %p (%s.%s)\n", obj,
609 dvmGetCurrentJNIMethod()->clazz->descriptor,
610 dvmGetCurrentJNIMethod()->name);
611 }
612
613 return obj;
614}
615
616/*
617 * Ensure that at least "capacity" references can be held in the local
618 * refs table of the current thread.
619 */
620static bool ensureLocalCapacity(int capacity)
621{
622 ReferenceTable* pRef = getLocalRefTable();
623
624 return (kJniLocalRefMax - (pRef->nextEntry - pRef->table) >= capacity);
625}
626
627/*
628 * Explicitly delete a reference from the local list.
629 */
630static void deleteLocalReference(jobject obj)
631{
632 if (obj == NULL)
633 return;
634
635 ReferenceTable* pRef = getLocalRefTable();
636 Thread* self = dvmThreadSelf();
637 Object** top = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefTop;
638
639 if (!dvmRemoveFromReferenceTable(pRef, top, (Object*) obj)) {
640 /*
641 * Attempting to delete a local reference that is not in the
642 * topmost local reference frame is a no-op. DeleteLocalRef returns
643 * void and doesn't throw any exceptions, but we should probably
644 * complain about it so the user will notice that things aren't
645 * going quite the way they expect.
646 */
647 LOGW("JNI WARNING: DeleteLocalRef(%p) failed to find entry (valid=%d)\n",
648 obj, dvmIsValidObject((Object*) obj));
649 }
650}
651
652/*
653 * Add a global reference for an object.
654 *
655 * We may add the same object more than once. Add/remove calls are paired,
656 * so it needs to appear on the list multiple times.
657 */
658static jobject addGlobalReference(jobject obj)
659{
660 if (obj == NULL)
661 return NULL;
662
663 //LOGI("adding obj=%p\n", obj);
664 //dvmDumpThread(dvmThreadSelf(), false);
665
666 if (false && ((Object*)obj)->clazz == gDvm.classJavaLangClass) {
667 ClassObject* clazz = (ClassObject*) obj;
668 LOGI("-------\n");
669 LOGI("Adding global ref on class %s\n", clazz->descriptor);
670 dvmDumpThread(dvmThreadSelf(), false);
671 }
672 if (false && ((Object*)obj)->clazz == gDvm.classJavaLangString) {
673 StringObject* strObj = (StringObject*) obj;
674 char* str = dvmCreateCstrFromString(strObj);
675 if (strcmp(str, "sync-response") == 0) {
676 LOGI("-------\n");
677 LOGI("Adding global ref on string '%s'\n", str);
678 dvmDumpThread(dvmThreadSelf(), false);
679 //dvmAbort();
680 }
681 free(str);
682 }
683 if (false && ((Object*)obj)->clazz == gDvm.classArrayByte) {
684 ArrayObject* arrayObj = (ArrayObject*) obj;
685 if (arrayObj->length == 8192 &&
686 dvmReferenceTableEntries(&gDvm.jniGlobalRefTable) > 400)
687 {
688 LOGI("Adding global ref on byte array %p (len=%d)\n",
689 arrayObj, arrayObj->length);
690 dvmDumpThread(dvmThreadSelf(), false);
691 }
692 }
693
694 dvmLockMutex(&gDvm.jniGlobalRefLock);
695
696 /*
697 * Expanding the table should happen rarely, so I'm not overly
698 * concerned about the performance impact of copying the old list
699 * over. We shouldn't see one-time activity spikes, so freeing
700 * up storage shouldn't be required.
701 *
702 * Throwing an exception on failure is problematic, because JNI code
703 * may not be expecting an exception, and things sort of cascade. We
704 * want to have a hard limit to catch leaks during debugging, but this
705 * otherwise needs to expand until memory is consumed. As a practical
706 * matter, if we have many thousands of global references, chances are
707 * we're either leaking global ref table entries or we're going to
708 * run out of space in the GC heap.
709 */
710 if (!dvmAddToReferenceTable(&gDvm.jniGlobalRefTable, (Object*)obj)) {
711 dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global");
712 LOGE("Failed adding to JNI global ref table (%d entries)\n",
713 (int) dvmReferenceTableEntries(&gDvm.jniGlobalRefTable));
714 dvmAbort();
715 }
716
717 LOGVV("GREF add %p (%s.%s)\n", obj,
718 dvmGetCurrentJNIMethod()->clazz->descriptor,
719 dvmGetCurrentJNIMethod()->name);
720
721 /* GREF usage tracking; should probably be disabled for production env */
722 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
723 int count = dvmReferenceTableEntries(&gDvm.jniGlobalRefTable);
724 if (count > gDvm.jniGlobalRefHiMark) {
725 LOGD("GREF has increased to %d\n", count);
726 gDvm.jniGlobalRefHiMark += kGrefWaterInterval;
727 gDvm.jniGlobalRefLoMark += kGrefWaterInterval;
728
729 /* watch for "excessive" use; not generally appropriate */
730 if (count >= gDvm.jniGrefLimit) {
731 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
732 if (vm->warnError) {
733 dvmDumpReferenceTable(&gDvm.jniGlobalRefTable,"JNI global");
734 LOGE("Excessive JNI global references (%d)\n", count);
735 dvmAbort();
736 } else {
737 LOGW("Excessive JNI global references (%d)\n", count);
738 }
739 }
740 }
741 }
742
743bail:
744 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
745 return obj;
746}
747
748/*
749 * Remove a global reference. In most cases it's the entry most recently
750 * added, which makes this pretty quick.
751 *
752 * Thought: if it's not the most recent entry, just null it out. When we
753 * fill up, do a compaction pass before we expand the list.
754 */
755static void deleteGlobalReference(jobject obj)
756{
757 if (obj == NULL)
758 return;
759
760 dvmLockMutex(&gDvm.jniGlobalRefLock);
761
762 if (!dvmRemoveFromReferenceTable(&gDvm.jniGlobalRefTable,
763 gDvm.jniGlobalRefTable.table, obj))
764 {
765 LOGW("JNI: DeleteGlobalRef(%p) failed to find entry (valid=%d)\n",
766 obj, dvmIsValidObject((Object*) obj));
767 goto bail;
768 }
769
770 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
771 int count = dvmReferenceTableEntries(&gDvm.jniGlobalRefTable);
772 if (count < gDvm.jniGlobalRefLoMark) {
773 LOGD("GREF has decreased to %d\n", count);
774 gDvm.jniGlobalRefHiMark -= kGrefWaterInterval;
775 gDvm.jniGlobalRefLoMark -= kGrefWaterInterval;
776 }
777 }
778
779bail:
780 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
781}
782
783/*
784 * GC helper function to mark all JNI global references.
785 */
786void dvmGcMarkJniGlobalRefs()
787{
788 Object **op;
789
790 dvmLockMutex(&gDvm.jniGlobalRefLock);
791
792 op = gDvm.jniGlobalRefTable.table;
793 while ((uintptr_t)op < (uintptr_t)gDvm.jniGlobalRefTable.nextEntry) {
794 dvmMarkObjectNonNull(*(op++));
795 }
796
797 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
798}
799
800
801/*
802 * Determine if "obj" appears in the argument list for the native method.
803 *
804 * We use the "shorty" signature to determine which argument slots hold
805 * reference types.
806 */
807static bool findInArgList(Thread* self, Object* obj)
808{
809 const Method* meth;
810 u4* fp;
811 int i;
812
813 fp = self->curFrame;
814 while (1) {
815 /*
816 * Back up over JNI PushLocalFrame frames. This works because the
817 * previous frame on the interpreted stack is either a break frame
818 * (if we called here via native code) or an interpreted method (if
819 * we called here via the interpreter). In both cases the method
820 * pointer won't match.
821 */
822 StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
823 meth = saveArea->method;
824 if (meth != SAVEAREA_FROM_FP(saveArea->prevFrame)->method)
825 break;
826 fp = saveArea->prevFrame;
827 }
828
829 LOGVV("+++ scanning %d args in %s (%s)\n",
830 meth->insSize, meth->name, meth->shorty);
831 const char* shorty = meth->shorty +1; /* skip return type char */
832 for (i = 0; i < meth->insSize; i++) {
833 if (i == 0 && !dvmIsStaticMethod(meth)) {
834 /* first arg is "this" ref, not represented in "shorty" */
835 if (fp[i] == (u4) obj)
836 return true;
837 } else {
838 /* if this is a reference type, see if it matches */
839 switch (*shorty) {
840 case 'L':
841 if (fp[i] == (u4) obj)
842 return true;
843 break;
844 case 'D':
845 case 'J':
846 i++;
847 break;
848 case '\0':
849 LOGE("Whoops! ran off the end of %s (%d)\n",
850 meth->shorty, meth->insSize);
851 break;
852 default:
853 if (fp[i] == (u4) obj)
854 LOGI("NOTE: ref %p match on arg type %c\n", obj, *shorty);
855 break;
856 }
857 shorty++;
858 }
859 }
860
861 /*
862 * For static methods, we also pass a class pointer in.
863 */
864 if (dvmIsStaticMethod(meth)) {
865 //LOGI("+++ checking class pointer in %s\n", meth->name);
866 if ((void*)obj == (void*)meth->clazz)
867 return true;
868 }
869 return false;
870}
871
872/*
873 * Verify that a reference passed in from native code is one that the
874 * code is allowed to have.
875 *
876 * It's okay for native code to pass us a reference that:
877 * - was just passed in as an argument when invoked by native code
878 * - was returned to it from JNI (and is now in the JNI local refs table)
879 * - is present in the JNI global refs table
880 * The first one is a little awkward. The latter two are just table lookups.
881 *
882 * Used by -Xcheck:jni and GetObjectRefType.
883 *
884 * NOTE: in the current VM, global and local references are identical. If
885 * something is both global and local, we can't tell them apart, and always
886 * return "local".
887 */
888jobjectRefType dvmGetJNIRefType(Object* obj)
889{
890 ReferenceTable* pRef = getLocalRefTable();
891 Thread* self = dvmThreadSelf();
892 //Object** top;
893 Object** ptr;
894
895 /* check args */
896 if (findInArgList(self, obj)) {
897 //LOGI("--- REF found %p on stack\n", obj);
898 return JNILocalRefType;
899 }
900
901 /* check locals */
902 //top = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefTop;
903 if (dvmFindInReferenceTable(pRef, pRef->table, obj) != NULL) {
904 //LOGI("--- REF found %p in locals\n", obj);
905 return JNILocalRefType;
906 }
907
908 /* check globals */
909 dvmLockMutex(&gDvm.jniGlobalRefLock);
910 if (dvmFindInReferenceTable(&gDvm.jniGlobalRefTable,
911 gDvm.jniGlobalRefTable.table, obj))
912 {
913 //LOGI("--- REF found %p in globals\n", obj);
914 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
915 return JNIGlobalRefType;
916 }
917 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
918
919 /* not found! */
920 return JNIInvalidRefType;
921}
922
923/*
924 * Register a method that uses JNI calling conventions.
925 */
926static bool dvmRegisterJNIMethod(ClassObject* clazz, const char* methodName,
927 const char* signature, void* fnPtr)
928{
929 Method* method;
930 bool result = false;
931
932 if (fnPtr == NULL)
933 goto bail;
934
935 method = dvmFindDirectMethodByDescriptor(clazz, methodName, signature);
936 if (method == NULL)
937 method = dvmFindVirtualMethodByDescriptor(clazz, methodName, signature);
938 if (method == NULL) {
939 LOGW("ERROR: Unable to find decl for native %s.%s %s\n",
940 clazz->descriptor, methodName, signature);
941 goto bail;
942 }
943
944 if (!dvmIsNativeMethod(method)) {
945 LOGW("Unable to register: not native: %s.%s %s\n",
946 clazz->descriptor, methodName, signature);
947 goto bail;
948 }
949
950 if (method->nativeFunc != dvmResolveNativeMethod) {
951 LOGW("Warning: %s.%s %s was already registered/resolved?\n",
952 clazz->descriptor, methodName, signature);
953 /* keep going, I guess */
954 }
955
956 /*
957 * Point "nativeFunc" at the JNI bridge, and overload "insns" to
958 * point at the actual function.
959 */
960 if (dvmIsSynchronizedMethod(method))
961 dvmSetNativeFunc(method, dvmCallSynchronizedJNIMethod, fnPtr);
962 else
963 dvmSetNativeFunc(method, dvmCallJNIMethod, fnPtr);
964
965 LOGV("JNI-registered %s.%s %s\n", clazz->descriptor, methodName,
966 signature);
967 result = true;
968
969bail:
970 return result;
971}
972
973/*
974 * Get the method currently being executed by examining the interp stack.
975 */
976const Method* dvmGetCurrentJNIMethod(void)
977{
978 assert(dvmThreadSelf() != NULL);
979
980 void* fp = dvmThreadSelf()->curFrame;
981 const Method* meth = SAVEAREA_FROM_FP(fp)->method;
982
983 assert(meth != NULL);
984 assert(dvmIsNativeMethod(meth));
985 return meth;
986}
987
988
989/*
990 * Track a JNI MonitorEnter in the current thread.
991 *
992 * The goal is to be able to "implicitly" release all JNI-held monitors
993 * when the thread detaches.
994 *
995 * Monitors may be entered multiple times, so we add a new entry for each
996 * enter call. It would be more efficient to keep a counter. At present
997 * there's no real motivation to improve this however.
998 */
999static void trackMonitorEnter(Thread* self, Object* obj)
1000{
1001 static const int kInitialSize = 16;
1002 ReferenceTable* refTable = &self->jniMonitorRefTable;
1003
1004 /* init table on first use */
1005 if (refTable->table == NULL) {
1006 assert(refTable->maxEntries == 0);
1007
1008 if (!dvmInitReferenceTable(refTable, kInitialSize, INT_MAX)) {
1009 LOGE("Unable to initialize monitor tracking table\n");
1010 dvmAbort();
1011 }
1012 }
1013
1014 if (!dvmAddToReferenceTable(refTable, obj)) {
1015 /* ran out of memory? could throw exception instead */
1016 LOGE("Unable to add entry to monitor tracking table\n");
1017 dvmAbort();
1018 } else {
1019 LOGVV("--- added monitor %p\n", obj);
1020 }
1021}
1022
1023/*
1024 * Track a JNI MonitorExit in the current thread.
1025 */
1026static void trackMonitorExit(Thread* self, Object* obj)
1027{
1028 ReferenceTable* refTable = &self->jniMonitorRefTable;
1029
1030 if (!dvmRemoveFromReferenceTable(refTable, refTable->table, obj)) {
1031 LOGE("JNI monitor %p not found in tracking list\n", obj);
1032 /* keep going? */
1033 } else {
1034 LOGVV("--- removed monitor %p\n", obj);
1035 }
1036}
1037
1038/*
1039 * Release all monitors held by the jniMonitorRefTable list.
1040 */
1041void dvmReleaseJniMonitors(Thread* self)
1042{
1043 ReferenceTable* refTable = &self->jniMonitorRefTable;
1044 Object** top = refTable->table;
1045
1046 if (top == NULL)
1047 return;
1048
1049 Object** ptr = refTable->nextEntry;
1050 while (--ptr >= top) {
1051 if (!dvmUnlockObject(self, *ptr)) {
1052 LOGW("Unable to unlock monitor %p at thread detach\n", *ptr);
1053 } else {
1054 LOGVV("--- detach-releasing monitor %p\n", *ptr);
1055 }
1056 }
1057
1058 /* zap it */
1059 refTable->nextEntry = refTable->table;
1060}
1061
1062#ifdef WITH_JNI_STACK_CHECK
1063/*
1064 * Compute a CRC on the entire interpreted stack.
1065 *
1066 * Would be nice to compute it on "self" as well, but there are parts of
1067 * the Thread that can be altered by other threads (e.g. prev/next pointers).
1068 */
1069static void computeStackSum(Thread* self)
1070{
1071 const u1* low = (const u1*)SAVEAREA_FROM_FP(self->curFrame);
1072 u4 crc = dvmInitCrc32();
1073 self->stackCrc = 0;
1074 crc = dvmComputeCrc32(crc, low, self->interpStackStart - low);
1075 self->stackCrc = crc;
1076}
1077
1078/*
1079 * Compute a CRC on the entire interpreted stack, and compare it to what
1080 * we previously computed.
1081 *
1082 * We can execute JNI directly from native code without calling in from
1083 * interpreted code during VM initialization and immediately after JNI
1084 * thread attachment. Another opportunity exists during JNI_OnLoad. Rather
1085 * than catching these cases we just ignore them here, which is marginally
1086 * less accurate but reduces the amount of code we have to touch with #ifdefs.
1087 */
1088static void checkStackSum(Thread* self)
1089{
1090 const u1* low = (const u1*)SAVEAREA_FROM_FP(self->curFrame);
1091 u4 stackCrc, crc;
1092
1093 stackCrc = self->stackCrc;
1094 self->stackCrc = 0;
1095 crc = dvmInitCrc32();
1096 crc = dvmComputeCrc32(crc, low, self->interpStackStart - low);
1097 if (crc != stackCrc) {
1098 const Method* meth = dvmGetCurrentJNIMethod();
1099 if (dvmComputeExactFrameDepth(self->curFrame) == 1) {
1100 LOGD("JNI: bad stack CRC (0x%08x) -- okay during init\n",
1101 stackCrc);
1102 } else if (strcmp(meth->name, "nativeLoad") == 0 &&
1103 (strcmp(meth->clazz->descriptor, "Ljava/lang/Runtime;") == 0))
1104 {
1105 LOGD("JNI: bad stack CRC (0x%08x) -- okay during JNI_OnLoad\n",
1106 stackCrc);
1107 } else {
1108 LOGW("JNI: bad stack CRC (%08x vs %08x)\n", crc, stackCrc);
1109 dvmAbort();
1110 }
1111 }
1112 self->stackCrc = (u4) -1; /* make logic errors more noticeable */
1113}
1114#endif
1115
1116
1117/*
1118 * ===========================================================================
1119 * JNI implementation
1120 * ===========================================================================
1121 */
1122
1123/*
1124 * Return the version of the native method interface.
1125 */
1126static jint GetVersion(JNIEnv* env)
1127{
1128 JNI_ENTER();
1129 /*
1130 * There is absolutely no need to toggle the mode for correct behavior.
1131 * However, it does provide native code with a simple "suspend self
1132 * if necessary" call.
1133 */
1134 JNI_EXIT();
1135 return JNI_VERSION_1_6;
1136}
1137
1138/*
1139 * Create a new class from a bag of bytes.
1140 *
1141 * This is not currently supported within Dalvik.
1142 */
1143static jclass DefineClass(JNIEnv* env, const char *name, jobject loader,
1144 const jbyte* buf, jsize bufLen)
1145{
1146 UNUSED_PARAMETER(name);
1147 UNUSED_PARAMETER(loader);
1148 UNUSED_PARAMETER(buf);
1149 UNUSED_PARAMETER(bufLen);
1150
1151 JNI_ENTER();
1152 LOGW("Rejecting JNI DefineClass request\n");
1153 JNI_EXIT();
1154 return NULL;
1155}
1156
1157/*
1158 * Find a class by name.
1159 *
1160 * We have to use the "no init" version of FindClass here, because we might
1161 * be getting the class prior to registering native methods that will be
1162 * used in <clinit>.
1163 *
1164 * We need to get the class loader associated with the current native
1165 * method. If there is no native method, e.g. we're calling this from native
1166 * code right after creating the VM, the spec says we need to use the class
1167 * loader returned by "ClassLoader.getBaseClassLoader". There is no such
1168 * method, but it's likely they meant ClassLoader.getSystemClassLoader.
1169 * We can't get that until after the VM has initialized though.
1170 */
1171static jclass FindClass(JNIEnv* env, const char* name)
1172{
1173 JNI_ENTER();
1174
1175 const Method* thisMethod;
1176 ClassObject* clazz;
1177 Object* loader;
1178 char* descriptor = NULL;
1179
1180 thisMethod = dvmGetCurrentJNIMethod();
1181 assert(thisMethod != NULL);
1182
1183 descriptor = dvmNameToDescriptor(name);
1184 if (descriptor == NULL) {
1185 clazz = NULL;
1186 goto bail;
1187 }
1188
1189 //Thread* self = dvmThreadSelf();
1190 if (_self->classLoaderOverride != NULL) {
1191 /* hack for JNI_OnLoad */
1192 assert(strcmp(thisMethod->name, "nativeLoad") == 0);
1193 loader = _self->classLoaderOverride;
1194 } else if (thisMethod == gDvm.methFakeNativeEntry) {
1195 /* start point of invocation interface */
1196 if (!gDvm.initializing)
1197 loader = dvmGetSystemClassLoader();
1198 else
1199 loader = NULL;
1200 } else {
1201 loader = thisMethod->clazz->classLoader;
1202 }
1203
1204 clazz = dvmFindClassNoInit(descriptor, loader);
1205 clazz = addLocalReference(clazz);
1206
1207bail:
1208 free(descriptor);
1209
1210 JNI_EXIT();
1211 return (jclass)clazz;
1212}
1213
1214/*
1215 * Return the superclass of a class.
1216 */
1217static jclass GetSuperclass(JNIEnv* env, jclass clazz)
1218{
1219 JNI_ENTER();
1220 jclass super = (jclass) ((ClassObject*) clazz)->super;
1221 super = addLocalReference(super);
1222 JNI_EXIT();
1223 return super;
1224}
1225
1226/*
1227 * Determine whether an object of clazz1 can be safely cast to clazz2.
1228 *
1229 * Like IsInstanceOf, but with a pair of class objects instead of obj+class.
1230 */
1231static jboolean IsAssignableFrom(JNIEnv* env, jclass clazz1, jclass clazz2)
1232{
1233 JNI_ENTER();
1234
1235 jboolean result;
1236 result = dvmInstanceof((ClassObject*) clazz1, (ClassObject*) clazz2);
1237
1238 JNI_EXIT();
1239 return result;
1240}
1241
1242/*
1243 * Given a java.lang.reflect.Method or .Constructor, return a methodID.
1244 */
1245static jmethodID FromReflectedMethod(JNIEnv* env, jobject method)
1246{
1247 JNI_ENTER();
1248 jmethodID methodID;
1249 methodID = (jmethodID) dvmGetMethodFromReflectObj((Object*)method);
1250 JNI_EXIT();
1251 return methodID;
1252}
1253
1254/*
1255 * Given a java.lang.reflect.Field, return a fieldID.
1256 */
1257static jfieldID FromReflectedField(JNIEnv* env, jobject field)
1258{
1259 JNI_ENTER();
1260 jfieldID fieldID = (jfieldID) dvmGetFieldFromReflectObj((Object*)field);
1261 JNI_EXIT();
1262 return fieldID;
1263}
1264
1265/*
1266 * Convert a methodID to a java.lang.reflect.Method or .Constructor.
1267 *
1268 * (The "isStatic" field does not appear in the spec.)
1269 *
1270 * Throws OutOfMemory and returns NULL on failure.
1271 */
1272static jobject ToReflectedMethod(JNIEnv* env, jclass cls, jmethodID methodID,
1273 jboolean isStatic)
1274{
1275 JNI_ENTER();
1276 jobject obj;
1277 obj = (jobject) dvmCreateReflectObjForMethod((ClassObject*) cls,
1278 (Method*) methodID);
1279 dvmReleaseTrackedAlloc(obj, NULL);
1280 obj = addLocalReference(obj);
1281 JNI_EXIT();
1282 return obj;
1283}
1284
1285/*
1286 * Convert a fieldID to a java.lang.reflect.Field.
1287 *
1288 * (The "isStatic" field does not appear in the spec.)
1289 *
1290 * Throws OutOfMemory and returns NULL on failure.
1291 */
1292static jobject ToReflectedField(JNIEnv* env, jclass cls, jfieldID fieldID,
1293 jboolean isStatic)
1294{
1295 JNI_ENTER();
1296 jobject obj;
1297 obj = (jobject) dvmCreateReflectObjForField((ClassObject*) cls,
1298 (Field*) fieldID);
1299 dvmReleaseTrackedAlloc(obj, NULL);
1300 obj = addLocalReference(obj);
1301 JNI_EXIT();
1302 return obj;
1303}
1304
1305
1306/*
1307 * Take this exception and throw it.
1308 */
1309static jint Throw(JNIEnv* env, jthrowable obj)
1310{
1311 JNI_ENTER();
1312
1313 jint retval;
1314
1315 if (obj != NULL) {
1316 dvmSetException(_self, obj);
1317 retval = JNI_OK;
1318 } else
1319 retval = JNI_ERR;
1320
1321 JNI_EXIT();
1322 return retval;
1323}
1324
1325/*
1326 * Constructs an exeption object from the specified class with the message
1327 * specified by "message", and throws it.
1328 */
1329static jint ThrowNew(JNIEnv* env, jclass clazz, const char* message)
1330{
1331 JNI_ENTER();
1332
1333 ClassObject* classObj = (ClassObject*) clazz;
1334
1335 dvmThrowExceptionByClass(classObj, message);
1336
1337 JNI_EXIT();
1338 return JNI_OK;
1339}
1340
1341/*
1342 * If an exception is being thrown, return the exception object. Otherwise,
1343 * return NULL.
1344 *
1345 * TODO: if there is no pending exception, we should be able to skip the
1346 * enter/exit checks. If we find one, we need to enter and then re-fetch
1347 * the exception (in case it got moved by a compacting GC).
1348 */
1349static jthrowable ExceptionOccurred(JNIEnv* env)
1350{
1351 JNI_ENTER();
1352
1353 Object* exception;
1354 Object* localException;
1355
1356 exception = (Object*) dvmGetException(_self);
1357 localException = addLocalReference(exception);
1358 if (localException == NULL && exception != NULL) {
1359 /*
1360 * We were unable to add a new local reference, and threw a new
1361 * exception. We can't return "exception", because it's not a
1362 * local reference. So we have to return NULL, indicating that
1363 * there was no exception, even though it's pretty much raining
1364 * exceptions in here.
1365 */
1366 LOGW("JNI WARNING: addLocal/exception combo\n");
1367 }
1368
1369 JNI_EXIT();
1370 return localException;
1371}
1372
1373/*
1374 * Print an exception and stack trace to stderr.
1375 */
1376static void ExceptionDescribe(JNIEnv* env)
1377{
1378 JNI_ENTER();
1379
1380 Object* exception = dvmGetException(_self);
1381 if (exception != NULL) {
1382 dvmPrintExceptionStackTrace();
1383 } else {
1384 LOGI("Odd: ExceptionDescribe called, but no exception pending\n");
1385 }
1386
1387 JNI_EXIT();
1388}
1389
1390/*
1391 * Clear the exception currently being thrown.
1392 *
1393 * TODO: we should be able to skip the enter/exit stuff.
1394 */
1395static void ExceptionClear(JNIEnv* env)
1396{
1397 JNI_ENTER();
1398 dvmClearException(_self);
1399 JNI_EXIT();
1400}
1401
1402/*
1403 * Kill the VM. This function does not return.
1404 */
1405static void FatalError(JNIEnv* env, const char* msg)
1406{
1407 //dvmChangeStatus(NULL, THREAD_RUNNING);
1408 LOGE("JNI posting fatal error: %s\n", msg);
1409 dvmAbort();
1410}
1411
1412/*
1413 * Push a new JNI frame on the stack, with a new set of locals.
1414 *
1415 * The new frame must have the same method pointer. (If for no other
1416 * reason than FindClass needs it to get the appropriate class loader.)
1417 */
1418static jint PushLocalFrame(JNIEnv* env, jint capacity)
1419{
1420 JNI_ENTER();
1421 int result = JNI_OK;
1422 if (!ensureLocalCapacity(capacity) ||
1423 !dvmPushLocalFrame(_self /*dvmThreadSelf()*/, dvmGetCurrentJNIMethod()))
1424 {
1425 /* yes, OutOfMemoryError, not StackOverflowError */
1426 dvmClearException(_self);
1427 dvmThrowException("Ljava/lang/OutOfMemoryError;",
1428 "out of stack in JNI PushLocalFrame");
1429 result = JNI_ERR;
1430 }
1431 JNI_EXIT();
1432 return result;
1433}
1434
1435/*
1436 * Pop the local frame off. If "result" is not null, add it as a
1437 * local reference on the now-current frame.
1438 */
1439static jobject PopLocalFrame(JNIEnv* env, jobject result)
1440{
1441 JNI_ENTER();
1442 if (!dvmPopLocalFrame(_self /*dvmThreadSelf()*/)) {
1443 LOGW("JNI WARNING: too many PopLocalFrame calls\n");
1444 dvmClearException(_self);
1445 dvmThrowException("Ljava/lang/RuntimeException;",
1446 "too many PopLocalFrame calls");
1447 }
1448 result = addLocalReference(result);
1449 JNI_EXIT();
1450 return result;
1451}
1452
1453/*
1454 * Add a reference to the global list.
1455 */
1456static jobject NewGlobalRef(JNIEnv* env, jobject obj)
1457{
1458 JNI_ENTER();
1459 jobject retval = addGlobalReference(obj);
1460 JNI_EXIT();
1461 return retval;
1462}
1463
1464/*
1465 * Delete a reference from the global list.
1466 */
1467static void DeleteGlobalRef(JNIEnv* env, jobject globalRef)
1468{
1469 JNI_ENTER();
1470 deleteGlobalReference(globalRef);
1471 JNI_EXIT();
1472}
1473
1474
1475/*
1476 * Add a reference to the local list.
1477 */
1478static jobject NewLocalRef(JNIEnv* env, jobject ref)
1479{
1480 JNI_ENTER();
1481
1482 jobject retval = addLocalReference(ref);
1483
1484 JNI_EXIT();
1485 return retval;
1486}
1487
1488/*
1489 * Delete a reference from the local list.
1490 */
1491static void DeleteLocalRef(JNIEnv* env, jobject localRef)
1492{
1493 JNI_ENTER();
1494 deleteLocalReference(localRef);
1495 JNI_EXIT();
1496}
1497
1498/*
1499 * Ensure that the local references table can hold at least this many
1500 * references.
1501 */
1502static jint EnsureLocalCapacity(JNIEnv *env, jint capacity)
1503{
1504 JNI_ENTER();
1505 bool okay = ensureLocalCapacity(capacity);
1506 if (!okay) {
1507 dvmThrowException("Ljava/lang/OutOfMemoryError;",
1508 "can't ensure local reference capacity");
1509 }
1510 JNI_EXIT();
1511 if (okay)
1512 return 0;
1513 else
1514 return -1;
1515}
1516
1517
1518/*
1519 * Determine whether two Object references refer to the same underlying object.
1520 */
1521static jboolean IsSameObject(JNIEnv* env, jobject ref1, jobject ref2)
1522{
1523 JNI_ENTER();
1524 jboolean result = (ref1 == ref2);
1525 JNI_EXIT();
1526 return result;
1527}
1528
1529/*
1530 * Allocate a new object without invoking any constructors.
1531 */
1532static jobject AllocObject(JNIEnv* env, jclass jclazz)
1533{
1534 JNI_ENTER();
1535
1536 ClassObject* clazz = (ClassObject*) jclazz;
1537 jobject newObj;
1538
1539 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
1540 assert(dvmCheckException(_self));
1541 newObj = NULL;
1542 } else {
1543 newObj = (jobject) dvmAllocObject(clazz, ALLOC_DONT_TRACK);
1544 newObj = addLocalReference(newObj);
1545 }
1546
1547 JNI_EXIT();
1548 return newObj;
1549}
1550
1551/*
1552 * Construct a new object.
1553 */
1554static jobject NewObject(JNIEnv* env, jclass jclazz, jmethodID methodID, ...)
1555{
1556 JNI_ENTER();
1557
1558 ClassObject* clazz = (ClassObject*) jclazz;
1559 jobject newObj;
1560
1561 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
1562 assert(dvmCheckException(_self));
1563 newObj = NULL;
1564 } else {
1565 newObj = (jobject) dvmAllocObject(clazz, ALLOC_DONT_TRACK);
1566 newObj = addLocalReference(newObj);
1567 if (newObj != NULL) {
1568 JValue unused;
1569 va_list args;
1570 va_start(args, methodID);
1571 dvmCallMethodV(_self, (Method*) methodID, (Object*)newObj, &unused,
1572 args);
1573 va_end(args);
1574 }
1575 }
1576
1577 JNI_EXIT();
1578 return newObj;
1579}
1580static jobject NewObjectV(JNIEnv* env, jclass clazz, jmethodID methodID,
1581 va_list args)
1582{
1583 JNI_ENTER();
1584
1585 jobject newObj;
1586 newObj = (jobject) dvmAllocObject((ClassObject*) clazz, ALLOC_DONT_TRACK);
1587 newObj = addLocalReference(newObj);
1588 if (newObj != NULL) {
1589 JValue unused;
1590 dvmCallMethodV(_self, (Method*) methodID, (Object*)newObj, &unused,
1591 args);
1592 }
1593
1594 JNI_EXIT();
1595 return newObj;
1596}
1597static jobject NewObjectA(JNIEnv* env, jclass clazz, jmethodID methodID,
1598 jvalue* args)
1599{
1600 JNI_ENTER();
1601
1602 jobject newObj;
1603 newObj = (jobject) dvmAllocObject((ClassObject*) clazz, ALLOC_DONT_TRACK);
1604 newObj = addLocalReference(newObj);
1605 if (newObj != NULL) {
1606 JValue unused;
1607 dvmCallMethodA(_self, (Method*) methodID, (Object*)newObj, &unused,
1608 args);
1609 }
1610
1611 JNI_EXIT();
1612 return newObj;
1613}
1614
1615/*
1616 * Returns the class of an object.
1617 *
1618 * JNI spec says: obj must not be NULL.
1619 */
1620static jclass GetObjectClass(JNIEnv* env, jobject obj)
1621{
1622 JNI_ENTER();
1623
1624 assert(obj != NULL);
1625
1626 jclass clazz;
1627 clazz = (jclass) ((Object*)obj)->clazz;
1628 clazz = addLocalReference(clazz);
1629
1630 JNI_EXIT();
1631 return clazz;
1632}
1633
1634/*
1635 * Determine whether "obj" is an instance of "clazz".
1636 */
1637static jboolean IsInstanceOf(JNIEnv* env, jobject obj, jclass clazz)
1638{
1639 JNI_ENTER();
1640
1641 jboolean result;
1642
1643 if (obj == NULL)
1644 result = true;
1645 else
1646 result = dvmInstanceof(((Object*)obj)->clazz, (ClassObject*) clazz);
1647
1648 JNI_EXIT();
1649 return result;
1650}
1651
1652/*
1653 * Get a method ID for an instance method.
1654 *
1655 * JNI defines <init> as an instance method, but Dalvik considers it a
1656 * "direct" method, so we have to special-case it here.
1657 *
1658 * Dalvik also puts all private methods into the "direct" list, so we
1659 * really need to just search both lists.
1660 */
1661static jmethodID GetMethodID(JNIEnv* env, jclass jclazz, const char* name,
1662 const char* sig)
1663{
1664 JNI_ENTER();
1665
1666 ClassObject* clazz = (ClassObject*) jclazz;
1667 jmethodID id = NULL;
1668
1669 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
1670 assert(dvmCheckException(_self));
1671 } else {
1672 Method* meth;
1673
1674 meth = dvmFindVirtualMethodHierByDescriptor(clazz, name, sig);
1675 if (meth == NULL) {
1676 /* search private methods and constructors; non-hierarchical */
1677 meth = dvmFindDirectMethodByDescriptor(clazz, name, sig);
1678 }
1679 if (meth != NULL && dvmIsStaticMethod(meth)) {
1680 IF_LOGD() {
1681 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
1682 LOGD("GetMethodID: not returning static method %s.%s %s\n",
1683 clazz->descriptor, meth->name, desc);
1684 free(desc);
1685 }
1686 meth = NULL;
1687 }
1688 if (meth == NULL) {
1689 LOGI("Method not found: '%s' '%s' in %s\n",
1690 name, sig, clazz->descriptor);
1691 dvmThrowException("Ljava/lang/NoSuchMethodError;", name);
1692 }
1693
1694 /*
1695 * The method's class may not be the same as clazz, but if
1696 * it isn't this must be a virtual method and the class must
1697 * be a superclass (and, hence, already initialized).
1698 */
1699 if (meth != NULL) {
1700 assert(dvmIsClassInitialized(meth->clazz) ||
1701 dvmIsClassInitializing(meth->clazz));
1702 }
1703 id = (jmethodID) meth;
1704 }
1705 JNI_EXIT();
1706 return id;
1707}
1708
1709/*
1710 * Get a field ID (instance fields).
1711 */
1712static jfieldID GetFieldID(JNIEnv* env, jclass jclazz,
1713 const char* name, const char* sig)
1714{
1715 JNI_ENTER();
1716
1717 ClassObject* clazz = (ClassObject*) jclazz;
1718 jfieldID id;
1719
1720 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
1721 assert(dvmCheckException(_self));
1722 id = NULL;
1723 } else {
1724 id = (jfieldID) dvmFindInstanceFieldHier(clazz, name, sig);
1725 if (id == NULL)
1726 dvmThrowException("Ljava/lang/NoSuchFieldError;", name);
1727 }
1728 JNI_EXIT();
1729 return id;
1730}
1731
1732/*
1733 * Get the method ID for a static method in a class.
1734 */
1735static jmethodID GetStaticMethodID(JNIEnv* env, jclass jclazz,
1736 const char* name, const char* sig)
1737{
1738 JNI_ENTER();
1739
1740 ClassObject* clazz = (ClassObject*) jclazz;
1741 jmethodID id;
1742
1743 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
1744 assert(dvmCheckException(_self));
1745 id = NULL;
1746 } else {
1747 Method* meth;
1748
1749 meth = dvmFindDirectMethodHierByDescriptor(clazz, name, sig);
1750
1751 /* make sure it's static, not virtual+private */
1752 if (meth != NULL && !dvmIsStaticMethod(meth)) {
1753 IF_LOGD() {
1754 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
1755 LOGD("GetStaticMethodID: "
1756 "not returning nonstatic method %s.%s %s\n",
1757 clazz->descriptor, meth->name, desc);
1758 free(desc);
1759 }
1760 meth = NULL;
1761 }
1762
1763 id = (jmethodID) meth;
1764 if (id == NULL)
1765 dvmThrowException("Ljava/lang/NoSuchMethodError;", name);
1766 }
1767
1768 JNI_EXIT();
1769 return id;
1770}
1771
1772/*
1773 * Get a field ID (static fields).
1774 */
1775static jfieldID GetStaticFieldID(JNIEnv* env, jclass jclazz,
1776 const char* name, const char* sig)
1777{
1778 JNI_ENTER();
1779
1780 ClassObject* clazz = (ClassObject*) jclazz;
1781 jfieldID id;
1782
1783 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
1784 assert(dvmCheckException(_self));
1785 id = NULL;
1786 } else {
1787 id = (jfieldID) dvmFindStaticField(clazz, name, sig);
1788 if (id == NULL)
1789 dvmThrowException("Ljava/lang/NoSuchFieldError;", name);
1790 }
1791 JNI_EXIT();
1792 return id;
1793}
1794
1795/*
1796 * Get a static field.
1797 *
1798 * If we get an object reference, add it to the local refs list.
1799 */
1800#define GET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) \
1801 static _ctype GetStatic##_jname##Field(JNIEnv* env, jclass clazz, \
1802 jfieldID fieldID) \
1803 { \
1804 UNUSED_PARAMETER(clazz); \
1805 JNI_ENTER(); \
1806 StaticField* sfield = (StaticField*) fieldID; \
1807 _ctype value = dvmGetStaticField##_jname(sfield); \
1808 if (_isref) /* only when _ctype==jobject */ \
1809 value = (_ctype)(u4)addLocalReference((jobject)(u4)value); \
1810 JNI_EXIT(); \
1811 return value; \
1812 }
1813GET_STATIC_TYPE_FIELD(jobject, Object, true);
1814GET_STATIC_TYPE_FIELD(jboolean, Boolean, false);
1815GET_STATIC_TYPE_FIELD(jbyte, Byte, false);
1816GET_STATIC_TYPE_FIELD(jchar, Char, false);
1817GET_STATIC_TYPE_FIELD(jshort, Short, false);
1818GET_STATIC_TYPE_FIELD(jint, Int, false);
1819GET_STATIC_TYPE_FIELD(jlong, Long, false);
1820GET_STATIC_TYPE_FIELD(jfloat, Float, false);
1821GET_STATIC_TYPE_FIELD(jdouble, Double, false);
1822
1823/*
1824 * Set a static field.
1825 */
1826#define SET_STATIC_TYPE_FIELD(_ctype, _jname, _jvfld) \
1827 static void SetStatic##_jname##Field(JNIEnv* env, jclass clazz, \
1828 jfieldID fieldID, _ctype value) \
1829 { \
1830 UNUSED_PARAMETER(clazz); \
1831 JNI_ENTER(); \
1832 StaticField* sfield = (StaticField*) fieldID; \
1833 dvmSetStaticField##_jname(sfield, value); \
1834 JNI_EXIT(); \
1835 }
1836SET_STATIC_TYPE_FIELD(jobject, Object, l);
1837SET_STATIC_TYPE_FIELD(jboolean, Boolean, z);
1838SET_STATIC_TYPE_FIELD(jbyte, Byte, b);
1839SET_STATIC_TYPE_FIELD(jchar, Char, c);
1840SET_STATIC_TYPE_FIELD(jshort, Short, s);
1841SET_STATIC_TYPE_FIELD(jint, Int, i);
1842SET_STATIC_TYPE_FIELD(jlong, Long, j);
1843SET_STATIC_TYPE_FIELD(jfloat, Float, f);
1844SET_STATIC_TYPE_FIELD(jdouble, Double, d);
1845
1846/*
1847 * Get an instance field.
1848 *
1849 * If we get an object reference, add it to the local refs list.
1850 */
1851#define GET_TYPE_FIELD(_ctype, _jname, _isref) \
1852 static _ctype Get##_jname##Field(JNIEnv* env, jobject obj, \
1853 jfieldID fieldID) \
1854 { \
1855 JNI_ENTER(); \
1856 InstField* field = (InstField*) fieldID; \
1857 _ctype value = dvmGetField##_jname((Object*) obj,field->byteOffset);\
1858 if (_isref) /* only when _ctype==jobject */ \
1859 value = (_ctype)(u4)addLocalReference((jobject)(u4)value); \
1860 JNI_EXIT(); \
1861 return value; \
1862 }
1863GET_TYPE_FIELD(jobject, Object, true);
1864GET_TYPE_FIELD(jboolean, Boolean, false);
1865GET_TYPE_FIELD(jbyte, Byte, false);
1866GET_TYPE_FIELD(jchar, Char, false);
1867GET_TYPE_FIELD(jshort, Short, false);
1868GET_TYPE_FIELD(jint, Int, false);
1869GET_TYPE_FIELD(jlong, Long, false);
1870GET_TYPE_FIELD(jfloat, Float, false);
1871GET_TYPE_FIELD(jdouble, Double, false);
1872
1873/*
1874 * Set an instance field.
1875 */
1876#define SET_TYPE_FIELD(_ctype, _jname) \
1877 static void Set##_jname##Field(JNIEnv* env, jobject obj, \
1878 jfieldID fieldID, _ctype value) \
1879 { \
1880 JNI_ENTER(); \
1881 InstField* field = (InstField*) fieldID; \
1882 dvmSetField##_jname((Object*) obj, field->byteOffset, value); \
1883 JNI_EXIT(); \
1884 }
1885SET_TYPE_FIELD(jobject, Object);
1886SET_TYPE_FIELD(jboolean, Boolean);
1887SET_TYPE_FIELD(jbyte, Byte);
1888SET_TYPE_FIELD(jchar, Char);
1889SET_TYPE_FIELD(jshort, Short);
1890SET_TYPE_FIELD(jint, Int);
1891SET_TYPE_FIELD(jlong, Long);
1892SET_TYPE_FIELD(jfloat, Float);
1893SET_TYPE_FIELD(jdouble, Double);
1894
1895/*
1896 * Make a virtual method call.
1897 *
1898 * Three versions (..., va_list, jvalue[]) for each return type. If we're
1899 * returning an Object, we have to add it to the local references table.
1900 */
1901#define CALL_VIRTUAL(_ctype, _jname, _retfail, _retok, _isref) \
1902 static _ctype Call##_jname##Method(JNIEnv* env, jobject obj, \
1903 jmethodID methodID, ...) \
1904 { \
1905 JNI_ENTER(); \
1906 Object* dobj = (Object*) obj; \
1907 const Method* meth; \
1908 va_list args; \
1909 JValue result; \
1910 meth = dvmGetVirtualizedMethod(dobj->clazz, (Method*)methodID); \
1911 if (meth == NULL) { \
1912 JNI_EXIT(); \
1913 return _retfail; \
1914 } \
1915 va_start(args, methodID); \
1916 dvmCallMethodV(_self, meth, dobj, &result, args); \
1917 va_end(args); \
1918 if (_isref) \
1919 result.l = addLocalReference(result.l); \
1920 JNI_EXIT(); \
1921 return _retok; \
1922 } \
1923 static _ctype Call##_jname##MethodV(JNIEnv* env, jobject obj, \
1924 jmethodID methodID, va_list args) \
1925 { \
1926 JNI_ENTER(); \
1927 Object* dobj = (Object*) obj; \
1928 const Method* meth; \
1929 JValue result; \
1930 meth = dvmGetVirtualizedMethod(dobj->clazz, (Method*)methodID); \
1931 if (meth == NULL) { \
1932 JNI_EXIT(); \
1933 return _retfail; \
1934 } \
1935 dvmCallMethodV(_self, meth, dobj, &result, args); \
1936 if (_isref) \
1937 result.l = addLocalReference(result.l); \
1938 JNI_EXIT(); \
1939 return _retok; \
1940 } \
1941 static _ctype Call##_jname##MethodA(JNIEnv* env, jobject obj, \
1942 jmethodID methodID, jvalue* args) \
1943 { \
1944 JNI_ENTER(); \
1945 Object* dobj = (Object*) obj; \
1946 const Method* meth; \
1947 JValue result; \
1948 meth = dvmGetVirtualizedMethod(dobj->clazz, (Method*)methodID); \
1949 if (meth == NULL) { \
1950 JNI_EXIT(); \
1951 return _retfail; \
1952 } \
1953 dvmCallMethodA(_self, meth, dobj, &result, args); \
1954 if (_isref) \
1955 result.l = addLocalReference(result.l); \
1956 JNI_EXIT(); \
1957 return _retok; \
1958 }
1959CALL_VIRTUAL(jobject, Object, NULL, result.l, true);
1960CALL_VIRTUAL(jboolean, Boolean, 0, result.z, false);
1961CALL_VIRTUAL(jbyte, Byte, 0, result.b, false);
1962CALL_VIRTUAL(jchar, Char, 0, result.c, false);
1963CALL_VIRTUAL(jshort, Short, 0, result.s, false);
1964CALL_VIRTUAL(jint, Int, 0, result.i, false);
1965CALL_VIRTUAL(jlong, Long, 0, result.j, false);
1966CALL_VIRTUAL(jfloat, Float, 0.0f, result.f, false);
1967CALL_VIRTUAL(jdouble, Double, 0.0, result.d, false);
1968CALL_VIRTUAL(void, Void, , , false);
1969
1970/*
1971 * Make a "non-virtual" method call. We're still calling a virtual method,
1972 * but this time we're not doing an indirection through the object's vtable.
1973 * The "clazz" parameter defines which implementation of a method we want.
1974 *
1975 * Three versions (..., va_list, jvalue[]) for each return type.
1976 */
1977#define CALL_NONVIRTUAL(_ctype, _jname, _retfail, _retok, _isref) \
1978 static _ctype CallNonvirtual##_jname##Method(JNIEnv* env, jobject obj, \
1979 jclass clazz, jmethodID methodID, ...) \
1980 { \
1981 JNI_ENTER(); \
1982 Object* dobj = (Object*) obj; \
1983 const Method* meth; \
1984 va_list args; \
1985 JValue result; \
1986 meth = dvmGetVirtualizedMethod((ClassObject*)clazz, \
1987 (Method*)methodID); \
1988 if (meth == NULL) { \
1989 JNI_EXIT(); \
1990 return _retfail; \
1991 } \
1992 va_start(args, methodID); \
1993 dvmCallMethodV(_self, meth, dobj, &result, args); \
1994 if (_isref) \
1995 result.l = addLocalReference(result.l); \
1996 va_end(args); \
1997 JNI_EXIT(); \
1998 return _retok; \
1999 } \
2000 static _ctype CallNonvirtual##_jname##MethodV(JNIEnv* env, jobject obj, \
2001 jclass clazz, jmethodID methodID, va_list args) \
2002 { \
2003 JNI_ENTER(); \
2004 Object* dobj = (Object*) obj; \
2005 const Method* meth; \
2006 JValue result; \
2007 meth = dvmGetVirtualizedMethod((ClassObject*)clazz, \
2008 (Method*)methodID); \
2009 if (meth == NULL) { \
2010 JNI_EXIT(); \
2011 return _retfail; \
2012 } \
2013 dvmCallMethodV(_self, meth, dobj, &result, args); \
2014 if (_isref) \
2015 result.l = addLocalReference(result.l); \
2016 JNI_EXIT(); \
2017 return _retok; \
2018 } \
2019 static _ctype CallNonvirtual##_jname##MethodA(JNIEnv* env, jobject obj, \
2020 jclass clazz, jmethodID methodID, jvalue* args) \
2021 { \
2022 JNI_ENTER(); \
2023 Object* dobj = (Object*) obj; \
2024 const Method* meth; \
2025 JValue result; \
2026 meth = dvmGetVirtualizedMethod((ClassObject*)clazz, \
2027 (Method*)methodID); \
2028 if (meth == NULL) { \
2029 JNI_EXIT(); \
2030 return _retfail; \
2031 } \
2032 dvmCallMethodA(_self, meth, dobj, &result, args); \
2033 if (_isref) \
2034 result.l = addLocalReference(result.l); \
2035 JNI_EXIT(); \
2036 return _retok; \
2037 }
2038CALL_NONVIRTUAL(jobject, Object, NULL, result.l, true);
2039CALL_NONVIRTUAL(jboolean, Boolean, 0, result.z, false);
2040CALL_NONVIRTUAL(jbyte, Byte, 0, result.b, false);
2041CALL_NONVIRTUAL(jchar, Char, 0, result.c, false);
2042CALL_NONVIRTUAL(jshort, Short, 0, result.s, false);
2043CALL_NONVIRTUAL(jint, Int, 0, result.i, false);
2044CALL_NONVIRTUAL(jlong, Long, 0, result.j, false);
2045CALL_NONVIRTUAL(jfloat, Float, 0.0f, result.f, false);
2046CALL_NONVIRTUAL(jdouble, Double, 0.0, result.d, false);
2047CALL_NONVIRTUAL(void, Void, , , false);
2048
2049
2050/*
2051 * Call a static method.
2052 */
2053#define CALL_STATIC(_ctype, _jname, _retfail, _retok, _isref) \
2054 static _ctype CallStatic##_jname##Method(JNIEnv* env, jclass clazz, \
2055 jmethodID methodID, ...) \
2056 { \
2057 JNI_ENTER(); \
2058 JValue result; \
2059 va_list args; \
2060 assert((ClassObject*) clazz == ((Method*)methodID)->clazz); \
2061 va_start(args, methodID); \
2062 dvmCallMethodV(_self, (Method*) methodID, NULL, &result, args); \
2063 va_end(args); \
2064 if (_isref) \
2065 result.l = addLocalReference(result.l); \
2066 JNI_EXIT(); \
2067 return _retok; \
2068 } \
2069 static _ctype CallStatic##_jname##MethodV(JNIEnv* env, jclass clazz, \
2070 jmethodID methodID, va_list args) \
2071 { \
2072 JNI_ENTER(); \
2073 JValue result; \
2074 assert((ClassObject*) clazz == ((Method*)methodID)->clazz); \
2075 dvmCallMethodV(_self, (Method*) methodID, NULL, &result, args); \
2076 if (_isref) \
2077 result.l = addLocalReference(result.l); \
2078 JNI_EXIT(); \
2079 return _retok; \
2080 } \
2081 static _ctype CallStatic##_jname##MethodA(JNIEnv* env, jclass clazz, \
2082 jmethodID methodID, jvalue* args) \
2083 { \
2084 JNI_ENTER(); \
2085 JValue result; \
2086 assert((ClassObject*) clazz == ((Method*)methodID)->clazz); \
2087 dvmCallMethodA(_self, (Method*) methodID, NULL, &result, args); \
2088 if (_isref) \
2089 result.l = addLocalReference(result.l); \
2090 JNI_EXIT(); \
2091 return _retok; \
2092 }
2093CALL_STATIC(jobject, Object, NULL, result.l, true);
2094CALL_STATIC(jboolean, Boolean, 0, result.z, false);
2095CALL_STATIC(jbyte, Byte, 0, result.b, false);
2096CALL_STATIC(jchar, Char, 0, result.c, false);
2097CALL_STATIC(jshort, Short, 0, result.s, false);
2098CALL_STATIC(jint, Int, 0, result.i, false);
2099CALL_STATIC(jlong, Long, 0, result.j, false);
2100CALL_STATIC(jfloat, Float, 0.0f, result.f, false);
2101CALL_STATIC(jdouble, Double, 0.0, result.d, false);
2102CALL_STATIC(void, Void, , , false);
2103
2104/*
2105 * Create a new String from Unicode data.
2106 *
2107 * If "len" is zero, we will return an empty string even if "unicodeChars"
2108 * is NULL. (The JNI spec is vague here.)
2109 */
2110static jstring NewString(JNIEnv* env, const jchar* unicodeChars, jsize len)
2111{
2112 JNI_ENTER();
2113
2114 StringObject* jstr;
2115 jstr = dvmCreateStringFromUnicode(unicodeChars, len);
2116 if (jstr != NULL) {
2117 dvmReleaseTrackedAlloc((Object*) jstr, NULL);
2118 jstr = addLocalReference((jstring) jstr);
2119 }
2120
2121 JNI_EXIT();
2122 return jstr;
2123}
2124
2125/*
2126 * Return the length of a String in Unicode character units.
2127 */
2128static jsize GetStringLength(JNIEnv* env, jstring string)
2129{
2130 JNI_ENTER();
2131
2132 jsize len = dvmStringLen((StringObject*) string);
2133
2134 JNI_EXIT();
2135 return len;
2136}
2137
2138/*
2139 * Get a pointer to the string's character data.
2140 *
2141 * The result is guaranteed to be valid until ReleaseStringChars is
2142 * called, which means we can't just hold a reference to it in the local
2143 * refs table. We have to add it to the global refs.
2144 *
2145 * Technically, we don't need to hold a reference to the String, but rather
2146 * to the Char[] object within the String.
2147 *
2148 * We could also just allocate some storage and copy the data into it,
2149 * but it's a choice between our synchronized global reference table and
2150 * libc's synchronized heap allocator.
2151 */
2152static const jchar* GetStringChars(JNIEnv* env, jstring string,
2153 jboolean* isCopy)
2154{
2155 JNI_ENTER();
2156
2157 const u2* data = dvmStringChars((StringObject*) string);
2158 addGlobalReference(string);
2159
2160 if (isCopy != NULL)
2161 *isCopy = JNI_FALSE;
2162
2163 JNI_EXIT();
2164 return (jchar*)data;
2165}
2166
2167/*
2168 * Release our grip on some characters from a string.
2169 */
2170static void ReleaseStringChars(JNIEnv* env, jstring string, const jchar* chars)
2171{
2172 JNI_ENTER();
2173 deleteGlobalReference(string);
2174 JNI_EXIT();
2175}
2176
2177/*
2178 * Create a new java.lang.String object from chars in modified UTF-8 form.
2179 *
2180 * The spec doesn't say how to handle a NULL string. Popular desktop VMs
2181 * accept it and return a NULL pointer in response.
2182 */
2183static jstring NewStringUTF(JNIEnv* env, const char* bytes)
2184{
2185 JNI_ENTER();
2186
2187 StringObject* newStr;
2188
2189 if (bytes == NULL) {
2190 newStr = NULL;
2191 } else {
2192 newStr = dvmCreateStringFromCstr(bytes, ALLOC_DEFAULT);
2193 if (newStr != NULL) {
2194 dvmReleaseTrackedAlloc((Object*)newStr, NULL);
2195 newStr = addLocalReference((jstring) newStr);
2196 }
2197 }
2198
2199 JNI_EXIT();
2200 return (jstring)newStr;
2201}
2202
2203/*
2204 * Return the length in bytes of the modified UTF-8 form of the string.
2205 */
2206static jsize GetStringUTFLength(JNIEnv* env, jstring string)
2207{
2208 JNI_ENTER();
2209
2210 jsize len = dvmStringUtf8ByteLen((StringObject*) string);
2211
2212 JNI_EXIT();
2213 return len;
2214}
2215
2216/*
2217 * Convert "string" to modified UTF-8 and return a pointer. The returned
2218 * value must be released with ReleaseStringUTFChars.
2219 *
2220 * According to the JNI reference, "Returns a pointer to a UTF-8 string,
2221 * or NULL if the operation fails. Returns NULL if and only if an invocation
2222 * of this function has thrown an exception."
2223 *
2224 * The behavior here currently follows that of other open-source VMs, which
2225 * quietly return NULL if "string" is NULL. We should consider throwing an
2226 * NPE. (The CheckJNI code blows up if you try to pass in a NULL string,
2227 * which should catch this sort of thing during development.) Certain other
2228 * VMs will crash with a segmentation fault.
2229 */
2230static const char* GetStringUTFChars(JNIEnv* env, jstring string,
2231 jboolean* isCopy)
2232{
2233 JNI_ENTER();
2234 char* newStr;
2235
2236 if (string == NULL) {
2237 /* this shouldn't happen; throw NPE? */
2238 newStr = NULL;
2239 } else {
2240 if (isCopy != NULL)
2241 *isCopy = JNI_TRUE;
2242
2243 newStr = dvmCreateCstrFromString((StringObject*) string);
2244 if (newStr == NULL) {
2245 /* assume memory failure */
2246 dvmThrowException("Ljava/lang/OutOfMemoryError;",
2247 "native heap string alloc failed");
2248 }
2249 }
2250
2251 JNI_EXIT();
2252 return newStr;
2253}
2254
2255/*
2256 * Release a string created by GetStringUTFChars().
2257 */
2258static void ReleaseStringUTFChars(JNIEnv* env, jstring string, const char* utf)
2259{
2260 JNI_ENTER();
2261 free((char*)utf);
2262 JNI_EXIT();
2263}
2264
2265/*
2266 * Return the capacity of the array.
2267 */
2268static jsize GetArrayLength(JNIEnv* env, jarray array)
2269{
2270 JNI_ENTER();
2271
2272 jsize length = ((ArrayObject*) array)->length;
2273
2274 JNI_EXIT();
2275 return length;
2276}
2277
2278/*
2279 * Construct a new array that holds objects from class "elementClass".
2280 */
2281static jobjectArray NewObjectArray(JNIEnv* env, jsize length,
2282 jclass elementClass, jobject initialElement)
2283{
2284 JNI_ENTER();
2285
2286 ClassObject* elemClassObj = (ClassObject*) elementClass;
2287 ArrayObject* newObj = NULL;
2288
2289 if (elemClassObj == NULL) {
2290 dvmThrowException("Ljava/lang/NullPointerException;",
2291 "JNI NewObjectArray");
2292 goto bail;
2293 }
2294
2295 newObj = dvmAllocObjectArray(elemClassObj, length, ALLOC_DEFAULT);
2296 if (newObj == NULL) {
2297 assert(dvmCheckException(_self));
2298 goto bail;
2299 }
2300 dvmReleaseTrackedAlloc((Object*) newObj, NULL);
2301
2302 /*
2303 * Initialize the array. Trashes "length".
2304 */
2305 if (initialElement != NULL) {
2306 Object** arrayData = (Object**) newObj->contents;
2307
2308 while (length--)
2309 *arrayData++ = (Object*) initialElement;
2310 }
2311
2312 newObj = addLocalReference((jobjectArray) newObj);
2313
2314bail:
2315 JNI_EXIT();
2316 return (jobjectArray) newObj;
2317}
2318
2319/*
2320 * Get one element of an Object array.
2321 *
2322 * Add the object to the local references table in case the array goes away.
2323 */
2324static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray array,
2325 jsize index)
2326{
2327 JNI_ENTER();
2328
2329 ArrayObject* arrayObj = (ArrayObject*) array;
2330 Object* value = NULL;
2331
2332 assert(array != NULL);
2333
2334 /* check the array bounds */
2335 if (index < 0 || index >= (int) arrayObj->length) {
2336 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
2337 arrayObj->obj.clazz->descriptor);
2338 goto bail;
2339 }
2340
2341 value = ((Object**) arrayObj->contents)[index];
2342 value = addLocalReference(value);
2343
2344bail:
2345 JNI_EXIT();
2346 return (jobject) value;
2347}
2348
2349/*
2350 * Set one element of an Object array.
2351 */
2352static void SetObjectArrayElement(JNIEnv* env, jobjectArray array,
2353 jsize index, jobject value)
2354{
2355 JNI_ENTER();
2356
2357 ArrayObject* arrayObj = (ArrayObject*) array;
2358
2359 assert(array != NULL);
2360
2361 /* check the array bounds */
2362 if (index < 0 || index >= (int) arrayObj->length) {
2363 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
2364 arrayObj->obj.clazz->descriptor);
2365 goto bail;
2366 }
2367
2368 //LOGV("JNI: set element %d in array %p to %p\n", index, array, value);
2369
2370 ((Object**) arrayObj->contents)[index] = (Object*) value;
2371
2372bail:
2373 JNI_EXIT();
2374}
2375
2376/*
2377 * Create a new array of primitive elements.
2378 */
2379#define NEW_PRIMITIVE_ARRAY(_artype, _jname, _typechar) \
2380 static _artype New##_jname##Array(JNIEnv* env, jsize length) \
2381 { \
2382 JNI_ENTER(); \
2383 ArrayObject* arrayObj; \
2384 arrayObj = dvmAllocPrimitiveArray(_typechar, length, \
2385 ALLOC_DEFAULT); \
2386 if (arrayObj != NULL) { \
2387 dvmReleaseTrackedAlloc((Object*) arrayObj, NULL); \
2388 arrayObj = addLocalReference(arrayObj); \
2389 } \
2390 JNI_EXIT(); \
2391 return (_artype)arrayObj; \
2392 }
2393NEW_PRIMITIVE_ARRAY(jbooleanArray, Boolean, 'Z');
2394NEW_PRIMITIVE_ARRAY(jbyteArray, Byte, 'B');
2395NEW_PRIMITIVE_ARRAY(jcharArray, Char, 'C');
2396NEW_PRIMITIVE_ARRAY(jshortArray, Short, 'S');
2397NEW_PRIMITIVE_ARRAY(jintArray, Int, 'I');
2398NEW_PRIMITIVE_ARRAY(jlongArray, Long, 'J');
2399NEW_PRIMITIVE_ARRAY(jfloatArray, Float, 'F');
2400NEW_PRIMITIVE_ARRAY(jdoubleArray, Double, 'D');
2401
2402/*
2403 * Get a pointer to a C array of primitive elements from an array object
2404 * of the matching type.
2405 *
2406 * We guarantee availability until Release is called, so we have to add
2407 * the array object to the global refs table.
2408 *
2409 * In a compacting GC, we either need to return a copy of the elements
2410 * or "pin" the memory. Otherwise we run the risk of native code using
2411 * the buffer as the destination of a blocking read() call that wakes up
2412 * during a GC.
2413 */
2414#define GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
2415 static _ctype* Get##_jname##ArrayElements(JNIEnv* env, \
2416 _ctype##Array array, jboolean* isCopy) \
2417 { \
2418 JNI_ENTER(); \
2419 _ctype* data; \
2420 ArrayObject* arrayObj = (ArrayObject*)array; \
2421 addGlobalReference(arrayObj); \
2422 data = (_ctype*) arrayObj->contents; \
2423 if (isCopy != NULL) \
2424 *isCopy = JNI_FALSE; \
2425 JNI_EXIT(); \
2426 return data; \
2427 }
2428
2429/*
2430 * Release the storage locked down by the "get" function.
2431 *
2432 * The API says, ""'mode' has no effect if 'elems' is not a copy of the
2433 * elements in 'array'." They apparently did not anticipate the need to
2434 * create a global reference to avoid GC race conditions. We actually
2435 * want to delete the global reference in all circumstances that would
2436 * result in a copied array being freed. This means anything but a
2437 * JNI_COMMIT release.
2438 */
2439#define RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
2440 static void Release##_jname##ArrayElements(JNIEnv* env, \
2441 _ctype##Array array, _ctype* elems, jint mode) \
2442 { \
2443 UNUSED_PARAMETER(elems); \
2444 JNI_ENTER(); \
2445 if (mode != JNI_COMMIT) \
2446 deleteGlobalReference(array); \
2447 JNI_EXIT(); \
2448 }
2449
2450/*
2451 * Copy a section of a primitive array to a buffer.
2452 */
2453#define GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
2454 static void Get##_jname##ArrayRegion(JNIEnv* env, \
2455 _ctype##Array array, jsize start, jsize len, _ctype* buf) \
2456 { \
2457 JNI_ENTER(); \
2458 ArrayObject* arrayObj = (ArrayObject*)array; \
2459 _ctype* data = (_ctype*) arrayObj->contents; \
2460 if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
2461 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
2462 arrayObj->obj.clazz->descriptor); \
2463 } else { \
2464 memcpy(buf, data + start, len * sizeof(_ctype)); \
2465 } \
2466 JNI_EXIT(); \
2467 }
2468
2469/*
2470 * Copy a section of a primitive array to a buffer.
2471 */
2472#define SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
2473 static void Set##_jname##ArrayRegion(JNIEnv* env, \
2474 _ctype##Array array, jsize start, jsize len, const _ctype* buf) \
2475 { \
2476 JNI_ENTER(); \
2477 ArrayObject* arrayObj = (ArrayObject*)array; \
2478 _ctype* data = (_ctype*) arrayObj->contents; \
2479 if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
2480 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
2481 arrayObj->obj.clazz->descriptor); \
2482 } else { \
2483 memcpy(data + start, buf, len * sizeof(_ctype)); \
2484 } \
2485 JNI_EXIT(); \
2486 }
2487
2488/*
2489 * 4-in-1:
2490 * Get<Type>ArrayElements
2491 * Release<Type>ArrayElements
2492 * Get<Type>ArrayRegion
2493 * Set<Type>ArrayRegion
2494 */
2495#define PRIMITIVE_ARRAY_FUNCTIONS(_ctype, _jname) \
2496 GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
2497 RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
2498 GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname); \
2499 SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname);
2500
2501PRIMITIVE_ARRAY_FUNCTIONS(jboolean, Boolean);
2502PRIMITIVE_ARRAY_FUNCTIONS(jbyte, Byte);
2503PRIMITIVE_ARRAY_FUNCTIONS(jchar, Char);
2504PRIMITIVE_ARRAY_FUNCTIONS(jshort, Short);
2505PRIMITIVE_ARRAY_FUNCTIONS(jint, Int);
2506PRIMITIVE_ARRAY_FUNCTIONS(jlong, Long);
2507PRIMITIVE_ARRAY_FUNCTIONS(jfloat, Float);
2508PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double);
2509
2510/*
2511 * Register one or more native functions in one class.
2512 */
2513static jint RegisterNatives(JNIEnv* env, jclass clazz,
2514 const JNINativeMethod* methods, jint nMethods)
2515{
2516 JNI_ENTER();
2517
2518 jint retval;
2519 int i;
2520
2521 if (gDvm.verboseJni) {
2522 LOGI("[Registering JNI native methods for class %s]\n",
2523 ((ClassObject*) clazz)->descriptor);
2524 }
2525
2526 for (i = 0; i < nMethods; i++) {
2527 if (!dvmRegisterJNIMethod((ClassObject*) clazz,
2528 methods[i].name, methods[i].signature, methods[i].fnPtr))
2529 {
2530 retval = JNI_ERR;
2531 goto bail;
2532 }
2533 }
2534 retval = JNI_OK;
2535
2536bail:
2537 JNI_EXIT();
2538 return retval;
2539}
2540
2541/*
2542 * Un-register a native function.
2543 */
2544static jint UnregisterNatives(JNIEnv* env, jclass clazz)
2545{
2546 JNI_ENTER();
2547 /*
2548 * The JNI docs refer to this as a way to reload/relink native libraries,
2549 * and say it "should not be used in normal native code".
2550 *
2551 * We can implement it if we decide we need it.
2552 */
2553 JNI_EXIT();
2554 return JNI_ERR;
2555}
2556
2557/*
2558 * Lock the monitor.
2559 *
2560 * We have to track all monitor enters and exits, so that we can undo any
2561 * outstanding synchronization before the thread exits.
2562 */
2563static jint MonitorEnter(JNIEnv* env, jobject obj)
2564{
2565 JNI_ENTER();
2566 dvmLockObject(_self, (Object*) obj);
2567 trackMonitorEnter(_self, (Object*) obj);
2568 JNI_EXIT();
2569 return JNI_OK;
2570}
2571
2572/*
2573 * Unlock the monitor.
2574 *
2575 * Throws an IllegalMonitorStateException if the current thread
2576 * doesn't own the monitor. (dvmUnlockObject() takes care of the throw.)
2577 *
2578 * According to the 1.6 spec, it's legal to call here with an exception
2579 * pending. If this fails, we'll stomp the original exception.
2580 */
2581static jint MonitorExit(JNIEnv* env, jobject obj)
2582{
2583 JNI_ENTER();
2584 bool success = dvmUnlockObject(_self, (Object*) obj);
2585 if (success)
2586 trackMonitorExit(_self, (Object*) obj);
2587 JNI_EXIT();
2588 return success ? JNI_OK : JNI_ERR;
2589}
2590
2591/*
2592 * Return the JavaVM interface associated with the current thread.
2593 */
2594static jint GetJavaVM(JNIEnv* env, JavaVM** vm)
2595{
2596 JNI_ENTER();
2597 //*vm = gDvm.vmList;
2598 *vm = (JavaVM*) ((JNIEnvExt*)env)->vm;
2599 JNI_EXIT();
2600 if (*vm == NULL)
2601 return JNI_ERR;
2602 else
2603 return JNI_OK;
2604}
2605
2606/*
2607 * Copies "len" Unicode characters, from offset "start".
2608 */
2609static void GetStringRegion(JNIEnv* env, jstring str, jsize start, jsize len,
2610 jchar* buf)
2611{
2612 JNI_ENTER();
2613 StringObject* strObj = (StringObject*) str;
2614 if (start + len > dvmStringLen(strObj))
2615 dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
2616 else
2617 memcpy(buf, dvmStringChars(strObj) + start, len * sizeof(u2));
2618 JNI_EXIT();
2619}
2620
2621/*
2622 * Translates "len" Unicode characters, from offset "start", into
2623 * modified UTF-8 encoding.
2624 */
2625static void GetStringUTFRegion(JNIEnv* env, jstring str, jsize start,
2626 jsize len, char* buf)
2627{
2628 JNI_ENTER();
2629 StringObject* strObj = (StringObject*) str;
2630 if (start + len > dvmStringLen(strObj))
2631 dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
2632 else
2633 dvmCreateCstrFromStringRegion(strObj, start, len, buf);
2634 JNI_EXIT();
2635}
2636
2637/*
2638 * Get a raw pointer to array data.
2639 *
2640 * The caller is expected to call "release" before doing any JNI calls
2641 * or blocking I/O operations.
2642 *
2643 * In a compacting GC, we need to pin the memory or block GC.
2644 */
2645static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray array,
2646 jboolean* isCopy)
2647{
2648 JNI_ENTER();
2649 void* data;
2650 ArrayObject* arrayObj = (ArrayObject*)array;
2651 addGlobalReference(arrayObj);
2652 data = arrayObj->contents;
2653 if (isCopy != NULL)
2654 *isCopy = JNI_FALSE;
2655 JNI_EXIT();
2656 return data;
2657}
2658
2659/*
2660 * Release an array obtained with GetPrimitiveArrayCritical.
2661 */
2662static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray array,
2663 void* carray, jint mode)
2664{
2665 JNI_ENTER();
2666 if (mode != JNI_COMMIT)
2667 deleteGlobalReference(array);
2668 JNI_EXIT();
2669}
2670
2671/*
2672 * Like GetStringChars, but with restricted use.
2673 */
2674static const jchar* GetStringCritical(JNIEnv* env, jstring string,
2675 jboolean* isCopy)
2676{
2677 JNI_ENTER();
2678 const u2* data = dvmStringChars((StringObject*) string);
2679 addGlobalReference(string);
2680
2681 if (isCopy != NULL)
2682 *isCopy = JNI_FALSE;
2683
2684 JNI_EXIT();
2685 return (jchar*)data;
2686}
2687
2688/*
2689 * Like ReleaseStringChars, but with restricted use.
2690 */
2691static void ReleaseStringCritical(JNIEnv* env, jstring string,
2692 const jchar* carray)
2693{
2694 JNI_ENTER();
2695 deleteGlobalReference(string);
2696 JNI_EXIT();
2697}
2698
2699/*
2700 * Create a new weak global reference.
2701 */
2702static jweak NewWeakGlobalRef(JNIEnv* env, jobject obj)
2703{
2704 JNI_ENTER();
2705 // TODO - implement
2706 jobject gref = NULL;
2707 LOGE("JNI ERROR: NewWeakGlobalRef not implemented\n");
2708 dvmAbort();
2709 JNI_EXIT();
2710 return gref;
2711}
2712
2713/*
2714 * Delete the specified weak global reference.
2715 */
2716static void DeleteWeakGlobalRef(JNIEnv* env, jweak obj)
2717{
2718 JNI_ENTER();
2719 // TODO - implement
2720 LOGE("JNI ERROR: DeleteWeakGlobalRef not implemented\n");
2721 dvmAbort();
2722 JNI_EXIT();
2723}
2724
2725/*
2726 * Quick check for pending exceptions.
2727 *
2728 * TODO: we should be able to skip the enter/exit macros here.
2729 */
2730static jboolean ExceptionCheck(JNIEnv* env)
2731{
2732 JNI_ENTER();
2733 bool result = dvmCheckException(_self);
2734 JNI_EXIT();
2735 return result;
2736}
2737
2738/*
2739 * Returns the type of the object referred to by "obj". It can be local,
2740 * global, or weak global.
2741 *
2742 * In the current implementation, references can be global and local at
2743 * the same time, so while the return value is accurate it may not tell
2744 * the whole story.
2745 */
2746static jobjectRefType GetObjectRefType(JNIEnv* env, jobject obj)
2747{
2748 JNI_ENTER();
2749 jobjectRefType type;
2750
2751 if (obj == NULL)
2752 type = JNIInvalidRefType;
2753 else
2754 type = dvmGetJNIRefType(obj);
2755 JNI_EXIT();
2756 return type;
2757}
2758
2759/*
2760 * Allocate and return a new java.nio.ByteBuffer for this block of memory.
2761 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07002762 * "address" may not be NULL, and "capacity" must be > 0. (These are only
2763 * verified when CheckJNI is enabled.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002764 */
Andy McFadden8e5c7842009-07-23 17:47:18 -07002765static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002766{
Andy McFadden8e5c7842009-07-23 17:47:18 -07002767 JNI_ENTER();
2768
2769 Thread* self = _self /*dvmThreadSelf()*/;
2770 Object* platformAddress = NULL;
2771 JValue callResult;
Andy McFaddencda8f8a2009-06-23 16:27:45 -07002772 jobject result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002773
Andy McFadden8e5c7842009-07-23 17:47:18 -07002774 /* get an instance of PlatformAddress that wraps the provided address */
2775 dvmCallMethod(self,
2776 gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on,
2777 NULL, &callResult, address);
2778 if (dvmGetException(self) != NULL || callResult.l == NULL)
Andy McFaddencda8f8a2009-06-23 16:27:45 -07002779 goto bail;
Andy McFadden8e5c7842009-07-23 17:47:18 -07002780
2781 /* don't let the GC discard it */
2782 platformAddress = (Object*) callResult.l;
2783 dvmAddTrackedAlloc(platformAddress, self);
2784 LOGV("tracking %p for address=%p\n", platformAddress, address);
2785
2786 /* create an instance of java.nio.ReadWriteDirectByteBuffer */
2787 ClassObject* clazz = gDvm.classJavaNioReadWriteDirectByteBuffer;
2788 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))
2789 goto bail;
2790 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
2791 if (newObj != NULL) {
2792 /* call the (PlatformAddress, int, int) constructor */
2793 newObj = addLocalReference(newObj);
2794 dvmCallMethod(self, gDvm.methJavaNioReadWriteDirectByteBuffer_init,
2795 newObj, &callResult, platformAddress, (jint) capacity, (jint) 0);
2796 if (dvmGetException(self) != NULL)
2797 goto bail;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002798 }
2799
Andy McFadden8e5c7842009-07-23 17:47:18 -07002800 result = (jobject) newObj;
Andy McFaddencda8f8a2009-06-23 16:27:45 -07002801
2802bail:
Andy McFadden8e5c7842009-07-23 17:47:18 -07002803 if (platformAddress != NULL)
2804 dvmReleaseTrackedAlloc(platformAddress, self);
2805 JNI_EXIT();
Andy McFaddencda8f8a2009-06-23 16:27:45 -07002806 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002807}
2808
2809/*
2810 * Get the starting address of the buffer for the specified java.nio.Buffer.
2811 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07002812 * If this is not a "direct" buffer, we return NULL.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002813 */
Andy McFadden5f612b82009-07-22 15:07:27 -07002814static void* GetDirectBufferAddress(JNIEnv* env, jobject buf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002815{
Andy McFadden8e5c7842009-07-23 17:47:18 -07002816 JNI_ENTER();
2817
2818 Object* bufObj = (Object*) buf;
2819 Thread* self = _self /*dvmThreadSelf()*/;
Andy McFadden8e696dc2009-07-24 15:28:16 -07002820 void* result;
2821
2822 /*
2823 * All Buffer objects have an effectiveDirectAddress field. If it's
2824 * nonzero, we can just return that value. If not, we have to call
2825 * through DirectBuffer.getEffectiveAddress(), which as a side-effect
2826 * will set the effectiveDirectAddress field for direct buffers (and
2827 * things that wrap direct buffers).
2828 */
2829 result = (void*) dvmGetFieldInt(bufObj,
2830 gDvm.offJavaNioBuffer_effectiveDirectAddress);
2831 if (result != NULL) {
2832 //LOGI("fast path for %p\n", buf);
2833 goto bail;
2834 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002835
Andy McFadden72d61fb2009-06-24 16:56:06 -07002836 /*
2837 * Start by determining if the object supports the DirectBuffer
2838 * interfaces. Note this does not guarantee that it's a direct buffer.
2839 */
Andy McFadden8e5c7842009-07-23 17:47:18 -07002840 if (!dvmInstanceof(bufObj->clazz,
2841 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002842 {
Andy McFaddencda8f8a2009-06-23 16:27:45 -07002843 goto bail;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002844 }
2845
Andy McFadden72d61fb2009-06-24 16:56:06 -07002846 /*
Andy McFadden8e5c7842009-07-23 17:47:18 -07002847 * Get a PlatformAddress object with the effective address.
Andy McFadden5f612b82009-07-22 15:07:27 -07002848 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07002849 * If this isn't a direct buffer, the result will be NULL and/or an
Andy McFadden72d61fb2009-06-24 16:56:06 -07002850 * exception will have been thrown.
2851 */
Andy McFadden8e696dc2009-07-24 15:28:16 -07002852 JValue callResult;
Andy McFadden8e5c7842009-07-23 17:47:18 -07002853 const Method* meth = dvmGetVirtualizedMethod(bufObj->clazz,
2854 gDvm.methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress);
2855 dvmCallMethodA(self, meth, bufObj, &callResult, NULL);
2856 if (dvmGetException(self) != NULL) {
2857 dvmClearException(self);
2858 callResult.l = NULL;
Andy McFadden72d61fb2009-06-24 16:56:06 -07002859 }
Andy McFadden8e5c7842009-07-23 17:47:18 -07002860
Andy McFadden8e696dc2009-07-24 15:28:16 -07002861 Object* platformAddr = callResult.l;
Andy McFadden72d61fb2009-06-24 16:56:06 -07002862 if (platformAddr == NULL) {
Andy McFadden8e696dc2009-07-24 15:28:16 -07002863 LOGV("Got request for address of non-direct buffer\n");
Andy McFadden72d61fb2009-06-24 16:56:06 -07002864 goto bail;
2865 }
2866
Andy McFadden8e5c7842009-07-23 17:47:18 -07002867 /*
2868 * Extract the address from the PlatformAddress object. Instead of
2869 * calling the toLong() method, just grab the field directly. This
2870 * is faster but more fragile.
2871 */
2872 result = (void*) dvmGetFieldInt(platformAddr,
2873 gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr);
Andy McFaddencda8f8a2009-06-23 16:27:45 -07002874
Andy McFadden8e696dc2009-07-24 15:28:16 -07002875 //LOGI("slow path for %p --> %p\n", buf, result);
2876
Andy McFaddencda8f8a2009-06-23 16:27:45 -07002877bail:
Andy McFadden8e5c7842009-07-23 17:47:18 -07002878 JNI_EXIT();
Andy McFaddencda8f8a2009-06-23 16:27:45 -07002879 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002880}
2881
2882/*
2883 * Get the capacity of the buffer for the specified java.nio.Buffer.
2884 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07002885 * Returns -1 if the object is not a direct buffer. (We actually skip
2886 * this check, since it's expensive to determine, and just return the
2887 * capacity regardless.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002888 */
Andy McFadden8e5c7842009-07-23 17:47:18 -07002889static jlong GetDirectBufferCapacity(JNIEnv* env, jobject buf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002890{
Andy McFadden8e5c7842009-07-23 17:47:18 -07002891 JNI_ENTER();
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002892
Andy McFadden8e5c7842009-07-23 17:47:18 -07002893 /*
2894 * The capacity is always in the Buffer.capacity field.
2895 *
2896 * (The "check" version should verify that this is actually a Buffer,
2897 * but we're not required to do so here.)
2898 */
2899 jlong result = dvmGetFieldInt((Object*)buf, gDvm.offJavaNioBuffer_capacity);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002900
Andy McFadden8e5c7842009-07-23 17:47:18 -07002901 JNI_EXIT();
Andy McFaddencda8f8a2009-06-23 16:27:45 -07002902 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002903}
2904
2905
2906/*
2907 * ===========================================================================
2908 * JNI invocation functions
2909 * ===========================================================================
2910 */
2911
2912/*
2913 * Handle AttachCurrentThread{AsDaemon}.
2914 *
2915 * We need to make sure the VM is actually running. For example, if we start
2916 * up, issue an Attach, and the VM exits almost immediately, by the time the
2917 * attaching happens the VM could already be shutting down.
2918 *
2919 * It's hard to avoid a race condition here because we don't want to hold
2920 * a lock across the entire operation. What we can do is temporarily
2921 * increment the thread count to prevent a VM exit.
2922 *
2923 * This could potentially still have problems if a daemon thread calls here
2924 * while the VM is shutting down. dvmThreadSelf() will work, since it just
2925 * uses pthread TLS, but dereferencing "vm" could fail. Such is life when
2926 * you shut down a VM while threads are still running inside it.
2927 *
2928 * Remember that some code may call this as a way to find the per-thread
2929 * JNIEnv pointer. Don't do excess work for that case.
2930 */
2931static jint attachThread(JavaVM* vm, JNIEnv** p_env, void* thr_args,
2932 bool isDaemon)
2933{
2934 JavaVMAttachArgs* args = (JavaVMAttachArgs*) thr_args;
2935 Thread* self;
2936 bool result = false;
2937
2938 /*
2939 * Return immediately if we're already one with the VM.
2940 */
2941 self = dvmThreadSelf();
2942 if (self != NULL) {
2943 *p_env = self->jniEnv;
2944 return JNI_OK;
2945 }
2946
2947 /*
2948 * No threads allowed in zygote mode.
2949 */
2950 if (gDvm.zygote) {
2951 return JNI_ERR;
2952 }
2953
2954 /* increment the count to keep the VM from bailing while we run */
2955 dvmLockThreadList(NULL);
2956 if (gDvm.nonDaemonThreadCount == 0) {
2957 // dead or dying
2958 LOGV("Refusing to attach thread '%s' -- VM is shutting down\n",
2959 (thr_args == NULL) ? "(unknown)" : args->name);
2960 dvmUnlockThreadList();
2961 return JNI_ERR;
2962 }
2963 gDvm.nonDaemonThreadCount++;
2964 dvmUnlockThreadList();
2965
2966 /* tweak the JavaVMAttachArgs as needed */
2967 JavaVMAttachArgs argsCopy;
2968 if (args == NULL) {
2969 /* allow the v1.1 calling convention */
2970 argsCopy.version = JNI_VERSION_1_2;
2971 argsCopy.name = NULL;
2972 argsCopy.group = dvmGetMainThreadGroup();
2973 } else {
2974 assert(args->version >= JNI_VERSION_1_2);
2975
2976 argsCopy.version = args->version;
2977 argsCopy.name = args->name;
2978 if (args->group != NULL)
2979 argsCopy.group = args->group;
2980 else
2981 argsCopy.group = dvmGetMainThreadGroup();
2982 }
2983
2984 result = dvmAttachCurrentThread(&argsCopy, isDaemon);
2985
2986 /* restore the count */
2987 dvmLockThreadList(NULL);
2988 gDvm.nonDaemonThreadCount--;
2989 dvmUnlockThreadList();
2990
2991 /*
2992 * Change the status to indicate that we're out in native code. This
2993 * call is not guarded with state-change macros, so we have to do it
2994 * by hand.
2995 */
2996 if (result) {
2997 self = dvmThreadSelf();
2998 assert(self != NULL);
2999 dvmChangeStatus(self, THREAD_NATIVE);
3000 *p_env = self->jniEnv;
3001 return JNI_OK;
3002 } else {
3003 return JNI_ERR;
3004 }
3005}
3006
3007/*
3008 * Attach the current thread to the VM. If the thread is already attached,
3009 * this is a no-op.
3010 */
3011static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args)
3012{
3013 return attachThread(vm, p_env, thr_args, false);
3014}
3015
3016/*
3017 * Like AttachCurrentThread, but set the "daemon" flag.
3018 */
3019static jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env,
3020 void* thr_args)
3021{
3022 return attachThread(vm, p_env, thr_args, true);
3023}
3024
3025/*
3026 * Dissociate the current thread from the VM.
3027 */
3028static jint DetachCurrentThread(JavaVM* vm)
3029{
3030 Thread* self = dvmThreadSelf();
3031
3032 if (self == NULL) /* not attached, can't do anything */
3033 return JNI_ERR;
3034
3035 /* switch to "running" to check for suspension */
3036 dvmChangeStatus(self, THREAD_RUNNING);
3037
3038 /* detach the thread */
3039 dvmDetachCurrentThread();
3040
3041 /* (no need to change status back -- we have no status) */
3042 return JNI_OK;
3043}
3044
3045/*
3046 * If current thread is attached to VM, return the associated JNIEnv.
3047 * Otherwise, stuff NULL in and return JNI_EDETACHED.
3048 *
3049 * JVMTI overloads this by specifying a magic value for "version", so we
3050 * do want to check that here.
3051 */
3052static jint GetEnv(JavaVM* vm, void** env, jint version)
3053{
3054 Thread* self = dvmThreadSelf();
3055
3056 if (version < JNI_VERSION_1_1 || version > JNI_VERSION_1_6)
3057 return JNI_EVERSION;
3058
3059 if (self == NULL) {
3060 *env = NULL;
3061 } else {
3062 /* TODO: status change is probably unnecessary */
3063 dvmChangeStatus(self, THREAD_RUNNING);
3064 *env = (void*) dvmGetThreadJNIEnv(self);
3065 dvmChangeStatus(self, THREAD_NATIVE);
3066 }
3067 if (*env == NULL)
3068 return JNI_EDETACHED;
3069 else
3070 return JNI_OK;
3071}
3072
3073/*
3074 * Destroy the VM. This may be called from any thread.
3075 *
3076 * If the current thread is attached, wait until the current thread is
3077 * the only non-daemon user-level thread. If the current thread is not
3078 * attached, we attach it and do the processing as usual. (If the attach
3079 * fails, it's probably because all the non-daemon threads have already
3080 * exited and the VM doesn't want to let us back in.)
3081 *
3082 * TODO: we don't really deal with the situation where more than one thread
3083 * has called here. One thread wins, the other stays trapped waiting on
3084 * the condition variable forever. Not sure this situation is interesting
3085 * in real life.
3086 */
3087static jint DestroyJavaVM(JavaVM* vm)
3088{
3089 JavaVMExt* ext = (JavaVMExt*) vm;
3090 Thread* self;
3091
3092 if (ext == NULL)
3093 return JNI_ERR;
3094
3095 LOGD("DestroyJavaVM waiting for non-daemon threads to exit\n");
3096
3097 /*
3098 * Sleep on a condition variable until it's okay to exit.
3099 */
3100 self = dvmThreadSelf();
3101 if (self == NULL) {
3102 JNIEnv* tmpEnv;
3103 if (AttachCurrentThread(vm, &tmpEnv, NULL) != JNI_OK) {
3104 LOGV("Unable to reattach main for Destroy; assuming VM is "
3105 "shutting down (count=%d)\n",
3106 gDvm.nonDaemonThreadCount);
3107 goto shutdown;
3108 } else {
3109 LOGV("Attached to wait for shutdown in Destroy\n");
3110 }
3111 }
3112 dvmChangeStatus(self, THREAD_VMWAIT);
3113
3114 dvmLockThreadList(self);
3115 gDvm.nonDaemonThreadCount--; // remove current thread from count
3116
3117 while (gDvm.nonDaemonThreadCount > 0)
3118 pthread_cond_wait(&gDvm.vmExitCond, &gDvm.threadListLock);
3119
3120 dvmUnlockThreadList();
3121 self = NULL;
3122
3123shutdown:
3124 // TODO: call System.exit() to run any registered shutdown hooks
3125 // (this may not return -- figure out how this should work)
3126
3127 LOGD("DestroyJavaVM shutting VM down\n");
3128 dvmShutdown();
3129
3130 // TODO - free resources associated with JNI-attached daemon threads
3131 free(ext->envList);
3132 free(ext);
3133
3134 return JNI_OK;
3135}
3136
3137
3138/*
3139 * ===========================================================================
3140 * Function tables
3141 * ===========================================================================
3142 */
3143
3144static const struct JNINativeInterface gNativeInterface = {
3145 NULL,
3146 NULL,
3147 NULL,
3148 NULL,
3149
3150 GetVersion,
3151
3152 DefineClass,
3153 FindClass,
3154
3155 FromReflectedMethod,
3156 FromReflectedField,
3157 ToReflectedMethod,
3158
3159 GetSuperclass,
3160 IsAssignableFrom,
3161
3162 ToReflectedField,
3163
3164 Throw,
3165 ThrowNew,
3166 ExceptionOccurred,
3167 ExceptionDescribe,
3168 ExceptionClear,
3169 FatalError,
3170
3171 PushLocalFrame,
3172 PopLocalFrame,
3173
3174 NewGlobalRef,
3175 DeleteGlobalRef,
3176 DeleteLocalRef,
3177 IsSameObject,
3178 NewLocalRef,
3179 EnsureLocalCapacity,
3180
3181 AllocObject,
3182 NewObject,
3183 NewObjectV,
3184 NewObjectA,
3185
3186 GetObjectClass,
3187 IsInstanceOf,
3188
3189 GetMethodID,
3190
3191 CallObjectMethod,
3192 CallObjectMethodV,
3193 CallObjectMethodA,
3194 CallBooleanMethod,
3195 CallBooleanMethodV,
3196 CallBooleanMethodA,
3197 CallByteMethod,
3198 CallByteMethodV,
3199 CallByteMethodA,
3200 CallCharMethod,
3201 CallCharMethodV,
3202 CallCharMethodA,
3203 CallShortMethod,
3204 CallShortMethodV,
3205 CallShortMethodA,
3206 CallIntMethod,
3207 CallIntMethodV,
3208 CallIntMethodA,
3209 CallLongMethod,
3210 CallLongMethodV,
3211 CallLongMethodA,
3212 CallFloatMethod,
3213 CallFloatMethodV,
3214 CallFloatMethodA,
3215 CallDoubleMethod,
3216 CallDoubleMethodV,
3217 CallDoubleMethodA,
3218 CallVoidMethod,
3219 CallVoidMethodV,
3220 CallVoidMethodA,
3221
3222 CallNonvirtualObjectMethod,
3223 CallNonvirtualObjectMethodV,
3224 CallNonvirtualObjectMethodA,
3225 CallNonvirtualBooleanMethod,
3226 CallNonvirtualBooleanMethodV,
3227 CallNonvirtualBooleanMethodA,
3228 CallNonvirtualByteMethod,
3229 CallNonvirtualByteMethodV,
3230 CallNonvirtualByteMethodA,
3231 CallNonvirtualCharMethod,
3232 CallNonvirtualCharMethodV,
3233 CallNonvirtualCharMethodA,
3234 CallNonvirtualShortMethod,
3235 CallNonvirtualShortMethodV,
3236 CallNonvirtualShortMethodA,
3237 CallNonvirtualIntMethod,
3238 CallNonvirtualIntMethodV,
3239 CallNonvirtualIntMethodA,
3240 CallNonvirtualLongMethod,
3241 CallNonvirtualLongMethodV,
3242 CallNonvirtualLongMethodA,
3243 CallNonvirtualFloatMethod,
3244 CallNonvirtualFloatMethodV,
3245 CallNonvirtualFloatMethodA,
3246 CallNonvirtualDoubleMethod,
3247 CallNonvirtualDoubleMethodV,
3248 CallNonvirtualDoubleMethodA,
3249 CallNonvirtualVoidMethod,
3250 CallNonvirtualVoidMethodV,
3251 CallNonvirtualVoidMethodA,
3252
3253 GetFieldID,
3254
3255 GetObjectField,
3256 GetBooleanField,
3257 GetByteField,
3258 GetCharField,
3259 GetShortField,
3260 GetIntField,
3261 GetLongField,
3262 GetFloatField,
3263 GetDoubleField,
3264 SetObjectField,
3265 SetBooleanField,
3266 SetByteField,
3267 SetCharField,
3268 SetShortField,
3269 SetIntField,
3270 SetLongField,
3271 SetFloatField,
3272 SetDoubleField,
3273
3274 GetStaticMethodID,
3275
3276 CallStaticObjectMethod,
3277 CallStaticObjectMethodV,
3278 CallStaticObjectMethodA,
3279 CallStaticBooleanMethod,
3280 CallStaticBooleanMethodV,
3281 CallStaticBooleanMethodA,
3282 CallStaticByteMethod,
3283 CallStaticByteMethodV,
3284 CallStaticByteMethodA,
3285 CallStaticCharMethod,
3286 CallStaticCharMethodV,
3287 CallStaticCharMethodA,
3288 CallStaticShortMethod,
3289 CallStaticShortMethodV,
3290 CallStaticShortMethodA,
3291 CallStaticIntMethod,
3292 CallStaticIntMethodV,
3293 CallStaticIntMethodA,
3294 CallStaticLongMethod,
3295 CallStaticLongMethodV,
3296 CallStaticLongMethodA,
3297 CallStaticFloatMethod,
3298 CallStaticFloatMethodV,
3299 CallStaticFloatMethodA,
3300 CallStaticDoubleMethod,
3301 CallStaticDoubleMethodV,
3302 CallStaticDoubleMethodA,
3303 CallStaticVoidMethod,
3304 CallStaticVoidMethodV,
3305 CallStaticVoidMethodA,
3306
3307 GetStaticFieldID,
3308
3309 GetStaticObjectField,
3310 GetStaticBooleanField,
3311 GetStaticByteField,
3312 GetStaticCharField,
3313 GetStaticShortField,
3314 GetStaticIntField,
3315 GetStaticLongField,
3316 GetStaticFloatField,
3317 GetStaticDoubleField,
3318
3319 SetStaticObjectField,
3320 SetStaticBooleanField,
3321 SetStaticByteField,
3322 SetStaticCharField,
3323 SetStaticShortField,
3324 SetStaticIntField,
3325 SetStaticLongField,
3326 SetStaticFloatField,
3327 SetStaticDoubleField,
3328
3329 NewString,
3330
3331 GetStringLength,
3332 GetStringChars,
3333 ReleaseStringChars,
3334
3335 NewStringUTF,
3336 GetStringUTFLength,
3337 GetStringUTFChars,
3338 ReleaseStringUTFChars,
3339
3340 GetArrayLength,
3341 NewObjectArray,
3342 GetObjectArrayElement,
3343 SetObjectArrayElement,
3344
3345 NewBooleanArray,
3346 NewByteArray,
3347 NewCharArray,
3348 NewShortArray,
3349 NewIntArray,
3350 NewLongArray,
3351 NewFloatArray,
3352 NewDoubleArray,
3353
3354 GetBooleanArrayElements,
3355 GetByteArrayElements,
3356 GetCharArrayElements,
3357 GetShortArrayElements,
3358 GetIntArrayElements,
3359 GetLongArrayElements,
3360 GetFloatArrayElements,
3361 GetDoubleArrayElements,
3362
3363 ReleaseBooleanArrayElements,
3364 ReleaseByteArrayElements,
3365 ReleaseCharArrayElements,
3366 ReleaseShortArrayElements,
3367 ReleaseIntArrayElements,
3368 ReleaseLongArrayElements,
3369 ReleaseFloatArrayElements,
3370 ReleaseDoubleArrayElements,
3371
3372 GetBooleanArrayRegion,
3373 GetByteArrayRegion,
3374 GetCharArrayRegion,
3375 GetShortArrayRegion,
3376 GetIntArrayRegion,
3377 GetLongArrayRegion,
3378 GetFloatArrayRegion,
3379 GetDoubleArrayRegion,
3380 SetBooleanArrayRegion,
3381 SetByteArrayRegion,
3382 SetCharArrayRegion,
3383 SetShortArrayRegion,
3384 SetIntArrayRegion,
3385 SetLongArrayRegion,
3386 SetFloatArrayRegion,
3387 SetDoubleArrayRegion,
3388
3389 RegisterNatives,
3390 UnregisterNatives,
3391
3392 MonitorEnter,
3393 MonitorExit,
3394
3395 GetJavaVM,
3396
3397 GetStringRegion,
3398 GetStringUTFRegion,
3399
3400 GetPrimitiveArrayCritical,
3401 ReleasePrimitiveArrayCritical,
3402
3403 GetStringCritical,
3404 ReleaseStringCritical,
3405
3406 NewWeakGlobalRef,
3407 DeleteWeakGlobalRef,
3408
3409 ExceptionCheck,
3410
3411 NewDirectByteBuffer,
3412 GetDirectBufferAddress,
3413 GetDirectBufferCapacity,
3414
3415 GetObjectRefType
3416};
3417static const struct JNIInvokeInterface gInvokeInterface = {
3418 NULL,
3419 NULL,
3420 NULL,
3421
3422 DestroyJavaVM,
3423 AttachCurrentThread,
3424 DetachCurrentThread,
3425
3426 GetEnv,
3427
3428 AttachCurrentThreadAsDaemon,
3429};
3430
3431
3432/*
3433 * ===========================================================================
3434 * VM/Env creation
3435 * ===========================================================================
3436 */
3437
3438/*
3439 * Enable "checked JNI" after the VM has partially started. This must
3440 * only be called in "zygote" mode, when we have one thread running.
3441 */
3442void dvmLateEnableCheckedJni(void)
3443{
3444 JNIEnvExt* extEnv;
3445 JavaVMExt* extVm;
3446
3447 extEnv = dvmGetJNIEnvForThread();
3448 if (extEnv == NULL) {
3449 LOGE("dvmLateEnableCheckedJni: thread has no JNIEnv\n");
3450 return;
3451 }
3452 extVm = extEnv->vm;
3453 assert(extVm != NULL);
3454
3455 if (!extVm->useChecked) {
3456 LOGD("Late-enabling CheckJNI\n");
3457 dvmUseCheckedJniVm(extVm);
3458 extVm->useChecked = true;
3459 dvmUseCheckedJniEnv(extEnv);
3460
3461 /* currently no way to pick up jniopts features */
3462 } else {
3463 LOGD("Not late-enabling CheckJNI (already on)\n");
3464 }
3465}
3466
3467/*
3468 * Not supported.
3469 */
3470jint JNI_GetDefaultJavaVMInitArgs(void* vm_args)
3471{
3472 return JNI_ERR;
3473}
3474
3475/*
3476 * Return a buffer full of created VMs.
3477 *
3478 * We always have zero or one.
3479 */
3480jint JNI_GetCreatedJavaVMs(JavaVM** vmBuf, jsize bufLen, jsize* nVMs)
3481{
3482 if (gDvm.vmList != NULL) {
3483 *nVMs = 1;
3484
3485 if (bufLen > 0)
3486 *vmBuf++ = gDvm.vmList;
3487 } else {
3488 *nVMs = 0;
3489 }
3490
3491 return JNI_OK;
3492}
3493
3494
3495/*
3496 * Create a new VM instance.
3497 *
3498 * The current thread becomes the main VM thread. We return immediately,
3499 * which effectively means the caller is executing in a native method.
3500 */
3501jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args)
3502{
3503 const JavaVMInitArgs* args = (JavaVMInitArgs*) vm_args;
3504 JNIEnvExt* pEnv = NULL;
3505 JavaVMExt* pVM = NULL;
3506 const char** argv;
3507 int argc = 0;
3508 int i, curOpt;
3509 int result = JNI_ERR;
3510 bool checkJni = false;
3511 bool warnError = true;
3512 bool forceDataCopy = false;
3513
3514 if (args->version < JNI_VERSION_1_2)
3515 return JNI_EVERSION;
3516
3517 // TODO: don't allow creation of multiple VMs -- one per customer for now
3518
3519 /* zero globals; not strictly necessary the first time a VM is started */
3520 memset(&gDvm, 0, sizeof(gDvm));
3521
3522 /*
3523 * Set up structures for JNIEnv and VM.
3524 */
3525 //pEnv = (JNIEnvExt*) malloc(sizeof(JNIEnvExt));
3526 pVM = (JavaVMExt*) malloc(sizeof(JavaVMExt));
3527
3528 //memset(pEnv, 0, sizeof(JNIEnvExt));
3529 //pEnv->funcTable = &gNativeInterface;
3530 //pEnv->vm = pVM;
3531 memset(pVM, 0, sizeof(JavaVMExt));
3532 pVM->funcTable = &gInvokeInterface;
3533 pVM->envList = pEnv;
3534 dvmInitMutex(&pVM->envListLock);
3535
3536 argv = (const char**) malloc(sizeof(char*) * (args->nOptions));
3537 memset(argv, 0, sizeof(char*) * (args->nOptions));
3538
3539 curOpt = 0;
3540
3541 /*
3542 * Convert JNI args to argv.
3543 *
3544 * We have to pull out vfprintf/exit/abort, because they use the
3545 * "extraInfo" field to pass function pointer "hooks" in. We also
3546 * look for the -Xcheck:jni stuff here.
3547 */
3548 for (i = 0; i < args->nOptions; i++) {
3549 const char* optStr = args->options[i].optionString;
3550
3551 if (optStr == NULL) {
3552 fprintf(stderr, "ERROR: arg %d string was null\n", i);
3553 goto bail;
3554 } else if (strcmp(optStr, "vfprintf") == 0) {
3555 gDvm.vfprintfHook = args->options[i].extraInfo;
3556 } else if (strcmp(optStr, "exit") == 0) {
3557 gDvm.exitHook = args->options[i].extraInfo;
3558 } else if (strcmp(optStr, "abort") == 0) {
3559 gDvm.abortHook = args->options[i].extraInfo;
3560 } else if (strcmp(optStr, "-Xcheck:jni") == 0) {
3561 checkJni = true;
3562 } else if (strncmp(optStr, "-Xjniopts:", 10) == 0) {
3563 const char* jniOpts = optStr + 9;
3564 while (jniOpts != NULL) {
3565 jniOpts++; /* skip past ':' or ',' */
3566 if (strncmp(jniOpts, "warnonly", 8) == 0) {
3567 warnError = false;
3568 } else if (strncmp(jniOpts, "forcecopy", 9) == 0) {
3569 forceDataCopy = true;
3570 } else {
3571 LOGW("unknown jni opt starting at '%s'\n", jniOpts);
3572 }
3573 jniOpts = strchr(jniOpts, ',');
3574 }
3575 } else {
3576 /* regular option */
3577 argv[curOpt++] = optStr;
3578 }
3579 }
3580 argc = curOpt;
3581
3582 if (checkJni) {
3583 dvmUseCheckedJniVm(pVM);
3584 pVM->useChecked = true;
3585 }
3586 pVM->warnError = warnError;
3587 pVM->forceDataCopy = forceDataCopy;
3588
3589 /* set this up before initializing VM, so it can create some JNIEnvs */
3590 gDvm.vmList = (JavaVM*) pVM;
3591
3592 /*
3593 * Create an env for main thread. We need to have something set up
3594 * here because some of the class initialization we do when starting
3595 * up the VM will call into native code.
3596 */
3597 pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL);
3598
3599 /* initialize VM */
3600 gDvm.initializing = true;
3601 if (dvmStartup(argc, argv, args->ignoreUnrecognized, (JNIEnv*)pEnv) != 0) {
3602 free(pEnv);
3603 free(pVM);
3604 goto bail;
3605 }
3606
3607 /*
3608 * Success! Return stuff to caller.
3609 */
3610 dvmChangeStatus(NULL, THREAD_NATIVE);
3611 *p_env = (JNIEnv*) pEnv;
3612 *p_vm = (JavaVM*) pVM;
3613 result = JNI_OK;
3614
3615bail:
3616 gDvm.initializing = false;
3617 if (result == JNI_OK)
3618 LOGV("JNI_CreateJavaVM succeeded\n");
3619 else
3620 LOGW("JNI_CreateJavaVM failed\n");
3621 free(argv);
3622 return result;
3623}
3624