blob: abd520a9b39c2730d29b906348ebc0811ba44312 [file] [log] [blame]
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Andy McFadden59b61772009-05-13 16:44:34 -070016
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080017/*
18 * Dalvik implementation of JNI interfaces.
19 */
20#include "Dalvik.h"
21#include "JniInternal.h"
22
23#include <stdlib.h>
24#include <stdarg.h>
25#include <limits.h>
26
27/*
28Native methods and interaction with the GC
29
30All JNI methods must start by changing their thread status to
31THREAD_RUNNING, and finish by changing it back to THREAD_NATIVE before
32returning to native code. The switch to "running" triggers a thread
33suspension check.
34
35With a rudimentary GC we should be able to skip the status change for
36simple functions, e.g. IsSameObject, GetJavaVM, GetStringLength, maybe
37even access to fields with primitive types. Our options are more limited
38with a compacting GC, so we should replace JNI_ENTER with JNI_ENTER_NCGC
39or somesuch on the "lite" functions if we want to try this optimization.
40
41For performance reasons we do as little error-checking as possible here.
42For example, we don't check to make sure the correct type of Object is
43passed in when setting a field, and we don't prevent you from storing
44new values in a "final" field. Such things are best handled in the
45"check" version. For actions that are common, dangerous, and must be
46checked at runtime, such as array bounds checks, we do the tests here.
47
48
49General notes on local/global reference tracking
50
Andy McFadden0083d372009-08-21 14:44:04 -070051JNI provides explicit control over natively-held references that the GC
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080052needs to know about. These can be local, in which case they're released
Andy McFadden0083d372009-08-21 14:44:04 -070053when the native method returns into the VM, or global, which are held
54until explicitly released.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080055
Andy McFadden0083d372009-08-21 14:44:04 -070056The references can be created with explicit JNI NewLocalRef / NewGlobalRef
57calls. The former is very unusual, the latter is reasonably common
58(e.g. for caching references to class objects).
59
60Local references are most often created as a side-effect of JNI functions.
61For example, the AllocObject/NewObject functions must create local
62references to the objects returned, because nothing else in the GC root
63set has a reference to the new objects.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080064
65The most common mode of operation is for a method to create zero or
66more local references and return. Explicit "local delete" operations
67are expected to be exceedingly rare, except when walking through an
68object array, and the Push/PopLocalFrame calls are expected to be used
69infrequently. For efficient operation, we want to add new local refs
70with a simple store/increment operation; to avoid infinite growth in
71pathological situations, we need to reclaim the space used by deleted
72entries.
73
74The simplest implementation is an expanding append-only array that compacts
75when objects are deleted. In typical situations, e.g. running through
76an array of objects, we will be deleting one of the most recently added
77entries, so we can minimize the number of elements moved (or avoid having
78to move any).
79
80The spec says, "Local references are only valid in the thread in which
81they are created. The native code must not pass local references from
Andy McFadden0083d372009-08-21 14:44:04 -070082one thread to another."
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080083
84
85Global reference tracking
86
87There should be a small "active" set centered around the most-recently
88added items. We can use an append-only, compacting array like we do for
89local refs.
90
91Because it's global, access to it has to be synchronized.
92
93The JNI spec does not define any sort of limit, so the list must be able
94to expand. It may be useful to log significant increases in usage to
95help identify resource leaks.
96
97TODO: we currently use global references on strings and primitive array
98data, because they have the property we need (i.e. the pointer we return
99is guaranteed valid until we explicitly release it). However, if we have
100a compacting GC and don't want to pin all memory held by all global refs,
101we actually want to treat these differently. Either we need a way to
102tell the GC that specific global references are pinned, or we have to
103make a copy of the data and return that instead (something JNI supports).
104
105
106Local reference tracking
107
108The table of local references can be stored on the interpreted stack or
109in a parallel data structure (one per thread).
110
111*** Approach #1: use the interpreted stack
112
113The easiest place to tuck it is between the frame ptr and the first saved
114register, which is always in0. (See the ASCII art in Stack.h.) We can
115shift the "VM-specific goop" and frame ptr down, effectively inserting
116the JNI local refs in the space normally occupied by local variables.
117
118(Three things are accessed from the frame pointer:
119 (1) framePtr[N] is register vN, used to get at "ins" and "locals".
120 (2) framePtr - sizeof(StackSaveArea) is the VM frame goop.
121 (3) framePtr - sizeof(StackSaveArea) - numOuts is where the "outs" go.
122The only thing that isn't determined by an offset from the current FP
123is the previous frame. However, tucking things below the previous frame
124can be problematic because the "outs" of the previous frame overlap with
125the "ins" of the current frame. If the "ins" are altered they must be
126restored before we return. For a native method call, the easiest and
127safest thing to disrupt is #1, because there are no locals and the "ins"
128are all copied to the native stack.)
129
130We can implement Push/PopLocalFrame with the existing stack frame calls,
131making sure we copy some goop from the previous frame (notably the method
132ptr, so that dvmGetCurrentJNIMethod() doesn't require extra effort).
133
134We can pre-allocate the storage at the time the stack frame is first
135set up, but we have to be careful. When calling from interpreted code
136the frame ptr points directly at the arguments we're passing, but we can
137offset the args pointer when calling the native bridge.
138
139To manage the local ref collection, we need to be able to find three
140things: (1) the start of the region, (2) the end of the region, and (3)
141the next available entry. The last is only required for quick adds.
142We currently have two easily-accessible pointers, the current FP and the
143previous frame's FP. (The "stack pointer" shown in the ASCII art doesn't
144actually exist in the interpreted world.)
145
146We can't use the current FP to find the first "in", because we want to
147insert the variable-sized local refs table between them. It's awkward
148to use the previous frame's FP because native methods invoked via
149dvmCallMethod() or dvmInvokeMethod() don't have "ins", but native methods
150invoked from interpreted code do. We can either track the local refs
151table size with a field in the stack frame, or insert unnecessary items
152so that all native stack frames have "ins".
153
154Assuming we can find the region bounds, we still need pointer #3
155for an efficient implementation. This can be stored in an otherwise
156unused-for-native field in the frame goop.
157
158When we run out of room we have to make more space. If we start allocating
159locals immediately below in0 and grow downward, we will detect end-of-space
160by running into the current frame's FP. We then memmove() the goop down
161(memcpy if we guarantee the additional size is larger than the frame).
162This is nice because we only have to move sizeof(StackSaveArea) bytes
163each time.
164
165Stack walking should be okay so long as nothing tries to access the
166"ins" by an offset from the FP. In theory the "ins" could be read by
167the debugger or SIGQUIT handler looking for "this" or other arguments,
168but in practice this behavior isn't expected to work for native methods,
169so we can simply disallow it.
170
171A conservative GC can just scan the entire stack from top to bottom to find
172all references. An exact GC will need to understand the actual layout.
173
174*** Approach #2: use a parallel stack
175
176Each Thread/JNIEnv points to a ReferenceTable struct. The struct
177has a system-heap-allocated array of references and a pointer to the
178next-available entry ("nextEntry").
179
180Each stack frame has a pointer to what it sees as the "top" element in the
181array (we can double-up the "currentPc" field). This is set to "nextEntry"
182when the frame is pushed on. As local references are added or removed,
183"nextEntry" is updated.
184
185We implement Push/PopLocalFrame with actual stack frames. Before a JNI
186frame gets popped, we set "nextEntry" to the "top" pointer of the current
187frame, effectively releasing the references.
188
189The GC will scan all references from the start of the table to the
190"nextEntry" pointer.
191
192*** Comparison
193
194All approaches will return a failure result when they run out of local
195reference space. For #1 that means blowing out the stack, for #2 it's
196running out of room in the array.
197
198Compared to #1, approach #2:
199 - Needs only one pointer in the stack frame goop.
200 - Makes pre-allocating storage unnecessary.
201 - Doesn't contend with interpreted stack depth for space. In most
202 cases, if something blows out the local ref storage, it's because the
203 JNI code was misbehaving rather than called from way down.
204 - Allows the GC to do a linear scan per thread in a buffer that is 100%
205 references. The GC can be slightly less smart when scanning the stack.
206 - Will be easier to work with if we combine native and interpeted stacks.
207
208 - Isn't as clean, especially when popping frames, since we have to do
209 explicit work. Fortunately we only have to do it when popping native
210 method calls off, so it doesn't add overhead to interpreted code paths.
211 - Is awkward to expand dynamically. We'll want to pre-allocate the full
212 amount of space; this is fine, since something on the order of 1KB should
213 be plenty. The JNI spec allows us to limit this.
214 - Requires the GC to scan even more memory. With the references embedded
215 in the stack we get better locality of reference.
216
217*/
218
Andy McFadden5f612b82009-07-22 15:07:27 -0700219/* fwd */
220static const struct JNINativeInterface gNativeInterface;
Andy McFadden0083d372009-08-21 14:44:04 -0700221static jobject addLocalReference(JNIEnv* env, Object* obj);
Andy McFaddenab00d452009-08-19 07:21:41 -0700222static jobject addGlobalReference(Object* obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800223
224
225#ifdef WITH_JNI_STACK_CHECK
226# define COMPUTE_STACK_SUM(_self) computeStackSum(_self);
227# define CHECK_STACK_SUM(_self) checkStackSum(_self);
228static void computeStackSum(Thread* self);
229static void checkStackSum(Thread* self);
230#else
231# define COMPUTE_STACK_SUM(_self) ((void)0)
232# define CHECK_STACK_SUM(_self) ((void)0)
233#endif
234
235
236/*
237 * ===========================================================================
238 * JNI call bridge
239 * ===========================================================================
240 */
241
242/*
Andy McFadden0083d372009-08-21 14:44:04 -0700243 * The functions here form a bridge between interpreted code and JNI native
244 * functions. The basic task is to convert an array of primitives and
245 * references into C-style function arguments. This is architecture-specific
246 * and usually requires help from assembly code.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800247 *
Andy McFadden0083d372009-08-21 14:44:04 -0700248 * The bridge takes four arguments: the array of parameters, a place to
249 * store the function result (if any), the method to call, and a pointer
250 * to the current thread.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800251 *
Andy McFadden0083d372009-08-21 14:44:04 -0700252 * These functions aren't called directly from elsewhere in the VM.
253 * A pointer in the Method struct points to one of these, and when a native
254 * method is invoked the interpreter jumps to it.
255 *
256 * (The "internal native" methods are invoked the same way, but instead
257 * of calling through a bridge, the target method is called directly.)
258 *
259 * The "args" array should not be modified, but we do so anyway for
260 * performance reasons. We know that it points to the "outs" area on
261 * the current method's interpreted stack. This area is ignored by the
262 * precise GC, because there is no register map for a native method (for
263 * an interpreted method the args would be listed in the argument set).
264 * We know all of the values exist elsewhere on the interpreted stack,
265 * because the method call setup copies them right before making the call,
266 * so we don't have to worry about concealing stuff from the GC.
267 *
268 * If we don't want to modify "args", we either have to create a local
269 * copy and modify it before calling dvmPlatformInvoke, or we have to do
270 * the local reference replacement within dvmPlatformInvoke. The latter
271 * has some performance advantages, though if we can inline the local
272 * reference adds we may win when there's a lot of reference args (unless
273 * we want to code up some local ref table manipulation in assembly.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800274 */
Andy McFadden0083d372009-08-21 14:44:04 -0700275
276/*
277 * General form, handles all cases.
278 */
279void dvmCallJNIMethod_general(const u4* args, JValue* pResult,
280 const Method* method, Thread* self)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800281{
282 int oldStatus;
Andy McFadden0083d372009-08-21 14:44:04 -0700283 u4* modArgs = (u4*) args;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800284
285 assert(method->insns != NULL);
286
Andy McFadden0083d372009-08-21 14:44:04 -0700287 //LOGI("JNI calling %p (%s.%s:%s):\n", method->insns,
288 // method->clazz->descriptor, method->name, method->shorty);
289
290 /*
291 * Walk the argument list, creating local references for appropriate
292 * arguments.
293 */
294 JNIEnv* env = self->jniEnv;
295 jclass staticMethodClass;
296 int idx = 0;
297 if (dvmIsStaticMethod(method)) {
298 /* add the class object we pass in */
299 staticMethodClass = addLocalReference(env, (Object*) method->clazz);
300 if (staticMethodClass == NULL) {
301 assert(dvmCheckException(self));
302 return;
303 }
304 } else {
305 /* add "this" */
306 staticMethodClass = NULL;
307 jobject thisObj = addLocalReference(env, (Object*) modArgs[0]);
308 if (thisObj == NULL) {
309 assert(dvmCheckException(self));
310 return;
311 }
312 modArgs[idx] = (u4) thisObj;
313 idx = 1;
314 }
315
316 const char* shorty = &method->shorty[1]; /* skip return type */
317 while (*shorty != '\0') {
318 switch (*shorty++) {
319 case 'L':
320 //LOGI(" local %d: 0x%08x\n", idx, modArgs[idx]);
321 if (modArgs[idx] != 0) {
322 //if (!dvmIsValidObject((Object*) modArgs[idx]))
323 // dvmAbort();
324 jobject argObj = addLocalReference(env, (Object*) modArgs[idx]);
325 if (argObj == NULL) {
326 assert(dvmCheckException(self));
327 return;
328 }
329 modArgs[idx] = (u4) argObj;
330 }
331 break;
332 case 'D':
333 case 'J':
334 idx++;
335 break;
336 default:
337 /* Z B C S I -- do nothing */
338 break;
339 }
340
341 idx++;
342 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800343
344 oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
345
346 COMPUTE_STACK_SUM(self);
Andy McFadden0083d372009-08-21 14:44:04 -0700347 dvmPlatformInvoke(self->jniEnv, staticMethodClass,
348 method->jniArgInfo, method->insSize, modArgs, method->shorty,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800349 (void*)method->insns, pResult);
350 CHECK_STACK_SUM(self);
351
352 dvmChangeStatus(self, oldStatus);
353}
354
355/*
Andy McFadden0083d372009-08-21 14:44:04 -0700356 * Handler for the unusual case of a synchronized native method.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800357 *
Andy McFadden0083d372009-08-21 14:44:04 -0700358 * Lock the object, then call through the general function.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800359 */
Andy McFadden0083d372009-08-21 14:44:04 -0700360void dvmCallJNIMethod_synchronized(const u4* args, JValue* pResult,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800361 const Method* method, Thread* self)
362{
363 Object* lockObj;
364
365 assert(dvmIsSynchronizedMethod(method));
366
367 if (dvmIsStaticMethod(method))
368 lockObj = (Object*) method->clazz;
369 else
370 lockObj = (Object*) args[0];
371
372 LOGVV("Calling %s.%s: locking %p (%s)\n",
373 method->clazz->descriptor, method->name,
374 lockObj, lockObj->clazz->descriptor);
375
376 dvmLockObject(self, lockObj);
Andy McFadden0083d372009-08-21 14:44:04 -0700377 dvmCallJNIMethod_general(args, pResult, method, self);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800378 dvmUnlockObject(self, lockObj);
379}
380
381/*
Andy McFadden0083d372009-08-21 14:44:04 -0700382 * Virtual method call, no reference arguments.
383 *
384 * We need to local-ref the "this" argument, found in args[0].
385 */
386void dvmCallJNIMethod_virtualNoRef(const u4* args, JValue* pResult,
387 const Method* method, Thread* self)
388{
389 u4* modArgs = (u4*) args;
390 int oldStatus;
391
392 jobject thisObj = addLocalReference(self->jniEnv, (Object*) args[0]);
393 if (thisObj == NULL) {
394 assert(dvmCheckException(self));
395 return;
396 }
397 modArgs[0] = (u4) thisObj;
398
399 oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
400
401 COMPUTE_STACK_SUM(self);
402 dvmPlatformInvoke(self->jniEnv, NULL,
403 method->jniArgInfo, method->insSize, modArgs, method->shorty,
404 (void*)method->insns, pResult);
405 CHECK_STACK_SUM(self);
406
407 dvmChangeStatus(self, oldStatus);
408}
409
410/*
411 * Static method call, no reference arguments.
412 *
413 * We need to local-ref the class reference.
414 */
415void dvmCallJNIMethod_staticNoRef(const u4* args, JValue* pResult,
416 const Method* method, Thread* self)
417{
418 jclass staticMethodClass;
419 int oldStatus;
420
421 staticMethodClass = addLocalReference(self->jniEnv, (Object*)method->clazz);
422 if (staticMethodClass == NULL) {
423 assert(dvmCheckException(self));
424 return;
425 }
426
427 oldStatus = dvmChangeStatus(self, THREAD_NATIVE);
428
429 COMPUTE_STACK_SUM(self);
430 dvmPlatformInvoke(self->jniEnv, staticMethodClass,
431 method->jniArgInfo, method->insSize, args, method->shorty,
432 (void*)method->insns, pResult);
433 CHECK_STACK_SUM(self);
434
435 dvmChangeStatus(self, oldStatus);
436}
437
438/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800439 * Extract the return type enum from the "jniArgInfo" field.
440 */
441DalvikJniReturnType dvmGetArgInfoReturnType(int jniArgInfo)
442{
443 return (jniArgInfo & DALVIK_JNI_RETURN_MASK) >> DALVIK_JNI_RETURN_SHIFT;
444}
445
446
447/*
448 * ===========================================================================
449 * Utility functions
450 * ===========================================================================
451 */
452
453/*
454 * Entry/exit processing for all JNI calls.
455 *
456 * If TRUSTED_JNIENV is set, we get to skip the (curiously expensive)
457 * thread-local storage lookup on our Thread*. If the caller has passed
458 * the wrong JNIEnv in, we're going to be accessing unsynchronized
459 * structures from more than one thread, and things are going to fail
460 * in bizarre ways. This is only sensible if the native code has been
461 * fully exercised with CheckJNI enabled.
462 */
463#define TRUSTED_JNIENV
464#ifdef TRUSTED_JNIENV
465# define JNI_ENTER() \
466 Thread* _self = ((JNIEnvExt*)env)->self; \
467 CHECK_STACK_SUM(_self); \
468 dvmChangeStatus(_self, THREAD_RUNNING)
469#else
470# define JNI_ENTER() \
471 Thread* _self = dvmThreadSelf(); \
472 UNUSED_PARAMETER(env); \
473 CHECK_STACK_SUM(_self); \
474 dvmChangeStatus(_self, THREAD_RUNNING)
475#endif
476#define JNI_EXIT() \
477 dvmChangeStatus(_self, THREAD_NATIVE); \
478 COMPUTE_STACK_SUM(_self)
479
480#define kGlobalRefsTableInitialSize 512
Andy McFaddenab00d452009-08-19 07:21:41 -0700481#define kGlobalRefsTableMaxSize 51200 /* arbitrary, must be < 64K */
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800482#define kGrefWaterInterval 100
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800483#define kTrackGrefUsage true
484
Andy McFaddenc26bb632009-08-21 12:01:31 -0700485#define kPinTableInitialSize 16
486#define kPinTableMaxSize 1024
487#define kPinComplainThreshold 10
488
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800489/*
Andy McFadden5f612b82009-07-22 15:07:27 -0700490 * Allocate the global references table, and look up some classes for
491 * the benefit of direct buffer access.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800492 */
493bool dvmJniStartup(void)
494{
495 if (!dvmInitReferenceTable(&gDvm.jniGlobalRefTable,
496 kGlobalRefsTableInitialSize, kGlobalRefsTableMaxSize))
497 return false;
498
499 dvmInitMutex(&gDvm.jniGlobalRefLock);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800500 gDvm.jniGlobalRefLoMark = 0;
501 gDvm.jniGlobalRefHiMark = kGrefWaterInterval * 2;
502
Andy McFaddenc26bb632009-08-21 12:01:31 -0700503 if (!dvmInitReferenceTable(&gDvm.jniPinRefTable,
504 kPinTableInitialSize, kPinTableMaxSize))
505 return false;
506
507 dvmInitMutex(&gDvm.jniPinRefLock);
508
Andy McFadden8e5c7842009-07-23 17:47:18 -0700509 /*
510 * Look up and cache pointers to some direct buffer classes, fields,
511 * and methods.
512 */
513 Method* meth;
514
Andy McFadden5f612b82009-07-22 15:07:27 -0700515 ClassObject* platformAddressClass =
516 dvmFindSystemClassNoInit("Lorg/apache/harmony/luni/platform/PlatformAddress;");
Andy McFadden8e5c7842009-07-23 17:47:18 -0700517 ClassObject* platformAddressFactoryClass =
518 dvmFindSystemClassNoInit("Lorg/apache/harmony/luni/platform/PlatformAddressFactory;");
Andy McFadden5f612b82009-07-22 15:07:27 -0700519 ClassObject* directBufferClass =
520 dvmFindSystemClassNoInit("Lorg/apache/harmony/nio/internal/DirectBuffer;");
Andy McFadden8e5c7842009-07-23 17:47:18 -0700521 ClassObject* readWriteBufferClass =
522 dvmFindSystemClassNoInit("Ljava/nio/ReadWriteDirectByteBuffer;");
523 ClassObject* bufferClass =
524 dvmFindSystemClassNoInit("Ljava/nio/Buffer;");
525
526 if (platformAddressClass == NULL || platformAddressFactoryClass == NULL ||
527 directBufferClass == NULL || readWriteBufferClass == NULL ||
528 bufferClass == NULL)
529 {
Andy McFadden5f612b82009-07-22 15:07:27 -0700530 LOGE("Unable to find internal direct buffer classes\n");
531 return false;
532 }
533 /* needs to be a global ref so CheckJNI thinks we're allowed to see it */
534 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer =
535 addGlobalReference((Object*) directBufferClass);
Andy McFadden8e5c7842009-07-23 17:47:18 -0700536 gDvm.classJavaNioReadWriteDirectByteBuffer = readWriteBufferClass;
Andy McFadden5f612b82009-07-22 15:07:27 -0700537
Andy McFadden8e5c7842009-07-23 17:47:18 -0700538 /*
539 * We need a Method* here rather than a vtable offset, because
540 * DirectBuffer is an interface class.
541 */
Andy McFadden5f612b82009-07-22 15:07:27 -0700542 meth = dvmFindVirtualMethodByDescriptor(
543 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer,
544 "getEffectiveAddress",
545 "()Lorg/apache/harmony/luni/platform/PlatformAddress;");
546 if (meth == NULL) {
547 LOGE("Unable to find PlatformAddress.getEffectiveAddress\n");
548 return false;
549 }
550 gDvm.methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress = meth;
551
552 meth = dvmFindVirtualMethodByDescriptor(platformAddressClass,
553 "toLong", "()J");
554 if (meth == NULL) {
555 LOGE("Unable to find PlatformAddress.toLong\n");
556 return false;
557 }
Andy McFadden8e5c7842009-07-23 17:47:18 -0700558 gDvm.voffOrgApacheHarmonyLuniPlatformPlatformAddress_toLong =
559 meth->methodIndex;
560
561 meth = dvmFindDirectMethodByDescriptor(platformAddressFactoryClass,
562 "on",
563 "(I)Lorg/apache/harmony/luni/platform/PlatformAddress;");
564 if (meth == NULL) {
565 LOGE("Unable to find PlatformAddressFactory.on\n");
566 return false;
567 }
568 gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on = meth;
569
570 meth = dvmFindDirectMethodByDescriptor(readWriteBufferClass,
571 "<init>",
572 "(Lorg/apache/harmony/luni/platform/PlatformAddress;II)V");
573 if (meth == NULL) {
574 LOGE("Unable to find ReadWriteDirectByteBuffer.<init>\n");
575 return false;
576 }
577 gDvm.methJavaNioReadWriteDirectByteBuffer_init = meth;
578
579 gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr =
580 dvmFindFieldOffset(platformAddressClass, "osaddr", "I");
581 if (gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr < 0) {
582 LOGE("Unable to find PlatformAddress.osaddr\n");
583 return false;
584 }
585
586 gDvm.offJavaNioBuffer_capacity =
587 dvmFindFieldOffset(bufferClass, "capacity", "I");
588 if (gDvm.offJavaNioBuffer_capacity < 0) {
589 LOGE("Unable to find Buffer.capacity\n");
590 return false;
591 }
Andy McFadden5f612b82009-07-22 15:07:27 -0700592
Andy McFadden8e696dc2009-07-24 15:28:16 -0700593 gDvm.offJavaNioBuffer_effectiveDirectAddress =
594 dvmFindFieldOffset(bufferClass, "effectiveDirectAddress", "I");
595 if (gDvm.offJavaNioBuffer_effectiveDirectAddress < 0) {
596 LOGE("Unable to find Buffer.effectiveDirectAddress\n");
597 return false;
598 }
599
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800600 return true;
601}
602
603/*
604 * Free the global references table.
605 */
606void dvmJniShutdown(void)
607{
608 dvmClearReferenceTable(&gDvm.jniGlobalRefTable);
609}
610
611
612/*
613 * Find the JNIEnv associated with the current thread.
614 *
615 * Currently stored in the Thread struct. Could also just drop this into
616 * thread-local storage.
617 */
618JNIEnvExt* dvmGetJNIEnvForThread(void)
619{
620 Thread* self = dvmThreadSelf();
621 if (self == NULL)
622 return NULL;
623 return (JNIEnvExt*) dvmGetThreadJNIEnv(self);
624}
625
626/*
627 * Create a new JNIEnv struct and add it to the VM's list.
628 *
629 * "self" will be NULL for the main thread, since the VM hasn't started
630 * yet; the value will be filled in later.
631 */
632JNIEnv* dvmCreateJNIEnv(Thread* self)
633{
634 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
635 JNIEnvExt* newEnv;
636
637 //if (self != NULL)
638 // LOGI("Ent CreateJNIEnv: threadid=%d %p\n", self->threadId, self);
639
640 assert(vm != NULL);
641
642 newEnv = (JNIEnvExt*) calloc(1, sizeof(JNIEnvExt));
643 newEnv->funcTable = &gNativeInterface;
644 newEnv->vm = vm;
645 newEnv->forceDataCopy = vm->forceDataCopy;
646 if (self != NULL) {
647 dvmSetJniEnvThreadId((JNIEnv*) newEnv, self);
648 assert(newEnv->envThreadId != 0);
649 } else {
650 /* make it obvious if we fail to initialize these later */
651 newEnv->envThreadId = 0x77777775;
652 newEnv->self = (Thread*) 0x77777779;
653 }
654 if (vm->useChecked)
655 dvmUseCheckedJniEnv(newEnv);
656
657 dvmLockMutex(&vm->envListLock);
658
659 /* insert at head of list */
660 newEnv->next = vm->envList;
661 assert(newEnv->prev == NULL);
662 if (vm->envList == NULL) // rare, but possible
663 vm->envList = newEnv;
664 else
665 vm->envList->prev = newEnv;
666 vm->envList = newEnv;
667
668 dvmUnlockMutex(&vm->envListLock);
669
670 //if (self != NULL)
671 // LOGI("Xit CreateJNIEnv: threadid=%d %p\n", self->threadId, self);
672 return (JNIEnv*) newEnv;
673}
674
675/*
676 * Remove a JNIEnv struct from the list and free it.
677 */
678void dvmDestroyJNIEnv(JNIEnv* env)
679{
680 JNIEnvExt* extEnv = (JNIEnvExt*) env;
681 JavaVMExt* vm = extEnv->vm;
682 Thread* self;
683
684 if (env == NULL)
685 return;
686
687 self = dvmThreadSelf();
688 assert(self != NULL);
689
690 //LOGI("Ent DestroyJNIEnv: threadid=%d %p\n", self->threadId, self);
691
692 dvmLockMutex(&vm->envListLock);
693
694 if (extEnv == vm->envList) {
695 assert(extEnv->prev == NULL);
696 vm->envList = extEnv->next;
697 } else {
698 assert(extEnv->prev != NULL);
699 extEnv->prev->next = extEnv->next;
700 }
701 if (extEnv->next != NULL)
702 extEnv->next->prev = extEnv->prev;
703
704 dvmUnlockMutex(&extEnv->vm->envListLock);
705
706 free(env);
707 //LOGI("Xit DestroyJNIEnv: threadid=%d %p\n", self->threadId, self);
708}
709
710
711/*
712 * Retrieve the ReferenceTable struct for the current thread.
713 *
Andy McFaddenab00d452009-08-19 07:21:41 -0700714 * Going through "env" rather than dvmThreadSelf() is faster but will
715 * get weird if the JNI code is passing the wrong JNIEnv around.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800716 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700717static inline ReferenceTable* getLocalRefTable(JNIEnv* env)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800718{
Andy McFaddenab00d452009-08-19 07:21:41 -0700719 //return &dvmThreadSelf()->jniLocalRefTable;
720 return &((JNIEnvExt*)env)->self->jniLocalRefTable;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800721}
722
723/*
724 * Add a local reference for an object to the current stack frame. When
725 * the native function returns, the reference will be discarded.
726 *
727 * We need to allow the same reference to be added multiple times.
728 *
729 * This will be called on otherwise unreferenced objects. We cannot do
730 * GC allocations here, and it's best if we don't grab a mutex.
731 *
732 * Returns the local reference (currently just the same pointer that was
733 * passed in), or NULL on failure.
734 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700735static jobject addLocalReference(JNIEnv* env, Object* obj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800736{
737 if (obj == NULL)
738 return NULL;
739
Andy McFaddenab00d452009-08-19 07:21:41 -0700740 ReferenceTable* pRefTable = getLocalRefTable(env);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800741
Andy McFaddenab00d452009-08-19 07:21:41 -0700742 if (!dvmAddToReferenceTable(pRefTable, obj)) {
743 dvmDumpReferenceTable(pRefTable, "JNI local");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800744 LOGE("Failed adding to JNI local ref table (has %d entries)\n",
Andy McFaddenab00d452009-08-19 07:21:41 -0700745 (int) dvmReferenceTableEntries(pRefTable));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800746 dvmDumpThread(dvmThreadSelf(), false);
747 dvmAbort(); // spec says call FatalError; this is equivalent
748 } else {
Andy McFadden0083d372009-08-21 14:44:04 -0700749 LOGVV("LREF add %p (%s.%s) (ent=%d)\n", obj,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800750 dvmGetCurrentJNIMethod()->clazz->descriptor,
Andy McFadden0083d372009-08-21 14:44:04 -0700751 dvmGetCurrentJNIMethod()->name,
752 (int) dvmReferenceTableEntries(pRefTable));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800753 }
754
755 return obj;
756}
757
758/*
Andy McFaddenab00d452009-08-19 07:21:41 -0700759 * Convert an indirect reference to an Object reference. The indirect
760 * reference may be local, global, or weak-global.
761 *
762 * If "jobj" is NULL or an invalid indirect reference, this returns NULL.
763 *
764 * [ this is currently a no-op ]
765 */
766Object* dvmDecodeIndirectRef(JNIEnv* env, jobject jobj)
767{
768 return (Object*) jobj;
769}
770
771/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800772 * Ensure that at least "capacity" references can be held in the local
773 * refs table of the current thread.
774 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700775static bool ensureLocalCapacity(JNIEnv* env, int capacity)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800776{
Andy McFaddenab00d452009-08-19 07:21:41 -0700777 ReferenceTable* pRefTable = getLocalRefTable(env);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800778
Andy McFaddenab00d452009-08-19 07:21:41 -0700779 return (kJniLocalRefMax - (pRefTable->nextEntry - pRefTable->table) >= capacity);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800780}
781
782/*
783 * Explicitly delete a reference from the local list.
784 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700785static void deleteLocalReference(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800786{
Andy McFaddenab00d452009-08-19 07:21:41 -0700787 if (jobj == NULL)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800788 return;
789
Andy McFaddenab00d452009-08-19 07:21:41 -0700790 ReferenceTable* pRefTable = getLocalRefTable(env);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800791 Thread* self = dvmThreadSelf();
792 Object** top = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefTop;
793
Andy McFaddenab00d452009-08-19 07:21:41 -0700794 if (!dvmRemoveFromReferenceTable(pRefTable, top, (Object*) jobj)) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800795 /*
796 * Attempting to delete a local reference that is not in the
797 * topmost local reference frame is a no-op. DeleteLocalRef returns
798 * void and doesn't throw any exceptions, but we should probably
799 * complain about it so the user will notice that things aren't
800 * going quite the way they expect.
801 */
802 LOGW("JNI WARNING: DeleteLocalRef(%p) failed to find entry (valid=%d)\n",
Andy McFaddenab00d452009-08-19 07:21:41 -0700803 jobj, dvmIsValidObject((Object*) jobj));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800804 }
805}
806
807/*
808 * Add a global reference for an object.
809 *
810 * We may add the same object more than once. Add/remove calls are paired,
811 * so it needs to appear on the list multiple times.
812 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700813static jobject addGlobalReference(Object* obj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800814{
815 if (obj == NULL)
816 return NULL;
817
818 //LOGI("adding obj=%p\n", obj);
819 //dvmDumpThread(dvmThreadSelf(), false);
820
821 if (false && ((Object*)obj)->clazz == gDvm.classJavaLangClass) {
822 ClassObject* clazz = (ClassObject*) obj;
823 LOGI("-------\n");
824 LOGI("Adding global ref on class %s\n", clazz->descriptor);
825 dvmDumpThread(dvmThreadSelf(), false);
826 }
827 if (false && ((Object*)obj)->clazz == gDvm.classJavaLangString) {
828 StringObject* strObj = (StringObject*) obj;
829 char* str = dvmCreateCstrFromString(strObj);
830 if (strcmp(str, "sync-response") == 0) {
831 LOGI("-------\n");
832 LOGI("Adding global ref on string '%s'\n", str);
833 dvmDumpThread(dvmThreadSelf(), false);
834 //dvmAbort();
835 }
836 free(str);
837 }
838 if (false && ((Object*)obj)->clazz == gDvm.classArrayByte) {
839 ArrayObject* arrayObj = (ArrayObject*) obj;
840 if (arrayObj->length == 8192 &&
841 dvmReferenceTableEntries(&gDvm.jniGlobalRefTable) > 400)
842 {
843 LOGI("Adding global ref on byte array %p (len=%d)\n",
844 arrayObj, arrayObj->length);
845 dvmDumpThread(dvmThreadSelf(), false);
846 }
847 }
848
849 dvmLockMutex(&gDvm.jniGlobalRefLock);
850
851 /*
852 * Expanding the table should happen rarely, so I'm not overly
853 * concerned about the performance impact of copying the old list
854 * over. We shouldn't see one-time activity spikes, so freeing
855 * up storage shouldn't be required.
856 *
857 * Throwing an exception on failure is problematic, because JNI code
858 * may not be expecting an exception, and things sort of cascade. We
859 * want to have a hard limit to catch leaks during debugging, but this
860 * otherwise needs to expand until memory is consumed. As a practical
861 * matter, if we have many thousands of global references, chances are
862 * we're either leaking global ref table entries or we're going to
863 * run out of space in the GC heap.
864 */
865 if (!dvmAddToReferenceTable(&gDvm.jniGlobalRefTable, (Object*)obj)) {
866 dvmDumpReferenceTable(&gDvm.jniGlobalRefTable, "JNI global");
867 LOGE("Failed adding to JNI global ref table (%d entries)\n",
868 (int) dvmReferenceTableEntries(&gDvm.jniGlobalRefTable));
869 dvmAbort();
870 }
871
872 LOGVV("GREF add %p (%s.%s)\n", obj,
873 dvmGetCurrentJNIMethod()->clazz->descriptor,
874 dvmGetCurrentJNIMethod()->name);
875
876 /* GREF usage tracking; should probably be disabled for production env */
877 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
878 int count = dvmReferenceTableEntries(&gDvm.jniGlobalRefTable);
879 if (count > gDvm.jniGlobalRefHiMark) {
880 LOGD("GREF has increased to %d\n", count);
881 gDvm.jniGlobalRefHiMark += kGrefWaterInterval;
882 gDvm.jniGlobalRefLoMark += kGrefWaterInterval;
883
884 /* watch for "excessive" use; not generally appropriate */
885 if (count >= gDvm.jniGrefLimit) {
886 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
887 if (vm->warnError) {
888 dvmDumpReferenceTable(&gDvm.jniGlobalRefTable,"JNI global");
889 LOGE("Excessive JNI global references (%d)\n", count);
890 dvmAbort();
891 } else {
892 LOGW("Excessive JNI global references (%d)\n", count);
893 }
894 }
895 }
896 }
897
898bail:
899 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
Andy McFaddenab00d452009-08-19 07:21:41 -0700900 return (jobject) obj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800901}
902
903/*
904 * Remove a global reference. In most cases it's the entry most recently
905 * added, which makes this pretty quick.
906 *
907 * Thought: if it's not the most recent entry, just null it out. When we
908 * fill up, do a compaction pass before we expand the list.
909 */
Andy McFaddenab00d452009-08-19 07:21:41 -0700910static void deleteGlobalReference(jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800911{
Andy McFaddenab00d452009-08-19 07:21:41 -0700912 if (jobj == NULL)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800913 return;
914
915 dvmLockMutex(&gDvm.jniGlobalRefLock);
916
917 if (!dvmRemoveFromReferenceTable(&gDvm.jniGlobalRefTable,
Andy McFaddenab00d452009-08-19 07:21:41 -0700918 gDvm.jniGlobalRefTable.table, jobj))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800919 {
920 LOGW("JNI: DeleteGlobalRef(%p) failed to find entry (valid=%d)\n",
Andy McFaddenab00d452009-08-19 07:21:41 -0700921 jobj, dvmIsValidObject((Object*) jobj));
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800922 goto bail;
923 }
924
925 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
926 int count = dvmReferenceTableEntries(&gDvm.jniGlobalRefTable);
927 if (count < gDvm.jniGlobalRefLoMark) {
928 LOGD("GREF has decreased to %d\n", count);
929 gDvm.jniGlobalRefHiMark -= kGrefWaterInterval;
930 gDvm.jniGlobalRefLoMark -= kGrefWaterInterval;
931 }
932 }
933
934bail:
935 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
936}
937
938/*
Andy McFaddenab00d452009-08-19 07:21:41 -0700939 * Objects don't currently move, so we just need to create a reference
940 * that will ensure the array object isn't collected.
941 *
Andy McFaddenc26bb632009-08-21 12:01:31 -0700942 * We use a separate reference table, which is part of the GC root set.
Andy McFaddenab00d452009-08-19 07:21:41 -0700943 */
944static void pinPrimitiveArray(ArrayObject* arrayObj)
945{
Andy McFaddenc26bb632009-08-21 12:01:31 -0700946 if (arrayObj == NULL)
947 return;
948
949 dvmLockMutex(&gDvm.jniPinRefLock);
950 if (!dvmAddToReferenceTable(&gDvm.jniPinRefTable, (Object*)arrayObj)) {
951 dvmDumpReferenceTable(&gDvm.jniPinRefTable, "JNI pinned array");
952 LOGE("Failed adding to JNI pinned array ref table (%d entries)\n",
953 (int) dvmReferenceTableEntries(&gDvm.jniPinRefTable));
954 dvmDumpThread(dvmThreadSelf(), false);
955 dvmAbort();
956 }
957
958 /*
959 * If we're watching global ref usage, also keep an eye on these.
960 *
961 * The total number of pinned primitive arrays should be pretty small.
962 * A single array should not be pinned more than once or twice; any
963 * more than that is a strong indicator that a Release function is
964 * not being called.
965 */
966 if (kTrackGrefUsage && gDvm.jniGrefLimit != 0) {
967 int count = 0;
968 Object** ppObj = gDvm.jniPinRefTable.table;
969 while (ppObj < gDvm.jniPinRefTable.nextEntry) {
970 if (*ppObj++ == (Object*) arrayObj)
971 count++;
972 }
973
974 if (count > kPinComplainThreshold) {
975 LOGW("JNI: pin count on array %p (%s) is now %d\n",
976 arrayObj, arrayObj->obj.clazz->descriptor, count);
977 /* keep going */
978 }
979 }
980
981 dvmUnlockMutex(&gDvm.jniPinRefLock);
Andy McFaddenab00d452009-08-19 07:21:41 -0700982}
983
984/*
985 * Un-pin the array object. If an object was pinned twice, it must be
986 * unpinned twice before it's free to move.
Andy McFaddenab00d452009-08-19 07:21:41 -0700987 */
988static void unpinPrimitiveArray(ArrayObject* arrayObj)
989{
Andy McFaddenc26bb632009-08-21 12:01:31 -0700990 if (arrayObj == NULL)
991 return;
992
993 dvmLockMutex(&gDvm.jniPinRefLock);
994 if (!dvmRemoveFromReferenceTable(&gDvm.jniPinRefTable,
995 gDvm.jniPinRefTable.table, (Object*) arrayObj))
996 {
997 LOGW("JNI: unpinPrimitiveArray(%p) failed to find entry (valid=%d)\n",
998 arrayObj, dvmIsValidObject((Object*) arrayObj));
999 goto bail;
1000 }
1001
1002bail:
1003 dvmUnlockMutex(&gDvm.jniPinRefLock);
Andy McFaddenab00d452009-08-19 07:21:41 -07001004}
1005
1006/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001007 * GC helper function to mark all JNI global references.
Andy McFaddenc26bb632009-08-21 12:01:31 -07001008 *
1009 * We're currently handling the "pin" table here too.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001010 */
1011void dvmGcMarkJniGlobalRefs()
1012{
1013 Object **op;
1014
1015 dvmLockMutex(&gDvm.jniGlobalRefLock);
1016
1017 op = gDvm.jniGlobalRefTable.table;
1018 while ((uintptr_t)op < (uintptr_t)gDvm.jniGlobalRefTable.nextEntry) {
1019 dvmMarkObjectNonNull(*(op++));
1020 }
1021
1022 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
Andy McFaddenc26bb632009-08-21 12:01:31 -07001023
1024
1025 dvmLockMutex(&gDvm.jniPinRefLock);
1026
1027 op = gDvm.jniPinRefTable.table;
1028 while ((uintptr_t)op < (uintptr_t)gDvm.jniPinRefTable.nextEntry) {
1029 dvmMarkObjectNonNull(*(op++));
1030 }
1031
1032 dvmUnlockMutex(&gDvm.jniPinRefLock);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001033}
1034
1035
Andy McFadden0083d372009-08-21 14:44:04 -07001036#if 0
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001037/*
1038 * Determine if "obj" appears in the argument list for the native method.
1039 *
1040 * We use the "shorty" signature to determine which argument slots hold
1041 * reference types.
1042 */
1043static bool findInArgList(Thread* self, Object* obj)
1044{
1045 const Method* meth;
1046 u4* fp;
1047 int i;
1048
1049 fp = self->curFrame;
1050 while (1) {
1051 /*
1052 * Back up over JNI PushLocalFrame frames. This works because the
1053 * previous frame on the interpreted stack is either a break frame
1054 * (if we called here via native code) or an interpreted method (if
1055 * we called here via the interpreter). In both cases the method
1056 * pointer won't match.
1057 */
1058 StackSaveArea* saveArea = SAVEAREA_FROM_FP(fp);
1059 meth = saveArea->method;
1060 if (meth != SAVEAREA_FROM_FP(saveArea->prevFrame)->method)
1061 break;
1062 fp = saveArea->prevFrame;
1063 }
1064
1065 LOGVV("+++ scanning %d args in %s (%s)\n",
1066 meth->insSize, meth->name, meth->shorty);
1067 const char* shorty = meth->shorty +1; /* skip return type char */
1068 for (i = 0; i < meth->insSize; i++) {
1069 if (i == 0 && !dvmIsStaticMethod(meth)) {
1070 /* first arg is "this" ref, not represented in "shorty" */
1071 if (fp[i] == (u4) obj)
1072 return true;
1073 } else {
1074 /* if this is a reference type, see if it matches */
1075 switch (*shorty) {
1076 case 'L':
1077 if (fp[i] == (u4) obj)
1078 return true;
1079 break;
1080 case 'D':
1081 case 'J':
1082 i++;
1083 break;
1084 case '\0':
1085 LOGE("Whoops! ran off the end of %s (%d)\n",
1086 meth->shorty, meth->insSize);
1087 break;
1088 default:
1089 if (fp[i] == (u4) obj)
1090 LOGI("NOTE: ref %p match on arg type %c\n", obj, *shorty);
1091 break;
1092 }
1093 shorty++;
1094 }
1095 }
1096
1097 /*
1098 * For static methods, we also pass a class pointer in.
1099 */
1100 if (dvmIsStaticMethod(meth)) {
1101 //LOGI("+++ checking class pointer in %s\n", meth->name);
1102 if ((void*)obj == (void*)meth->clazz)
1103 return true;
1104 }
1105 return false;
1106}
Andy McFadden0083d372009-08-21 14:44:04 -07001107#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001108
1109/*
1110 * Verify that a reference passed in from native code is one that the
1111 * code is allowed to have.
1112 *
1113 * It's okay for native code to pass us a reference that:
Andy McFadden0083d372009-08-21 14:44:04 -07001114 * - was passed in as an argument when invoked by native code (and hence
1115 * is in the JNI local refs table)
1116 * - was returned to it from JNI (and is now in the local refs table)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001117 * - is present in the JNI global refs table
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001118 *
1119 * Used by -Xcheck:jni and GetObjectRefType.
1120 *
1121 * NOTE: in the current VM, global and local references are identical. If
1122 * something is both global and local, we can't tell them apart, and always
1123 * return "local".
1124 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001125jobjectRefType dvmGetJNIRefType(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001126{
Andy McFaddenab00d452009-08-19 07:21:41 -07001127 ReferenceTable* pRefTable = getLocalRefTable(env);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001128 Thread* self = dvmThreadSelf();
1129 //Object** top;
1130 Object** ptr;
1131
Andy McFadden0083d372009-08-21 14:44:04 -07001132#if 0
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001133 /* check args */
Andy McFaddenab00d452009-08-19 07:21:41 -07001134 if (findInArgList(self, jobj)) {
1135 //LOGI("--- REF found %p on stack\n", jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001136 return JNILocalRefType;
1137 }
Andy McFadden0083d372009-08-21 14:44:04 -07001138#endif
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001139
1140 /* check locals */
1141 //top = SAVEAREA_FROM_FP(self->curFrame)->xtra.localRefTop;
Andy McFaddenab00d452009-08-19 07:21:41 -07001142 if (dvmFindInReferenceTable(pRefTable, pRefTable->table, jobj) != NULL) {
1143 //LOGI("--- REF found %p in locals\n", jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001144 return JNILocalRefType;
1145 }
1146
1147 /* check globals */
1148 dvmLockMutex(&gDvm.jniGlobalRefLock);
1149 if (dvmFindInReferenceTable(&gDvm.jniGlobalRefTable,
Andy McFaddenab00d452009-08-19 07:21:41 -07001150 gDvm.jniGlobalRefTable.table, jobj))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001151 {
Andy McFaddenab00d452009-08-19 07:21:41 -07001152 //LOGI("--- REF found %p in globals\n", jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001153 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
1154 return JNIGlobalRefType;
1155 }
1156 dvmUnlockMutex(&gDvm.jniGlobalRefLock);
1157
1158 /* not found! */
1159 return JNIInvalidRefType;
1160}
1161
1162/*
1163 * Register a method that uses JNI calling conventions.
1164 */
1165static bool dvmRegisterJNIMethod(ClassObject* clazz, const char* methodName,
1166 const char* signature, void* fnPtr)
1167{
1168 Method* method;
1169 bool result = false;
1170
1171 if (fnPtr == NULL)
1172 goto bail;
1173
1174 method = dvmFindDirectMethodByDescriptor(clazz, methodName, signature);
1175 if (method == NULL)
1176 method = dvmFindVirtualMethodByDescriptor(clazz, methodName, signature);
1177 if (method == NULL) {
1178 LOGW("ERROR: Unable to find decl for native %s.%s %s\n",
1179 clazz->descriptor, methodName, signature);
1180 goto bail;
1181 }
1182
1183 if (!dvmIsNativeMethod(method)) {
1184 LOGW("Unable to register: not native: %s.%s %s\n",
1185 clazz->descriptor, methodName, signature);
1186 goto bail;
1187 }
1188
1189 if (method->nativeFunc != dvmResolveNativeMethod) {
1190 LOGW("Warning: %s.%s %s was already registered/resolved?\n",
1191 clazz->descriptor, methodName, signature);
1192 /* keep going, I guess */
1193 }
1194
Andy McFadden59b61772009-05-13 16:44:34 -07001195 dvmUseJNIBridge(method, fnPtr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001196
1197 LOGV("JNI-registered %s.%s %s\n", clazz->descriptor, methodName,
1198 signature);
1199 result = true;
1200
1201bail:
1202 return result;
1203}
1204
1205/*
Andy McFadden59b61772009-05-13 16:44:34 -07001206 * Returns "true" if CheckJNI is enabled in the VM.
1207 */
1208static bool dvmIsCheckJNIEnabled(void)
1209{
1210 JavaVMExt* vm = (JavaVMExt*) gDvm.vmList;
1211 return vm->useChecked;
1212}
1213
1214/*
1215 * Point "method->nativeFunc" at the JNI bridge, and overload "method->insns"
1216 * to point at the actual function.
1217 */
1218void dvmUseJNIBridge(Method* method, void* func)
1219{
Andy McFadden0083d372009-08-21 14:44:04 -07001220 enum {
1221 kJNIGeneral = 0,
1222 kJNISync = 1,
1223 kJNIVirtualNoRef = 2,
1224 kJNIStaticNoRef = 3,
1225 } kind;
1226 static const DalvikBridgeFunc stdFunc[] = {
1227 dvmCallJNIMethod_general,
1228 dvmCallJNIMethod_synchronized,
1229 dvmCallJNIMethod_virtualNoRef,
1230 dvmCallJNIMethod_staticNoRef
1231 };
1232 static const DalvikBridgeFunc checkFunc[] = {
1233 dvmCheckCallJNIMethod_general,
1234 dvmCheckCallJNIMethod_synchronized,
1235 dvmCheckCallJNIMethod_virtualNoRef,
1236 dvmCheckCallJNIMethod_staticNoRef
1237 };
1238
1239 bool hasRefArg = false;
1240
1241 if (dvmIsSynchronizedMethod(method)) {
1242 /* use version with synchronization; calls into general handler */
1243 kind = kJNISync;
Andy McFadden59b61772009-05-13 16:44:34 -07001244 } else {
Andy McFadden0083d372009-08-21 14:44:04 -07001245 /*
1246 * Do a quick scan through the "shorty" signature to see if the method
1247 * takes any reference arguments.
1248 */
1249 const char* cp = method->shorty;
1250 while (*++cp != '\0') { /* pre-incr to skip return type */
1251 if (*cp == 'L') {
1252 /* 'L' used for both object and array references */
1253 hasRefArg = true;
1254 break;
1255 }
1256 }
1257
1258 if (hasRefArg) {
1259 /* use general handler to slurp up reference args */
1260 kind = kJNIGeneral;
1261 } else {
1262 /* virtual methods have a ref in args[0] (not in signature) */
1263 if (dvmIsStaticMethod(method))
1264 kind = kJNIStaticNoRef;
1265 else
1266 kind = kJNIVirtualNoRef;
1267 }
1268 }
1269
1270 if (dvmIsCheckJNIEnabled()) {
1271 dvmSetNativeFunc(method, checkFunc[kind], func);
1272 } else {
1273 dvmSetNativeFunc(method, stdFunc[kind], func);
Andy McFadden59b61772009-05-13 16:44:34 -07001274 }
1275}
1276
1277/*
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001278 * Get the method currently being executed by examining the interp stack.
1279 */
1280const Method* dvmGetCurrentJNIMethod(void)
1281{
1282 assert(dvmThreadSelf() != NULL);
1283
1284 void* fp = dvmThreadSelf()->curFrame;
1285 const Method* meth = SAVEAREA_FROM_FP(fp)->method;
1286
1287 assert(meth != NULL);
1288 assert(dvmIsNativeMethod(meth));
1289 return meth;
1290}
1291
1292
1293/*
1294 * Track a JNI MonitorEnter in the current thread.
1295 *
1296 * The goal is to be able to "implicitly" release all JNI-held monitors
1297 * when the thread detaches.
1298 *
1299 * Monitors may be entered multiple times, so we add a new entry for each
1300 * enter call. It would be more efficient to keep a counter. At present
1301 * there's no real motivation to improve this however.
1302 */
1303static void trackMonitorEnter(Thread* self, Object* obj)
1304{
1305 static const int kInitialSize = 16;
1306 ReferenceTable* refTable = &self->jniMonitorRefTable;
1307
1308 /* init table on first use */
1309 if (refTable->table == NULL) {
1310 assert(refTable->maxEntries == 0);
1311
1312 if (!dvmInitReferenceTable(refTable, kInitialSize, INT_MAX)) {
1313 LOGE("Unable to initialize monitor tracking table\n");
1314 dvmAbort();
1315 }
1316 }
1317
1318 if (!dvmAddToReferenceTable(refTable, obj)) {
1319 /* ran out of memory? could throw exception instead */
1320 LOGE("Unable to add entry to monitor tracking table\n");
1321 dvmAbort();
1322 } else {
1323 LOGVV("--- added monitor %p\n", obj);
1324 }
1325}
1326
Andy McFaddenab00d452009-08-19 07:21:41 -07001327
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001328/*
1329 * Track a JNI MonitorExit in the current thread.
1330 */
1331static void trackMonitorExit(Thread* self, Object* obj)
1332{
Andy McFaddenab00d452009-08-19 07:21:41 -07001333 ReferenceTable* pRefTable = &self->jniMonitorRefTable;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001334
Andy McFaddenab00d452009-08-19 07:21:41 -07001335 if (!dvmRemoveFromReferenceTable(pRefTable, pRefTable->table, obj)) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001336 LOGE("JNI monitor %p not found in tracking list\n", obj);
1337 /* keep going? */
1338 } else {
1339 LOGVV("--- removed monitor %p\n", obj);
1340 }
1341}
1342
1343/*
1344 * Release all monitors held by the jniMonitorRefTable list.
1345 */
1346void dvmReleaseJniMonitors(Thread* self)
1347{
Andy McFaddenab00d452009-08-19 07:21:41 -07001348 ReferenceTable* pRefTable = &self->jniMonitorRefTable;
1349 Object** top = pRefTable->table;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001350
1351 if (top == NULL)
1352 return;
1353
Andy McFaddenab00d452009-08-19 07:21:41 -07001354 Object** ptr = pRefTable->nextEntry;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001355 while (--ptr >= top) {
1356 if (!dvmUnlockObject(self, *ptr)) {
1357 LOGW("Unable to unlock monitor %p at thread detach\n", *ptr);
1358 } else {
1359 LOGVV("--- detach-releasing monitor %p\n", *ptr);
1360 }
1361 }
1362
1363 /* zap it */
Andy McFaddenab00d452009-08-19 07:21:41 -07001364 pRefTable->nextEntry = pRefTable->table;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001365}
1366
1367#ifdef WITH_JNI_STACK_CHECK
1368/*
1369 * Compute a CRC on the entire interpreted stack.
1370 *
1371 * Would be nice to compute it on "self" as well, but there are parts of
1372 * the Thread that can be altered by other threads (e.g. prev/next pointers).
1373 */
1374static void computeStackSum(Thread* self)
1375{
1376 const u1* low = (const u1*)SAVEAREA_FROM_FP(self->curFrame);
1377 u4 crc = dvmInitCrc32();
1378 self->stackCrc = 0;
1379 crc = dvmComputeCrc32(crc, low, self->interpStackStart - low);
1380 self->stackCrc = crc;
1381}
1382
1383/*
1384 * Compute a CRC on the entire interpreted stack, and compare it to what
1385 * we previously computed.
1386 *
1387 * We can execute JNI directly from native code without calling in from
1388 * interpreted code during VM initialization and immediately after JNI
1389 * thread attachment. Another opportunity exists during JNI_OnLoad. Rather
1390 * than catching these cases we just ignore them here, which is marginally
1391 * less accurate but reduces the amount of code we have to touch with #ifdefs.
1392 */
1393static void checkStackSum(Thread* self)
1394{
1395 const u1* low = (const u1*)SAVEAREA_FROM_FP(self->curFrame);
1396 u4 stackCrc, crc;
1397
1398 stackCrc = self->stackCrc;
1399 self->stackCrc = 0;
1400 crc = dvmInitCrc32();
1401 crc = dvmComputeCrc32(crc, low, self->interpStackStart - low);
1402 if (crc != stackCrc) {
1403 const Method* meth = dvmGetCurrentJNIMethod();
1404 if (dvmComputeExactFrameDepth(self->curFrame) == 1) {
1405 LOGD("JNI: bad stack CRC (0x%08x) -- okay during init\n",
1406 stackCrc);
1407 } else if (strcmp(meth->name, "nativeLoad") == 0 &&
1408 (strcmp(meth->clazz->descriptor, "Ljava/lang/Runtime;") == 0))
1409 {
1410 LOGD("JNI: bad stack CRC (0x%08x) -- okay during JNI_OnLoad\n",
1411 stackCrc);
1412 } else {
1413 LOGW("JNI: bad stack CRC (%08x vs %08x)\n", crc, stackCrc);
1414 dvmAbort();
1415 }
1416 }
1417 self->stackCrc = (u4) -1; /* make logic errors more noticeable */
1418}
1419#endif
1420
1421
1422/*
1423 * ===========================================================================
1424 * JNI implementation
1425 * ===========================================================================
1426 */
1427
1428/*
1429 * Return the version of the native method interface.
1430 */
1431static jint GetVersion(JNIEnv* env)
1432{
1433 JNI_ENTER();
1434 /*
1435 * There is absolutely no need to toggle the mode for correct behavior.
1436 * However, it does provide native code with a simple "suspend self
1437 * if necessary" call.
1438 */
1439 JNI_EXIT();
1440 return JNI_VERSION_1_6;
1441}
1442
1443/*
1444 * Create a new class from a bag of bytes.
1445 *
1446 * This is not currently supported within Dalvik.
1447 */
1448static jclass DefineClass(JNIEnv* env, const char *name, jobject loader,
1449 const jbyte* buf, jsize bufLen)
1450{
1451 UNUSED_PARAMETER(name);
1452 UNUSED_PARAMETER(loader);
1453 UNUSED_PARAMETER(buf);
1454 UNUSED_PARAMETER(bufLen);
1455
1456 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001457 LOGW("JNI DefineClass is not supported\n");
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001458 JNI_EXIT();
1459 return NULL;
1460}
1461
1462/*
1463 * Find a class by name.
1464 *
1465 * We have to use the "no init" version of FindClass here, because we might
1466 * be getting the class prior to registering native methods that will be
1467 * used in <clinit>.
1468 *
1469 * We need to get the class loader associated with the current native
1470 * method. If there is no native method, e.g. we're calling this from native
1471 * code right after creating the VM, the spec says we need to use the class
1472 * loader returned by "ClassLoader.getBaseClassLoader". There is no such
1473 * method, but it's likely they meant ClassLoader.getSystemClassLoader.
1474 * We can't get that until after the VM has initialized though.
1475 */
1476static jclass FindClass(JNIEnv* env, const char* name)
1477{
1478 JNI_ENTER();
1479
1480 const Method* thisMethod;
1481 ClassObject* clazz;
Andy McFaddenab00d452009-08-19 07:21:41 -07001482 jclass jclazz = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001483 Object* loader;
1484 char* descriptor = NULL;
1485
1486 thisMethod = dvmGetCurrentJNIMethod();
1487 assert(thisMethod != NULL);
1488
1489 descriptor = dvmNameToDescriptor(name);
1490 if (descriptor == NULL) {
1491 clazz = NULL;
1492 goto bail;
1493 }
1494
1495 //Thread* self = dvmThreadSelf();
1496 if (_self->classLoaderOverride != NULL) {
1497 /* hack for JNI_OnLoad */
1498 assert(strcmp(thisMethod->name, "nativeLoad") == 0);
1499 loader = _self->classLoaderOverride;
1500 } else if (thisMethod == gDvm.methFakeNativeEntry) {
1501 /* start point of invocation interface */
1502 if (!gDvm.initializing)
1503 loader = dvmGetSystemClassLoader();
1504 else
1505 loader = NULL;
1506 } else {
1507 loader = thisMethod->clazz->classLoader;
1508 }
1509
1510 clazz = dvmFindClassNoInit(descriptor, loader);
Andy McFaddenab00d452009-08-19 07:21:41 -07001511 jclazz = addLocalReference(env, (Object*) clazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001512
1513bail:
1514 free(descriptor);
Andy McFaddenab00d452009-08-19 07:21:41 -07001515
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001516 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001517 return jclazz;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001518}
1519
1520/*
1521 * Return the superclass of a class.
1522 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001523static jclass GetSuperclass(JNIEnv* env, jclass jclazz)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001524{
1525 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001526 jclass jsuper;
1527
1528 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1529 if (clazz == NULL)
1530 jsuper = NULL;
1531 else
1532 jsuper = addLocalReference(env, (Object*)clazz->super);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001533 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001534 return jsuper;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001535}
1536
1537/*
1538 * Determine whether an object of clazz1 can be safely cast to clazz2.
1539 *
1540 * Like IsInstanceOf, but with a pair of class objects instead of obj+class.
1541 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001542static jboolean IsAssignableFrom(JNIEnv* env, jclass jclazz1, jclass jclazz2)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001543{
1544 JNI_ENTER();
1545
Andy McFaddenab00d452009-08-19 07:21:41 -07001546 ClassObject* clazz1 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz1);
1547 ClassObject* clazz2 = (ClassObject*) dvmDecodeIndirectRef(env, jclazz2);
1548
1549 jboolean result = dvmInstanceof(clazz1, clazz2);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001550
1551 JNI_EXIT();
1552 return result;
1553}
1554
1555/*
1556 * Given a java.lang.reflect.Method or .Constructor, return a methodID.
1557 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001558static jmethodID FromReflectedMethod(JNIEnv* env, jobject jmethod)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001559{
1560 JNI_ENTER();
1561 jmethodID methodID;
Andy McFaddenab00d452009-08-19 07:21:41 -07001562 Object* method = dvmDecodeIndirectRef(env, jmethod);
1563 methodID = (jmethodID) dvmGetMethodFromReflectObj(method);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001564 JNI_EXIT();
1565 return methodID;
1566}
1567
1568/*
1569 * Given a java.lang.reflect.Field, return a fieldID.
1570 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001571static jfieldID FromReflectedField(JNIEnv* env, jobject jfield)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001572{
1573 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001574 jfieldID fieldID;
1575 Object* field = dvmDecodeIndirectRef(env, jfield);
1576 fieldID = (jfieldID) dvmGetFieldFromReflectObj(field);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001577 JNI_EXIT();
1578 return fieldID;
1579}
1580
1581/*
1582 * Convert a methodID to a java.lang.reflect.Method or .Constructor.
1583 *
1584 * (The "isStatic" field does not appear in the spec.)
1585 *
1586 * Throws OutOfMemory and returns NULL on failure.
1587 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001588static jobject ToReflectedMethod(JNIEnv* env, jclass jcls, jmethodID methodID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001589 jboolean isStatic)
1590{
1591 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001592 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls);
1593 Object* obj = dvmCreateReflectObjForMethod(clazz, (Method*) methodID);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001594 dvmReleaseTrackedAlloc(obj, NULL);
Andy McFaddenab00d452009-08-19 07:21:41 -07001595 jobject jobj = addLocalReference(env, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001596 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001597 return jobj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001598}
1599
1600/*
1601 * Convert a fieldID to a java.lang.reflect.Field.
1602 *
1603 * (The "isStatic" field does not appear in the spec.)
1604 *
1605 * Throws OutOfMemory and returns NULL on failure.
1606 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001607static jobject ToReflectedField(JNIEnv* env, jclass jcls, jfieldID fieldID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001608 jboolean isStatic)
1609{
1610 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001611 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jcls);
1612 Object* obj = dvmCreateReflectObjForField(jcls, (Field*) fieldID);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001613 dvmReleaseTrackedAlloc(obj, NULL);
Andy McFaddenab00d452009-08-19 07:21:41 -07001614 jobject jobj = addLocalReference(env, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001615 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001616 return jobj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001617}
1618
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001619/*
1620 * Take this exception and throw it.
1621 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001622static jint Throw(JNIEnv* env, jthrowable jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001623{
1624 JNI_ENTER();
1625
1626 jint retval;
1627
Andy McFaddenab00d452009-08-19 07:21:41 -07001628 if (jobj != NULL) {
1629 Object* obj = dvmDecodeIndirectRef(env, jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001630 dvmSetException(_self, obj);
1631 retval = JNI_OK;
Andy McFaddenab00d452009-08-19 07:21:41 -07001632 } else {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001633 retval = JNI_ERR;
Andy McFaddenab00d452009-08-19 07:21:41 -07001634 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001635
1636 JNI_EXIT();
1637 return retval;
1638}
1639
1640/*
Andy McFaddenab00d452009-08-19 07:21:41 -07001641 * Constructs an exception object from the specified class with the message
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001642 * specified by "message", and throws it.
1643 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001644static jint ThrowNew(JNIEnv* env, jclass jclazz, const char* message)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001645{
1646 JNI_ENTER();
1647
Andy McFaddenab00d452009-08-19 07:21:41 -07001648 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1649 dvmThrowExceptionByClass(clazz, message);
1650 // TODO: should return failure if this didn't work (e.g. OOM)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001651
1652 JNI_EXIT();
1653 return JNI_OK;
1654}
1655
1656/*
1657 * If an exception is being thrown, return the exception object. Otherwise,
1658 * return NULL.
1659 *
1660 * TODO: if there is no pending exception, we should be able to skip the
1661 * enter/exit checks. If we find one, we need to enter and then re-fetch
1662 * the exception (in case it got moved by a compacting GC).
1663 */
1664static jthrowable ExceptionOccurred(JNIEnv* env)
1665{
1666 JNI_ENTER();
1667
1668 Object* exception;
Andy McFaddenab00d452009-08-19 07:21:41 -07001669 jobject localException;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001670
Andy McFaddenab00d452009-08-19 07:21:41 -07001671 exception = dvmGetException(_self);
1672 localException = addLocalReference(env, exception);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001673 if (localException == NULL && exception != NULL) {
1674 /*
1675 * We were unable to add a new local reference, and threw a new
1676 * exception. We can't return "exception", because it's not a
1677 * local reference. So we have to return NULL, indicating that
1678 * there was no exception, even though it's pretty much raining
1679 * exceptions in here.
1680 */
1681 LOGW("JNI WARNING: addLocal/exception combo\n");
1682 }
1683
1684 JNI_EXIT();
1685 return localException;
1686}
1687
1688/*
1689 * Print an exception and stack trace to stderr.
1690 */
1691static void ExceptionDescribe(JNIEnv* env)
1692{
1693 JNI_ENTER();
1694
1695 Object* exception = dvmGetException(_self);
1696 if (exception != NULL) {
1697 dvmPrintExceptionStackTrace();
1698 } else {
1699 LOGI("Odd: ExceptionDescribe called, but no exception pending\n");
1700 }
1701
1702 JNI_EXIT();
1703}
1704
1705/*
1706 * Clear the exception currently being thrown.
1707 *
1708 * TODO: we should be able to skip the enter/exit stuff.
1709 */
1710static void ExceptionClear(JNIEnv* env)
1711{
1712 JNI_ENTER();
1713 dvmClearException(_self);
1714 JNI_EXIT();
1715}
1716
1717/*
1718 * Kill the VM. This function does not return.
1719 */
1720static void FatalError(JNIEnv* env, const char* msg)
1721{
1722 //dvmChangeStatus(NULL, THREAD_RUNNING);
1723 LOGE("JNI posting fatal error: %s\n", msg);
1724 dvmAbort();
1725}
1726
1727/*
1728 * Push a new JNI frame on the stack, with a new set of locals.
1729 *
1730 * The new frame must have the same method pointer. (If for no other
1731 * reason than FindClass needs it to get the appropriate class loader.)
1732 */
1733static jint PushLocalFrame(JNIEnv* env, jint capacity)
1734{
1735 JNI_ENTER();
1736 int result = JNI_OK;
Andy McFaddenab00d452009-08-19 07:21:41 -07001737 if (!ensureLocalCapacity(env, capacity) ||
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001738 !dvmPushLocalFrame(_self /*dvmThreadSelf()*/, dvmGetCurrentJNIMethod()))
1739 {
1740 /* yes, OutOfMemoryError, not StackOverflowError */
1741 dvmClearException(_self);
1742 dvmThrowException("Ljava/lang/OutOfMemoryError;",
1743 "out of stack in JNI PushLocalFrame");
1744 result = JNI_ERR;
1745 }
1746 JNI_EXIT();
1747 return result;
1748}
1749
1750/*
1751 * Pop the local frame off. If "result" is not null, add it as a
1752 * local reference on the now-current frame.
1753 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001754static jobject PopLocalFrame(JNIEnv* env, jobject jresult)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001755{
1756 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001757 Object* result = dvmDecodeIndirectRef(env, jresult);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001758 if (!dvmPopLocalFrame(_self /*dvmThreadSelf()*/)) {
1759 LOGW("JNI WARNING: too many PopLocalFrame calls\n");
1760 dvmClearException(_self);
1761 dvmThrowException("Ljava/lang/RuntimeException;",
1762 "too many PopLocalFrame calls");
1763 }
Andy McFaddenab00d452009-08-19 07:21:41 -07001764 jresult = addLocalReference(env, result);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001765 JNI_EXIT();
1766 return result;
1767}
1768
1769/*
1770 * Add a reference to the global list.
1771 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001772static jobject NewGlobalRef(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001773{
1774 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001775 Object* obj = dvmDecodeIndirectRef(env, jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001776 jobject retval = addGlobalReference(obj);
1777 JNI_EXIT();
1778 return retval;
1779}
1780
1781/*
1782 * Delete a reference from the global list.
1783 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001784static void DeleteGlobalRef(JNIEnv* env, jobject jglobalRef)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001785{
1786 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001787 deleteGlobalReference(jglobalRef);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001788 JNI_EXIT();
1789}
1790
1791
1792/*
1793 * Add a reference to the local list.
1794 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001795static jobject NewLocalRef(JNIEnv* env, jobject jref)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001796{
1797 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001798 Object* obj = dvmDecodeIndirectRef(env, jref);
1799 jobject retval = addLocalReference(env, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001800 JNI_EXIT();
1801 return retval;
1802}
1803
1804/*
1805 * Delete a reference from the local list.
1806 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001807static void DeleteLocalRef(JNIEnv* env, jobject jlocalRef)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001808{
1809 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001810 deleteLocalReference(env, jlocalRef);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001811 JNI_EXIT();
1812}
1813
1814/*
1815 * Ensure that the local references table can hold at least this many
1816 * references.
1817 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001818static jint EnsureLocalCapacity(JNIEnv* env, jint capacity)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001819{
1820 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001821 bool okay = ensureLocalCapacity(env, capacity);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001822 if (!okay) {
1823 dvmThrowException("Ljava/lang/OutOfMemoryError;",
1824 "can't ensure local reference capacity");
1825 }
1826 JNI_EXIT();
1827 if (okay)
1828 return 0;
1829 else
1830 return -1;
1831}
1832
1833
1834/*
1835 * Determine whether two Object references refer to the same underlying object.
1836 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001837static jboolean IsSameObject(JNIEnv* env, jobject jref1, jobject jref2)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001838{
1839 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001840 Object* obj1 = dvmDecodeIndirectRef(env, jref1);
1841 Object* obj2 = dvmDecodeIndirectRef(env, jref2);
1842 jboolean result = (obj1 == obj2);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001843 JNI_EXIT();
1844 return result;
1845}
1846
1847/*
1848 * Allocate a new object without invoking any constructors.
1849 */
1850static jobject AllocObject(JNIEnv* env, jclass jclazz)
1851{
1852 JNI_ENTER();
1853
Andy McFaddenab00d452009-08-19 07:21:41 -07001854 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1855 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001856
1857 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
1858 assert(dvmCheckException(_self));
Andy McFaddenab00d452009-08-19 07:21:41 -07001859 result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001860 } else {
Andy McFaddenab00d452009-08-19 07:21:41 -07001861 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
1862 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001863 }
1864
1865 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001866 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001867}
1868
1869/*
Andy McFaddenab00d452009-08-19 07:21:41 -07001870 * Allocate a new object and invoke the supplied constructor.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001871 */
1872static jobject NewObject(JNIEnv* env, jclass jclazz, jmethodID methodID, ...)
1873{
1874 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001875 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1876 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001877
1878 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
1879 assert(dvmCheckException(_self));
Andy McFaddenab00d452009-08-19 07:21:41 -07001880 result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001881 } else {
Andy McFaddenab00d452009-08-19 07:21:41 -07001882 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
1883 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001884 if (newObj != NULL) {
1885 JValue unused;
1886 va_list args;
1887 va_start(args, methodID);
Andy McFaddenab00d452009-08-19 07:21:41 -07001888 dvmCallMethodV(_self, (Method*) methodID, newObj, &unused, args);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001889 va_end(args);
1890 }
1891 }
1892
1893 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001894 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001895}
Andy McFaddenab00d452009-08-19 07:21:41 -07001896static jobject NewObjectV(JNIEnv* env, jclass jclazz, jmethodID methodID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001897 va_list args)
1898{
1899 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001900 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1901 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001902
Andy McFaddenab00d452009-08-19 07:21:41 -07001903 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
1904 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001905 if (newObj != NULL) {
1906 JValue unused;
Andy McFaddenab00d452009-08-19 07:21:41 -07001907 dvmCallMethodV(_self, (Method*) methodID, newObj, &unused, args);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001908 }
1909
1910 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001911 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001912}
Andy McFaddenab00d452009-08-19 07:21:41 -07001913static jobject NewObjectA(JNIEnv* env, jclass jclazz, jmethodID methodID,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001914 jvalue* args)
1915{
1916 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07001917 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1918 jobject result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001919
Andy McFaddenab00d452009-08-19 07:21:41 -07001920 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
1921 result = addLocalReference(env, newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001922 if (newObj != NULL) {
1923 JValue unused;
Andy McFaddenab00d452009-08-19 07:21:41 -07001924 dvmCallMethodA(_self, (Method*) methodID, newObj, &unused, args);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001925 }
1926
1927 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001928 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001929}
1930
1931/*
1932 * Returns the class of an object.
1933 *
1934 * JNI spec says: obj must not be NULL.
1935 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001936static jclass GetObjectClass(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001937{
1938 JNI_ENTER();
1939
Andy McFaddenab00d452009-08-19 07:21:41 -07001940 assert(jobj != NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001941
Andy McFaddenab00d452009-08-19 07:21:41 -07001942 Object* obj = dvmDecodeIndirectRef(env, jobj);
1943 jclass jclazz = addLocalReference(env, (Object*) obj->clazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001944
1945 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07001946 return jclazz;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001947}
1948
1949/*
1950 * Determine whether "obj" is an instance of "clazz".
1951 */
Andy McFaddenab00d452009-08-19 07:21:41 -07001952static jboolean IsInstanceOf(JNIEnv* env, jobject jobj, jclass jclazz)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001953{
1954 JNI_ENTER();
1955
Andy McFaddenab00d452009-08-19 07:21:41 -07001956 assert(jclazz != NULL);
1957
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001958 jboolean result;
1959
Andy McFaddenab00d452009-08-19 07:21:41 -07001960 if (jobj == NULL) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001961 result = true;
Andy McFaddenab00d452009-08-19 07:21:41 -07001962 } else {
1963 Object* obj = dvmDecodeIndirectRef(env, jobj);
1964 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
1965 result = dvmInstanceof(obj->clazz, clazz);
1966 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001967
1968 JNI_EXIT();
1969 return result;
1970}
1971
1972/*
1973 * Get a method ID for an instance method.
1974 *
1975 * JNI defines <init> as an instance method, but Dalvik considers it a
1976 * "direct" method, so we have to special-case it here.
1977 *
1978 * Dalvik also puts all private methods into the "direct" list, so we
1979 * really need to just search both lists.
1980 */
1981static jmethodID GetMethodID(JNIEnv* env, jclass jclazz, const char* name,
1982 const char* sig)
1983{
1984 JNI_ENTER();
1985
Andy McFaddenab00d452009-08-19 07:21:41 -07001986 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001987 jmethodID id = NULL;
1988
1989 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
1990 assert(dvmCheckException(_self));
1991 } else {
1992 Method* meth;
1993
1994 meth = dvmFindVirtualMethodHierByDescriptor(clazz, name, sig);
1995 if (meth == NULL) {
1996 /* search private methods and constructors; non-hierarchical */
1997 meth = dvmFindDirectMethodByDescriptor(clazz, name, sig);
1998 }
1999 if (meth != NULL && dvmIsStaticMethod(meth)) {
2000 IF_LOGD() {
2001 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
2002 LOGD("GetMethodID: not returning static method %s.%s %s\n",
2003 clazz->descriptor, meth->name, desc);
2004 free(desc);
2005 }
2006 meth = NULL;
2007 }
2008 if (meth == NULL) {
Andy McFadden03bd0d52009-08-18 15:32:27 -07002009 LOGD("GetMethodID: method not found: %s.%s:%s\n",
2010 clazz->descriptor, name, sig);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002011 dvmThrowException("Ljava/lang/NoSuchMethodError;", name);
2012 }
2013
2014 /*
2015 * The method's class may not be the same as clazz, but if
2016 * it isn't this must be a virtual method and the class must
2017 * be a superclass (and, hence, already initialized).
2018 */
2019 if (meth != NULL) {
2020 assert(dvmIsClassInitialized(meth->clazz) ||
2021 dvmIsClassInitializing(meth->clazz));
2022 }
2023 id = (jmethodID) meth;
2024 }
2025 JNI_EXIT();
2026 return id;
2027}
2028
2029/*
2030 * Get a field ID (instance fields).
2031 */
2032static jfieldID GetFieldID(JNIEnv* env, jclass jclazz,
2033 const char* name, const char* sig)
2034{
2035 JNI_ENTER();
2036
Andy McFaddenab00d452009-08-19 07:21:41 -07002037 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002038 jfieldID id;
2039
2040 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2041 assert(dvmCheckException(_self));
2042 id = NULL;
2043 } else {
2044 id = (jfieldID) dvmFindInstanceFieldHier(clazz, name, sig);
Andy McFadden03bd0d52009-08-18 15:32:27 -07002045 if (id == NULL) {
2046 LOGD("GetFieldID: unable to find field %s.%s:%s\n",
2047 clazz->descriptor, name, sig);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002048 dvmThrowException("Ljava/lang/NoSuchFieldError;", name);
Andy McFadden03bd0d52009-08-18 15:32:27 -07002049 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002050 }
2051 JNI_EXIT();
2052 return id;
2053}
2054
2055/*
2056 * Get the method ID for a static method in a class.
2057 */
2058static jmethodID GetStaticMethodID(JNIEnv* env, jclass jclazz,
2059 const char* name, const char* sig)
2060{
2061 JNI_ENTER();
2062
Andy McFaddenab00d452009-08-19 07:21:41 -07002063 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002064 jmethodID id;
2065
2066 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2067 assert(dvmCheckException(_self));
2068 id = NULL;
2069 } else {
2070 Method* meth;
2071
2072 meth = dvmFindDirectMethodHierByDescriptor(clazz, name, sig);
2073
2074 /* make sure it's static, not virtual+private */
2075 if (meth != NULL && !dvmIsStaticMethod(meth)) {
2076 IF_LOGD() {
2077 char* desc = dexProtoCopyMethodDescriptor(&meth->prototype);
2078 LOGD("GetStaticMethodID: "
2079 "not returning nonstatic method %s.%s %s\n",
2080 clazz->descriptor, meth->name, desc);
2081 free(desc);
2082 }
2083 meth = NULL;
2084 }
2085
2086 id = (jmethodID) meth;
2087 if (id == NULL)
2088 dvmThrowException("Ljava/lang/NoSuchMethodError;", name);
2089 }
2090
2091 JNI_EXIT();
2092 return id;
2093}
2094
2095/*
2096 * Get a field ID (static fields).
2097 */
2098static jfieldID GetStaticFieldID(JNIEnv* env, jclass jclazz,
2099 const char* name, const char* sig)
2100{
2101 JNI_ENTER();
2102
Andy McFaddenab00d452009-08-19 07:21:41 -07002103 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002104 jfieldID id;
2105
2106 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz)) {
2107 assert(dvmCheckException(_self));
2108 id = NULL;
2109 } else {
2110 id = (jfieldID) dvmFindStaticField(clazz, name, sig);
2111 if (id == NULL)
2112 dvmThrowException("Ljava/lang/NoSuchFieldError;", name);
2113 }
2114 JNI_EXIT();
2115 return id;
2116}
2117
2118/*
2119 * Get a static field.
2120 *
2121 * If we get an object reference, add it to the local refs list.
2122 */
2123#define GET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002124 static _ctype GetStatic##_jname##Field(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002125 jfieldID fieldID) \
2126 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002127 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002128 JNI_ENTER(); \
2129 StaticField* sfield = (StaticField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002130 _ctype value; \
2131 if (_isref) { /* only when _ctype==jobject */ \
2132 Object* obj = dvmGetStaticFieldObject(sfield); \
2133 value = (_ctype)(u4)addLocalReference(env, obj); \
2134 } else { \
2135 value = dvmGetStaticField##_jname(sfield); \
2136 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002137 JNI_EXIT(); \
2138 return value; \
2139 }
2140GET_STATIC_TYPE_FIELD(jobject, Object, true);
2141GET_STATIC_TYPE_FIELD(jboolean, Boolean, false);
2142GET_STATIC_TYPE_FIELD(jbyte, Byte, false);
2143GET_STATIC_TYPE_FIELD(jchar, Char, false);
2144GET_STATIC_TYPE_FIELD(jshort, Short, false);
2145GET_STATIC_TYPE_FIELD(jint, Int, false);
2146GET_STATIC_TYPE_FIELD(jlong, Long, false);
2147GET_STATIC_TYPE_FIELD(jfloat, Float, false);
2148GET_STATIC_TYPE_FIELD(jdouble, Double, false);
2149
2150/*
2151 * Set a static field.
2152 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002153#define SET_STATIC_TYPE_FIELD(_ctype, _jname, _isref) \
2154 static void SetStatic##_jname##Field(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002155 jfieldID fieldID, _ctype value) \
2156 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002157 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002158 JNI_ENTER(); \
2159 StaticField* sfield = (StaticField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002160 if (_isref) { /* only when _ctype==jobject */ \
2161 Object* valObj = dvmDecodeIndirectRef(env, (jobject)(u4)value); \
2162 dvmSetStaticFieldObject(sfield, valObj); \
2163 } else { \
2164 dvmSetStaticField##_jname(sfield, value); \
2165 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002166 JNI_EXIT(); \
2167 }
Andy McFaddenab00d452009-08-19 07:21:41 -07002168SET_STATIC_TYPE_FIELD(jobject, Object, true);
2169SET_STATIC_TYPE_FIELD(jboolean, Boolean, false);
2170SET_STATIC_TYPE_FIELD(jbyte, Byte, false);
2171SET_STATIC_TYPE_FIELD(jchar, Char, false);
2172SET_STATIC_TYPE_FIELD(jshort, Short, false);
2173SET_STATIC_TYPE_FIELD(jint, Int, false);
2174SET_STATIC_TYPE_FIELD(jlong, Long, false);
2175SET_STATIC_TYPE_FIELD(jfloat, Float, false);
2176SET_STATIC_TYPE_FIELD(jdouble, Double, false);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002177
2178/*
2179 * Get an instance field.
2180 *
2181 * If we get an object reference, add it to the local refs list.
2182 */
2183#define GET_TYPE_FIELD(_ctype, _jname, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002184 static _ctype Get##_jname##Field(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002185 jfieldID fieldID) \
2186 { \
2187 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002188 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002189 InstField* field = (InstField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002190 _ctype value; \
2191 if (_isref) { /* only when _ctype==jobject */ \
2192 Object* valObj = dvmGetFieldObject(obj, field->byteOffset); \
2193 value = (_ctype)(u4)addLocalReference(env, valObj); \
2194 } else { \
2195 value = dvmGetField##_jname(obj, field->byteOffset); \
2196 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002197 JNI_EXIT(); \
2198 return value; \
2199 }
2200GET_TYPE_FIELD(jobject, Object, true);
2201GET_TYPE_FIELD(jboolean, Boolean, false);
2202GET_TYPE_FIELD(jbyte, Byte, false);
2203GET_TYPE_FIELD(jchar, Char, false);
2204GET_TYPE_FIELD(jshort, Short, false);
2205GET_TYPE_FIELD(jint, Int, false);
2206GET_TYPE_FIELD(jlong, Long, false);
2207GET_TYPE_FIELD(jfloat, Float, false);
2208GET_TYPE_FIELD(jdouble, Double, false);
2209
2210/*
2211 * Set an instance field.
2212 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002213#define SET_TYPE_FIELD(_ctype, _jname, _isref) \
2214 static void Set##_jname##Field(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002215 jfieldID fieldID, _ctype value) \
2216 { \
2217 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002218 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002219 InstField* field = (InstField*) fieldID; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002220 if (_isref) { /* only when _ctype==jobject */ \
2221 Object* valObj = dvmDecodeIndirectRef(env, (jobject)(u4)value); \
2222 dvmSetFieldObject(obj, field->byteOffset, valObj); \
2223 } else { \
2224 dvmSetField##_jname(obj, field->byteOffset, value); \
2225 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002226 JNI_EXIT(); \
2227 }
Andy McFaddenab00d452009-08-19 07:21:41 -07002228SET_TYPE_FIELD(jobject, Object, true);
2229SET_TYPE_FIELD(jboolean, Boolean, false);
2230SET_TYPE_FIELD(jbyte, Byte, false);
2231SET_TYPE_FIELD(jchar, Char, false);
2232SET_TYPE_FIELD(jshort, Short, false);
2233SET_TYPE_FIELD(jint, Int, false);
2234SET_TYPE_FIELD(jlong, Long, false);
2235SET_TYPE_FIELD(jfloat, Float, false);
2236SET_TYPE_FIELD(jdouble, Double, false);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002237
2238/*
2239 * Make a virtual method call.
2240 *
2241 * Three versions (..., va_list, jvalue[]) for each return type. If we're
2242 * returning an Object, we have to add it to the local references table.
2243 */
2244#define CALL_VIRTUAL(_ctype, _jname, _retfail, _retok, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002245 static _ctype Call##_jname##Method(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002246 jmethodID methodID, ...) \
2247 { \
2248 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002249 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002250 const Method* meth; \
2251 va_list args; \
2252 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002253 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002254 if (meth == NULL) { \
2255 JNI_EXIT(); \
2256 return _retfail; \
2257 } \
2258 va_start(args, methodID); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002259 dvmCallMethodV(_self, meth, obj, &result, args); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002260 va_end(args); \
2261 if (_isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002262 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002263 JNI_EXIT(); \
2264 return _retok; \
2265 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002266 static _ctype Call##_jname##MethodV(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002267 jmethodID methodID, va_list args) \
2268 { \
2269 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002270 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002271 const Method* meth; \
2272 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002273 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002274 if (meth == NULL) { \
2275 JNI_EXIT(); \
2276 return _retfail; \
2277 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002278 dvmCallMethodV(_self, meth, obj, &result, args); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002279 if (_isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002280 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002281 JNI_EXIT(); \
2282 return _retok; \
2283 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002284 static _ctype Call##_jname##MethodA(JNIEnv* env, jobject jobj, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002285 jmethodID methodID, jvalue* args) \
2286 { \
2287 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002288 Object* obj = dvmDecodeIndirectRef(env, jobj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002289 const Method* meth; \
2290 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002291 meth = dvmGetVirtualizedMethod(obj->clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002292 if (meth == NULL) { \
2293 JNI_EXIT(); \
2294 return _retfail; \
2295 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002296 dvmCallMethodA(_self, meth, obj, &result, args); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002297 if (_isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002298 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002299 JNI_EXIT(); \
2300 return _retok; \
2301 }
2302CALL_VIRTUAL(jobject, Object, NULL, result.l, true);
2303CALL_VIRTUAL(jboolean, Boolean, 0, result.z, false);
2304CALL_VIRTUAL(jbyte, Byte, 0, result.b, false);
2305CALL_VIRTUAL(jchar, Char, 0, result.c, false);
2306CALL_VIRTUAL(jshort, Short, 0, result.s, false);
2307CALL_VIRTUAL(jint, Int, 0, result.i, false);
2308CALL_VIRTUAL(jlong, Long, 0, result.j, false);
2309CALL_VIRTUAL(jfloat, Float, 0.0f, result.f, false);
2310CALL_VIRTUAL(jdouble, Double, 0.0, result.d, false);
2311CALL_VIRTUAL(void, Void, , , false);
2312
2313/*
2314 * Make a "non-virtual" method call. We're still calling a virtual method,
2315 * but this time we're not doing an indirection through the object's vtable.
2316 * The "clazz" parameter defines which implementation of a method we want.
2317 *
2318 * Three versions (..., va_list, jvalue[]) for each return type.
2319 */
2320#define CALL_NONVIRTUAL(_ctype, _jname, _retfail, _retok, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002321 static _ctype CallNonvirtual##_jname##Method(JNIEnv* env, jobject jobj, \
2322 jclass jclazz, jmethodID methodID, ...) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002323 { \
2324 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002325 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2326 ClassObject* clazz = \
2327 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002328 const Method* meth; \
2329 va_list args; \
2330 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002331 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002332 if (meth == NULL) { \
2333 JNI_EXIT(); \
2334 return _retfail; \
2335 } \
2336 va_start(args, methodID); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002337 dvmCallMethodV(_self, meth, obj, &result, args); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002338 if (_isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002339 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002340 va_end(args); \
2341 JNI_EXIT(); \
2342 return _retok; \
2343 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002344 static _ctype CallNonvirtual##_jname##MethodV(JNIEnv* env, jobject jobj,\
2345 jclass jclazz, jmethodID methodID, va_list args) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002346 { \
2347 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002348 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2349 ClassObject* clazz = \
2350 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002351 const Method* meth; \
2352 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002353 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002354 if (meth == NULL) { \
2355 JNI_EXIT(); \
2356 return _retfail; \
2357 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002358 dvmCallMethodV(_self, meth, obj, &result, args); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002359 if (_isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002360 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002361 JNI_EXIT(); \
2362 return _retok; \
2363 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002364 static _ctype CallNonvirtual##_jname##MethodA(JNIEnv* env, jobject jobj,\
2365 jclass jclazz, jmethodID methodID, jvalue* args) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002366 { \
2367 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002368 Object* obj = dvmDecodeIndirectRef(env, jobj); \
2369 ClassObject* clazz = \
2370 (ClassObject*) dvmDecodeIndirectRef(env, jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002371 const Method* meth; \
2372 JValue result; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002373 meth = dvmGetVirtualizedMethod(clazz, (Method*)methodID); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002374 if (meth == NULL) { \
2375 JNI_EXIT(); \
2376 return _retfail; \
2377 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002378 dvmCallMethodA(_self, meth, obj, &result, args); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002379 if (_isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002380 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002381 JNI_EXIT(); \
2382 return _retok; \
2383 }
2384CALL_NONVIRTUAL(jobject, Object, NULL, result.l, true);
2385CALL_NONVIRTUAL(jboolean, Boolean, 0, result.z, false);
2386CALL_NONVIRTUAL(jbyte, Byte, 0, result.b, false);
2387CALL_NONVIRTUAL(jchar, Char, 0, result.c, false);
2388CALL_NONVIRTUAL(jshort, Short, 0, result.s, false);
2389CALL_NONVIRTUAL(jint, Int, 0, result.i, false);
2390CALL_NONVIRTUAL(jlong, Long, 0, result.j, false);
2391CALL_NONVIRTUAL(jfloat, Float, 0.0f, result.f, false);
2392CALL_NONVIRTUAL(jdouble, Double, 0.0, result.d, false);
2393CALL_NONVIRTUAL(void, Void, , , false);
2394
2395
2396/*
2397 * Call a static method.
2398 */
2399#define CALL_STATIC(_ctype, _jname, _retfail, _retok, _isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002400 static _ctype CallStatic##_jname##Method(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002401 jmethodID methodID, ...) \
2402 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002403 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002404 JNI_ENTER(); \
2405 JValue result; \
2406 va_list args; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002407 va_start(args, methodID); \
2408 dvmCallMethodV(_self, (Method*) methodID, NULL, &result, args); \
2409 va_end(args); \
2410 if (_isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002411 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002412 JNI_EXIT(); \
2413 return _retok; \
2414 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002415 static _ctype CallStatic##_jname##MethodV(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002416 jmethodID methodID, va_list args) \
2417 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002418 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002419 JNI_ENTER(); \
2420 JValue result; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002421 dvmCallMethodV(_self, (Method*) methodID, NULL, &result, args); \
2422 if (_isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002423 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002424 JNI_EXIT(); \
2425 return _retok; \
2426 } \
Andy McFaddenab00d452009-08-19 07:21:41 -07002427 static _ctype CallStatic##_jname##MethodA(JNIEnv* env, jclass jclazz, \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002428 jmethodID methodID, jvalue* args) \
2429 { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002430 UNUSED_PARAMETER(jclazz); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002431 JNI_ENTER(); \
2432 JValue result; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002433 dvmCallMethodA(_self, (Method*) methodID, NULL, &result, args); \
2434 if (_isref) \
Andy McFaddenab00d452009-08-19 07:21:41 -07002435 result.l = addLocalReference(env, result.l); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002436 JNI_EXIT(); \
2437 return _retok; \
2438 }
2439CALL_STATIC(jobject, Object, NULL, result.l, true);
2440CALL_STATIC(jboolean, Boolean, 0, result.z, false);
2441CALL_STATIC(jbyte, Byte, 0, result.b, false);
2442CALL_STATIC(jchar, Char, 0, result.c, false);
2443CALL_STATIC(jshort, Short, 0, result.s, false);
2444CALL_STATIC(jint, Int, 0, result.i, false);
2445CALL_STATIC(jlong, Long, 0, result.j, false);
2446CALL_STATIC(jfloat, Float, 0.0f, result.f, false);
2447CALL_STATIC(jdouble, Double, 0.0, result.d, false);
2448CALL_STATIC(void, Void, , , false);
2449
2450/*
2451 * Create a new String from Unicode data.
2452 *
2453 * If "len" is zero, we will return an empty string even if "unicodeChars"
2454 * is NULL. (The JNI spec is vague here.)
2455 */
2456static jstring NewString(JNIEnv* env, const jchar* unicodeChars, jsize len)
2457{
2458 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002459 jobject retval;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002460
Andy McFaddenab00d452009-08-19 07:21:41 -07002461 StringObject* jstr = dvmCreateStringFromUnicode(unicodeChars, len);
2462 if (jstr == NULL) {
2463 retval = NULL;
2464 } else {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002465 dvmReleaseTrackedAlloc((Object*) jstr, NULL);
Andy McFaddenab00d452009-08-19 07:21:41 -07002466 retval = addLocalReference(env, (Object*) jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002467 }
2468
2469 JNI_EXIT();
2470 return jstr;
2471}
2472
2473/*
2474 * Return the length of a String in Unicode character units.
2475 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002476static jsize GetStringLength(JNIEnv* env, jstring jstr)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002477{
2478 JNI_ENTER();
2479
Andy McFaddenab00d452009-08-19 07:21:41 -07002480 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2481 jsize len = dvmStringLen(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002482
2483 JNI_EXIT();
2484 return len;
2485}
2486
Andy McFaddenab00d452009-08-19 07:21:41 -07002487
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002488/*
Andy McFaddenab00d452009-08-19 07:21:41 -07002489 * Get a string's character data.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002490 *
2491 * The result is guaranteed to be valid until ReleaseStringChars is
Andy McFaddenab00d452009-08-19 07:21:41 -07002492 * called, which means we have to pin it or return a copy.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002493 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002494static const jchar* GetStringChars(JNIEnv* env, jstring jstr, jboolean* isCopy)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002495{
2496 JNI_ENTER();
2497
Andy McFaddenab00d452009-08-19 07:21:41 -07002498 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2499 ArrayObject* strChars = dvmStringCharArray(jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002500
Andy McFaddenab00d452009-08-19 07:21:41 -07002501 pinPrimitiveArray(strChars);
2502
2503 const u2* data = dvmStringChars(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002504 if (isCopy != NULL)
2505 *isCopy = JNI_FALSE;
2506
2507 JNI_EXIT();
2508 return (jchar*)data;
2509}
2510
2511/*
2512 * Release our grip on some characters from a string.
2513 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002514static void ReleaseStringChars(JNIEnv* env, jstring jstr, const jchar* chars)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002515{
2516 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002517 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2518 ArrayObject* strChars = dvmStringCharArray(jstr);
2519 unpinPrimitiveArray(strChars);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002520 JNI_EXIT();
2521}
2522
2523/*
2524 * Create a new java.lang.String object from chars in modified UTF-8 form.
2525 *
2526 * The spec doesn't say how to handle a NULL string. Popular desktop VMs
2527 * accept it and return a NULL pointer in response.
2528 */
2529static jstring NewStringUTF(JNIEnv* env, const char* bytes)
2530{
2531 JNI_ENTER();
2532
Andy McFaddenab00d452009-08-19 07:21:41 -07002533 jstring result;
2534
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002535 if (bytes == NULL) {
Andy McFaddenab00d452009-08-19 07:21:41 -07002536 result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002537 } else {
Andy McFaddenab00d452009-08-19 07:21:41 -07002538 /* note newStr could come back NULL on OOM */
2539 StringObject* newStr = dvmCreateStringFromCstr(bytes, ALLOC_DEFAULT);
2540 result = addLocalReference(env, (Object*) newStr);
2541 dvmReleaseTrackedAlloc((Object*)newStr, NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002542 }
2543
2544 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002545 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002546}
2547
2548/*
2549 * Return the length in bytes of the modified UTF-8 form of the string.
2550 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002551static jsize GetStringUTFLength(JNIEnv* env, jstring jstr)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002552{
2553 JNI_ENTER();
2554
Andy McFaddenab00d452009-08-19 07:21:41 -07002555 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2556 jsize len = dvmStringUtf8ByteLen(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002557
2558 JNI_EXIT();
2559 return len;
2560}
2561
2562/*
2563 * Convert "string" to modified UTF-8 and return a pointer. The returned
2564 * value must be released with ReleaseStringUTFChars.
2565 *
2566 * According to the JNI reference, "Returns a pointer to a UTF-8 string,
2567 * or NULL if the operation fails. Returns NULL if and only if an invocation
2568 * of this function has thrown an exception."
2569 *
2570 * The behavior here currently follows that of other open-source VMs, which
2571 * quietly return NULL if "string" is NULL. We should consider throwing an
2572 * NPE. (The CheckJNI code blows up if you try to pass in a NULL string,
2573 * which should catch this sort of thing during development.) Certain other
2574 * VMs will crash with a segmentation fault.
2575 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002576static const char* GetStringUTFChars(JNIEnv* env, jstring jstr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002577 jboolean* isCopy)
2578{
2579 JNI_ENTER();
2580 char* newStr;
2581
Andy McFaddenab00d452009-08-19 07:21:41 -07002582 if (jstr == NULL) {
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002583 /* this shouldn't happen; throw NPE? */
2584 newStr = NULL;
2585 } else {
2586 if (isCopy != NULL)
2587 *isCopy = JNI_TRUE;
2588
Andy McFaddenab00d452009-08-19 07:21:41 -07002589 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
2590 newStr = dvmCreateCstrFromString(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002591 if (newStr == NULL) {
2592 /* assume memory failure */
2593 dvmThrowException("Ljava/lang/OutOfMemoryError;",
2594 "native heap string alloc failed");
2595 }
2596 }
2597
2598 JNI_EXIT();
2599 return newStr;
2600}
2601
2602/*
2603 * Release a string created by GetStringUTFChars().
2604 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002605static void ReleaseStringUTFChars(JNIEnv* env, jstring jstr, const char* utf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002606{
2607 JNI_ENTER();
2608 free((char*)utf);
2609 JNI_EXIT();
2610}
2611
2612/*
2613 * Return the capacity of the array.
2614 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002615static jsize GetArrayLength(JNIEnv* env, jarray jarr)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002616{
2617 JNI_ENTER();
2618
Andy McFaddenab00d452009-08-19 07:21:41 -07002619 ArrayObject* arrObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
2620 jsize length = arrObj->length;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002621
2622 JNI_EXIT();
2623 return length;
2624}
2625
2626/*
2627 * Construct a new array that holds objects from class "elementClass".
2628 */
2629static jobjectArray NewObjectArray(JNIEnv* env, jsize length,
Andy McFaddenab00d452009-08-19 07:21:41 -07002630 jclass jelementClass, jobject jinitialElement)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002631{
2632 JNI_ENTER();
2633
Andy McFaddenab00d452009-08-19 07:21:41 -07002634 jobjectArray newArray = NULL;
2635 ClassObject* elemClassObj =
2636 (ClassObject*) dvmDecodeIndirectRef(env, jelementClass);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002637
2638 if (elemClassObj == NULL) {
2639 dvmThrowException("Ljava/lang/NullPointerException;",
2640 "JNI NewObjectArray");
2641 goto bail;
2642 }
2643
Andy McFaddenab00d452009-08-19 07:21:41 -07002644 ArrayObject* newObj =
2645 dvmAllocObjectArray(elemClassObj, length, ALLOC_DEFAULT);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002646 if (newObj == NULL) {
2647 assert(dvmCheckException(_self));
2648 goto bail;
2649 }
Andy McFaddenab00d452009-08-19 07:21:41 -07002650 newArray = addLocalReference(env, (Object*) newObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002651 dvmReleaseTrackedAlloc((Object*) newObj, NULL);
2652
2653 /*
2654 * Initialize the array. Trashes "length".
2655 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002656 if (jinitialElement != NULL) {
2657 Object* initialElement = dvmDecodeIndirectRef(env, jinitialElement);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002658 Object** arrayData = (Object**) newObj->contents;
2659
2660 while (length--)
Andy McFaddenab00d452009-08-19 07:21:41 -07002661 *arrayData++ = initialElement;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002662 }
2663
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002664
2665bail:
2666 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002667 return newArray;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002668}
2669
2670/*
2671 * Get one element of an Object array.
2672 *
2673 * Add the object to the local references table in case the array goes away.
2674 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002675static jobject GetObjectArrayElement(JNIEnv* env, jobjectArray jarr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002676 jsize index)
2677{
2678 JNI_ENTER();
2679
Andy McFaddenab00d452009-08-19 07:21:41 -07002680 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
2681 jobject retval = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002682
Andy McFaddenab00d452009-08-19 07:21:41 -07002683 assert(arrayObj != NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002684
2685 /* check the array bounds */
2686 if (index < 0 || index >= (int) arrayObj->length) {
2687 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
2688 arrayObj->obj.clazz->descriptor);
2689 goto bail;
2690 }
2691
Andy McFaddenab00d452009-08-19 07:21:41 -07002692 Object* value = ((Object**) arrayObj->contents)[index];
2693 retval = addLocalReference(env, value);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002694
2695bail:
2696 JNI_EXIT();
Andy McFaddenab00d452009-08-19 07:21:41 -07002697 return retval;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002698}
2699
2700/*
2701 * Set one element of an Object array.
2702 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002703static void SetObjectArrayElement(JNIEnv* env, jobjectArray jarr,
2704 jsize index, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002705{
2706 JNI_ENTER();
2707
Andy McFaddenab00d452009-08-19 07:21:41 -07002708 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002709
Andy McFaddenab00d452009-08-19 07:21:41 -07002710 assert(arrayObj != NULL);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002711
2712 /* check the array bounds */
2713 if (index < 0 || index >= (int) arrayObj->length) {
2714 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;",
2715 arrayObj->obj.clazz->descriptor);
2716 goto bail;
2717 }
2718
2719 //LOGV("JNI: set element %d in array %p to %p\n", index, array, value);
2720
Andy McFaddenab00d452009-08-19 07:21:41 -07002721 Object* obj;
2722 if (jobj == NULL)
2723 obj = NULL;
2724 else
2725 obj = dvmDecodeIndirectRef(env, jobj);
2726 ((Object**) arrayObj->contents)[index] = obj;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002727
2728bail:
2729 JNI_EXIT();
2730}
2731
2732/*
2733 * Create a new array of primitive elements.
2734 */
2735#define NEW_PRIMITIVE_ARRAY(_artype, _jname, _typechar) \
2736 static _artype New##_jname##Array(JNIEnv* env, jsize length) \
2737 { \
2738 JNI_ENTER(); \
2739 ArrayObject* arrayObj; \
2740 arrayObj = dvmAllocPrimitiveArray(_typechar, length, \
2741 ALLOC_DEFAULT); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002742 jarray jarr = NULL; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002743 if (arrayObj != NULL) { \
Andy McFaddenab00d452009-08-19 07:21:41 -07002744 jarr = addLocalReference(env, (Object*) arrayObj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002745 dvmReleaseTrackedAlloc((Object*) arrayObj, NULL); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002746 } \
2747 JNI_EXIT(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002748 return (_artype)jarr; \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002749 }
2750NEW_PRIMITIVE_ARRAY(jbooleanArray, Boolean, 'Z');
2751NEW_PRIMITIVE_ARRAY(jbyteArray, Byte, 'B');
2752NEW_PRIMITIVE_ARRAY(jcharArray, Char, 'C');
2753NEW_PRIMITIVE_ARRAY(jshortArray, Short, 'S');
2754NEW_PRIMITIVE_ARRAY(jintArray, Int, 'I');
2755NEW_PRIMITIVE_ARRAY(jlongArray, Long, 'J');
2756NEW_PRIMITIVE_ARRAY(jfloatArray, Float, 'F');
2757NEW_PRIMITIVE_ARRAY(jdoubleArray, Double, 'D');
2758
2759/*
2760 * Get a pointer to a C array of primitive elements from an array object
2761 * of the matching type.
2762 *
Andy McFaddenab00d452009-08-19 07:21:41 -07002763 * In a compacting GC, we either need to return a copy of the elements or
2764 * "pin" the memory. Otherwise we run the risk of native code using the
2765 * buffer as the destination of e.g. a blocking read() call that wakes up
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002766 * during a GC.
2767 */
2768#define GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
2769 static _ctype* Get##_jname##ArrayElements(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07002770 _ctype##Array jarr, jboolean* isCopy) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002771 { \
2772 JNI_ENTER(); \
2773 _ctype* data; \
Andy McFaddenab00d452009-08-19 07:21:41 -07002774 ArrayObject* arrayObj = \
2775 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
2776 pinPrimitiveArray(arrayObj); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002777 data = (_ctype*) arrayObj->contents; \
2778 if (isCopy != NULL) \
2779 *isCopy = JNI_FALSE; \
2780 JNI_EXIT(); \
2781 return data; \
2782 }
2783
2784/*
2785 * Release the storage locked down by the "get" function.
2786 *
Andy McFaddenab00d452009-08-19 07:21:41 -07002787 * The spec says, "'mode' has no effect if 'elems' is not a copy of the
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002788 * elements in 'array'." They apparently did not anticipate the need to
Andy McFaddenab00d452009-08-19 07:21:41 -07002789 * un-pin memory.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002790 */
2791#define RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname) \
2792 static void Release##_jname##ArrayElements(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07002793 _ctype##Array jarr, _ctype* elems, jint mode) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002794 { \
2795 UNUSED_PARAMETER(elems); \
2796 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002797 if (mode != JNI_COMMIT) { \
2798 ArrayObject* arrayObj = \
2799 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
2800 unpinPrimitiveArray(arrayObj); \
2801 } \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002802 JNI_EXIT(); \
2803 }
2804
2805/*
2806 * Copy a section of a primitive array to a buffer.
2807 */
2808#define GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
2809 static void Get##_jname##ArrayRegion(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07002810 _ctype##Array jarr, jsize start, jsize len, _ctype* buf) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002811 { \
2812 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002813 ArrayObject* arrayObj = \
2814 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002815 _ctype* data = (_ctype*) arrayObj->contents; \
2816 if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
2817 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
2818 arrayObj->obj.clazz->descriptor); \
2819 } else { \
2820 memcpy(buf, data + start, len * sizeof(_ctype)); \
2821 } \
2822 JNI_EXIT(); \
2823 }
2824
2825/*
2826 * Copy a section of a primitive array to a buffer.
2827 */
2828#define SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname) \
2829 static void Set##_jname##ArrayRegion(JNIEnv* env, \
Andy McFaddenab00d452009-08-19 07:21:41 -07002830 _ctype##Array jarr, jsize start, jsize len, const _ctype* buf) \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002831 { \
2832 JNI_ENTER(); \
Andy McFaddenab00d452009-08-19 07:21:41 -07002833 ArrayObject* arrayObj = \
2834 (ArrayObject*) dvmDecodeIndirectRef(env, jarr); \
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002835 _ctype* data = (_ctype*) arrayObj->contents; \
2836 if (start < 0 || len < 0 || start + len > (int) arrayObj->length) { \
2837 dvmThrowException("Ljava/lang/ArrayIndexOutOfBoundsException;", \
2838 arrayObj->obj.clazz->descriptor); \
2839 } else { \
2840 memcpy(data + start, buf, len * sizeof(_ctype)); \
2841 } \
2842 JNI_EXIT(); \
2843 }
2844
2845/*
2846 * 4-in-1:
2847 * Get<Type>ArrayElements
2848 * Release<Type>ArrayElements
2849 * Get<Type>ArrayRegion
2850 * Set<Type>ArrayRegion
2851 */
2852#define PRIMITIVE_ARRAY_FUNCTIONS(_ctype, _jname) \
2853 GET_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
2854 RELEASE_PRIMITIVE_ARRAY_ELEMENTS(_ctype, _jname); \
2855 GET_PRIMITIVE_ARRAY_REGION(_ctype, _jname); \
2856 SET_PRIMITIVE_ARRAY_REGION(_ctype, _jname);
2857
2858PRIMITIVE_ARRAY_FUNCTIONS(jboolean, Boolean);
2859PRIMITIVE_ARRAY_FUNCTIONS(jbyte, Byte);
2860PRIMITIVE_ARRAY_FUNCTIONS(jchar, Char);
2861PRIMITIVE_ARRAY_FUNCTIONS(jshort, Short);
2862PRIMITIVE_ARRAY_FUNCTIONS(jint, Int);
2863PRIMITIVE_ARRAY_FUNCTIONS(jlong, Long);
2864PRIMITIVE_ARRAY_FUNCTIONS(jfloat, Float);
2865PRIMITIVE_ARRAY_FUNCTIONS(jdouble, Double);
2866
2867/*
2868 * Register one or more native functions in one class.
2869 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002870static jint RegisterNatives(JNIEnv* env, jclass jclazz,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002871 const JNINativeMethod* methods, jint nMethods)
2872{
2873 JNI_ENTER();
2874
Andy McFaddenab00d452009-08-19 07:21:41 -07002875 ClassObject* clazz = (ClassObject*) dvmDecodeIndirectRef(env, jclazz);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002876 jint retval;
2877 int i;
2878
2879 if (gDvm.verboseJni) {
2880 LOGI("[Registering JNI native methods for class %s]\n",
Andy McFaddenab00d452009-08-19 07:21:41 -07002881 clazz->descriptor);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002882 }
2883
2884 for (i = 0; i < nMethods; i++) {
Andy McFaddenab00d452009-08-19 07:21:41 -07002885 if (!dvmRegisterJNIMethod(clazz, methods[i].name,
2886 methods[i].signature, methods[i].fnPtr))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002887 {
2888 retval = JNI_ERR;
2889 goto bail;
2890 }
2891 }
2892 retval = JNI_OK;
2893
2894bail:
2895 JNI_EXIT();
2896 return retval;
2897}
2898
2899/*
2900 * Un-register a native function.
2901 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002902static jint UnregisterNatives(JNIEnv* env, jclass jclazz)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002903{
2904 JNI_ENTER();
2905 /*
2906 * The JNI docs refer to this as a way to reload/relink native libraries,
2907 * and say it "should not be used in normal native code".
2908 *
2909 * We can implement it if we decide we need it.
2910 */
2911 JNI_EXIT();
2912 return JNI_ERR;
2913}
2914
2915/*
2916 * Lock the monitor.
2917 *
2918 * We have to track all monitor enters and exits, so that we can undo any
2919 * outstanding synchronization before the thread exits.
2920 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002921static jint MonitorEnter(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002922{
2923 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002924 Object* obj = dvmDecodeIndirectRef(env, jobj);
2925 dvmLockObject(_self, obj);
2926 trackMonitorEnter(_self, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002927 JNI_EXIT();
2928 return JNI_OK;
2929}
2930
2931/*
2932 * Unlock the monitor.
2933 *
2934 * Throws an IllegalMonitorStateException if the current thread
Andy McFaddenab00d452009-08-19 07:21:41 -07002935 * doesn't own the monitor. (dvmUnlockObject() takes care of the throw.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002936 *
2937 * According to the 1.6 spec, it's legal to call here with an exception
2938 * pending. If this fails, we'll stomp the original exception.
2939 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002940static jint MonitorExit(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002941{
2942 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002943 Object* obj = dvmDecodeIndirectRef(env, jobj);
2944 bool success = dvmUnlockObject(_self, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002945 if (success)
Andy McFaddenab00d452009-08-19 07:21:41 -07002946 trackMonitorExit(_self, obj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002947 JNI_EXIT();
2948 return success ? JNI_OK : JNI_ERR;
2949}
2950
2951/*
2952 * Return the JavaVM interface associated with the current thread.
2953 */
2954static jint GetJavaVM(JNIEnv* env, JavaVM** vm)
2955{
2956 JNI_ENTER();
2957 //*vm = gDvm.vmList;
2958 *vm = (JavaVM*) ((JNIEnvExt*)env)->vm;
2959 JNI_EXIT();
2960 if (*vm == NULL)
2961 return JNI_ERR;
2962 else
2963 return JNI_OK;
2964}
2965
2966/*
2967 * Copies "len" Unicode characters, from offset "start".
2968 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002969static void GetStringRegion(JNIEnv* env, jstring jstr, jsize start, jsize len,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002970 jchar* buf)
2971{
2972 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002973 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002974 if (start + len > dvmStringLen(strObj))
2975 dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
2976 else
2977 memcpy(buf, dvmStringChars(strObj) + start, len * sizeof(u2));
2978 JNI_EXIT();
2979}
2980
2981/*
2982 * Translates "len" Unicode characters, from offset "start", into
2983 * modified UTF-8 encoding.
2984 */
Andy McFaddenab00d452009-08-19 07:21:41 -07002985static void GetStringUTFRegion(JNIEnv* env, jstring jstr, jsize start,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002986 jsize len, char* buf)
2987{
2988 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07002989 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08002990 if (start + len > dvmStringLen(strObj))
2991 dvmThrowException("Ljava/lang/StringIndexOutOfBoundsException;", NULL);
2992 else
2993 dvmCreateCstrFromStringRegion(strObj, start, len, buf);
2994 JNI_EXIT();
2995}
2996
2997/*
2998 * Get a raw pointer to array data.
2999 *
3000 * The caller is expected to call "release" before doing any JNI calls
3001 * or blocking I/O operations.
3002 *
Andy McFaddenab00d452009-08-19 07:21:41 -07003003 * We need to pin the memory or block GC.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003004 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003005static void* GetPrimitiveArrayCritical(JNIEnv* env, jarray jarr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003006 jboolean* isCopy)
3007{
3008 JNI_ENTER();
3009 void* data;
Andy McFaddenab00d452009-08-19 07:21:41 -07003010 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
3011 pinPrimitiveArray(arrayObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003012 data = arrayObj->contents;
3013 if (isCopy != NULL)
3014 *isCopy = JNI_FALSE;
3015 JNI_EXIT();
3016 return data;
3017}
3018
3019/*
3020 * Release an array obtained with GetPrimitiveArrayCritical.
3021 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003022static void ReleasePrimitiveArrayCritical(JNIEnv* env, jarray jarr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003023 void* carray, jint mode)
3024{
3025 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003026 if (mode != JNI_COMMIT) {
3027 ArrayObject* arrayObj = (ArrayObject*) dvmDecodeIndirectRef(env, jarr);
3028 unpinPrimitiveArray(arrayObj);
3029 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003030 JNI_EXIT();
3031}
3032
3033/*
3034 * Like GetStringChars, but with restricted use.
3035 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003036static const jchar* GetStringCritical(JNIEnv* env, jstring jstr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003037 jboolean* isCopy)
3038{
3039 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003040 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
3041 ArrayObject* strChars = dvmStringCharArray(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003042
Andy McFaddenab00d452009-08-19 07:21:41 -07003043 pinPrimitiveArray(strChars);
3044
3045 const u2* data = dvmStringChars(strObj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003046 if (isCopy != NULL)
3047 *isCopy = JNI_FALSE;
3048
3049 JNI_EXIT();
3050 return (jchar*)data;
3051}
3052
3053/*
3054 * Like ReleaseStringChars, but with restricted use.
3055 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003056static void ReleaseStringCritical(JNIEnv* env, jstring jstr,
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003057 const jchar* carray)
3058{
3059 JNI_ENTER();
Andy McFaddenab00d452009-08-19 07:21:41 -07003060 StringObject* strObj = (StringObject*) dvmDecodeIndirectRef(env, jstr);
3061 ArrayObject* strChars = dvmStringCharArray(jstr);
3062 unpinPrimitiveArray(strChars);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003063 JNI_EXIT();
3064}
3065
3066/*
3067 * Create a new weak global reference.
3068 */
3069static jweak NewWeakGlobalRef(JNIEnv* env, jobject obj)
3070{
3071 JNI_ENTER();
3072 // TODO - implement
3073 jobject gref = NULL;
3074 LOGE("JNI ERROR: NewWeakGlobalRef not implemented\n");
3075 dvmAbort();
3076 JNI_EXIT();
3077 return gref;
3078}
3079
3080/*
3081 * Delete the specified weak global reference.
3082 */
3083static void DeleteWeakGlobalRef(JNIEnv* env, jweak obj)
3084{
3085 JNI_ENTER();
3086 // TODO - implement
3087 LOGE("JNI ERROR: DeleteWeakGlobalRef not implemented\n");
3088 dvmAbort();
3089 JNI_EXIT();
3090}
3091
3092/*
3093 * Quick check for pending exceptions.
3094 *
3095 * TODO: we should be able to skip the enter/exit macros here.
3096 */
3097static jboolean ExceptionCheck(JNIEnv* env)
3098{
3099 JNI_ENTER();
3100 bool result = dvmCheckException(_self);
3101 JNI_EXIT();
3102 return result;
3103}
3104
3105/*
3106 * Returns the type of the object referred to by "obj". It can be local,
3107 * global, or weak global.
3108 *
3109 * In the current implementation, references can be global and local at
3110 * the same time, so while the return value is accurate it may not tell
3111 * the whole story.
3112 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003113static jobjectRefType GetObjectRefType(JNIEnv* env, jobject jobj)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003114{
3115 JNI_ENTER();
3116 jobjectRefType type;
The Android Open Source Project99409882009-03-18 22:20:24 -07003117
Andy McFaddenab00d452009-08-19 07:21:41 -07003118 if (jobj == NULL)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003119 type = JNIInvalidRefType;
3120 else
Andy McFaddenab00d452009-08-19 07:21:41 -07003121 type = dvmGetJNIRefType(env, jobj);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003122 JNI_EXIT();
3123 return type;
3124}
3125
3126/*
3127 * Allocate and return a new java.nio.ByteBuffer for this block of memory.
3128 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003129 * "address" may not be NULL, and "capacity" must be > 0. (These are only
3130 * verified when CheckJNI is enabled.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003131 */
Andy McFadden8e5c7842009-07-23 17:47:18 -07003132static jobject NewDirectByteBuffer(JNIEnv* env, void* address, jlong capacity)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003133{
Andy McFadden8e5c7842009-07-23 17:47:18 -07003134 JNI_ENTER();
3135
3136 Thread* self = _self /*dvmThreadSelf()*/;
3137 Object* platformAddress = NULL;
3138 JValue callResult;
The Android Open Source Project99409882009-03-18 22:20:24 -07003139 jobject result = NULL;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003140
Andy McFadden8e5c7842009-07-23 17:47:18 -07003141 /* get an instance of PlatformAddress that wraps the provided address */
3142 dvmCallMethod(self,
3143 gDvm.methOrgApacheHarmonyLuniPlatformPlatformAddress_on,
3144 NULL, &callResult, address);
3145 if (dvmGetException(self) != NULL || callResult.l == NULL)
The Android Open Source Project99409882009-03-18 22:20:24 -07003146 goto bail;
Andy McFadden8e5c7842009-07-23 17:47:18 -07003147
3148 /* don't let the GC discard it */
3149 platformAddress = (Object*) callResult.l;
3150 dvmAddTrackedAlloc(platformAddress, self);
3151 LOGV("tracking %p for address=%p\n", platformAddress, address);
3152
3153 /* create an instance of java.nio.ReadWriteDirectByteBuffer */
3154 ClassObject* clazz = gDvm.classJavaNioReadWriteDirectByteBuffer;
3155 if (!dvmIsClassInitialized(clazz) && !dvmInitClass(clazz))
3156 goto bail;
3157 Object* newObj = dvmAllocObject(clazz, ALLOC_DONT_TRACK);
3158 if (newObj != NULL) {
3159 /* call the (PlatformAddress, int, int) constructor */
Andy McFaddenab00d452009-08-19 07:21:41 -07003160 result = addLocalReference(env, newObj);
Andy McFadden8e5c7842009-07-23 17:47:18 -07003161 dvmCallMethod(self, gDvm.methJavaNioReadWriteDirectByteBuffer_init,
3162 newObj, &callResult, platformAddress, (jint) capacity, (jint) 0);
Andy McFaddenab00d452009-08-19 07:21:41 -07003163 if (dvmGetException(self) != NULL) {
3164 deleteLocalReference(env, result);
3165 result = NULL;
Andy McFadden8e5c7842009-07-23 17:47:18 -07003166 goto bail;
Andy McFaddenab00d452009-08-19 07:21:41 -07003167 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003168 }
3169
The Android Open Source Project99409882009-03-18 22:20:24 -07003170bail:
Andy McFadden8e5c7842009-07-23 17:47:18 -07003171 if (platformAddress != NULL)
3172 dvmReleaseTrackedAlloc(platformAddress, self);
3173 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07003174 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003175}
3176
3177/*
3178 * Get the starting address of the buffer for the specified java.nio.Buffer.
3179 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003180 * If this is not a "direct" buffer, we return NULL.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003181 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003182static void* GetDirectBufferAddress(JNIEnv* env, jobject jbuf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003183{
Andy McFadden8e5c7842009-07-23 17:47:18 -07003184 JNI_ENTER();
3185
Andy McFaddenab00d452009-08-19 07:21:41 -07003186 Object* bufObj = dvmDecodeIndirectRef(env, jbuf);
Andy McFadden8e5c7842009-07-23 17:47:18 -07003187 Thread* self = _self /*dvmThreadSelf()*/;
Andy McFadden8e696dc2009-07-24 15:28:16 -07003188 void* result;
3189
3190 /*
3191 * All Buffer objects have an effectiveDirectAddress field. If it's
3192 * nonzero, we can just return that value. If not, we have to call
3193 * through DirectBuffer.getEffectiveAddress(), which as a side-effect
3194 * will set the effectiveDirectAddress field for direct buffers (and
3195 * things that wrap direct buffers).
3196 */
3197 result = (void*) dvmGetFieldInt(bufObj,
3198 gDvm.offJavaNioBuffer_effectiveDirectAddress);
3199 if (result != NULL) {
3200 //LOGI("fast path for %p\n", buf);
3201 goto bail;
3202 }
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003203
Andy McFadden72d61fb2009-06-24 16:56:06 -07003204 /*
3205 * Start by determining if the object supports the DirectBuffer
3206 * interfaces. Note this does not guarantee that it's a direct buffer.
3207 */
Andy McFadden8e5c7842009-07-23 17:47:18 -07003208 if (!dvmInstanceof(bufObj->clazz,
3209 gDvm.classOrgApacheHarmonyNioInternalDirectBuffer))
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003210 {
The Android Open Source Project99409882009-03-18 22:20:24 -07003211 goto bail;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003212 }
3213
Andy McFadden72d61fb2009-06-24 16:56:06 -07003214 /*
Andy McFadden8e5c7842009-07-23 17:47:18 -07003215 * Get a PlatformAddress object with the effective address.
Andy McFadden5f612b82009-07-22 15:07:27 -07003216 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003217 * If this isn't a direct buffer, the result will be NULL and/or an
Andy McFadden72d61fb2009-06-24 16:56:06 -07003218 * exception will have been thrown.
3219 */
Andy McFadden8e696dc2009-07-24 15:28:16 -07003220 JValue callResult;
Andy McFadden8e5c7842009-07-23 17:47:18 -07003221 const Method* meth = dvmGetVirtualizedMethod(bufObj->clazz,
3222 gDvm.methOrgApacheHarmonyNioInternalDirectBuffer_getEffectiveAddress);
3223 dvmCallMethodA(self, meth, bufObj, &callResult, NULL);
3224 if (dvmGetException(self) != NULL) {
3225 dvmClearException(self);
3226 callResult.l = NULL;
Andy McFadden72d61fb2009-06-24 16:56:06 -07003227 }
Andy McFadden8e5c7842009-07-23 17:47:18 -07003228
Andy McFadden8e696dc2009-07-24 15:28:16 -07003229 Object* platformAddr = callResult.l;
Andy McFadden72d61fb2009-06-24 16:56:06 -07003230 if (platformAddr == NULL) {
Andy McFadden8e696dc2009-07-24 15:28:16 -07003231 LOGV("Got request for address of non-direct buffer\n");
Andy McFadden72d61fb2009-06-24 16:56:06 -07003232 goto bail;
3233 }
3234
Andy McFadden8e5c7842009-07-23 17:47:18 -07003235 /*
3236 * Extract the address from the PlatformAddress object. Instead of
3237 * calling the toLong() method, just grab the field directly. This
3238 * is faster but more fragile.
3239 */
3240 result = (void*) dvmGetFieldInt(platformAddr,
3241 gDvm.offOrgApacheHarmonyLuniPlatformPlatformAddress_osaddr);
The Android Open Source Project99409882009-03-18 22:20:24 -07003242
Andy McFadden8e696dc2009-07-24 15:28:16 -07003243 //LOGI("slow path for %p --> %p\n", buf, result);
3244
The Android Open Source Project99409882009-03-18 22:20:24 -07003245bail:
Andy McFadden8e5c7842009-07-23 17:47:18 -07003246 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07003247 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003248}
3249
3250/*
3251 * Get the capacity of the buffer for the specified java.nio.Buffer.
3252 *
Andy McFadden8e5c7842009-07-23 17:47:18 -07003253 * Returns -1 if the object is not a direct buffer. (We actually skip
3254 * this check, since it's expensive to determine, and just return the
3255 * capacity regardless.)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003256 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003257static jlong GetDirectBufferCapacity(JNIEnv* env, jobject jbuf)
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003258{
Andy McFadden8e5c7842009-07-23 17:47:18 -07003259 JNI_ENTER();
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003260
Andy McFadden8e5c7842009-07-23 17:47:18 -07003261 /*
3262 * The capacity is always in the Buffer.capacity field.
3263 *
3264 * (The "check" version should verify that this is actually a Buffer,
3265 * but we're not required to do so here.)
3266 */
Andy McFaddenab00d452009-08-19 07:21:41 -07003267 Object* buf = dvmDecodeIndirectRef(env, jbuf);
3268 jlong result = dvmGetFieldInt(buf, gDvm.offJavaNioBuffer_capacity);
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003269
Andy McFadden8e5c7842009-07-23 17:47:18 -07003270 JNI_EXIT();
The Android Open Source Project99409882009-03-18 22:20:24 -07003271 return result;
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003272}
3273
3274
3275/*
3276 * ===========================================================================
3277 * JNI invocation functions
3278 * ===========================================================================
3279 */
3280
3281/*
3282 * Handle AttachCurrentThread{AsDaemon}.
3283 *
3284 * We need to make sure the VM is actually running. For example, if we start
3285 * up, issue an Attach, and the VM exits almost immediately, by the time the
3286 * attaching happens the VM could already be shutting down.
3287 *
3288 * It's hard to avoid a race condition here because we don't want to hold
3289 * a lock across the entire operation. What we can do is temporarily
3290 * increment the thread count to prevent a VM exit.
3291 *
3292 * This could potentially still have problems if a daemon thread calls here
3293 * while the VM is shutting down. dvmThreadSelf() will work, since it just
3294 * uses pthread TLS, but dereferencing "vm" could fail. Such is life when
3295 * you shut down a VM while threads are still running inside it.
3296 *
3297 * Remember that some code may call this as a way to find the per-thread
3298 * JNIEnv pointer. Don't do excess work for that case.
3299 */
3300static jint attachThread(JavaVM* vm, JNIEnv** p_env, void* thr_args,
3301 bool isDaemon)
3302{
3303 JavaVMAttachArgs* args = (JavaVMAttachArgs*) thr_args;
3304 Thread* self;
3305 bool result = false;
3306
3307 /*
3308 * Return immediately if we're already one with the VM.
3309 */
3310 self = dvmThreadSelf();
3311 if (self != NULL) {
3312 *p_env = self->jniEnv;
3313 return JNI_OK;
3314 }
3315
3316 /*
3317 * No threads allowed in zygote mode.
3318 */
3319 if (gDvm.zygote) {
3320 return JNI_ERR;
3321 }
3322
3323 /* increment the count to keep the VM from bailing while we run */
3324 dvmLockThreadList(NULL);
3325 if (gDvm.nonDaemonThreadCount == 0) {
3326 // dead or dying
3327 LOGV("Refusing to attach thread '%s' -- VM is shutting down\n",
3328 (thr_args == NULL) ? "(unknown)" : args->name);
3329 dvmUnlockThreadList();
3330 return JNI_ERR;
3331 }
3332 gDvm.nonDaemonThreadCount++;
3333 dvmUnlockThreadList();
3334
3335 /* tweak the JavaVMAttachArgs as needed */
3336 JavaVMAttachArgs argsCopy;
3337 if (args == NULL) {
3338 /* allow the v1.1 calling convention */
3339 argsCopy.version = JNI_VERSION_1_2;
3340 argsCopy.name = NULL;
3341 argsCopy.group = dvmGetMainThreadGroup();
3342 } else {
3343 assert(args->version >= JNI_VERSION_1_2);
3344
3345 argsCopy.version = args->version;
3346 argsCopy.name = args->name;
3347 if (args->group != NULL)
3348 argsCopy.group = args->group;
3349 else
3350 argsCopy.group = dvmGetMainThreadGroup();
3351 }
3352
3353 result = dvmAttachCurrentThread(&argsCopy, isDaemon);
3354
3355 /* restore the count */
3356 dvmLockThreadList(NULL);
3357 gDvm.nonDaemonThreadCount--;
3358 dvmUnlockThreadList();
3359
3360 /*
3361 * Change the status to indicate that we're out in native code. This
3362 * call is not guarded with state-change macros, so we have to do it
3363 * by hand.
3364 */
3365 if (result) {
3366 self = dvmThreadSelf();
3367 assert(self != NULL);
3368 dvmChangeStatus(self, THREAD_NATIVE);
3369 *p_env = self->jniEnv;
3370 return JNI_OK;
3371 } else {
3372 return JNI_ERR;
3373 }
3374}
3375
3376/*
3377 * Attach the current thread to the VM. If the thread is already attached,
3378 * this is a no-op.
3379 */
3380static jint AttachCurrentThread(JavaVM* vm, JNIEnv** p_env, void* thr_args)
3381{
3382 return attachThread(vm, p_env, thr_args, false);
3383}
3384
3385/*
3386 * Like AttachCurrentThread, but set the "daemon" flag.
3387 */
3388static jint AttachCurrentThreadAsDaemon(JavaVM* vm, JNIEnv** p_env,
3389 void* thr_args)
3390{
3391 return attachThread(vm, p_env, thr_args, true);
3392}
3393
3394/*
3395 * Dissociate the current thread from the VM.
3396 */
3397static jint DetachCurrentThread(JavaVM* vm)
3398{
3399 Thread* self = dvmThreadSelf();
3400
3401 if (self == NULL) /* not attached, can't do anything */
3402 return JNI_ERR;
3403
3404 /* switch to "running" to check for suspension */
3405 dvmChangeStatus(self, THREAD_RUNNING);
3406
3407 /* detach the thread */
3408 dvmDetachCurrentThread();
3409
3410 /* (no need to change status back -- we have no status) */
3411 return JNI_OK;
3412}
3413
3414/*
3415 * If current thread is attached to VM, return the associated JNIEnv.
3416 * Otherwise, stuff NULL in and return JNI_EDETACHED.
3417 *
3418 * JVMTI overloads this by specifying a magic value for "version", so we
3419 * do want to check that here.
3420 */
3421static jint GetEnv(JavaVM* vm, void** env, jint version)
3422{
3423 Thread* self = dvmThreadSelf();
3424
3425 if (version < JNI_VERSION_1_1 || version > JNI_VERSION_1_6)
3426 return JNI_EVERSION;
3427
3428 if (self == NULL) {
3429 *env = NULL;
3430 } else {
3431 /* TODO: status change is probably unnecessary */
3432 dvmChangeStatus(self, THREAD_RUNNING);
3433 *env = (void*) dvmGetThreadJNIEnv(self);
3434 dvmChangeStatus(self, THREAD_NATIVE);
3435 }
3436 if (*env == NULL)
3437 return JNI_EDETACHED;
3438 else
3439 return JNI_OK;
3440}
3441
3442/*
3443 * Destroy the VM. This may be called from any thread.
3444 *
3445 * If the current thread is attached, wait until the current thread is
3446 * the only non-daemon user-level thread. If the current thread is not
3447 * attached, we attach it and do the processing as usual. (If the attach
3448 * fails, it's probably because all the non-daemon threads have already
3449 * exited and the VM doesn't want to let us back in.)
3450 *
3451 * TODO: we don't really deal with the situation where more than one thread
3452 * has called here. One thread wins, the other stays trapped waiting on
3453 * the condition variable forever. Not sure this situation is interesting
3454 * in real life.
3455 */
3456static jint DestroyJavaVM(JavaVM* vm)
3457{
3458 JavaVMExt* ext = (JavaVMExt*) vm;
3459 Thread* self;
3460
3461 if (ext == NULL)
3462 return JNI_ERR;
3463
3464 LOGD("DestroyJavaVM waiting for non-daemon threads to exit\n");
3465
3466 /*
3467 * Sleep on a condition variable until it's okay to exit.
3468 */
3469 self = dvmThreadSelf();
3470 if (self == NULL) {
3471 JNIEnv* tmpEnv;
3472 if (AttachCurrentThread(vm, &tmpEnv, NULL) != JNI_OK) {
3473 LOGV("Unable to reattach main for Destroy; assuming VM is "
3474 "shutting down (count=%d)\n",
3475 gDvm.nonDaemonThreadCount);
3476 goto shutdown;
3477 } else {
3478 LOGV("Attached to wait for shutdown in Destroy\n");
3479 }
3480 }
3481 dvmChangeStatus(self, THREAD_VMWAIT);
3482
3483 dvmLockThreadList(self);
3484 gDvm.nonDaemonThreadCount--; // remove current thread from count
3485
3486 while (gDvm.nonDaemonThreadCount > 0)
3487 pthread_cond_wait(&gDvm.vmExitCond, &gDvm.threadListLock);
3488
3489 dvmUnlockThreadList();
3490 self = NULL;
3491
3492shutdown:
3493 // TODO: call System.exit() to run any registered shutdown hooks
3494 // (this may not return -- figure out how this should work)
3495
3496 LOGD("DestroyJavaVM shutting VM down\n");
3497 dvmShutdown();
3498
3499 // TODO - free resources associated with JNI-attached daemon threads
3500 free(ext->envList);
3501 free(ext);
3502
3503 return JNI_OK;
3504}
3505
3506
3507/*
3508 * ===========================================================================
3509 * Function tables
3510 * ===========================================================================
3511 */
3512
3513static const struct JNINativeInterface gNativeInterface = {
3514 NULL,
3515 NULL,
3516 NULL,
3517 NULL,
3518
3519 GetVersion,
3520
3521 DefineClass,
3522 FindClass,
3523
3524 FromReflectedMethod,
3525 FromReflectedField,
3526 ToReflectedMethod,
3527
3528 GetSuperclass,
3529 IsAssignableFrom,
3530
3531 ToReflectedField,
3532
3533 Throw,
3534 ThrowNew,
3535 ExceptionOccurred,
3536 ExceptionDescribe,
3537 ExceptionClear,
3538 FatalError,
3539
3540 PushLocalFrame,
3541 PopLocalFrame,
3542
3543 NewGlobalRef,
3544 DeleteGlobalRef,
3545 DeleteLocalRef,
3546 IsSameObject,
3547 NewLocalRef,
3548 EnsureLocalCapacity,
3549
3550 AllocObject,
3551 NewObject,
3552 NewObjectV,
3553 NewObjectA,
3554
3555 GetObjectClass,
3556 IsInstanceOf,
3557
3558 GetMethodID,
3559
3560 CallObjectMethod,
3561 CallObjectMethodV,
3562 CallObjectMethodA,
3563 CallBooleanMethod,
3564 CallBooleanMethodV,
3565 CallBooleanMethodA,
3566 CallByteMethod,
3567 CallByteMethodV,
3568 CallByteMethodA,
3569 CallCharMethod,
3570 CallCharMethodV,
3571 CallCharMethodA,
3572 CallShortMethod,
3573 CallShortMethodV,
3574 CallShortMethodA,
3575 CallIntMethod,
3576 CallIntMethodV,
3577 CallIntMethodA,
3578 CallLongMethod,
3579 CallLongMethodV,
3580 CallLongMethodA,
3581 CallFloatMethod,
3582 CallFloatMethodV,
3583 CallFloatMethodA,
3584 CallDoubleMethod,
3585 CallDoubleMethodV,
3586 CallDoubleMethodA,
3587 CallVoidMethod,
3588 CallVoidMethodV,
3589 CallVoidMethodA,
3590
3591 CallNonvirtualObjectMethod,
3592 CallNonvirtualObjectMethodV,
3593 CallNonvirtualObjectMethodA,
3594 CallNonvirtualBooleanMethod,
3595 CallNonvirtualBooleanMethodV,
3596 CallNonvirtualBooleanMethodA,
3597 CallNonvirtualByteMethod,
3598 CallNonvirtualByteMethodV,
3599 CallNonvirtualByteMethodA,
3600 CallNonvirtualCharMethod,
3601 CallNonvirtualCharMethodV,
3602 CallNonvirtualCharMethodA,
3603 CallNonvirtualShortMethod,
3604 CallNonvirtualShortMethodV,
3605 CallNonvirtualShortMethodA,
3606 CallNonvirtualIntMethod,
3607 CallNonvirtualIntMethodV,
3608 CallNonvirtualIntMethodA,
3609 CallNonvirtualLongMethod,
3610 CallNonvirtualLongMethodV,
3611 CallNonvirtualLongMethodA,
3612 CallNonvirtualFloatMethod,
3613 CallNonvirtualFloatMethodV,
3614 CallNonvirtualFloatMethodA,
3615 CallNonvirtualDoubleMethod,
3616 CallNonvirtualDoubleMethodV,
3617 CallNonvirtualDoubleMethodA,
3618 CallNonvirtualVoidMethod,
3619 CallNonvirtualVoidMethodV,
3620 CallNonvirtualVoidMethodA,
3621
3622 GetFieldID,
3623
3624 GetObjectField,
3625 GetBooleanField,
3626 GetByteField,
3627 GetCharField,
3628 GetShortField,
3629 GetIntField,
3630 GetLongField,
3631 GetFloatField,
3632 GetDoubleField,
3633 SetObjectField,
3634 SetBooleanField,
3635 SetByteField,
3636 SetCharField,
3637 SetShortField,
3638 SetIntField,
3639 SetLongField,
3640 SetFloatField,
3641 SetDoubleField,
3642
3643 GetStaticMethodID,
3644
3645 CallStaticObjectMethod,
3646 CallStaticObjectMethodV,
3647 CallStaticObjectMethodA,
3648 CallStaticBooleanMethod,
3649 CallStaticBooleanMethodV,
3650 CallStaticBooleanMethodA,
3651 CallStaticByteMethod,
3652 CallStaticByteMethodV,
3653 CallStaticByteMethodA,
3654 CallStaticCharMethod,
3655 CallStaticCharMethodV,
3656 CallStaticCharMethodA,
3657 CallStaticShortMethod,
3658 CallStaticShortMethodV,
3659 CallStaticShortMethodA,
3660 CallStaticIntMethod,
3661 CallStaticIntMethodV,
3662 CallStaticIntMethodA,
3663 CallStaticLongMethod,
3664 CallStaticLongMethodV,
3665 CallStaticLongMethodA,
3666 CallStaticFloatMethod,
3667 CallStaticFloatMethodV,
3668 CallStaticFloatMethodA,
3669 CallStaticDoubleMethod,
3670 CallStaticDoubleMethodV,
3671 CallStaticDoubleMethodA,
3672 CallStaticVoidMethod,
3673 CallStaticVoidMethodV,
3674 CallStaticVoidMethodA,
3675
3676 GetStaticFieldID,
3677
3678 GetStaticObjectField,
3679 GetStaticBooleanField,
3680 GetStaticByteField,
3681 GetStaticCharField,
3682 GetStaticShortField,
3683 GetStaticIntField,
3684 GetStaticLongField,
3685 GetStaticFloatField,
3686 GetStaticDoubleField,
3687
3688 SetStaticObjectField,
3689 SetStaticBooleanField,
3690 SetStaticByteField,
3691 SetStaticCharField,
3692 SetStaticShortField,
3693 SetStaticIntField,
3694 SetStaticLongField,
3695 SetStaticFloatField,
3696 SetStaticDoubleField,
3697
3698 NewString,
3699
3700 GetStringLength,
3701 GetStringChars,
3702 ReleaseStringChars,
3703
3704 NewStringUTF,
3705 GetStringUTFLength,
3706 GetStringUTFChars,
3707 ReleaseStringUTFChars,
3708
3709 GetArrayLength,
3710 NewObjectArray,
3711 GetObjectArrayElement,
3712 SetObjectArrayElement,
3713
3714 NewBooleanArray,
3715 NewByteArray,
3716 NewCharArray,
3717 NewShortArray,
3718 NewIntArray,
3719 NewLongArray,
3720 NewFloatArray,
3721 NewDoubleArray,
3722
3723 GetBooleanArrayElements,
3724 GetByteArrayElements,
3725 GetCharArrayElements,
3726 GetShortArrayElements,
3727 GetIntArrayElements,
3728 GetLongArrayElements,
3729 GetFloatArrayElements,
3730 GetDoubleArrayElements,
3731
3732 ReleaseBooleanArrayElements,
3733 ReleaseByteArrayElements,
3734 ReleaseCharArrayElements,
3735 ReleaseShortArrayElements,
3736 ReleaseIntArrayElements,
3737 ReleaseLongArrayElements,
3738 ReleaseFloatArrayElements,
3739 ReleaseDoubleArrayElements,
3740
3741 GetBooleanArrayRegion,
3742 GetByteArrayRegion,
3743 GetCharArrayRegion,
3744 GetShortArrayRegion,
3745 GetIntArrayRegion,
3746 GetLongArrayRegion,
3747 GetFloatArrayRegion,
3748 GetDoubleArrayRegion,
3749 SetBooleanArrayRegion,
3750 SetByteArrayRegion,
3751 SetCharArrayRegion,
3752 SetShortArrayRegion,
3753 SetIntArrayRegion,
3754 SetLongArrayRegion,
3755 SetFloatArrayRegion,
3756 SetDoubleArrayRegion,
3757
3758 RegisterNatives,
3759 UnregisterNatives,
3760
3761 MonitorEnter,
3762 MonitorExit,
3763
3764 GetJavaVM,
3765
3766 GetStringRegion,
3767 GetStringUTFRegion,
3768
3769 GetPrimitiveArrayCritical,
3770 ReleasePrimitiveArrayCritical,
3771
3772 GetStringCritical,
3773 ReleaseStringCritical,
3774
3775 NewWeakGlobalRef,
3776 DeleteWeakGlobalRef,
3777
3778 ExceptionCheck,
3779
3780 NewDirectByteBuffer,
3781 GetDirectBufferAddress,
3782 GetDirectBufferCapacity,
3783
3784 GetObjectRefType
3785};
3786static const struct JNIInvokeInterface gInvokeInterface = {
3787 NULL,
3788 NULL,
3789 NULL,
3790
3791 DestroyJavaVM,
3792 AttachCurrentThread,
3793 DetachCurrentThread,
3794
3795 GetEnv,
3796
3797 AttachCurrentThreadAsDaemon,
3798};
3799
3800
3801/*
3802 * ===========================================================================
3803 * VM/Env creation
3804 * ===========================================================================
3805 */
3806
3807/*
3808 * Enable "checked JNI" after the VM has partially started. This must
3809 * only be called in "zygote" mode, when we have one thread running.
Andy McFadden59b61772009-05-13 16:44:34 -07003810 *
3811 * This doesn't attempt to rewrite the JNI call bridge associated with
3812 * native methods, so we won't get those checks for any methods that have
3813 * already been resolved.
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003814 */
3815void dvmLateEnableCheckedJni(void)
3816{
3817 JNIEnvExt* extEnv;
3818 JavaVMExt* extVm;
Andy McFaddenab00d452009-08-19 07:21:41 -07003819
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08003820 extEnv = dvmGetJNIEnvForThread();
3821 if (extEnv == NULL) {
3822 LOGE("dvmLateEnableCheckedJni: thread has no JNIEnv\n");
3823 return;
3824 }
3825 extVm = extEnv->vm;
3826 assert(extVm != NULL);
3827
3828 if (!extVm->useChecked) {
3829 LOGD("Late-enabling CheckJNI\n");
3830 dvmUseCheckedJniVm(extVm);
3831 extVm->useChecked = true;
3832 dvmUseCheckedJniEnv(extEnv);
3833
3834 /* currently no way to pick up jniopts features */
3835 } else {
3836 LOGD("Not late-enabling CheckJNI (already on)\n");
3837 }
3838}
3839
3840/*
3841 * Not supported.
3842 */
3843jint JNI_GetDefaultJavaVMInitArgs(void* vm_args)
3844{
3845 return JNI_ERR;
3846}
3847
3848/*
3849 * Return a buffer full of created VMs.
3850 *
3851 * We always have zero or one.
3852 */
3853jint JNI_GetCreatedJavaVMs(JavaVM** vmBuf, jsize bufLen, jsize* nVMs)
3854{
3855 if (gDvm.vmList != NULL) {
3856 *nVMs = 1;
3857
3858 if (bufLen > 0)
3859 *vmBuf++ = gDvm.vmList;
3860 } else {
3861 *nVMs = 0;
3862 }
3863
3864 return JNI_OK;
3865}
3866
3867
3868/*
3869 * Create a new VM instance.
3870 *
3871 * The current thread becomes the main VM thread. We return immediately,
3872 * which effectively means the caller is executing in a native method.
3873 */
3874jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args)
3875{
3876 const JavaVMInitArgs* args = (JavaVMInitArgs*) vm_args;
3877 JNIEnvExt* pEnv = NULL;
3878 JavaVMExt* pVM = NULL;
3879 const char** argv;
3880 int argc = 0;
3881 int i, curOpt;
3882 int result = JNI_ERR;
3883 bool checkJni = false;
3884 bool warnError = true;
3885 bool forceDataCopy = false;
3886
3887 if (args->version < JNI_VERSION_1_2)
3888 return JNI_EVERSION;
3889
3890 // TODO: don't allow creation of multiple VMs -- one per customer for now
3891
3892 /* zero globals; not strictly necessary the first time a VM is started */
3893 memset(&gDvm, 0, sizeof(gDvm));
3894
3895 /*
3896 * Set up structures for JNIEnv and VM.
3897 */
3898 //pEnv = (JNIEnvExt*) malloc(sizeof(JNIEnvExt));
3899 pVM = (JavaVMExt*) malloc(sizeof(JavaVMExt));
3900
3901 //memset(pEnv, 0, sizeof(JNIEnvExt));
3902 //pEnv->funcTable = &gNativeInterface;
3903 //pEnv->vm = pVM;
3904 memset(pVM, 0, sizeof(JavaVMExt));
3905 pVM->funcTable = &gInvokeInterface;
3906 pVM->envList = pEnv;
3907 dvmInitMutex(&pVM->envListLock);
3908
3909 argv = (const char**) malloc(sizeof(char*) * (args->nOptions));
3910 memset(argv, 0, sizeof(char*) * (args->nOptions));
3911
3912 curOpt = 0;
3913
3914 /*
3915 * Convert JNI args to argv.
3916 *
3917 * We have to pull out vfprintf/exit/abort, because they use the
3918 * "extraInfo" field to pass function pointer "hooks" in. We also
3919 * look for the -Xcheck:jni stuff here.
3920 */
3921 for (i = 0; i < args->nOptions; i++) {
3922 const char* optStr = args->options[i].optionString;
3923
3924 if (optStr == NULL) {
3925 fprintf(stderr, "ERROR: arg %d string was null\n", i);
3926 goto bail;
3927 } else if (strcmp(optStr, "vfprintf") == 0) {
3928 gDvm.vfprintfHook = args->options[i].extraInfo;
3929 } else if (strcmp(optStr, "exit") == 0) {
3930 gDvm.exitHook = args->options[i].extraInfo;
3931 } else if (strcmp(optStr, "abort") == 0) {
3932 gDvm.abortHook = args->options[i].extraInfo;
3933 } else if (strcmp(optStr, "-Xcheck:jni") == 0) {
3934 checkJni = true;
3935 } else if (strncmp(optStr, "-Xjniopts:", 10) == 0) {
3936 const char* jniOpts = optStr + 9;
3937 while (jniOpts != NULL) {
3938 jniOpts++; /* skip past ':' or ',' */
3939 if (strncmp(jniOpts, "warnonly", 8) == 0) {
3940 warnError = false;
3941 } else if (strncmp(jniOpts, "forcecopy", 9) == 0) {
3942 forceDataCopy = true;
3943 } else {
3944 LOGW("unknown jni opt starting at '%s'\n", jniOpts);
3945 }
3946 jniOpts = strchr(jniOpts, ',');
3947 }
3948 } else {
3949 /* regular option */
3950 argv[curOpt++] = optStr;
3951 }
3952 }
3953 argc = curOpt;
3954
3955 if (checkJni) {
3956 dvmUseCheckedJniVm(pVM);
3957 pVM->useChecked = true;
3958 }
3959 pVM->warnError = warnError;
3960 pVM->forceDataCopy = forceDataCopy;
3961
3962 /* set this up before initializing VM, so it can create some JNIEnvs */
3963 gDvm.vmList = (JavaVM*) pVM;
3964
3965 /*
3966 * Create an env for main thread. We need to have something set up
3967 * here because some of the class initialization we do when starting
3968 * up the VM will call into native code.
3969 */
3970 pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL);
3971
3972 /* initialize VM */
3973 gDvm.initializing = true;
3974 if (dvmStartup(argc, argv, args->ignoreUnrecognized, (JNIEnv*)pEnv) != 0) {
3975 free(pEnv);
3976 free(pVM);
3977 goto bail;
3978 }
3979
3980 /*
3981 * Success! Return stuff to caller.
3982 */
3983 dvmChangeStatus(NULL, THREAD_NATIVE);
3984 *p_env = (JNIEnv*) pEnv;
3985 *p_vm = (JavaVM*) pVM;
3986 result = JNI_OK;
3987
3988bail:
3989 gDvm.initializing = false;
3990 if (result == JNI_OK)
3991 LOGV("JNI_CreateJavaVM succeeded\n");
3992 else
3993 LOGW("JNI_CreateJavaVM failed\n");
3994 free(argv);
3995 return result;
3996}
3997