diff options
Diffstat (limited to 'header_only_include/nativehelper/utils.h')
-rw-r--r-- | header_only_include/nativehelper/utils.h | 154 |
1 files changed, 154 insertions, 0 deletions
diff --git a/header_only_include/nativehelper/utils.h b/header_only_include/nativehelper/utils.h new file mode 100644 index 0000000..30f239e --- /dev/null +++ b/header_only_include/nativehelper/utils.h @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * JNI utils for external use. + * + * This file may only be included by C++ code. + */ + +#pragma once + +#include <jni.h> + +#include <string> + +#include "nativehelper/scoped_local_ref.h" +#include "nativehelper/scoped_utf_chars.h" + +namespace android { +namespace jnihelp { + +// Implementation details. DO NOT use directly. +namespace internal { + +[[maybe_unused]] static const char* get_c_str(const char* str) { return str; } +[[maybe_unused]] static const char* get_c_str(const std::string& str) { return str.c_str(); } + +} // namespace internal + +// A class that implicitly casts to the default values of various JNI types. +// Used for returning from a JNI method when an exception occurs, where we don't care about the +// return value. +class JniDefaultValue { + public: + operator jboolean() const { return JNI_FALSE; } + operator jbyte() const { return 0; } + operator jchar() const { return 0; } + operator jshort() const { return 0; } + operator jint() const { return 0; } + operator jlong() const { return 0; } + operator jfloat() const { return 0; } + operator jdouble() const { return 0; } + operator jobject() const { return nullptr; } + operator jclass() const { return nullptr; } + operator jstring() const { return nullptr; } + operator jarray() const { return nullptr; } + operator jobjectArray() const { return nullptr; } + operator jbooleanArray() const { return nullptr; } + operator jbyteArray() const { return nullptr; } + operator jcharArray() const { return nullptr; } + operator jshortArray() const { return nullptr; } + operator jintArray() const { return nullptr; } + operator jlongArray() const { return nullptr; } + operator jfloatArray() const { return nullptr; } + operator jdoubleArray() const { return nullptr; } + operator jthrowable() const { return nullptr; } +}; + +// Gets `ScopedUtfChars` from a `jstring` expression. +// +// Throws `NullPointerException` and returns the default value if the given `jstring` is a null +// pointer. +// +// Examples: +// +// - If the function returns a value: +// +// jobject MyJniMethod(JNIEnv* env, jstring j_str) { +// ScopedUtfChars str = GET_UTF_OR_RETURN(env, j_str); +// // Safely use `str` here... +// } +// +// - If the function returns void: +// +// void MyJniMethod(JNIEnv* env, jstring j_str) { +// ScopedUtfChars str = GET_UTF_OR_RETURN_VOID(env, j_str); +// // Safely use `str` here... +// } +// +// The idiomatic way to construct an `std::string` using this macro (an additional string copy is +// performed): +// +// jobject MyJniMethod(JNIEnv* env, jstring j_str) { +// std::string str(GET_UTF_OR_RETURN(env, j_str)); +// // Safely use `str` here... +// } +#define GET_UTF_OR_RETURN(env, expr) \ + GET_UTF_OR_RETURN_IMPL_((env), (expr), android::jnihelp::JniDefaultValue()) +#define GET_UTF_OR_RETURN_VOID(env, expr) GET_UTF_OR_RETURN_IMPL_((env), (expr)) + +#define GET_UTF_OR_RETURN_IMPL_(env, expr, ...) \ + ({ \ + ScopedUtfChars tmp_scoped_utf_chars_(env, expr); \ + if (tmp_scoped_utf_chars_.c_str() == nullptr) { \ + /* Return with a pending exception from `ScopedUtfChars`. */ \ + return __VA_ARGS__; \ + } \ + std::move(tmp_scoped_utf_chars_); \ + }) + +// Creates `ScopedLocalRef<jstring>` from a `const char*` or `std::string` expression using +// NewStringUTF. +// +// Throws `OutOfMemoryError` and returns the default value if the system runs out of memory. +// +// Examples: +// +// - If the function returns a value: +// +// jobject MyJniMethod(JNIEnv* env) { +// std::string str = "foo"; +// ScopedLocalRef<jstring> j_str = CREATE_UTF_OR_RETURN(env, str); +// // Safely use `j_str` here... +// } +// +// - If the function returns void: +// +// void MyJniMethod(JNIEnv* env) { +// std::string str = "foo"; +// ScopedLocalRef<jstring> j_str = CREATE_UTF_OR_RETURN_VOID(env, str); +// // Safely use `j_str` here... +// } +#define CREATE_UTF_OR_RETURN(env, expr) \ + CREATE_UTF_OR_RETURN_IMPL_((env), (expr), android::jnihelp::JniDefaultValue()) +#define CREATE_UTF_OR_RETURN_VOID(env, expr) CREATE_UTF_OR_RETURN_IMPL_((env), (expr)) + +#define CREATE_UTF_OR_RETURN_IMPL_(env, expr, ...) \ + ({ \ + const char* tmp_c_str_ = android::jnihelp::internal::get_c_str(expr); \ + ScopedLocalRef<jstring> tmp_local_ref_(env, env->NewStringUTF(tmp_c_str_)); \ + /* `NewStringUTF` returns nullptr when OOM or the input is nullptr, but only throws an \ + * exception when OOM. */ \ + if (tmp_local_ref_ == nullptr && tmp_c_str_ != nullptr) { \ + /* Return with a pending exception from `NewStringUTF`. */ \ + return __VA_ARGS__; \ + } \ + std::move(tmp_local_ref_); \ + }) + +} // namespace jnihelp +} // namespace android |