OSDN Git Service

am af242824: Revert "Fix a type error in the allocation of non-moving arrays." per...
[android-x86/dalvik.git] / libnativehelper / JNIHelp.c
1 /*
2  * Copyright (C) 2006 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 /*
18  * JNI helper functions.
19  */
20 #define LOG_TAG "JNIHelp"
21 #include "JNIHelp.h"
22 #include "utils/Log.h"
23
24 #include <stdlib.h>
25 #include <string.h>
26 #include <assert.h>
27
28 /*
29  * Register native JNI-callable methods.
30  *
31  * "className" looks like "java/lang/String".
32  */
33 int jniRegisterNativeMethods(JNIEnv* env, const char* className,
34     const JNINativeMethod* gMethods, int numMethods)
35 {
36     jclass clazz;
37
38     LOGV("Registering %s natives\n", className);
39     clazz = (*env)->FindClass(env, className);
40     if (clazz == NULL) {
41         LOGE("Native registration unable to find class '%s', aborting\n",
42             className);
43         abort();
44     }
45
46     if ((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0) {
47         LOGE("RegisterNatives failed for '%s', aborting\n", className);
48         abort();
49     }
50
51     (*env)->DeleteLocalRef(env, clazz);
52     return 0;
53 }
54
55 /*
56  * Get a human-readable summary of an exception object.  The buffer will
57  * be populated with the "binary" class name and, if present, the
58  * exception message.
59  */
60 static void getExceptionSummary(JNIEnv* env, jthrowable exception, char* buf, size_t bufLen)
61 {
62     int success = 0;
63
64     /* get the name of the exception's class */
65     jclass exceptionClazz = (*env)->GetObjectClass(env, exception); // can't fail
66     jclass classClazz = (*env)->GetObjectClass(env, exceptionClazz); // java.lang.Class, can't fail
67     jmethodID classGetNameMethod = (*env)->GetMethodID(
68             env, classClazz, "getName", "()Ljava/lang/String;");
69     jstring classNameStr = (*env)->CallObjectMethod(env, exceptionClazz, classGetNameMethod);
70     if (classNameStr != NULL) {
71         /* get printable string */
72         const char* classNameChars = (*env)->GetStringUTFChars(env, classNameStr, NULL);
73         if (classNameChars != NULL) {
74             /* if the exception has a message string, get that */
75             jmethodID throwableGetMessageMethod = (*env)->GetMethodID(
76                     env, exceptionClazz, "getMessage", "()Ljava/lang/String;");
77             jstring messageStr = (*env)->CallObjectMethod(
78                     env, exception, throwableGetMessageMethod);
79
80             if (messageStr != NULL) {
81                 const char* messageChars = (*env)->GetStringUTFChars(env, messageStr, NULL);
82                 if (messageChars != NULL) {
83                     snprintf(buf, bufLen, "%s: %s", classNameChars, messageChars);
84                     (*env)->ReleaseStringUTFChars(env, messageStr, messageChars);
85                 } else {
86                     (*env)->ExceptionClear(env); // clear OOM
87                     snprintf(buf, bufLen, "%s: <error getting message>", classNameChars);
88                 }
89                 (*env)->DeleteLocalRef(env, messageStr);
90             } else {
91                 strncpy(buf, classNameChars, bufLen);
92                 buf[bufLen - 1] = '\0';
93             }
94
95             (*env)->ReleaseStringUTFChars(env, classNameStr, classNameChars);
96             success = 1;
97         }
98         (*env)->DeleteLocalRef(env, classNameStr);
99     }
100     (*env)->DeleteLocalRef(env, classClazz);
101     (*env)->DeleteLocalRef(env, exceptionClazz);
102
103     if (! success) {
104         (*env)->ExceptionClear(env);
105         snprintf(buf, bufLen, "%s", "<error getting class name>");
106     }
107 }
108
109 /*
110  * Formats an exception as a string with its stack trace.
111  */
112 static void printStackTrace(JNIEnv* env, jthrowable exception, char* buf, size_t bufLen)
113 {
114     int success = 0;
115
116     jclass stringWriterClazz = (*env)->FindClass(env, "java/io/StringWriter");
117     if (stringWriterClazz != NULL) {
118         jmethodID stringWriterCtor = (*env)->GetMethodID(env, stringWriterClazz,
119                 "<init>", "()V");
120         jmethodID stringWriterToStringMethod = (*env)->GetMethodID(env, stringWriterClazz,
121                 "toString", "()Ljava/lang/String;");
122
123         jclass printWriterClazz = (*env)->FindClass(env, "java/io/PrintWriter");
124         if (printWriterClazz != NULL) {
125             jmethodID printWriterCtor = (*env)->GetMethodID(env, printWriterClazz,
126                     "<init>", "(Ljava/io/Writer;)V");
127
128             jobject stringWriterObj = (*env)->NewObject(env, stringWriterClazz, stringWriterCtor);
129             if (stringWriterObj != NULL) {
130                 jobject printWriterObj = (*env)->NewObject(env, printWriterClazz, printWriterCtor,
131                         stringWriterObj);
132                 if (printWriterObj != NULL) {
133                     jclass exceptionClazz = (*env)->GetObjectClass(env, exception); // can't fail
134                     jmethodID printStackTraceMethod = (*env)->GetMethodID(
135                             env, exceptionClazz, "printStackTrace", "(Ljava/io/PrintWriter;)V");
136
137                     (*env)->CallVoidMethod(
138                             env, exception, printStackTraceMethod, printWriterObj);
139                     if (! (*env)->ExceptionCheck(env)) {
140                         jstring messageStr = (*env)->CallObjectMethod(
141                                 env, stringWriterObj, stringWriterToStringMethod);
142                         if (messageStr != NULL) {
143                             jsize messageStrLength = (*env)->GetStringLength(env, messageStr);
144                             if (messageStrLength >= (jsize) bufLen) {
145                                 messageStrLength = bufLen - 1;
146                             }
147                             (*env)->GetStringUTFRegion(env, messageStr, 0, messageStrLength, buf);
148                             (*env)->DeleteLocalRef(env, messageStr);
149                             buf[messageStrLength] = '\0';
150                             success = 1;
151                         }
152                     }
153                     (*env)->DeleteLocalRef(env, exceptionClazz);
154                     (*env)->DeleteLocalRef(env, printWriterObj);
155                 }
156                 (*env)->DeleteLocalRef(env, stringWriterObj);
157             }
158             (*env)->DeleteLocalRef(env, printWriterClazz);
159         }
160         (*env)->DeleteLocalRef(env, stringWriterClazz);
161     }
162
163     if (! success) {
164         (*env)->ExceptionClear(env);
165         getExceptionSummary(env, exception, buf, bufLen);
166     }
167 }
168
169 /*
170  * Throw an exception with the specified class and an optional message.
171  *
172  * If an exception is currently pending, we log a warning message and
173  * clear it.
174  *
175  * Returns 0 if the specified exception was successfully thrown.  (Some
176  * sort of exception will always be pending when this returns.)
177  */
178 int jniThrowException(JNIEnv* env, const char* className, const char* msg)
179 {
180     jclass exceptionClass;
181
182     if ((*env)->ExceptionCheck(env)) {
183         /* TODO: consider creating the new exception with this as "cause" */
184         char buf[256];
185
186         jthrowable exception = (*env)->ExceptionOccurred(env);
187         (*env)->ExceptionClear(env);
188
189         if (exception != NULL) {
190             getExceptionSummary(env, exception, buf, sizeof(buf));
191             LOGW("Discarding pending exception (%s) to throw %s\n", buf, className);
192             (*env)->DeleteLocalRef(env, exception);
193         }
194     }
195
196     exceptionClass = (*env)->FindClass(env, className);
197     if (exceptionClass == NULL) {
198         LOGE("Unable to find exception class %s\n", className);
199         /* ClassNotFoundException now pending */
200         return -1;
201     }
202
203     int result = 0;
204     if ((*env)->ThrowNew(env, exceptionClass, msg) != JNI_OK) {
205         LOGE("Failed throwing '%s' '%s'\n", className, msg);
206         /* an exception, most likely OOM, will now be pending */
207         result = -1;
208     }
209
210     (*env)->DeleteLocalRef(env, exceptionClass);
211     return result;
212 }
213
214 /*
215  * Throw a java.lang.NullPointerException, with an optional message.
216  */
217 int jniThrowNullPointerException(JNIEnv* env, const char* msg)
218 {
219     return jniThrowException(env, "java/lang/NullPointerException", msg);
220 }
221
222 /*
223  * Throw a java.lang.RuntimeException, with an optional message.
224  */
225 int jniThrowRuntimeException(JNIEnv* env, const char* msg)
226 {
227     return jniThrowException(env, "java/lang/RuntimeException", msg);
228 }
229
230 /*
231  * Throw a java.io.IOException, generating the message from errno.
232  */
233 int jniThrowIOException(JNIEnv* env, int errnum)
234 {
235     char buffer[80];
236     const char* message = jniStrError(errnum, buffer, sizeof(buffer));
237     return jniThrowException(env, "java/io/IOException", message);
238 }
239
240 /*
241  * Log an exception.
242  * If exception is NULL, logs the current exception in the JNI environment, if any.
243  */
244 void jniLogException(JNIEnv* env, int priority, const char* tag, jthrowable exception)
245 {
246     int currentException = 0;
247     if (exception == NULL) {
248         exception = (*env)->ExceptionOccurred(env);
249         if (exception == NULL) {
250             return;
251         }
252
253         (*env)->ExceptionClear(env);
254         currentException = 1;
255     }
256
257     char buffer[1024];
258     printStackTrace(env, exception, buffer, sizeof(buffer));
259     __android_log_write(priority, tag, buffer);
260
261     if (currentException) {
262         (*env)->Throw(env, exception); // rethrow
263         (*env)->DeleteLocalRef(env, exception);
264     }
265 }
266
267 const char* jniStrError(int errnum, char* buf, size_t buflen)
268 {
269     // note: glibc has a nonstandard strerror_r that returns char* rather
270     // than POSIX's int.
271     // char *strerror_r(int errnum, char *buf, size_t n);
272     char* ret = (char*) strerror_r(errnum, buf, buflen);
273     if (((int)ret) == 0) {
274         //POSIX strerror_r, success
275         return buf;
276     } else if (((int)ret) == -1) {
277         //POSIX strerror_r, failure
278         // (Strictly, POSIX only guarantees a value other than 0. The safest
279         // way to implement this function is to use C++ and overload on the
280         // type of strerror_r to accurately distinguish GNU from POSIX. But
281         // realistic implementations will always return -1.)
282         snprintf(buf, buflen, "errno %d", errnum);
283         return buf;
284     } else {
285         //glibc strerror_r returning a string
286         return ret;
287     }
288 }
289
290 static struct CachedFields {
291     jclass fileDescriptorClass;
292     jmethodID fileDescriptorCtor;
293     jfieldID descriptorField;
294 } gCachedFields;
295
296 int registerJniHelp(JNIEnv* env) {
297     gCachedFields.fileDescriptorClass =
298             (*env)->NewGlobalRef(env, (*env)->FindClass(env, "java/io/FileDescriptor"));
299     if (gCachedFields.fileDescriptorClass == NULL) {
300         return -1;
301     }
302
303     gCachedFields.fileDescriptorCtor =
304             (*env)->GetMethodID(env, gCachedFields.fileDescriptorClass, "<init>", "()V");
305     if (gCachedFields.fileDescriptorCtor == NULL) {
306         return -1;
307     }
308
309     gCachedFields.descriptorField =
310             (*env)->GetFieldID(env, gCachedFields.fileDescriptorClass, "descriptor", "I");
311     if (gCachedFields.descriptorField == NULL) {
312         return -1;
313     }
314
315     return 0;
316 }
317
318 /*
319  * Create a java.io.FileDescriptor given an integer fd
320  */
321 jobject jniCreateFileDescriptor(JNIEnv* env, int fd) {
322     jobject fileDescriptor = (*env)->NewObject(env,
323             gCachedFields.fileDescriptorClass, gCachedFields.fileDescriptorCtor);
324     jniSetFileDescriptorOfFD(env, fileDescriptor, fd);
325     return fileDescriptor;
326 }
327
328 /*
329  * Get an int file descriptor from a java.io.FileDescriptor
330  */
331 int jniGetFDFromFileDescriptor(JNIEnv* env, jobject fileDescriptor) {
332     return (*env)->GetIntField(env, fileDescriptor, gCachedFields.descriptorField);
333 }
334
335 /*
336  * Set the descriptor of a java.io.FileDescriptor
337  */
338 void jniSetFileDescriptorOfFD(JNIEnv* env, jobject fileDescriptor, int value) {
339     (*env)->SetIntField(env, fileDescriptor, gCachedFields.descriptorField, value);
340 }
341
342 /*
343  * DO NOT USE THIS FUNCTION
344  *
345  * Get a pointer to the elements of a non-movable array.
346  *
347  * The semantics are similar to GetDirectBufferAddress.  Specifically, the VM
348  * guarantees that the array will not move, and the caller must ensure that
349  * it does not continue to use the pointer after the object is collected.
350  *
351  * We currently use an illegal sequence that trips up CheckJNI when
352  * the "forcecopy" mode is enabled.  We pass in a magic value to work
353  * around the problem.
354  *
355  * Returns NULL if the array is movable.
356  */
357 jbyte* jniGetNonMovableArrayElements(JNIEnv* env, jarray arrayObj)
358 {
359 #define kNoCopyMagic 0xd5aab57f     /* also in CheckJni.c */
360
361     /*
362      * Normally the "isCopy" parameter is for a return value only, so the
363      * non-CheckJNI VM will ignore whatever we pass in.
364      */
365     uint32_t noCopy = kNoCopyMagic;
366     jbyte *addr = (*env)->GetByteArrayElements(env, arrayObj,
367             (jboolean*)&noCopy);
368
369     /*
370      * The non-CheckJNI implementation only cares about the array object,
371      * so we can replace the element pointer with the magic value.
372      */
373     (*env)->ReleaseByteArrayElements(env, arrayObj, (jbyte*) kNoCopyMagic, 0);
374     return addr;
375 }