blob: 22f95b684b9028b426f53e9f1e6e487e026c6df9 [file] [log] [blame]
Elliott Hughes87f62a82011-04-22 19:22:54 -07001/*
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
Elliott Hughes2fc9d0a2014-08-18 19:50:01 -070017#if defined(__ANDROID__)
18/* libnativehelper is built by NDK 19 in one variant, which doesn't yet have the GNU strerror_r. */
19#undef _GNU_SOURCE
Elliott Hughes1ed11b42016-06-11 16:58:34 -070020/* ...but this code uses asprintf, which is a BSD/GNU extension. */
21#define _BSD_SOURCE
Elliott Hughes2fc9d0a2014-08-18 19:50:01 -070022#endif
23
Elliott Hughes87f62a82011-04-22 19:22:54 -070024#define LOG_TAG "JNIHelp"
25
Steven Moreland3544a932017-07-19 10:26:05 -070026#include <nativehelper/JniConstants.h>
27#include <nativehelper/JNIHelp.h>
Ruben Brunka77f3a22013-09-09 02:21:31 -070028#include "ALog-priv.h"
Elliott Hughes87f62a82011-04-22 19:22:54 -070029
Ruben Brunka77f3a22013-09-09 02:21:31 -070030#include <stdio.h>
Elliott Hughes87f62a82011-04-22 19:22:54 -070031#include <stdlib.h>
32#include <string.h>
33#include <assert.h>
34
Brian Carlstromdd8af232012-05-13 23:56:07 -070035#include <string>
36
Logan Chien63e49172016-06-08 11:33:36 +080037namespace {
38
39// java.io.FileDescriptor.descriptor.
40jfieldID fileDescriptorDescriptorField = nullptr;
Josh Gao669bc9e2018-06-25 16:22:11 -070041// java.io.FileDescriptor.ownerId.
42jfieldID fileDescriptorOwnerIdField = nullptr;
Logan Chien63e49172016-06-08 11:33:36 +080043
44// void java.io.FileDescriptor.<init>().
45jmethodID fileDescriptorInitMethod = nullptr;
46// Object java.lang.ref.Reference.get()
47jmethodID referenceGetMethod = nullptr;
48
49jfieldID FindField(JNIEnv* env, jclass klass, const char* name, const char* desc) {
50 jfieldID result = env->GetFieldID(klass, name, desc);
51 if (result == NULL) {
52 ALOGV("failed to find field '%s:%s'", name, desc);
53 abort();
54 }
55 return result;
56}
57
58jmethodID FindMethod(JNIEnv* env, jclass klass, const char* name, const char* signature) {
59 jmethodID result = env->GetMethodID(klass, name, signature);
60 if (result == NULL) {
61 ALOGV("failed to find method '%s%s'", name, signature);
62 abort();
63 }
64 return result;
65}
66
67void InitFieldsAndMethods(JNIEnv* env) {
68 JniConstants::init(env); // Ensure that classes are cached.
69 fileDescriptorDescriptorField = FindField(env, JniConstants::fileDescriptorClass, "descriptor",
70 "I");
Josh Gao669bc9e2018-06-25 16:22:11 -070071 fileDescriptorOwnerIdField = FindField(env, JniConstants::fileDescriptorClass, "ownerId",
72 "J");
Logan Chien63e49172016-06-08 11:33:36 +080073 fileDescriptorInitMethod = FindMethod(env, JniConstants::fileDescriptorClass, "<init>", "()V");
74 referenceGetMethod = FindMethod(env, JniConstants::referenceClass, "get",
75 "()Ljava/lang/Object;");
76}
77
78}
79
80namespace android {
81
82void ClearJNIHelpLocalCache() {
83 fileDescriptorDescriptorField = nullptr;
84 fileDescriptorInitMethod = nullptr;
85 referenceGetMethod = nullptr;
86}
87
88}
89
Elliott Hughes87f62a82011-04-22 19:22:54 -070090/**
91 * Equivalent to ScopedLocalRef, but for C_JNIEnv instead. (And slightly more powerful.)
92 */
93template<typename T>
94class scoped_local_ref {
95public:
Chih-Hung Hsieh01332142016-05-02 11:47:43 -070096 explicit scoped_local_ref(C_JNIEnv* env, T localRef = NULL)
Elliott Hughes87f62a82011-04-22 19:22:54 -070097 : mEnv(env), mLocalRef(localRef)
98 {
99 }
100
101 ~scoped_local_ref() {
102 reset();
103 }
104
105 void reset(T localRef = NULL) {
106 if (mLocalRef != NULL) {
107 (*mEnv)->DeleteLocalRef(reinterpret_cast<JNIEnv*>(mEnv), mLocalRef);
108 mLocalRef = localRef;
109 }
110 }
111
112 T get() const {
113 return mLocalRef;
114 }
115
116private:
Ian Rogers8288dde2014-11-04 11:42:02 -0800117 C_JNIEnv* const mEnv;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700118 T mLocalRef;
119
Ian Rogers8288dde2014-11-04 11:42:02 -0800120 DISALLOW_COPY_AND_ASSIGN(scoped_local_ref);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700121};
122
123static jclass findClass(C_JNIEnv* env, const char* className) {
124 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
125 return (*env)->FindClass(e, className);
126}
127
128extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
129 const JNINativeMethod* gMethods, int numMethods)
130{
131 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
132
Brian Carlstromdd8af232012-05-13 23:56:07 -0700133 ALOGV("Registering %s's %d native methods...", className, numMethods);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700134
135 scoped_local_ref<jclass> c(env, findClass(env, className));
136 if (c.get() == NULL) {
Andreas Gampe6c5be192015-08-03 10:31:52 -0700137 char* tmp;
138 const char* msg;
139 if (asprintf(&tmp,
140 "Native registration unable to find class '%s'; aborting...",
141 className) == -1) {
142 // Allocation failed, print default warning.
143 msg = "Native registration unable to find class; aborting...";
144 } else {
145 msg = tmp;
146 }
Elliott Hughesa3b57002013-01-22 09:35:09 -0800147 e->FatalError(msg);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700148 }
149
150 if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
Andreas Gampe6c5be192015-08-03 10:31:52 -0700151 char* tmp;
152 const char* msg;
153 if (asprintf(&tmp, "RegisterNatives failed for '%s'; aborting...", className) == -1) {
154 // Allocation failed, print default warning.
155 msg = "RegisterNatives failed; aborting...";
156 } else {
157 msg = tmp;
158 }
Elliott Hughesa3b57002013-01-22 09:35:09 -0800159 e->FatalError(msg);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700160 }
161
162 return 0;
163}
164
165/*
166 * Returns a human-readable summary of an exception object. The buffer will
167 * be populated with the "binary" class name and, if present, the
168 * exception message.
169 */
Brian Carlstromdd8af232012-05-13 23:56:07 -0700170static bool getExceptionSummary(C_JNIEnv* env, jthrowable exception, std::string& result) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700171 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
172
173 /* get the name of the exception's class */
174 scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
175 scoped_local_ref<jclass> classClass(env,
176 (*env)->GetObjectClass(e, exceptionClass.get())); // java.lang.Class, can't fail
177 jmethodID classGetNameMethod =
178 (*env)->GetMethodID(e, classClass.get(), "getName", "()Ljava/lang/String;");
179 scoped_local_ref<jstring> classNameStr(env,
180 (jstring) (*env)->CallObjectMethod(e, exceptionClass.get(), classGetNameMethod));
181 if (classNameStr.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700182 (*env)->ExceptionClear(e);
183 result = "<error getting class name>";
184 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700185 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700186 const char* classNameChars = (*env)->GetStringUTFChars(e, classNameStr.get(), NULL);
187 if (classNameChars == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700188 (*env)->ExceptionClear(e);
189 result = "<error getting class name UTF-8>";
190 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700191 }
Brian Carlstromdd8af232012-05-13 23:56:07 -0700192 result += classNameChars;
193 (*env)->ReleaseStringUTFChars(e, classNameStr.get(), classNameChars);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700194
195 /* if the exception has a detail message, get that */
196 jmethodID getMessage =
197 (*env)->GetMethodID(e, exceptionClass.get(), "getMessage", "()Ljava/lang/String;");
198 scoped_local_ref<jstring> messageStr(env,
199 (jstring) (*env)->CallObjectMethod(e, exception, getMessage));
200 if (messageStr.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700201 return true;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700202 }
203
Brian Carlstromdd8af232012-05-13 23:56:07 -0700204 result += ": ";
205
Elliott Hughes87f62a82011-04-22 19:22:54 -0700206 const char* messageChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
207 if (messageChars != NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700208 result += messageChars;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700209 (*env)->ReleaseStringUTFChars(e, messageStr.get(), messageChars);
210 } else {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700211 result += "<error getting message>";
Elliott Hughes87f62a82011-04-22 19:22:54 -0700212 (*env)->ExceptionClear(e); // clear OOM
Elliott Hughes87f62a82011-04-22 19:22:54 -0700213 }
214
Brian Carlstromdd8af232012-05-13 23:56:07 -0700215 return true;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700216}
217
218/*
219 * Returns an exception (with stack trace) as a string.
220 */
Brian Carlstromdd8af232012-05-13 23:56:07 -0700221static bool getStackTrace(C_JNIEnv* env, jthrowable exception, std::string& result) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700222 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
223
224 scoped_local_ref<jclass> stringWriterClass(env, findClass(env, "java/io/StringWriter"));
225 if (stringWriterClass.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700226 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700227 }
228
229 jmethodID stringWriterCtor = (*env)->GetMethodID(e, stringWriterClass.get(), "<init>", "()V");
230 jmethodID stringWriterToStringMethod =
231 (*env)->GetMethodID(e, stringWriterClass.get(), "toString", "()Ljava/lang/String;");
232
233 scoped_local_ref<jclass> printWriterClass(env, findClass(env, "java/io/PrintWriter"));
234 if (printWriterClass.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700235 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700236 }
237
238 jmethodID printWriterCtor =
239 (*env)->GetMethodID(e, printWriterClass.get(), "<init>", "(Ljava/io/Writer;)V");
240
241 scoped_local_ref<jobject> stringWriter(env,
242 (*env)->NewObject(e, stringWriterClass.get(), stringWriterCtor));
243 if (stringWriter.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700244 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700245 }
246
Huanqi Chend47c3252014-06-24 13:31:31 +0200247 scoped_local_ref<jobject> printWriter(env,
248 (*env)->NewObject(e, printWriterClass.get(), printWriterCtor, stringWriter.get()));
249 if (printWriter.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700250 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700251 }
252
253 scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
254 jmethodID printStackTraceMethod =
255 (*env)->GetMethodID(e, exceptionClass.get(), "printStackTrace", "(Ljava/io/PrintWriter;)V");
Huanqi Chend47c3252014-06-24 13:31:31 +0200256 (*env)->CallVoidMethod(e, exception, printStackTraceMethod, printWriter.get());
Elliott Hughes87f62a82011-04-22 19:22:54 -0700257
258 if ((*env)->ExceptionCheck(e)) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700259 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700260 }
261
262 scoped_local_ref<jstring> messageStr(env,
263 (jstring) (*env)->CallObjectMethod(e, stringWriter.get(), stringWriterToStringMethod));
264 if (messageStr.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700265 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700266 }
267
268 const char* utfChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
269 if (utfChars == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700270 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700271 }
272
Brian Carlstromdd8af232012-05-13 23:56:07 -0700273 result = utfChars;
274
Elliott Hughes87f62a82011-04-22 19:22:54 -0700275 (*env)->ReleaseStringUTFChars(e, messageStr.get(), utfChars);
Brian Carlstromdd8af232012-05-13 23:56:07 -0700276 return true;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700277}
278
279extern "C" int jniThrowException(C_JNIEnv* env, const char* className, const char* msg) {
280 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
281
282 if ((*env)->ExceptionCheck(e)) {
283 /* TODO: consider creating the new exception with this as "cause" */
284 scoped_local_ref<jthrowable> exception(env, (*env)->ExceptionOccurred(e));
285 (*env)->ExceptionClear(e);
286
287 if (exception.get() != NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700288 std::string text;
289 getExceptionSummary(env, exception.get(), text);
290 ALOGW("Discarding pending exception (%s) to throw %s", text.c_str(), className);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700291 }
292 }
293
294 scoped_local_ref<jclass> exceptionClass(env, findClass(env, className));
295 if (exceptionClass.get() == NULL) {
Steve Block79a0d9c2012-01-06 19:16:58 +0000296 ALOGE("Unable to find exception class %s", className);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700297 /* ClassNotFoundException now pending */
298 return -1;
299 }
300
301 if ((*env)->ThrowNew(e, exceptionClass.get(), msg) != JNI_OK) {
Steve Block79a0d9c2012-01-06 19:16:58 +0000302 ALOGE("Failed throwing '%s' '%s'", className, msg);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700303 /* an exception, most likely OOM, will now be pending */
304 return -1;
305 }
306
307 return 0;
308}
309
310int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args) {
311 char msgBuf[512];
312 vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
313 return jniThrowException(env, className, msgBuf);
314}
315
316int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) {
317 return jniThrowException(env, "java/lang/NullPointerException", msg);
318}
319
320int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) {
321 return jniThrowException(env, "java/lang/RuntimeException", msg);
322}
323
324int jniThrowIOException(C_JNIEnv* env, int errnum) {
325 char buffer[80];
326 const char* message = jniStrError(errnum, buffer, sizeof(buffer));
327 return jniThrowException(env, "java/io/IOException", message);
328}
329
Elliott Hughesa4e4e742013-09-03 17:47:18 -0700330static std::string jniGetStackTrace(C_JNIEnv* env, jthrowable exception) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700331 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
332
Jeff Browne2b11e72012-04-10 20:34:39 -0700333 scoped_local_ref<jthrowable> currentException(env, (*env)->ExceptionOccurred(e));
Elliott Hughes87f62a82011-04-22 19:22:54 -0700334 if (exception == NULL) {
Jeff Browne2b11e72012-04-10 20:34:39 -0700335 exception = currentException.get();
Elliott Hughes87f62a82011-04-22 19:22:54 -0700336 if (exception == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700337 return "<no pending exception>";
Elliott Hughes87f62a82011-04-22 19:22:54 -0700338 }
Jeff Browne2b11e72012-04-10 20:34:39 -0700339 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700340
Jeff Browne2b11e72012-04-10 20:34:39 -0700341 if (currentException.get() != NULL) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700342 (*env)->ExceptionClear(e);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700343 }
344
Brian Carlstromdd8af232012-05-13 23:56:07 -0700345 std::string trace;
346 if (!getStackTrace(env, exception, trace)) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700347 (*env)->ExceptionClear(e);
Brian Carlstromdd8af232012-05-13 23:56:07 -0700348 getExceptionSummary(env, exception, trace);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700349 }
350
Elliott Hughes87f62a82011-04-22 19:22:54 -0700351 if (currentException.get() != NULL) {
Jeff Browne2b11e72012-04-10 20:34:39 -0700352 (*env)->Throw(e, currentException.get()); // rethrow
Elliott Hughes87f62a82011-04-22 19:22:54 -0700353 }
Brian Carlstromdd8af232012-05-13 23:56:07 -0700354
355 return trace;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700356}
357
Elliott Hughesa4e4e742013-09-03 17:47:18 -0700358void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception) {
359 std::string trace(jniGetStackTrace(env, exception));
360 __android_log_write(priority, tag, trace.c_str());
361}
362
Elliott Hughes87f62a82011-04-22 19:22:54 -0700363const char* jniStrError(int errnum, char* buf, size_t buflen) {
Elliott Hughesd60512c2013-10-08 14:15:18 -0700364#if __GLIBC__
Elliott Hughes87f62a82011-04-22 19:22:54 -0700365 // Note: glibc has a nonstandard strerror_r that returns char* rather than POSIX's int.
366 // char *strerror_r(int errnum, char *buf, size_t n);
Elliott Hughesd60512c2013-10-08 14:15:18 -0700367 return strerror_r(errnum, buf, buflen);
368#else
369 int rc = strerror_r(errnum, buf, buflen);
370 if (rc != 0) {
371 // (POSIX only guarantees a value other than 0. The safest
Elliott Hughes87f62a82011-04-22 19:22:54 -0700372 // way to implement this function is to use C++ and overload on the
Elliott Hughesd60512c2013-10-08 14:15:18 -0700373 // type of strerror_r to accurately distinguish GNU from POSIX.)
Elliott Hughes87f62a82011-04-22 19:22:54 -0700374 snprintf(buf, buflen, "errno %d", errnum);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700375 }
Elliott Hughesd60512c2013-10-08 14:15:18 -0700376 return buf;
377#endif
Elliott Hughes87f62a82011-04-22 19:22:54 -0700378}
379
Elliott Hughes87f62a82011-04-22 19:22:54 -0700380jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) {
381 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Logan Chien63e49172016-06-08 11:33:36 +0800382 if (fileDescriptorInitMethod == nullptr) {
383 InitFieldsAndMethods(e);
384 }
385 jobject fileDescriptor = (*env)->NewObject(e, JniConstants::fileDescriptorClass,
386 fileDescriptorInitMethod);
Narayan Kamathe29dbf52013-10-31 17:17:10 +0000387 // NOTE: NewObject ensures that an OutOfMemoryError will be seen by the Java
388 // caller if the alloc fails, so we just return NULL when that happens.
389 if (fileDescriptor != NULL) {
390 jniSetFileDescriptorOfFD(env, fileDescriptor, fd);
391 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700392 return fileDescriptor;
393}
394
395int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) {
396 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Logan Chien63e49172016-06-08 11:33:36 +0800397 if (fileDescriptorDescriptorField == nullptr) {
398 InitFieldsAndMethods(e);
399 }
Narayan Kamathe29dbf52013-10-31 17:17:10 +0000400 if (fileDescriptor != NULL) {
Logan Chien63e49172016-06-08 11:33:36 +0800401 return (*env)->GetIntField(e, fileDescriptor,
402 fileDescriptorDescriptorField);
Narayan Kamathe29dbf52013-10-31 17:17:10 +0000403 } else {
404 return -1;
405 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700406}
407
408void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value) {
409 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Logan Chien63e49172016-06-08 11:33:36 +0800410 if (fileDescriptorDescriptorField == nullptr) {
411 InitFieldsAndMethods(e);
412 }
413 (*env)->SetIntField(e, fileDescriptor, fileDescriptorDescriptorField, value);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700414}
415
Josh Gao669bc9e2018-06-25 16:22:11 -0700416jlong jniGetOwnerIdFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) {
417 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
418 if (fileDescriptorOwnerIdField == nullptr) {
419 InitFieldsAndMethods(e);
420 }
421 return (*env)->GetLongField(e, fileDescriptor, fileDescriptorOwnerIdField);
422}
423
Jeff Browndc176c52013-04-02 18:09:29 -0700424jobject jniGetReferent(C_JNIEnv* env, jobject ref) {
425 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Logan Chien63e49172016-06-08 11:33:36 +0800426 if (referenceGetMethod == nullptr) {
427 InitFieldsAndMethods(e);
428 }
429 return (*env)->CallObjectMethod(e, ref, referenceGetMethod);
Jeff Browndc176c52013-04-02 18:09:29 -0700430}
431
Fredrik Roubert2e312802017-07-04 21:53:08 +0200432jstring jniCreateString(C_JNIEnv* env, const jchar* unicodeChars, jsize len) {
433 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
434 return (*env)->NewString(e, unicodeChars, len);
435}