OSDN Git Service

DexOptions should default targetApiLevel to API_NO_EXTENDED_OPCODES
[android-x86/dalvik.git] / libnativehelper / JNIHelp.cpp
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 #define LOG_TAG "JNIHelp"
18
19 #include "JNIHelp.h"
20 #include "cutils/log.h"
21
22 #include <stdlib.h>
23 #include <string.h>
24 #include <assert.h>
25
26 /**
27  * Equivalent to ScopedLocalRef, but for C_JNIEnv instead. (And slightly more powerful.)
28  */
29 template<typename T>
30 class scoped_local_ref {
31 public:
32     scoped_local_ref(C_JNIEnv* env, T localRef = NULL)
33     : mEnv(env), mLocalRef(localRef)
34     {
35     }
36
37     ~scoped_local_ref() {
38         reset();
39     }
40
41     void reset(T localRef = NULL) {
42         if (mLocalRef != NULL) {
43             (*mEnv)->DeleteLocalRef(reinterpret_cast<JNIEnv*>(mEnv), mLocalRef);
44             mLocalRef = localRef;
45         }
46     }
47
48     T get() const {
49         return mLocalRef;
50     }
51
52 private:
53     C_JNIEnv* mEnv;
54     T mLocalRef;
55
56     // Disallow copy and assignment.
57     scoped_local_ref(const scoped_local_ref&);
58     void operator=(const scoped_local_ref&);
59 };
60
61 static jclass findClass(C_JNIEnv* env, const char* className) {
62     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
63     return (*env)->FindClass(e, className);
64 }
65
66 extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
67     const JNINativeMethod* gMethods, int numMethods)
68 {
69     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
70
71     ALOGV("Registering %s natives", className);
72
73     scoped_local_ref<jclass> c(env, findClass(env, className));
74     if (c.get() == NULL) {
75         LOGE("Native registration unable to find class '%s', aborting", className);
76         abort();
77     }
78
79     if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
80         LOGE("RegisterNatives failed for '%s', aborting", className);
81         abort();
82     }
83
84     return 0;
85 }
86
87 /*
88  * Returns a human-readable summary of an exception object.  The buffer will
89  * be populated with the "binary" class name and, if present, the
90  * exception message.
91  */
92 static char* getExceptionSummary0(C_JNIEnv* env, jthrowable exception) {
93     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
94
95     /* get the name of the exception's class */
96     scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
97     scoped_local_ref<jclass> classClass(env,
98             (*env)->GetObjectClass(e, exceptionClass.get())); // java.lang.Class, can't fail
99     jmethodID classGetNameMethod =
100             (*env)->GetMethodID(e, classClass.get(), "getName", "()Ljava/lang/String;");
101     scoped_local_ref<jstring> classNameStr(env,
102             (jstring) (*env)->CallObjectMethod(e, exceptionClass.get(), classGetNameMethod));
103     if (classNameStr.get() == NULL) {
104         return NULL;
105     }
106
107     /* get printable string */
108     const char* classNameChars = (*env)->GetStringUTFChars(e, classNameStr.get(), NULL);
109     if (classNameChars == NULL) {
110         return NULL;
111     }
112
113     /* if the exception has a detail message, get that */
114     jmethodID getMessage =
115             (*env)->GetMethodID(e, exceptionClass.get(), "getMessage", "()Ljava/lang/String;");
116     scoped_local_ref<jstring> messageStr(env,
117             (jstring) (*env)->CallObjectMethod(e, exception, getMessage));
118     if (messageStr.get() == NULL) {
119         return strdup(classNameChars);
120     }
121
122     char* result = NULL;
123     const char* messageChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
124     if (messageChars != NULL) {
125         asprintf(&result, "%s: %s", classNameChars, messageChars);
126         (*env)->ReleaseStringUTFChars(e, messageStr.get(), messageChars);
127     } else {
128         (*env)->ExceptionClear(e); // clear OOM
129         asprintf(&result, "%s: <error getting message>", classNameChars);
130     }
131
132     (*env)->ReleaseStringUTFChars(e, classNameStr.get(), classNameChars);
133     return result;
134 }
135
136 static char* getExceptionSummary(C_JNIEnv* env, jthrowable exception) {
137     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
138     char* result = getExceptionSummary0(env, exception);
139     if (result == NULL) {
140         (*env)->ExceptionClear(e);
141         result = strdup("<error getting class name>");
142     }
143     return result;
144 }
145
146 /*
147  * Returns an exception (with stack trace) as a string.
148  */
149 static char* getStackTrace(C_JNIEnv* env, jthrowable exception) {
150     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
151
152     scoped_local_ref<jclass> stringWriterClass(env, findClass(env, "java/io/StringWriter"));
153     if (stringWriterClass.get() == NULL) {
154         return NULL;
155     }
156
157     jmethodID stringWriterCtor = (*env)->GetMethodID(e, stringWriterClass.get(), "<init>", "()V");
158     jmethodID stringWriterToStringMethod =
159             (*env)->GetMethodID(e, stringWriterClass.get(), "toString", "()Ljava/lang/String;");
160
161     scoped_local_ref<jclass> printWriterClass(env, findClass(env, "java/io/PrintWriter"));
162     if (printWriterClass.get() == NULL) {
163         return NULL;
164     }
165
166     jmethodID printWriterCtor =
167             (*env)->GetMethodID(e, printWriterClass.get(), "<init>", "(Ljava/io/Writer;)V");
168
169     scoped_local_ref<jobject> stringWriter(env,
170             (*env)->NewObject(e, stringWriterClass.get(), stringWriterCtor));
171     if (stringWriter.get() == NULL) {
172         return NULL;
173     }
174
175     jobject printWriter =
176             (*env)->NewObject(e, printWriterClass.get(), printWriterCtor, stringWriter.get());
177     if (printWriter == NULL) {
178         return NULL;
179     }
180
181     scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
182     jmethodID printStackTraceMethod =
183             (*env)->GetMethodID(e, exceptionClass.get(), "printStackTrace", "(Ljava/io/PrintWriter;)V");
184     (*env)->CallVoidMethod(e, exception, printStackTraceMethod, printWriter);
185
186     if ((*env)->ExceptionCheck(e)) {
187         return NULL;
188     }
189
190     scoped_local_ref<jstring> messageStr(env,
191             (jstring) (*env)->CallObjectMethod(e, stringWriter.get(), stringWriterToStringMethod));
192     if (messageStr.get() == NULL) {
193         return NULL;
194     }
195
196     const char* utfChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
197     if (utfChars == NULL) {
198         return NULL;
199     }
200
201     char* result = strdup(utfChars);
202     (*env)->ReleaseStringUTFChars(e, messageStr.get(), utfChars);
203     return result;
204 }
205
206 extern "C" int jniThrowException(C_JNIEnv* env, const char* className, const char* msg) {
207     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
208
209     if ((*env)->ExceptionCheck(e)) {
210         /* TODO: consider creating the new exception with this as "cause" */
211         scoped_local_ref<jthrowable> exception(env, (*env)->ExceptionOccurred(e));
212         (*env)->ExceptionClear(e);
213
214         if (exception.get() != NULL) {
215             char* text = getExceptionSummary(env, exception.get());
216             LOGW("Discarding pending exception (%s) to throw %s", text, className);
217             free(text);
218         }
219     }
220
221     scoped_local_ref<jclass> exceptionClass(env, findClass(env, className));
222     if (exceptionClass.get() == NULL) {
223         LOGE("Unable to find exception class %s", className);
224         /* ClassNotFoundException now pending */
225         return -1;
226     }
227
228     if ((*env)->ThrowNew(e, exceptionClass.get(), msg) != JNI_OK) {
229         LOGE("Failed throwing '%s' '%s'", className, msg);
230         /* an exception, most likely OOM, will now be pending */
231         return -1;
232     }
233
234     return 0;
235 }
236
237 int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args) {
238     char msgBuf[512];
239     vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
240     return jniThrowException(env, className, msgBuf);
241 }
242
243 int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) {
244     return jniThrowException(env, "java/lang/NullPointerException", msg);
245 }
246
247 int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) {
248     return jniThrowException(env, "java/lang/RuntimeException", msg);
249 }
250
251 int jniThrowIOException(C_JNIEnv* env, int errnum) {
252     char buffer[80];
253     const char* message = jniStrError(errnum, buffer, sizeof(buffer));
254     return jniThrowException(env, "java/io/IOException", message);
255 }
256
257 void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception) {
258     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
259
260     scoped_local_ref<jthrowable> currentException(env);
261     if (exception == NULL) {
262         exception = (*env)->ExceptionOccurred(e);
263         if (exception == NULL) {
264             return;
265         }
266
267         (*env)->ExceptionClear(e);
268         currentException.reset(exception);
269     }
270
271     char* buffer = getStackTrace(env, exception);
272     if (buffer == NULL) {
273         (*env)->ExceptionClear(e);
274         buffer = getExceptionSummary(env, exception);
275     }
276
277     __android_log_write(priority, tag, buffer);
278     free(buffer);
279
280     if (currentException.get() != NULL) {
281         (*env)->Throw(e, exception); // rethrow
282     }
283 }
284
285 const char* jniStrError(int errnum, char* buf, size_t buflen) {
286     // Note: glibc has a nonstandard strerror_r that returns char* rather than POSIX's int.
287     // char *strerror_r(int errnum, char *buf, size_t n);
288     char* ret = (char*) strerror_r(errnum, buf, buflen);
289     if (((int)ret) == 0) {
290         // POSIX strerror_r, success
291         return buf;
292     } else if (((int)ret) == -1) {
293         // POSIX strerror_r, failure
294         // (Strictly, POSIX only guarantees a value other than 0. The safest
295         // way to implement this function is to use C++ and overload on the
296         // type of strerror_r to accurately distinguish GNU from POSIX. But
297         // realistic implementations will always return -1.)
298         snprintf(buf, buflen, "errno %d", errnum);
299         return buf;
300     } else {
301         // glibc strerror_r returning a string
302         return ret;
303     }
304 }
305
306 static struct CachedFields {
307     jclass fileDescriptorClass;
308     jmethodID fileDescriptorCtor;
309     jfieldID descriptorField;
310 } gCachedFields;
311
312 int registerJniHelp(JNIEnv* env) {
313     gCachedFields.fileDescriptorClass =
314             reinterpret_cast<jclass>(env->NewGlobalRef(env->FindClass("java/io/FileDescriptor")));
315     if (gCachedFields.fileDescriptorClass == NULL) {
316         return -1;
317     }
318
319     gCachedFields.fileDescriptorCtor =
320             env->GetMethodID(gCachedFields.fileDescriptorClass, "<init>", "()V");
321     if (gCachedFields.fileDescriptorCtor == NULL) {
322         return -1;
323     }
324
325     gCachedFields.descriptorField =
326             env->GetFieldID(gCachedFields.fileDescriptorClass, "descriptor", "I");
327     if (gCachedFields.descriptorField == NULL) {
328         return -1;
329     }
330
331     return 0;
332 }
333
334 jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) {
335     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
336     jobject fileDescriptor = (*env)->NewObject(e,
337             gCachedFields.fileDescriptorClass, gCachedFields.fileDescriptorCtor);
338     jniSetFileDescriptorOfFD(env, fileDescriptor, fd);
339     return fileDescriptor;
340 }
341
342 int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) {
343     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
344     return (*env)->GetIntField(e, fileDescriptor, gCachedFields.descriptorField);
345 }
346
347 void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value) {
348     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
349     (*env)->SetIntField(e, fileDescriptor, gCachedFields.descriptorField, value);
350 }
351
352 /*
353  * DO NOT USE THIS FUNCTION
354  *
355  * Get a pointer to the elements of a non-movable array.
356  *
357  * The semantics are similar to GetDirectBufferAddress.  Specifically, the VM
358  * guarantees that the array will not move, and the caller must ensure that
359  * it does not continue to use the pointer after the object is collected.
360  *
361  * We currently use an illegal sequence that trips up CheckJNI when
362  * the "forcecopy" mode is enabled.  We pass in a magic value to work
363  * around the problem.
364  *
365  * Returns NULL if the array is movable.
366  */
367 #define kNoCopyMagic 0xd5aab57f     /* also in CheckJni.c */
368 extern "C" jbyte* jniGetNonMovableArrayElements(C_JNIEnv* env, jarray arrayObj) {
369     JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
370
371     jbyteArray byteArray = reinterpret_cast<jbyteArray>(arrayObj);
372
373     /*
374      * Normally the "isCopy" parameter is for a return value only, so the
375      * non-CheckJNI VM will ignore whatever we pass in.
376      */
377     uint32_t noCopy = kNoCopyMagic;
378     jbyte* result = (*env)->GetByteArrayElements(e, byteArray, reinterpret_cast<jboolean*>(&noCopy));
379
380     /*
381      * The non-CheckJNI implementation only cares about the array object,
382      * so we can replace the element pointer with the magic value.
383      */
384     (*env)->ReleaseByteArrayElements(e, byteArray, reinterpret_cast<jbyte*>(kNoCopyMagic), 0);
385     return result;
386 }