blob: b57ec50ed1afc43abf04513fbe93fd24fcc3ad0f [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
Orion Hodsonaadb3732018-11-21 12:58:34 +000017#include "nativehelper/JNIHelp.h"
Elliott Hughes87f62a82011-04-22 19:22:54 -070018
Brian Carlstromdd8af232012-05-13 23:56:07 -070019#include <string>
20
Orion Hodson85cdde82019-10-24 15:48:11 +010021#define LOG_TAG "JNIHelp"
22#include "ALog-priv.h"
23
24#include "jni.h"
Orion Hodsonaadb3732018-11-21 12:58:34 +000025#include "JniConstants.h"
Orion Hodson85cdde82019-10-24 15:48:11 +010026
27namespace {
Logan Chien63e49172016-06-08 11:33:36 +080028
Elliott Hughes87f62a82011-04-22 19:22:54 -070029/**
30 * Equivalent to ScopedLocalRef, but for C_JNIEnv instead. (And slightly more powerful.)
31 */
32template<typename T>
Orion Hodson85cdde82019-10-24 15:48:11 +010033class scoped_local_ref final {
Elliott Hughes87f62a82011-04-22 19:22:54 -070034public:
Chih-Hung Hsieh01332142016-05-02 11:47:43 -070035 explicit scoped_local_ref(C_JNIEnv* env, T localRef = NULL)
Elliott Hughes87f62a82011-04-22 19:22:54 -070036 : mEnv(env), mLocalRef(localRef)
37 {
38 }
39
40 ~scoped_local_ref() {
41 reset();
42 }
43
44 void reset(T localRef = NULL) {
45 if (mLocalRef != NULL) {
46 (*mEnv)->DeleteLocalRef(reinterpret_cast<JNIEnv*>(mEnv), mLocalRef);
47 mLocalRef = localRef;
48 }
49 }
50
51 T get() const {
52 return mLocalRef;
53 }
54
55private:
Orion Hodson85cdde82019-10-24 15:48:11 +010056 // scoped_local_ref does not support copy or move semantics.
57 scoped_local_ref(const scoped_local_ref&) = delete;
58 scoped_local_ref(scoped_local_ref&&) = delete;
59 scoped_local_ref& operator=(const scoped_local_ref&) = delete;
60 scoped_local_ref& operator=(scoped_local_ref&&) = delete;
61
62private:
Ian Rogers8288dde2014-11-04 11:42:02 -080063 C_JNIEnv* const mEnv;
Elliott Hughes87f62a82011-04-22 19:22:54 -070064 T mLocalRef;
Elliott Hughes87f62a82011-04-22 19:22:54 -070065};
66
Orion Hodson85cdde82019-10-24 15:48:11 +010067jclass findClass(C_JNIEnv* env, const char* className) {
Elliott Hughes87f62a82011-04-22 19:22:54 -070068 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
69 return (*env)->FindClass(e, className);
70}
71
Elliott Hughes87f62a82011-04-22 19:22:54 -070072/*
73 * Returns a human-readable summary of an exception object. The buffer will
74 * be populated with the "binary" class name and, if present, the
75 * exception message.
76 */
Orion Hodson85cdde82019-10-24 15:48:11 +010077bool getExceptionSummary(C_JNIEnv* env, jthrowable exception, std::string& result) {
Elliott Hughes87f62a82011-04-22 19:22:54 -070078 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
79
80 /* get the name of the exception's class */
81 scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
82 scoped_local_ref<jclass> classClass(env,
83 (*env)->GetObjectClass(e, exceptionClass.get())); // java.lang.Class, can't fail
84 jmethodID classGetNameMethod =
85 (*env)->GetMethodID(e, classClass.get(), "getName", "()Ljava/lang/String;");
86 scoped_local_ref<jstring> classNameStr(env,
87 (jstring) (*env)->CallObjectMethod(e, exceptionClass.get(), classGetNameMethod));
88 if (classNameStr.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -070089 (*env)->ExceptionClear(e);
90 result = "<error getting class name>";
91 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -070092 }
Elliott Hughes87f62a82011-04-22 19:22:54 -070093 const char* classNameChars = (*env)->GetStringUTFChars(e, classNameStr.get(), NULL);
94 if (classNameChars == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -070095 (*env)->ExceptionClear(e);
96 result = "<error getting class name UTF-8>";
97 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -070098 }
Brian Carlstromdd8af232012-05-13 23:56:07 -070099 result += classNameChars;
100 (*env)->ReleaseStringUTFChars(e, classNameStr.get(), classNameChars);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700101
102 /* if the exception has a detail message, get that */
103 jmethodID getMessage =
104 (*env)->GetMethodID(e, exceptionClass.get(), "getMessage", "()Ljava/lang/String;");
105 scoped_local_ref<jstring> messageStr(env,
106 (jstring) (*env)->CallObjectMethod(e, exception, getMessage));
107 if (messageStr.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700108 return true;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700109 }
110
Brian Carlstromdd8af232012-05-13 23:56:07 -0700111 result += ": ";
112
Elliott Hughes87f62a82011-04-22 19:22:54 -0700113 const char* messageChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
114 if (messageChars != NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700115 result += messageChars;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700116 (*env)->ReleaseStringUTFChars(e, messageStr.get(), messageChars);
117 } else {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700118 result += "<error getting message>";
Elliott Hughes87f62a82011-04-22 19:22:54 -0700119 (*env)->ExceptionClear(e); // clear OOM
Elliott Hughes87f62a82011-04-22 19:22:54 -0700120 }
121
Brian Carlstromdd8af232012-05-13 23:56:07 -0700122 return true;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700123}
124
125/*
126 * Returns an exception (with stack trace) as a string.
127 */
Orion Hodson85cdde82019-10-24 15:48:11 +0100128bool getStackTrace(C_JNIEnv* env, jthrowable exception, std::string& result) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700129 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
130
131 scoped_local_ref<jclass> stringWriterClass(env, findClass(env, "java/io/StringWriter"));
132 if (stringWriterClass.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700133 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700134 }
135
136 jmethodID stringWriterCtor = (*env)->GetMethodID(e, stringWriterClass.get(), "<init>", "()V");
137 jmethodID stringWriterToStringMethod =
138 (*env)->GetMethodID(e, stringWriterClass.get(), "toString", "()Ljava/lang/String;");
139
140 scoped_local_ref<jclass> printWriterClass(env, findClass(env, "java/io/PrintWriter"));
141 if (printWriterClass.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700142 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700143 }
144
145 jmethodID printWriterCtor =
146 (*env)->GetMethodID(e, printWriterClass.get(), "<init>", "(Ljava/io/Writer;)V");
147
148 scoped_local_ref<jobject> stringWriter(env,
149 (*env)->NewObject(e, stringWriterClass.get(), stringWriterCtor));
150 if (stringWriter.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700151 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700152 }
153
Huanqi Chend47c3252014-06-24 13:31:31 +0200154 scoped_local_ref<jobject> printWriter(env,
155 (*env)->NewObject(e, printWriterClass.get(), printWriterCtor, stringWriter.get()));
156 if (printWriter.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700157 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700158 }
159
160 scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
161 jmethodID printStackTraceMethod =
162 (*env)->GetMethodID(e, exceptionClass.get(), "printStackTrace", "(Ljava/io/PrintWriter;)V");
Huanqi Chend47c3252014-06-24 13:31:31 +0200163 (*env)->CallVoidMethod(e, exception, printStackTraceMethod, printWriter.get());
Elliott Hughes87f62a82011-04-22 19:22:54 -0700164
165 if ((*env)->ExceptionCheck(e)) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700166 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700167 }
168
169 scoped_local_ref<jstring> messageStr(env,
170 (jstring) (*env)->CallObjectMethod(e, stringWriter.get(), stringWriterToStringMethod));
171 if (messageStr.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700172 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700173 }
174
175 const char* utfChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
176 if (utfChars == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700177 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700178 }
179
Brian Carlstromdd8af232012-05-13 23:56:07 -0700180 result = utfChars;
181
Elliott Hughes87f62a82011-04-22 19:22:54 -0700182 (*env)->ReleaseStringUTFChars(e, messageStr.get(), utfChars);
Brian Carlstromdd8af232012-05-13 23:56:07 -0700183 return true;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700184}
185
Orion Hodson85cdde82019-10-24 15:48:11 +0100186std::string jniGetStackTrace(C_JNIEnv* env, jthrowable exception) {
187 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
188
189 scoped_local_ref<jthrowable> currentException(env, (*env)->ExceptionOccurred(e));
190 if (exception == NULL) {
191 exception = currentException.get();
192 if (exception == NULL) {
193 return "<no pending exception>";
194 }
195 }
196
197 if (currentException.get() != NULL) {
198 (*env)->ExceptionClear(e);
199 }
200
201 std::string trace;
202 if (!getStackTrace(env, exception, trace)) {
203 (*env)->ExceptionClear(e);
204 getExceptionSummary(env, exception, trace);
205 }
206
207 if (currentException.get() != NULL) {
208 (*env)->Throw(e, currentException.get()); // rethrow
209 }
210
211 return trace;
212}
213
214// Note: glibc has a nonstandard strerror_r that returns char* rather than POSIX's int.
215// char *strerror_r(int errnum, char *buf, size_t n);
216//
217// Some versions of bionic support the glibc style call. Since the set of defines that determine
218// which version is used is byzantine in its complexity we will just use this C++ template hack to
219// select the correct jniStrError implementation based on the libc being used.
220
221using GNUStrError = char* (*)(int,char*,size_t);
222using POSIXStrError = int (*)(int,char*,size_t);
223
224inline const char* realJniStrError(GNUStrError func, int errnum, char* buf, size_t buflen) {
225 return func(errnum, buf, buflen);
226}
227
228inline const char* realJniStrError(POSIXStrError func, int errnum, char* buf, size_t buflen) {
229 int rc = func(errnum, buf, buflen);
230 if (rc != 0) {
231 // (POSIX only guarantees a value other than 0. The safest
232 // way to implement this function is to use C++ and overload on the
233 // type of strerror_r to accurately distinguish GNU from POSIX.)
234 snprintf(buf, buflen, "errno %d", errnum);
235 }
236 return buf;
237}
238
239} // namespace
240
241int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
242 const JNINativeMethod* gMethods, int numMethods)
243{
244 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
245
246 ALOGV("Registering %s's %d native methods...", className, numMethods);
247
248 scoped_local_ref<jclass> c(env, findClass(env, className));
249 ALOG_ALWAYS_FATAL_IF(c.get() == NULL,
250 "Native registration unable to find class '%s'; aborting...",
251 className);
252
253 int result = e->RegisterNatives(c.get(), gMethods, numMethods);
254 ALOG_ALWAYS_FATAL_IF(result < 0, "RegisterNatives failed for '%s'; aborting...",
255 className);
256
257 return 0;
258}
259
260int jniThrowException(C_JNIEnv* env, const char* className, const char* msg) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700261 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
262
263 if ((*env)->ExceptionCheck(e)) {
264 /* TODO: consider creating the new exception with this as "cause" */
265 scoped_local_ref<jthrowable> exception(env, (*env)->ExceptionOccurred(e));
266 (*env)->ExceptionClear(e);
267
268 if (exception.get() != NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700269 std::string text;
270 getExceptionSummary(env, exception.get(), text);
271 ALOGW("Discarding pending exception (%s) to throw %s", text.c_str(), className);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700272 }
273 }
274
275 scoped_local_ref<jclass> exceptionClass(env, findClass(env, className));
276 if (exceptionClass.get() == NULL) {
Steve Block79a0d9c2012-01-06 19:16:58 +0000277 ALOGE("Unable to find exception class %s", className);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700278 /* ClassNotFoundException now pending */
279 return -1;
280 }
281
282 if ((*env)->ThrowNew(e, exceptionClass.get(), msg) != JNI_OK) {
Steve Block79a0d9c2012-01-06 19:16:58 +0000283 ALOGE("Failed throwing '%s' '%s'", className, msg);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700284 /* an exception, most likely OOM, will now be pending */
285 return -1;
286 }
287
288 return 0;
289}
290
Orion Hodson85cdde82019-10-24 15:48:11 +0100291int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700292 char msgBuf[512];
293 vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
294 return jniThrowException(env, className, msgBuf);
295}
296
Orion Hodson85cdde82019-10-24 15:48:11 +0100297int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700298 return jniThrowException(env, "java/lang/NullPointerException", msg);
299}
300
Orion Hodson85cdde82019-10-24 15:48:11 +0100301int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700302 return jniThrowException(env, "java/lang/RuntimeException", msg);
303}
304
Orion Hodson85cdde82019-10-24 15:48:11 +0100305int jniThrowIOException(C_JNIEnv* env, int errnum) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700306 char buffer[80];
307 const char* message = jniStrError(errnum, buffer, sizeof(buffer));
308 return jniThrowException(env, "java/io/IOException", message);
309}
310
Orion Hodson85cdde82019-10-24 15:48:11 +0100311void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception) {
Elliott Hughesa4e4e742013-09-03 17:47:18 -0700312 std::string trace(jniGetStackTrace(env, exception));
313 __android_log_write(priority, tag, trace.c_str());
314}
315
Orion Hodson85cdde82019-10-24 15:48:11 +0100316const char* jniStrError(int errnum, char* buf, size_t buflen) {
Jerome Gaillardf0d42052019-03-12 15:17:22 +0000317#ifdef _WIN32
318 strerror_s(buf, buflen, errnum);
319 return buf;
320#else
Elliott Hughes71cb8412018-11-09 11:10:40 -0800321 // The magic of C++ overloading selects the correct implementation based on the declared type of
Alex Light48694f72018-10-31 14:50:54 -0700322 // strerror_r. The inline will ensure that we don't have any indirect calls.
Orion Hodson85cdde82019-10-24 15:48:11 +0100323 return realJniStrError(strerror_r, errnum, buf, buflen);
Jerome Gaillardf0d42052019-03-12 15:17:22 +0000324#endif
Elliott Hughes87f62a82011-04-22 19:22:54 -0700325}
326
Orion Hodson85cdde82019-10-24 15:48:11 +0100327jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700328 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Orion Hodsonaadb3732018-11-21 12:58:34 +0000329 jobject fileDescriptor = e->NewObject(JniConstants::GetFileDescriptorClass(e),
330 JniConstants::GetFileDescriptorInitMethod(e));
Narayan Kamathe29dbf52013-10-31 17:17:10 +0000331 // NOTE: NewObject ensures that an OutOfMemoryError will be seen by the Java
Orion Hodsonaadb3732018-11-21 12:58:34 +0000332 // caller if the alloc fails, so we just return nullptr when that happens.
333 if (fileDescriptor != nullptr) {
Narayan Kamathe29dbf52013-10-31 17:17:10 +0000334 jniSetFileDescriptorOfFD(env, fileDescriptor, fd);
335 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700336 return fileDescriptor;
337}
338
Orion Hodson85cdde82019-10-24 15:48:11 +0100339int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700340 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Orion Hodsonaadb3732018-11-21 12:58:34 +0000341 if (fileDescriptor != nullptr) {
342 return e->GetIntField(fileDescriptor,
343 JniConstants::GetFileDescriptorDescriptorField(e));
Narayan Kamathe29dbf52013-10-31 17:17:10 +0000344 } else {
345 return -1;
346 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700347}
348
Orion Hodson85cdde82019-10-24 15:48:11 +0100349void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700350 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Pete Bentley0505b222018-07-20 18:18:44 +0100351 if (fileDescriptor == nullptr) {
352 jniThrowNullPointerException(e, "null FileDescriptor");
353 } else {
Orion Hodsonaadb3732018-11-21 12:58:34 +0000354 e->SetIntField(fileDescriptor, JniConstants::GetFileDescriptorDescriptorField(e), value);
Pete Bentley0505b222018-07-20 18:18:44 +0100355 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700356}
357
Orion Hodson85cdde82019-10-24 15:48:11 +0100358jlong jniGetOwnerIdFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) {
Josh Gao669bc9e2018-06-25 16:22:11 -0700359 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Orion Hodsonaadb3732018-11-21 12:58:34 +0000360 return e->GetLongField(fileDescriptor, JniConstants::GetFileDescriptorOwnerIdField(e));
Josh Gao669bc9e2018-06-25 16:22:11 -0700361}
362
Orion Hodson85cdde82019-10-24 15:48:11 +0100363jarray jniGetNioBufferBaseArray(C_JNIEnv* env, jobject nioBuffer) {
Orion Hodsonbcc2b6f2019-02-22 16:41:10 +0000364 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
365 jclass nioAccessClass = JniConstants::GetNioAccessClass(e);
366 jmethodID getBaseArrayMethod = JniConstants::GetNioAccessGetBaseArrayMethod(e);
367 jobject object = e->CallStaticObjectMethod(nioAccessClass, getBaseArrayMethod, nioBuffer);
368 return static_cast<jarray>(object);
369}
370
Orion Hodson85cdde82019-10-24 15:48:11 +0100371int jniGetNioBufferBaseArrayOffset(C_JNIEnv* env, jobject nioBuffer) {
Orion Hodsonbcc2b6f2019-02-22 16:41:10 +0000372 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
373 jclass nioAccessClass = JniConstants::GetNioAccessClass(e);
374 jmethodID getBaseArrayOffsetMethod = JniConstants::GetNioAccessGetBaseArrayOffsetMethod(e);
375 return e->CallStaticIntMethod(nioAccessClass, getBaseArrayOffsetMethod, nioBuffer);
376}
377
Orion Hodson85cdde82019-10-24 15:48:11 +0100378jlong jniGetNioBufferPointer(C_JNIEnv* env, jobject nioBuffer) {
Orion Hodsonbcc2b6f2019-02-22 16:41:10 +0000379 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
380 jlong baseAddress = e->GetLongField(nioBuffer, JniConstants::GetNioBufferAddressField(e));
381 if (baseAddress != 0) {
382 const int position = e->GetIntField(nioBuffer, JniConstants::GetNioBufferPositionField(e));
383 const int shift =
384 e->GetIntField(nioBuffer, JniConstants::GetNioBufferElementSizeShiftField(e));
385 baseAddress += position << shift;
386 }
387 return baseAddress;
388}
389
Orion Hodson85cdde82019-10-24 15:48:11 +0100390jlong jniGetNioBufferFields(C_JNIEnv* env, jobject nioBuffer,
391 jint* position, jint* limit, jint* elementSizeShift) {
Orion Hodsonbcc2b6f2019-02-22 16:41:10 +0000392 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
393 *position = e->GetIntField(nioBuffer, JniConstants::GetNioBufferPositionField(e));
394 *limit = e->GetIntField(nioBuffer, JniConstants::GetNioBufferLimitField(e));
395 *elementSizeShift =
396 e->GetIntField(nioBuffer, JniConstants::GetNioBufferElementSizeShiftField(e));
397 return e->GetLongField(nioBuffer, JniConstants::GetNioBufferAddressField(e));
398}
399
Orion Hodson85cdde82019-10-24 15:48:11 +0100400jobject jniGetReferent(C_JNIEnv* env, jobject ref) {
Jeff Browndc176c52013-04-02 18:09:29 -0700401 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Orion Hodsonaadb3732018-11-21 12:58:34 +0000402 return e->CallObjectMethod(ref, JniConstants::GetReferenceGetMethod(e));
Jeff Browndc176c52013-04-02 18:09:29 -0700403}
404
Orion Hodson85cdde82019-10-24 15:48:11 +0100405jstring jniCreateString(C_JNIEnv* env, const jchar* unicodeChars, jsize len) {
Fredrik Roubert2e312802017-07-04 21:53:08 +0200406 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Orion Hodsonaadb3732018-11-21 12:58:34 +0000407 return e->NewString(unicodeChars, len);
Fredrik Roubert2e312802017-07-04 21:53:08 +0200408}
Nicolas Geoffray44e418e2019-03-12 00:59:02 +0000409
Orion Hodson85cdde82019-10-24 15:48:11 +0100410void jniUninitializeConstants() {
Nicolas Geoffray44e418e2019-03-12 00:59:02 +0000411 JniConstants::Uninitialize();
412}