1 /*
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 /*
27 Native methods and interaction with the GC
28
29 All JNI methods must start by changing their thread status to
30 THREAD_RUNNING, and finish by changing it back to THREAD_NATIVE before
31 returning to native code. The switch to "running" triggers a thread
32 suspension check.
33
34 With a rudimentary GC we should be able to skip the status change for
35 simple functions, e.g. IsSameObject, GetJavaVM, GetStringLength, maybe
36 even access to fields with primitive types. Our options are more limited
37 with a compacting GC, so we should replace JNI_ENTER with JNI_ENTER_NCGC
38 or somesuch on the "lite" functions if we want to try this optimization.
39
40 For performance reasons we do as little error-checking as possible here.
41 For example, we don't check to make sure the correct type of Object is
42 passed in when setting a field, and we don't prevent you from storing
43 new values in a "final" field. Such things are best handled in the
44 "check" version. For actions that are common, dangerous, and must be
45 checked at runtime, such as array bounds checks, we do the tests here.
46
47
48 General notes on local/global reference tracking
49
50 JNI provides explicit control over natively-held references that the VM GC
51 needs to know about. These can be local, in which case they're released
52 when the native method returns, or global, which are held until explicitly
53 released.
54
55 The references can be created and deleted with JNI NewLocalRef /
56 NewGlobalRef calls, but this is unusual except perhaps for holding on
57 to a Class reference. Most often they are created transparently by the
58 JNI functions. For example, the paired Get/Release calls guarantee that
59 objects survive until explicitly released, so a simple way to implement
60 this is to create a global reference on "Get" and delete it on "Release".
61 The AllocObject/NewObject functions must create local references, because
62 nothing else in the GC root set has a reference to the new objects.
63
64 The most common mode of operation is for a method to create zero or
65 more local references and return. Explicit "local delete" operations
66 are expected to be exceedingly rare, except when walking through an
67 object array, and the Push/PopLocalFrame calls are expected to be used
68 infrequently. For efficient operation, we want to add new local refs
69 with a simple store/increment operation; to avoid infinite growth in
70 pathological situations, we need to reclaim the space used by deleted
71 entries.
72
73 The simplest implementation is an expanding append-only array that compacts
74 when objects are deleted. In typical situations, e.g. running through
75 an array of objects, we will be deleting one of the most recently added
76 entries, so we can minimize the number of elements moved (or avoid having
77 to move any).
78
79 The spec says, "Local references are only valid in the thread in which
80 they are created. The native code must not pass local references from
81 one thread to another." It should also be noted that, while some calls
82 will *create* global references as a side-effect, only the NewGlobalRef
83 and NewWeakGlobalRef calls actually *return* global references.
84
85
86 Global reference tracking
87
88 There should be a small "active" set centered around the most-recently
89 added items. We can use an append-only, compacting array like we do for
90 local refs.
91
92 Because it's global, access to it has to be synchronized.
93
94 The JNI spec does not define any sort of limit, so the list must be able
95 to expand. It may be useful to log significant increases in usage to
96 help identify resource leaks.
97
98 TODO: we currently use global references on strings and primitive array
99 data, because they have the property we need (i.e. the pointer we return
100 is guaranteed valid until we explicitly release it). However, if we have
101 a compacting GC and don't want to pin all memory held by all global refs,
102 we actually want to treat these differently. Either we need a way to
103 tell the GC that specific global references are pinned, or we have to
104 make a copy of the data and return that instead (something JNI supports).
105
106
107 Local reference tracking
108
109 The table of local references can be stored on the interpreted stack or
110 in a parallel data structure (one per thread).
111
112 *** Approach #1: use the interpreted stack
113
114 The easiest place to tuck it is between the frame ptr and the first saved
115 register, which is always in0. (See the ASCII art in Stack.h.) We can
116 shift the "VM-specific goop" and frame ptr down, effectively inserting
117 the 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.
123 The only thing that isn't determined by an offset from the current FP
124 is the previous frame. However, tucking things below the previous frame
125 can be problematic because the "outs" of the previous frame overlap with
126 the "ins" of the current frame. If the "ins" are altered they must be
127 restored before we return. For a native method call, the easiest and
128 safest thing to disrupt is #1, because there are no locals and the "ins"
129 are all copied to the native stack.)
130
131 We can implement Push/PopLocalFrame with the existing stack frame calls,
132 making sure we copy some goop from the previous frame (notably the method
133 ptr, so that dvmGetCurrentJNIMethod() doesn't require extra effort).
134
135 We can pre-allocate the storage at the time the stack frame is first
136 set up, but we have to be careful. When calling from interpreted code
137 the frame ptr points directly at the arguments we're passing, but we can
138 offset the args pointer when calling the native bridge.
139
140 To manage the local ref collection, we need to be able to find three
141 things: (1) the start of the region, (2) the end of the region, and (3)
142 the next available entry. The last is only required for quick adds.
143 We currently have two easily-accessible pointers, the current FP and the
144 previous frame's FP. (The "stack pointer" shown in the ASCII art doesn't
145 actually exist in the interpreted world.)
146
147 We can't use the current FP to find the first "in", because we want to
148 insert the variable-sized local refs table between them. It's awkward
149 to use the previous frame's FP because native methods invoked via
150 dvmCallMethod() or dvmInvokeMethod() don't have "ins", but native methods
151 invoked from interpreted code do. We can either track the local refs
152 table size with a field in the stack frame, or insert unnecessary items
153 so that all native stack frames have "ins".
154
155 Assuming we can find the region bounds, we still need pointer #3
156 for an efficient implementation. This can be stored in an otherwise
157 unused-for-native field in the frame goop.
158
159 When we run out of room we have to make more space. If we start allocating
160 locals immediately below in0 and grow downward, we will detect end-of-space
161 by 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).
163 This is nice because we only have to move sizeof(StackSaveArea) bytes
164 each time.
165
166 Stack 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
168 the debugger or SIGQUIT handler looking for "this" or other arguments,
169 but in practice this behavior isn't expected to work for native methods,
170 so we can simply disallow it.
171
172 A conservative GC can just scan the entire stack from top to bottom to find
173 all references. An exact GC will need to understand the actual layout.
174
175 *** Approach #2: use a parallel stack
176
177 Each Thread/JNIEnv points to a ReferenceTable struct. The struct
178 has a system-heap-allocated array of references and a pointer to the
179 next-available entry ("nextEntry").
180
181 Each stack frame has a pointer to what it sees as the "top" element in the
182 array (we can double-up the "currentPc" field). This is set to "nextEntry"
183 when the frame is pushed on. As local references are added or removed,
184 "nextEntry" is updated.
185
186 We implement Push/PopLocalFrame with actual stack frames. Before a JNI
187 frame gets popped, we set "nextEntry" to the "top" pointer of the current
188 frame, effectively releasing the references.
189
190 The GC will scan all references from the start of the table to the
191 "nextEntry" pointer.
192
193 *** Comparison
194
195 All approaches will return a failure result when they run out of local
196 reference space. For #1 that means blowing out the stack, for #2 it's
197 running out of room in the array.
198
199 Compared 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
220 /* fwd */
221 static const struct JNINativeInterface gNativeInterface;
222 static jobject addGlobalReference(jobject obj);
223
224
225 #ifdef WITH_JNI_STACK_CHECK
226 # define COMPUTE_STACK_SUM(_self) computeStackSum(_self);
227 # define CHECK_STACK_SUM(_self) checkStackSum(_self);
228 static void computeStackSum(Thread* self);
229 static 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 */
dvmCallJNIMethod(const u4 * args,JValue * pResult,const Method * method,Thread * self)254 void 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 */
dvmCallSynchronizedJNIMethod(const u4 * args,JValue * pResult,const Method * method,Thread * self)285 void 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 */
dvmGetArgInfoReturnType(int jniArgInfo)309 DalvikJniReturnType 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 /*
355 * Allocate the global references table, and look up some classes for
356 * the benefit of direct buffer access.
357 */
dvmJniStartup(void)358 bool 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
369 /*
370 * Look up and cache pointers to some direct buffer classes, fields,
371 * and methods.
372 */
373 Method* meth;
374
375 ClassObject* platformAddressClass =
376 dvmFindSystemClassNoInit("Lorg/apache/harmony/luni/platform/PlatformAddress;");
377 ClassObject* platformAddressFactoryClass =
378 dvmFindSystemClassNoInit("Lorg/apache/harmony/luni/platform/PlatformAddressFactory;");
379 ClassObject* directBufferClass =
380 dvmFindSystemClassNoInit("Lorg/apache/harmony/nio/internal/DirectBuffer;");
381 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 {
390 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);
396 gDvm.classJavaNioReadWriteDirectByteBuffer = readWriteBufferClass;
397
398 /*
399 * We need a Method* here rather than a vtable offset, because
400 * DirectBuffer is an interface class.
401 */
402 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 }
418 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 }
452
453 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
460 return true;
461 }
462
463 /*
464 * Free the global references table.
465 */
dvmJniShutdown(void)466 void 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 */
dvmGetJNIEnvForThread(void)478 JNIEnvExt* 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 */
dvmCreateJNIEnv(Thread * self)492 JNIEnv* 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 */
dvmDestroyJNIEnv(JNIEnv * env)538 void 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 */
getLocalRefTable(void)577 static 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 */
addLocalReference(jobject obj)594 static 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 */
ensureLocalCapacity(int capacity)620 static 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 */
deleteLocalReference(jobject obj)630 static 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 */
addGlobalReference(jobject obj)658 static 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
743 bail:
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 */
deleteGlobalReference(jobject obj)755 static 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
779 bail:
780 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
781 }
782
783 /*
784 * GC helper function to mark all JNI global references.
785 */
dvmGcMarkJniGlobalRefs()786 void 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 */
findInArgList(Thread * self,Object * obj)807 static 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 */
dvmGetJNIRefType(Object * obj)888 jobjectRefType 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 */
dvmRegisterJNIMethod(ClassObject * clazz,const char * methodName,const char * signature,void * fnPtr)926 static 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
969 bail:
970 return result;
971 }
972
973 /*
974 * Get the method currently being executed by examining the interp stack.
975 */
dvmGetCurrentJNIMethod(void)976 const 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 */
trackMonitorEnter(Thread * self,Object * obj)999 static 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 */
trackMonitorExit(Thread * self,Object * obj)1026 static 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 */
dvmReleaseJniMonitors(Thread * self)1041 void 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 */
computeStackSum(Thread * self)1069 static 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 */
checkStackSum(Thread * self)1088 static 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 */
GetVersion(JNIEnv * env)1126 static 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 */
DefineClass(JNIEnv * env,const char * name,jobject loader,const jbyte * buf,jsize bufLen)1143 static 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 */
FindClass(JNIEnv * env,const char * name)1171 static 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
1207 bail:
1208 free(descriptor);
1209
1210 JNI_EXIT();
1211 return (jclass)clazz;
1212 }
1213
1214 /*
1215 * Return the superclass of a class.
1216 */
GetSuperclass(JNIEnv * env,jclass clazz)1217 static 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 */
IsAssignableFrom(JNIEnv * env,jclass clazz1,jclass clazz2)1231 static 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 */
FromReflectedMethod(JNIEnv * env,jobject method)1245 static 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 */
FromReflectedField(JNIEnv * env,jobject field)1257 static 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 */
ToReflectedMethod(JNIEnv * env,jclass cls,jmethodID methodID,jboolean isStatic)1272 static 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 */
ToReflectedField(JNIEnv * env,jclass cls,jfieldID fieldID,jboolean isStatic)1292 static 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 */
Throw(JNIEnv * env,jthrowable obj)1309 static 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 */
ThrowNew(JNIEnv * env,jclass clazz,const char * message)1329 static 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 */
ExceptionOccurred(JNIEnv * env)1349 static 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 */
ExceptionDescribe(JNIEnv * env)1376 static 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 */
ExceptionClear(JNIEnv * env)1395 static 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 */
FatalError(JNIEnv * env,const char * msg)1405 static 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 */
PushLocalFrame(JNIEnv * env,jint capacity)1418 static 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 */
PopLocalFrame(JNIEnv * env,jobject result)1439 static 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 */
NewGlobalRef(JNIEnv * env,jobject obj)1456 static 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 */
DeleteGlobalRef(JNIEnv * env,jobject globalRef)1467 static 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 */
NewLocalRef(JNIEnv * env,jobject ref)1478 static 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 */
DeleteLocalRef(JNIEnv * env,jobject localRef)1491 static 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 */
EnsureLocalCapacity(JNIEnv * env,jint capacity)1502 static 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 */
IsSameObject(JNIEnv * env,jobject ref1,jobject ref2)1521 static 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 */
AllocObject(JNIEnv * env,jclass jclazz)1532 static 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 */
NewObject(JNIEnv * env,jclass jclazz,jmethodID methodID,...)1554 static 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 }
NewObjectV(JNIEnv * env,jclass clazz,jmethodID methodID,va_list args)1580 static 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 }
NewObjectA(JNIEnv * env,jclass clazz,jmethodID methodID,jvalue * args)1597 static 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 */
GetObjectClass(JNIEnv * env,jobject obj)1620 static 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 */
IsInstanceOf(JNIEnv * env,jobject obj,jclass clazz)1637 static 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 */
GetMethodID(JNIEnv * env,jclass jclazz,const char * name,const char * sig)1661 static 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 */
GetFieldID(JNIEnv * env,jclass jclazz,const char * name,const char * sig)1712 static 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 */
GetStaticMethodID(JNIEnv * env,jclass jclazz,const char * name,const char * sig)1735 static 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 */
GetStaticFieldID(JNIEnv * env,jclass jclazz,const char * name,const char * sig)1775 static 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 }
1813 GET_STATIC_TYPE_FIELD(jobject, Object, true);
1814 GET_STATIC_TYPE_FIELD(jboolean, Boolean, false);
1815 GET_STATIC_TYPE_FIELD(jbyte, Byte, false);
1816 GET_STATIC_TYPE_FIELD(jchar, Char, false);
1817 GET_STATIC_TYPE_FIELD(jshort, Short, false);
1818 GET_STATIC_TYPE_FIELD(jint, Int, false);
1819 GET_STATIC_TYPE_FIELD(jlong, Long, false);
1820 GET_STATIC_TYPE_FIELD(jfloat, Float, false);
1821 GET_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 }
1836 SET_STATIC_TYPE_FIELD(jobject, Object, l);
1837 SET_STATIC_TYPE_FIELD(jboolean, Boolean, z);
1838 SET_STATIC_TYPE_FIELD(jbyte, Byte, b);
1839 SET_STATIC_TYPE_FIELD(jchar, Char, c);
1840 SET_STATIC_TYPE_FIELD(jshort, Short, s);
1841 SET_STATIC_TYPE_FIELD(jint, Int, i);
1842 SET_STATIC_TYPE_FIELD(jlong, Long, j);
1843 SET_STATIC_TYPE_FIELD(jfloat, Float, f);
1844 SET_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 }
1863 GET_TYPE_FIELD(jobject, Object, true);
1864 GET_TYPE_FIELD(jboolean, Boolean, false);
1865 GET_TYPE_FIELD(jbyte, Byte, false);
1866 GET_TYPE_FIELD(jchar, Char, false);
1867 GET_TYPE_FIELD(jshort, Short, false);
1868 GET_TYPE_FIELD(jint, Int, false);
1869 GET_TYPE_FIELD(jlong, Long, false);
1870 GET_TYPE_FIELD(jfloat, Float, false);
1871 GET_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 }
1885 SET_TYPE_FIELD(jobject, Object);
1886 SET_TYPE_FIELD(jboolean, Boolean);
1887 SET_TYPE_FIELD(jbyte, Byte);
1888 SET_TYPE_FIELD(jchar, Char);
1889 SET_TYPE_FIELD(jshort, Short);
1890 SET_TYPE_FIELD(jint, Int);
1891 SET_TYPE_FIELD(jlong, Long);
1892 SET_TYPE_FIELD(jfloat, Float);
1893 SET_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 }
1959 CALL_VIRTUAL(jobject, Object, NULL, result.l, true);
1960 CALL_VIRTUAL(jboolean, Boolean, 0, result.z, false);
1961 CALL_VIRTUAL(jbyte, Byte, 0, result.b, false);
1962 CALL_VIRTUAL(jchar, Char, 0, result.c, false);
1963 CALL_VIRTUAL(jshort, Short, 0, result.s, false);
1964 CALL_VIRTUAL(jint, Int, 0, result.i, false);
1965 CALL_VIRTUAL(jlong, Long, 0, result.j, false);
1966 CALL_VIRTUAL(jfloat, Float, 0.0f, result.f, false);
1967 CALL_VIRTUAL(jdouble, Double, 0.0, result.d, false);
1968 CALL_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 }
2038 CALL_NONVIRTUAL(jobject, Object, NULL, result.l, true);
2039 CALL_NONVIRTUAL(jboolean, Boolean, 0, result.z, false);
2040 CALL_NONVIRTUAL(jbyte, Byte, 0, result.b, false);
2041 CALL_NONVIRTUAL(jchar, Char, 0, result.c, false);
2042 CALL_NONVIRTUAL(jshort, Short, 0, result.s, false);
2043 CALL_NONVIRTUAL(jint, Int, 0, result.i, false);
2044 CALL_NONVIRTUAL(jlong, Long, 0, result.j, false);
2045 CALL_NONVIRTUAL(jfloat, Float, 0.0f, result.f, false);
2046 CALL_NONVIRTUAL(jdouble, Double, 0.0, result.d, false);
2047 CALL_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 }
2093 CALL_STATIC(jobject, Object, NULL, result.l, true);
2094 CALL_STATIC(jboolean, Boolean, 0, result.z, false);
2095 CALL_STATIC(jbyte, Byte, 0, result.b, false);
2096 CALL_STATIC(jchar, Char, 0, result.c, false);
2097 CALL_STATIC(jshort, Short, 0, result.s, false);
2098 CALL_STATIC(jint, Int, 0, result.i, false);
2099 CALL_STATIC(jlong, Long, 0, result.j, false);
2100 CALL_STATIC(jfloat, Float, 0.0f, result.f, false);
2101 CALL_STATIC(jdouble, Double, 0.0, result.d, false);
2102 CALL_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 */
NewString(JNIEnv * env,const jchar * unicodeChars,jsize len)2110 static 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 */
GetStringLength(JNIEnv * env,jstring string)2128 static 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 */
GetStringChars(JNIEnv * env,jstring string,jboolean * isCopy)2152 static 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 */
ReleaseStringChars(JNIEnv * env,jstring string,const jchar * chars)2170 static 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 */
NewStringUTF(JNIEnv * env,const char * bytes)2183 static 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 */
GetStringUTFLength(JNIEnv * env,jstring string)2206 static 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 */
GetStringUTFChars(JNIEnv * env,jstring string,jboolean * isCopy)2230 static 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 */
ReleaseStringUTFChars(JNIEnv * env,jstring string,const char * utf)2258 static 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 */
GetArrayLength(JNIEnv * env,jarray array)2268 static 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 */
NewObjectArray(JNIEnv * env,jsize length,jclass elementClass,jobject initialElement)2281 static 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
2314 bail:
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 */
GetObjectArrayElement(JNIEnv * env,jobjectArray array,jsize index)2324 static 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
2344 bail:
2345 JNI_EXIT();
2346 return (jobject) value;
2347 }
2348
2349 /*
2350 * Set one element of an Object array.
2351 */
SetObjectArrayElement(JNIEnv * env,jobjectArray array,jsize index,jobject value)2352 static 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
2372 bail:
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 }
2393 NEW_PRIMITIVE_ARRAY(jbooleanArray, Boolean, 'Z');
2394 NEW_PRIMITIVE_ARRAY(jbyteArray, Byte, 'B');
2395 NEW_PRIMITIVE_ARRAY(jcharArray, Char, 'C');
2396 NEW_PRIMITIVE_ARRAY(jshortArray, Short, 'S');
2397 NEW_PRIMITIVE_ARRAY(jintArray, Int, 'I');
2398 NEW_PRIMITIVE_ARRAY(jlongArray, Long, 'J');
2399 NEW_PRIMITIVE_ARRAY(jfloatArray, Float, 'F');
2400 NEW_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
2501 PRIMITIVE_ARRAY_FUNCTIONS(jboolean, Boolean);
2502 PRIMITIVE_ARRAY_FUNCTIONS(jbyte, Byte);
2503 PRIMITIVE_ARRAY_FUNCTIONS(jchar, Char);
2504 PRIMITIVE_ARRAY_FUNCTIONS(jshort, Short);
2505 PRIMITIVE_ARRAY_FUNCTIONS(jint, Int);
2506 PRIMITIVE_ARRAY_FUNCTIONS(jlong, Long);
2507 PRIMITIVE_ARRAY_FUNCTIONS(jfloat, Float);
2508 PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double);
2509
2510 /*
2511 * Register one or more native functions in one class.
2512 */
RegisterNatives(JNIEnv * env,jclass clazz,const JNINativeMethod * methods,jint nMethods)2513 static 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
2536 bail:
2537 JNI_EXIT();
2538 return retval;
2539 }
2540
2541 /*
2542 * Un-register a native function.
2543 */
UnregisterNatives(JNIEnv * env,jclass clazz)2544 static 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 */
MonitorEnter(JNIEnv * env,jobject obj)2563 static 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 */
MonitorExit(JNIEnv * env,jobject obj)2581 static 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 */
GetJavaVM(JNIEnv * env,JavaVM ** vm)2594 static 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 */
GetStringRegion(JNIEnv * env,jstring str,jsize start,jsize len,jchar * buf)2609 static 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 */
GetStringUTFRegion(JNIEnv * env,jstring str,jsize start,jsize len,char * buf)2625 static 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 */
GetPrimitiveArrayCritical(JNIEnv * env,jarray array,jboolean * isCopy)2645 static 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 */
ReleasePrimitiveArrayCritical(JNIEnv * env,jarray array,void * carray,jint mode)2662 static 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 */
GetStringCritical(JNIEnv * env,jstring string,jboolean * isCopy)2674 static 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 */
ReleaseStringCritical(JNIEnv * env,jstring string,const jchar * carray)2691 static 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 */
NewWeakGlobalRef(JNIEnv * env,jobject obj)2702 static 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 */
DeleteWeakGlobalRef(JNIEnv * env,jweak obj)2716 static 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 */
ExceptionCheck(JNIEnv * env)2730 static 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 */
GetObjectRefType(JNIEnv * env,jobject obj)2746 static 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 *
2762 * "address" may not be NULL, and "capacity" must be > 0. (These are only
2763 * verified when CheckJNI is enabled.)
2764 */
NewDirectByteBuffer(JNIEnv * env,void * address,jlong capacity)2765 static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity)
2766 {
2767 JNI_ENTER();
2768
2769 Thread* self = _self /*dvmThreadSelf()*/;
2770 Object* platformAddress = NULL;
2771 JValue callResult;
2772 jobject result = NULL;
2773
2774 /* 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)
2779 goto bail;
2780
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;
2798 }
2799
2800 result = (jobject) newObj;
2801
2802 bail:
2803 if (platformAddress != NULL)
2804 dvmReleaseTrackedAlloc(platformAddress, self);
2805 JNI_EXIT();
2806 return result;
2807 }
2808
2809 /*
2810 * Get the starting address of the buffer for the specified java.nio.Buffer.
2811 *
2812 * If this is not a "direct" buffer, we return NULL.
2813 */
GetDirectBufferAddress(JNIEnv * env,jobject buf)2814 static void* GetDirectBufferAddress(JNIEnv* env, jobject buf)
2815 {
2816 JNI_ENTER();
2817
2818 Object* bufObj = (Object*) buf;
2819 Thread* self = _self /*dvmThreadSelf()*/;
2820 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 }
2835
2836 /*
2837 * Start by determining if the object supports the DirectBuffer
2838 * interfaces. Note this does not guarantee that it's a direct buffer.
2839 */
2840 if (!dvmInstanceof(bufObj->clazz,
2841 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer))
2842 {
2843 goto bail;
2844 }
2845
2846 /*
2847 * Get a PlatformAddress object with the effective address.
2848 *
2849 * If this isn't a direct buffer, the result will be NULL and/or an
2850 * exception will have been thrown.
2851 */
2852 JValue callResult;
2853 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;
2859 }
2860
2861 Object* platformAddr = callResult.l;
2862 if (platformAddr == NULL) {
2863 LOGV("Got request for address of non-direct buffer\n");
2864 goto bail;
2865 }
2866
2867 /*
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);
2874
2875 //LOGI("slow path for %p --> %p\n", buf, result);
2876
2877 bail:
2878 JNI_EXIT();
2879 return result;
2880 }
2881
2882 /*
2883 * Get the capacity of the buffer for the specified java.nio.Buffer.
2884 *
2885 * 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.)
2888 */
GetDirectBufferCapacity(JNIEnv * env,jobject buf)2889 static jlong GetDirectBufferCapacity(JNIEnv* env, jobject buf)
2890 {
2891 JNI_ENTER();
2892
2893 /*
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);
2900
2901 JNI_EXIT();
2902 return result;
2903 }
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 */
attachThread(JavaVM * vm,JNIEnv ** p_env,void * thr_args,bool isDaemon)2931 static 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 */
AttachCurrentThread(JavaVM * vm,JNIEnv ** p_env,void * thr_args)3011 static 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 */
AttachCurrentThreadAsDaemon(JavaVM * vm,JNIEnv ** p_env,void * thr_args)3019 static 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 */
DetachCurrentThread(JavaVM * vm)3028 static 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 */
GetEnv(JavaVM * vm,void ** env,jint version)3052 static 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 */
DestroyJavaVM(JavaVM * vm)3087 static 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
3123 shutdown:
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
3144 static 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 };
3417 static 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 */
dvmLateEnableCheckedJni(void)3442 void 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 */
JNI_GetDefaultJavaVMInitArgs(void * vm_args)3470 jint 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 */
JNI_GetCreatedJavaVMs(JavaVM ** vmBuf,jsize bufLen,jsize * nVMs)3480 jint 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 */
JNI_CreateJavaVM(JavaVM ** p_vm,JNIEnv ** p_env,void * vm_args)3501 jint 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
3615 bail:
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
3625