aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2024-01-10 19:00:09 +0000
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2024-01-10 19:00:09 +0000
commit11c2fa0ce8936d19e45cd6d1201dc2c8b2296a6a (patch)
tree899a2d71e3fd4029dedb2552f696933d8edcfad6
parent8638d84afdfd8ac3e4d049a328b31dfb27bf2f8a (diff)
parent31a261d502eeac8ffe7b03829f2450c8fb794c9c (diff)
downloadlibnativehelper-aml_tz5_341510010.tar.gz
Snap for 11296156 from 31a261d502eeac8ffe7b03829f2450c8fb794c9c to mainline-tzdata5-releaseaml_tz5_341510070aml_tz5_341510050aml_tz5_341510010aml_tz5_341510010
Change-Id: I9cb6983209f33163e756d34bdfb5744d1de18f01
-rw-r--r--Android.bp1
-rw-r--r--README.md2
-rw-r--r--header_only_include/nativehelper/nativehelper_utils.h2
-rw-r--r--header_only_include/nativehelper/scoped_local_ref.h2
-rw-r--r--header_only_include/nativehelper/scoped_utf_chars.h12
-rw-r--r--header_only_include/nativehelper/utils.h158
-rw-r--r--include/nativehelper/Utils.h19
-rw-r--r--tests_mts/jni/libnativehelper_test.cpp162
8 files changed, 355 insertions, 3 deletions
diff --git a/Android.bp b/Android.bp
index b161d58..a29384f 100644
--- a/Android.bp
+++ b/Android.bp
@@ -36,6 +36,7 @@ cc_defaults {
"-std=c11",
],
shared_libs: ["liblog"],
+ export_shared_lib_headers: ["liblog"],
}
cc_library_headers {
diff --git a/README.md b/README.md
index ea96f65..d9cae65 100644
--- a/README.md
+++ b/README.md
@@ -40,6 +40,7 @@ See:
* [nativehelper/scoped_primitive_array.h](header_only_include/nativehelper/scoped_primitive_array.h)
* [nativehelper/scoped_local_ref.h](header_only_include/nativehelper/scoped_local_ref.h)
* [nativehelper/scoped_local_frame.h](header_only_include/nativehelper/scoped_local_frame.h)
+* [nativehelper/utils.h](header_only_include/nativehelper/utils.h)
### jni_platform_headers
@@ -76,6 +77,7 @@ See:
* [nativehelper/ScopedPrimitiveArray.h](include/nativehelper/ScopedPrimitiveArray.h)
* [nativehelper/ScopedStringChars.h](include/nativehelper/ScopedStringChars.h)
* [nativehelper/toStringArray.h](include/nativehelper/toStringArray.h)
+* [nativehelper/Utils.h](include/nativehelper/Utils.h)
### libnativehelper_compat_libc++
diff --git a/header_only_include/nativehelper/nativehelper_utils.h b/header_only_include/nativehelper/nativehelper_utils.h
index 9cb8195..6c95c68 100644
--- a/header_only_include/nativehelper/nativehelper_utils.h
+++ b/header_only_include/nativehelper/nativehelper_utils.h
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+/** JNI utils for nativehelper-internal use. */
+
#pragma once
#include <jni.h>
diff --git a/header_only_include/nativehelper/scoped_local_ref.h b/header_only_include/nativehelper/scoped_local_ref.h
index cd35a88..32ae885 100644
--- a/header_only_include/nativehelper/scoped_local_ref.h
+++ b/header_only_include/nativehelper/scoped_local_ref.h
@@ -23,6 +23,8 @@
#include "nativehelper_utils.h"
// A smart pointer that deletes a JNI local reference when it goes out of scope.
+//
+// For creating a `ScopedLocalRef<jstring>`, consider using `CREATE_UTF_OR_RETURN`.
template<typename T>
class ScopedLocalRef {
public:
diff --git a/header_only_include/nativehelper/scoped_utf_chars.h b/header_only_include/nativehelper/scoped_utf_chars.h
index 363ff42..25de0fc 100644
--- a/header_only_include/nativehelper/scoped_utf_chars.h
+++ b/header_only_include/nativehelper/scoped_utf_chars.h
@@ -23,6 +23,11 @@
#include "nativehelper_utils.h"
+// Protect this with __has_include to cope with `stl: "none"` users.
+#if __has_include(<string_view>)
+#include <string_view>
+#endif
+
// A smart pointer that provides read-only access to a Java string's UTF chars.
// Unlike GetStringUTFChars, we throw NullPointerException rather than abort if
// passed a null jstring, and c_str will return nullptr.
@@ -32,6 +37,8 @@
// if (name.c_str() == nullptr) {
// return nullptr;
// }
+//
+// Also consider using `GET_UTF_OR_RETURN`, a shorthand for the 4 lines above.
class ScopedUtfChars {
public:
ScopedUtfChars(JNIEnv* env, jstring s) : env_(env), string_(s) {
@@ -84,6 +91,10 @@ class ScopedUtfChars {
return utf_chars_[n];
}
+#if __has_include(<string_view>)
+ operator std::string_view() const { return utf_chars_; }
+#endif
+
private:
JNIEnv* env_;
jstring string_;
@@ -91,4 +102,3 @@ class ScopedUtfChars {
DISALLOW_COPY_AND_ASSIGN(ScopedUtfChars);
};
-
diff --git a/header_only_include/nativehelper/utils.h b/header_only_include/nativehelper/utils.h
new file mode 100644
index 0000000..12f591b
--- /dev/null
+++ b/header_only_include/nativehelper/utils.h
@@ -0,0 +1,158 @@
+/*
+ * 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* GetCStr(const char* str) { return str; }
+[[maybe_unused]] static const char* GetCStr(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 __or_return_scoped_utf_chars(env, expr); \
+ if (__or_return_scoped_utf_chars.c_str() == nullptr) { \
+ /* Return with a pending exception from `ScopedUtfChars`. */ \
+ return __VA_ARGS__; \
+ } \
+ std::move(__or_return_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* __or_return_c_str; \
+ ScopedLocalRef<jstring> __or_return_local_ref( \
+ env, \
+ env->NewStringUTF(__or_return_c_str = android::jnihelp::internal::GetCStr(expr))); \
+ /* `*__or_return_c_str` may be freed here, but we only compare the pointer against \
+ * nullptr. DO NOT DEREFERENCE `*__or_return_c_str` after this point. */ \
+ /* `NewStringUTF` returns nullptr when OOM or the input is nullptr, but only throws an \
+ * exception when OOM. */ \
+ if (__or_return_local_ref == nullptr && __or_return_c_str != nullptr) { \
+ /* Return with a pending exception from `NewStringUTF`. */ \
+ return __VA_ARGS__; \
+ } \
+ std::move(__or_return_local_ref); \
+ })
+
+} // namespace jnihelp
+} // namespace android
diff --git a/include/nativehelper/Utils.h b/include/nativehelper/Utils.h
new file mode 100644
index 0000000..c05b2f1
--- /dev/null
+++ b/include/nativehelper/Utils.h
@@ -0,0 +1,19 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <nativehelper/utils.h>
diff --git a/tests_mts/jni/libnativehelper_test.cpp b/tests_mts/jni/libnativehelper_test.cpp
index 5371004..bd448f9 100644
--- a/tests_mts/jni/libnativehelper_test.cpp
+++ b/tests_mts/jni/libnativehelper_test.cpp
@@ -14,9 +14,15 @@
* limitations under the License.
*/
-#include <libnativehelper_test.h>
+#include "libnativehelper_test.h"
-#include <nativetesthelper_jni/utils.h>
+#include <memory>
+
+#include "jni.h"
+#include "nativehelper/scoped_local_ref.h"
+#include "nativehelper/scoped_utf_chars.h"
+#include "nativehelper/utils.h"
+#include "nativetesthelper_jni/utils.h"
void LibnativehelperTest::SetUp() {
int result = GetJavaVM()->GetEnv(reinterpret_cast<void**>(&mEnv), JNI_VERSION_1_6);
@@ -27,3 +33,155 @@ void LibnativehelperTest::SetUp() {
void LibnativehelperTest::TearDown() {
mEnv = nullptr;
}
+
+TEST_F(LibnativehelperTest, GetUtfOrReturn) {
+ ScopedLocalRef<jstring> j_str(mEnv, mEnv->NewStringUTF("foo"));
+ std::unique_ptr<ScopedUtfChars> result;
+
+ jint ret = [&](JNIEnv* env) -> jint {
+ ScopedUtfChars str = GET_UTF_OR_RETURN(env, j_str.get());
+ result.reset(new ScopedUtfChars(std::move(str)));
+ return 1;
+ }(mEnv);
+
+ EXPECT_EQ(result->c_str(), std::string_view("foo"));
+ EXPECT_FALSE(mEnv->ExceptionCheck());
+ EXPECT_EQ(ret, 1);
+}
+
+TEST_F(LibnativehelperTest, GetUtfOrReturnVoid) {
+ ScopedLocalRef<jstring> j_str(mEnv, mEnv->NewStringUTF("foo"));
+ std::unique_ptr<ScopedUtfChars> result;
+
+ [&](JNIEnv* env) -> void {
+ ScopedUtfChars str = GET_UTF_OR_RETURN_VOID(env, j_str.get());
+ result.reset(new ScopedUtfChars(std::move(str)));
+ }(mEnv);
+
+ EXPECT_EQ(result->c_str(), std::string_view("foo"));
+ EXPECT_FALSE(mEnv->ExceptionCheck());
+}
+
+TEST_F(LibnativehelperTest, GetUtfOrReturnFailed) {
+ jint ret = [&](JNIEnv* env) -> jint {
+ ScopedUtfChars str = GET_UTF_OR_RETURN(env, nullptr);
+ return 1;
+ }(mEnv);
+
+ EXPECT_TRUE(mEnv->ExceptionCheck());
+ EXPECT_EQ(ret, 0);
+
+ mEnv->ExceptionClear();
+}
+
+TEST_F(LibnativehelperTest, GetUtfOrReturnVoidFailed) {
+ bool execution_completed = false;
+
+ [&](JNIEnv* env) -> void {
+ ScopedUtfChars str = GET_UTF_OR_RETURN_VOID(env, nullptr);
+ execution_completed = true;
+ }(mEnv);
+
+ EXPECT_TRUE(mEnv->ExceptionCheck());
+ EXPECT_FALSE(execution_completed);
+
+ mEnv->ExceptionClear();
+}
+
+TEST_F(LibnativehelperTest, CreateUtfOrReturn) {
+ std::unique_ptr<ScopedLocalRef<jstring>> result;
+
+ jint ret = [&](JNIEnv* env) -> jint {
+ ScopedLocalRef<jstring> j_str = CREATE_UTF_OR_RETURN(env, "foo");
+ result.reset(new ScopedLocalRef<jstring>(std::move(j_str)));
+ return 1;
+ }(mEnv);
+
+ ScopedUtfChars str(mEnv, result->get());
+ EXPECT_EQ(str.c_str(), std::string_view("foo"));
+ EXPECT_FALSE(mEnv->ExceptionCheck());
+ EXPECT_EQ(ret, 1);
+}
+
+class MyString : public std::string {
+ public:
+ explicit MyString(const char* c_str) : std::string(c_str) {}
+
+ // Force clear the string to catch use-after-free issues.
+ ~MyString() { clear(); }
+};
+
+// `expr` creates a temporary object and evaluates to it.
+TEST_F(LibnativehelperTest, CreateUtfOrReturnExprEvaluatesToTemporary) {
+ std::unique_ptr<ScopedLocalRef<jstring>> result;
+
+ jint ret = [&](JNIEnv* env) -> jint {
+ ScopedLocalRef<jstring> j_str = CREATE_UTF_OR_RETURN(env, MyString("foo"));
+ result.reset(new ScopedLocalRef<jstring>(std::move(j_str)));
+ return 1;
+ }(mEnv);
+
+ ScopedUtfChars str(mEnv, result->get());
+ EXPECT_EQ(str.c_str(), std::string_view("foo"));
+ EXPECT_FALSE(mEnv->ExceptionCheck());
+ EXPECT_EQ(ret, 1);
+}
+
+// `expr` creates a temporary object and evaluates to something else backed by it.
+TEST_F(LibnativehelperTest, CreateUtfOrReturnExprEvaluatesToValueBackedByTemporary) {
+ std::unique_ptr<ScopedLocalRef<jstring>> result;
+
+ jint ret = [&](JNIEnv* env) -> jint {
+ ScopedLocalRef<jstring> j_str = CREATE_UTF_OR_RETURN(env, MyString("foo").c_str());
+ result.reset(new ScopedLocalRef<jstring>(std::move(j_str)));
+ return 1;
+ }(mEnv);
+
+ ScopedUtfChars str(mEnv, result->get());
+ EXPECT_EQ(str.c_str(), std::string_view("foo"));
+ EXPECT_FALSE(mEnv->ExceptionCheck());
+ EXPECT_EQ(ret, 1);
+}
+
+TEST_F(LibnativehelperTest, CreateUtfOrReturnVoid) {
+ std::unique_ptr<ScopedLocalRef<jstring>> result;
+
+ [&](JNIEnv* env) -> void {
+ ScopedLocalRef<jstring> j_str = CREATE_UTF_OR_RETURN_VOID(env, "foo");
+ result.reset(new ScopedLocalRef<jstring>(std::move(j_str)));
+ }(mEnv);
+
+ ScopedUtfChars str(mEnv, result->get());
+ EXPECT_EQ(str.c_str(), std::string_view("foo"));
+ EXPECT_FALSE(mEnv->ExceptionCheck());
+}
+
+TEST_F(LibnativehelperTest, CreateUtfOrReturnFailed) {
+ JNINativeInterface interface;
+ interface.NewStringUTF = [](JNIEnv*, const char*) -> jstring { return nullptr; };
+ JNIEnv fake_env;
+ fake_env.functions = &interface;
+
+ jint ret = [&](JNIEnv* env) -> jint {
+ ScopedLocalRef<jstring> j_str = CREATE_UTF_OR_RETURN(env, "foo");
+ return 1;
+ }(&fake_env);
+
+ EXPECT_EQ(ret, 0);
+}
+
+TEST_F(LibnativehelperTest, CreateUtfOrReturnVoidFailed) {
+ JNINativeInterface interface;
+ interface.NewStringUTF = [](JNIEnv*, const char*) -> jstring { return nullptr; };
+ JNIEnv fake_env;
+ fake_env.functions = &interface;
+
+ bool execution_completed = false;
+
+ [&](JNIEnv* env) -> void {
+ ScopedLocalRef<jstring> j_str = CREATE_UTF_OR_RETURN_VOID(env, "foo");
+ execution_completed = true;
+ }(&fake_env);
+
+ EXPECT_FALSE(execution_completed);
+}