blob: 0cd048d67b5b4f3a5b5e348999e3dd76085dcf76 [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
17#define LOG_TAG "JNIHelp"
18
Orion Hodsonaadb3732018-11-21 12:58:34 +000019#include "nativehelper/JNIHelp.h"
Elliott Hughes87f62a82011-04-22 19:22:54 -070020
Orion Hodsonaadb3732018-11-21 12:58:34 +000021#include "ALog-priv.h"
Elliott Hughes87f62a82011-04-22 19:22:54 -070022
Brian Carlstromdd8af232012-05-13 23:56:07 -070023#include <string>
24
Orion Hodsonaadb3732018-11-21 12:58:34 +000025#include "JniConstants.h"
26#include "nativehelper/ScopedLocalRef.h"
Logan Chien63e49172016-06-08 11:33:36 +080027
Elliott Hughes87f62a82011-04-22 19:22:54 -070028/**
29 * Equivalent to ScopedLocalRef, but for C_JNIEnv instead. (And slightly more powerful.)
30 */
31template<typename T>
32class scoped_local_ref {
33public:
Chih-Hung Hsieh01332142016-05-02 11:47:43 -070034 explicit scoped_local_ref(C_JNIEnv* env, T localRef = NULL)
Elliott Hughes87f62a82011-04-22 19:22:54 -070035 : mEnv(env), mLocalRef(localRef)
36 {
37 }
38
39 ~scoped_local_ref() {
40 reset();
41 }
42
43 void reset(T localRef = NULL) {
44 if (mLocalRef != NULL) {
45 (*mEnv)->DeleteLocalRef(reinterpret_cast<JNIEnv*>(mEnv), mLocalRef);
46 mLocalRef = localRef;
47 }
48 }
49
50 T get() const {
51 return mLocalRef;
52 }
53
54private:
Ian Rogers8288dde2014-11-04 11:42:02 -080055 C_JNIEnv* const mEnv;
Elliott Hughes87f62a82011-04-22 19:22:54 -070056 T mLocalRef;
57
Ian Rogers8288dde2014-11-04 11:42:02 -080058 DISALLOW_COPY_AND_ASSIGN(scoped_local_ref);
Elliott Hughes87f62a82011-04-22 19:22:54 -070059};
60
61static jclass findClass(C_JNIEnv* env, const char* className) {
62 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
63 return (*env)->FindClass(e, className);
64}
65
Orion Hodsonb01e7fe2018-11-07 06:07:50 +000066MODULE_API int jniRegisterNativeMethods(C_JNIEnv* env, const char* className,
Elliott Hughes87f62a82011-04-22 19:22:54 -070067 const JNINativeMethod* gMethods, int numMethods)
68{
69 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
70
Brian Carlstromdd8af232012-05-13 23:56:07 -070071 ALOGV("Registering %s's %d native methods...", className, numMethods);
Elliott Hughes87f62a82011-04-22 19:22:54 -070072
73 scoped_local_ref<jclass> c(env, findClass(env, className));
74 if (c.get() == NULL) {
Andreas Gampe6c5be192015-08-03 10:31:52 -070075 char* tmp;
76 const char* msg;
77 if (asprintf(&tmp,
78 "Native registration unable to find class '%s'; aborting...",
79 className) == -1) {
80 // Allocation failed, print default warning.
81 msg = "Native registration unable to find class; aborting...";
82 } else {
83 msg = tmp;
84 }
Elliott Hughesa3b57002013-01-22 09:35:09 -080085 e->FatalError(msg);
Elliott Hughes87f62a82011-04-22 19:22:54 -070086 }
87
88 if ((*env)->RegisterNatives(e, c.get(), gMethods, numMethods) < 0) {
Andreas Gampe6c5be192015-08-03 10:31:52 -070089 char* tmp;
90 const char* msg;
91 if (asprintf(&tmp, "RegisterNatives failed for '%s'; aborting...", className) == -1) {
92 // Allocation failed, print default warning.
93 msg = "RegisterNatives failed; aborting...";
94 } else {
95 msg = tmp;
96 }
Elliott Hughesa3b57002013-01-22 09:35:09 -080097 e->FatalError(msg);
Elliott Hughes87f62a82011-04-22 19:22:54 -070098 }
99
100 return 0;
101}
102
103/*
104 * Returns a human-readable summary of an exception object. The buffer will
105 * be populated with the "binary" class name and, if present, the
106 * exception message.
107 */
Brian Carlstromdd8af232012-05-13 23:56:07 -0700108static bool getExceptionSummary(C_JNIEnv* env, jthrowable exception, std::string& result) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700109 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
110
111 /* get the name of the exception's class */
112 scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
113 scoped_local_ref<jclass> classClass(env,
114 (*env)->GetObjectClass(e, exceptionClass.get())); // java.lang.Class, can't fail
115 jmethodID classGetNameMethod =
116 (*env)->GetMethodID(e, classClass.get(), "getName", "()Ljava/lang/String;");
117 scoped_local_ref<jstring> classNameStr(env,
118 (jstring) (*env)->CallObjectMethod(e, exceptionClass.get(), classGetNameMethod));
119 if (classNameStr.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700120 (*env)->ExceptionClear(e);
121 result = "<error getting class name>";
122 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700123 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700124 const char* classNameChars = (*env)->GetStringUTFChars(e, classNameStr.get(), NULL);
125 if (classNameChars == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700126 (*env)->ExceptionClear(e);
127 result = "<error getting class name UTF-8>";
128 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700129 }
Brian Carlstromdd8af232012-05-13 23:56:07 -0700130 result += classNameChars;
131 (*env)->ReleaseStringUTFChars(e, classNameStr.get(), classNameChars);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700132
133 /* if the exception has a detail message, get that */
134 jmethodID getMessage =
135 (*env)->GetMethodID(e, exceptionClass.get(), "getMessage", "()Ljava/lang/String;");
136 scoped_local_ref<jstring> messageStr(env,
137 (jstring) (*env)->CallObjectMethod(e, exception, getMessage));
138 if (messageStr.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700139 return true;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700140 }
141
Brian Carlstromdd8af232012-05-13 23:56:07 -0700142 result += ": ";
143
Elliott Hughes87f62a82011-04-22 19:22:54 -0700144 const char* messageChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
145 if (messageChars != NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700146 result += messageChars;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700147 (*env)->ReleaseStringUTFChars(e, messageStr.get(), messageChars);
148 } else {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700149 result += "<error getting message>";
Elliott Hughes87f62a82011-04-22 19:22:54 -0700150 (*env)->ExceptionClear(e); // clear OOM
Elliott Hughes87f62a82011-04-22 19:22:54 -0700151 }
152
Brian Carlstromdd8af232012-05-13 23:56:07 -0700153 return true;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700154}
155
156/*
157 * Returns an exception (with stack trace) as a string.
158 */
Brian Carlstromdd8af232012-05-13 23:56:07 -0700159static bool getStackTrace(C_JNIEnv* env, jthrowable exception, std::string& result) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700160 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
161
162 scoped_local_ref<jclass> stringWriterClass(env, findClass(env, "java/io/StringWriter"));
163 if (stringWriterClass.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700164 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700165 }
166
167 jmethodID stringWriterCtor = (*env)->GetMethodID(e, stringWriterClass.get(), "<init>", "()V");
168 jmethodID stringWriterToStringMethod =
169 (*env)->GetMethodID(e, stringWriterClass.get(), "toString", "()Ljava/lang/String;");
170
171 scoped_local_ref<jclass> printWriterClass(env, findClass(env, "java/io/PrintWriter"));
172 if (printWriterClass.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700173 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700174 }
175
176 jmethodID printWriterCtor =
177 (*env)->GetMethodID(e, printWriterClass.get(), "<init>", "(Ljava/io/Writer;)V");
178
179 scoped_local_ref<jobject> stringWriter(env,
180 (*env)->NewObject(e, stringWriterClass.get(), stringWriterCtor));
181 if (stringWriter.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700182 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700183 }
184
Huanqi Chend47c3252014-06-24 13:31:31 +0200185 scoped_local_ref<jobject> printWriter(env,
186 (*env)->NewObject(e, printWriterClass.get(), printWriterCtor, stringWriter.get()));
187 if (printWriter.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700188 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700189 }
190
191 scoped_local_ref<jclass> exceptionClass(env, (*env)->GetObjectClass(e, exception)); // can't fail
192 jmethodID printStackTraceMethod =
193 (*env)->GetMethodID(e, exceptionClass.get(), "printStackTrace", "(Ljava/io/PrintWriter;)V");
Huanqi Chend47c3252014-06-24 13:31:31 +0200194 (*env)->CallVoidMethod(e, exception, printStackTraceMethod, printWriter.get());
Elliott Hughes87f62a82011-04-22 19:22:54 -0700195
196 if ((*env)->ExceptionCheck(e)) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700197 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700198 }
199
200 scoped_local_ref<jstring> messageStr(env,
201 (jstring) (*env)->CallObjectMethod(e, stringWriter.get(), stringWriterToStringMethod));
202 if (messageStr.get() == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700203 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700204 }
205
206 const char* utfChars = (*env)->GetStringUTFChars(e, messageStr.get(), NULL);
207 if (utfChars == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700208 return false;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700209 }
210
Brian Carlstromdd8af232012-05-13 23:56:07 -0700211 result = utfChars;
212
Elliott Hughes87f62a82011-04-22 19:22:54 -0700213 (*env)->ReleaseStringUTFChars(e, messageStr.get(), utfChars);
Brian Carlstromdd8af232012-05-13 23:56:07 -0700214 return true;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700215}
216
Orion Hodsonb01e7fe2018-11-07 06:07:50 +0000217MODULE_API int jniThrowException(C_JNIEnv* env, const char* className, const char* msg) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700218 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
219
220 if ((*env)->ExceptionCheck(e)) {
221 /* TODO: consider creating the new exception with this as "cause" */
222 scoped_local_ref<jthrowable> exception(env, (*env)->ExceptionOccurred(e));
223 (*env)->ExceptionClear(e);
224
225 if (exception.get() != NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700226 std::string text;
227 getExceptionSummary(env, exception.get(), text);
228 ALOGW("Discarding pending exception (%s) to throw %s", text.c_str(), className);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700229 }
230 }
231
232 scoped_local_ref<jclass> exceptionClass(env, findClass(env, className));
233 if (exceptionClass.get() == NULL) {
Steve Block79a0d9c2012-01-06 19:16:58 +0000234 ALOGE("Unable to find exception class %s", className);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700235 /* ClassNotFoundException now pending */
236 return -1;
237 }
238
239 if ((*env)->ThrowNew(e, exceptionClass.get(), msg) != JNI_OK) {
Steve Block79a0d9c2012-01-06 19:16:58 +0000240 ALOGE("Failed throwing '%s' '%s'", className, msg);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700241 /* an exception, most likely OOM, will now be pending */
242 return -1;
243 }
244
245 return 0;
246}
247
Orion Hodsonb01e7fe2018-11-07 06:07:50 +0000248MODULE_API int jniThrowExceptionFmt(C_JNIEnv* env, const char* className, const char* fmt, va_list args) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700249 char msgBuf[512];
250 vsnprintf(msgBuf, sizeof(msgBuf), fmt, args);
251 return jniThrowException(env, className, msgBuf);
252}
253
Orion Hodsonb01e7fe2018-11-07 06:07:50 +0000254MODULE_API int jniThrowNullPointerException(C_JNIEnv* env, const char* msg) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700255 return jniThrowException(env, "java/lang/NullPointerException", msg);
256}
257
Orion Hodsonb01e7fe2018-11-07 06:07:50 +0000258MODULE_API int jniThrowRuntimeException(C_JNIEnv* env, const char* msg) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700259 return jniThrowException(env, "java/lang/RuntimeException", msg);
260}
261
Orion Hodsonb01e7fe2018-11-07 06:07:50 +0000262MODULE_API int jniThrowIOException(C_JNIEnv* env, int errnum) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700263 char buffer[80];
264 const char* message = jniStrError(errnum, buffer, sizeof(buffer));
265 return jniThrowException(env, "java/io/IOException", message);
266}
267
Elliott Hughesa4e4e742013-09-03 17:47:18 -0700268static std::string jniGetStackTrace(C_JNIEnv* env, jthrowable exception) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700269 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
270
Jeff Browne2b11e72012-04-10 20:34:39 -0700271 scoped_local_ref<jthrowable> currentException(env, (*env)->ExceptionOccurred(e));
Elliott Hughes87f62a82011-04-22 19:22:54 -0700272 if (exception == NULL) {
Jeff Browne2b11e72012-04-10 20:34:39 -0700273 exception = currentException.get();
Elliott Hughes87f62a82011-04-22 19:22:54 -0700274 if (exception == NULL) {
Brian Carlstromdd8af232012-05-13 23:56:07 -0700275 return "<no pending exception>";
Elliott Hughes87f62a82011-04-22 19:22:54 -0700276 }
Jeff Browne2b11e72012-04-10 20:34:39 -0700277 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700278
Jeff Browne2b11e72012-04-10 20:34:39 -0700279 if (currentException.get() != NULL) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700280 (*env)->ExceptionClear(e);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700281 }
282
Brian Carlstromdd8af232012-05-13 23:56:07 -0700283 std::string trace;
284 if (!getStackTrace(env, exception, trace)) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700285 (*env)->ExceptionClear(e);
Brian Carlstromdd8af232012-05-13 23:56:07 -0700286 getExceptionSummary(env, exception, trace);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700287 }
288
Elliott Hughes87f62a82011-04-22 19:22:54 -0700289 if (currentException.get() != NULL) {
Jeff Browne2b11e72012-04-10 20:34:39 -0700290 (*env)->Throw(e, currentException.get()); // rethrow
Elliott Hughes87f62a82011-04-22 19:22:54 -0700291 }
Brian Carlstromdd8af232012-05-13 23:56:07 -0700292
293 return trace;
Elliott Hughes87f62a82011-04-22 19:22:54 -0700294}
295
Orion Hodsonb01e7fe2018-11-07 06:07:50 +0000296MODULE_API void jniLogException(C_JNIEnv* env, int priority, const char* tag, jthrowable exception) {
Elliott Hughesa4e4e742013-09-03 17:47:18 -0700297 std::string trace(jniGetStackTrace(env, exception));
298 __android_log_write(priority, tag, trace.c_str());
299}
300
Alex Light48694f72018-10-31 14:50:54 -0700301// Note: glibc has a nonstandard strerror_r that returns char* rather than POSIX's int.
302// char *strerror_r(int errnum, char *buf, size_t n);
303//
304// Some versions of bionic support the glibc style call. Since the set of defines that determine
305// which version is used is byzantine in its complexity we will just use this C++ template hack to
306// select the correct jniStrError implementation based on the libc being used.
307namespace impl {
308
Alex Light48694f72018-10-31 14:50:54 -0700309using GNUStrError = char* (*)(int,char*,size_t);
310using POSIXStrError = int (*)(int,char*,size_t);
311
Alex Light48694f72018-10-31 14:50:54 -0700312inline const char* realJniStrError(GNUStrError func, int errnum, char* buf, size_t buflen) {
313 return func(errnum, buf, buflen);
314}
315
Alex Light48694f72018-10-31 14:50:54 -0700316inline const char* realJniStrError(POSIXStrError func, int errnum, char* buf, size_t buflen) {
317 int rc = func(errnum, buf, buflen);
Elliott Hughesd60512c2013-10-08 14:15:18 -0700318 if (rc != 0) {
319 // (POSIX only guarantees a value other than 0. The safest
Elliott Hughes87f62a82011-04-22 19:22:54 -0700320 // way to implement this function is to use C++ and overload on the
Elliott Hughesd60512c2013-10-08 14:15:18 -0700321 // type of strerror_r to accurately distinguish GNU from POSIX.)
Elliott Hughes87f62a82011-04-22 19:22:54 -0700322 snprintf(buf, buflen, "errno %d", errnum);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700323 }
Elliott Hughesd60512c2013-10-08 14:15:18 -0700324 return buf;
Alex Light48694f72018-10-31 14:50:54 -0700325}
Elliott Hughes71cb8412018-11-09 11:10:40 -0800326
Alex Light48694f72018-10-31 14:50:54 -0700327} // namespace impl
328
Orion Hodsonb01e7fe2018-11-07 06:07:50 +0000329MODULE_API const char* jniStrError(int errnum, char* buf, size_t buflen) {
Elliott Hughes71cb8412018-11-09 11:10:40 -0800330 // The magic of C++ overloading selects the correct implementation based on the declared type of
Alex Light48694f72018-10-31 14:50:54 -0700331 // strerror_r. The inline will ensure that we don't have any indirect calls.
332 return impl::realJniStrError(strerror_r, errnum, buf, buflen);
Elliott Hughes87f62a82011-04-22 19:22:54 -0700333}
334
Orion Hodsonb01e7fe2018-11-07 06:07:50 +0000335MODULE_API jobject jniCreateFileDescriptor(C_JNIEnv* env, int fd) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700336 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Orion Hodsonaadb3732018-11-21 12:58:34 +0000337 jobject fileDescriptor = e->NewObject(JniConstants::GetFileDescriptorClass(e),
338 JniConstants::GetFileDescriptorInitMethod(e));
Narayan Kamathe29dbf52013-10-31 17:17:10 +0000339 // NOTE: NewObject ensures that an OutOfMemoryError will be seen by the Java
Orion Hodsonaadb3732018-11-21 12:58:34 +0000340 // caller if the alloc fails, so we just return nullptr when that happens.
341 if (fileDescriptor != nullptr) {
Narayan Kamathe29dbf52013-10-31 17:17:10 +0000342 jniSetFileDescriptorOfFD(env, fileDescriptor, fd);
343 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700344 return fileDescriptor;
345}
346
Orion Hodsonb01e7fe2018-11-07 06:07:50 +0000347MODULE_API int jniGetFDFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700348 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Orion Hodsonaadb3732018-11-21 12:58:34 +0000349 if (fileDescriptor != nullptr) {
350 return e->GetIntField(fileDescriptor,
351 JniConstants::GetFileDescriptorDescriptorField(e));
Narayan Kamathe29dbf52013-10-31 17:17:10 +0000352 } else {
353 return -1;
354 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700355}
356
Orion Hodsonb01e7fe2018-11-07 06:07:50 +0000357MODULE_API void jniSetFileDescriptorOfFD(C_JNIEnv* env, jobject fileDescriptor, int value) {
Elliott Hughes87f62a82011-04-22 19:22:54 -0700358 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Pete Bentley0505b222018-07-20 18:18:44 +0100359 if (fileDescriptor == nullptr) {
360 jniThrowNullPointerException(e, "null FileDescriptor");
361 } else {
Orion Hodsonaadb3732018-11-21 12:58:34 +0000362 e->SetIntField(fileDescriptor, JniConstants::GetFileDescriptorDescriptorField(e), value);
Pete Bentley0505b222018-07-20 18:18:44 +0100363 }
Elliott Hughes87f62a82011-04-22 19:22:54 -0700364}
365
Orion Hodsonb01e7fe2018-11-07 06:07:50 +0000366MODULE_API jlong jniGetOwnerIdFromFileDescriptor(C_JNIEnv* env, jobject fileDescriptor) {
Josh Gao669bc9e2018-06-25 16:22:11 -0700367 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Orion Hodsonaadb3732018-11-21 12:58:34 +0000368 return e->GetLongField(fileDescriptor, JniConstants::GetFileDescriptorOwnerIdField(e));
Josh Gao669bc9e2018-06-25 16:22:11 -0700369}
370
Orion Hodsonb01e7fe2018-11-07 06:07:50 +0000371MODULE_API jobject jniGetReferent(C_JNIEnv* env, jobject ref) {
Jeff Browndc176c52013-04-02 18:09:29 -0700372 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Orion Hodsonaadb3732018-11-21 12:58:34 +0000373 return e->CallObjectMethod(ref, JniConstants::GetReferenceGetMethod(e));
Jeff Browndc176c52013-04-02 18:09:29 -0700374}
375
Orion Hodsonb01e7fe2018-11-07 06:07:50 +0000376MODULE_API jstring jniCreateString(C_JNIEnv* env, const jchar* unicodeChars, jsize len) {
Fredrik Roubert2e312802017-07-04 21:53:08 +0200377 JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
Orion Hodsonaadb3732018-11-21 12:58:34 +0000378 return e->NewString(unicodeChars, len);
Fredrik Roubert2e312802017-07-04 21:53:08 +0200379}