1 /*
2  * Copyright (C) 2023 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 /**
18  * JNI utils for external use.
19  *
20  * This file may only be included by C++ code.
21  */
22 
23 #pragma once
24 
25 #include <jni.h>
26 
27 #include <string>
28 
29 #include "nativehelper/scoped_local_ref.h"
30 #include "nativehelper/scoped_utf_chars.h"
31 
32 namespace android {
33 namespace jnihelp {
34 
35 // Implementation details. DO NOT use directly.
36 namespace internal {
37 
GetCStr(const char * str)38 [[maybe_unused]] static const char* GetCStr(const char* str) { return str; }
GetCStr(const std::string & str)39 [[maybe_unused]] static const char* GetCStr(const std::string& str) { return str.c_str(); }
40 
41 }  // namespace internal
42 
43 // A class that implicitly casts to the default values of various JNI types.
44 // Used for returning from a JNI method when an exception occurs, where we don't care about the
45 // return value.
46 class JniDefaultValue {
47    public:
jboolean()48     operator jboolean() const { return JNI_FALSE; }
jbyte()49     operator jbyte() const { return 0; }
jchar()50     operator jchar() const { return 0; }
jshort()51     operator jshort() const { return 0; }
jint()52     operator jint() const { return 0; }
jlong()53     operator jlong() const { return 0; }
jfloat()54     operator jfloat() const { return 0; }
jdouble()55     operator jdouble() const { return 0; }
jobject()56     operator jobject() const { return nullptr; }
jclass()57     operator jclass() const { return nullptr; }
jstring()58     operator jstring() const { return nullptr; }
jarray()59     operator jarray() const { return nullptr; }
jobjectArray()60     operator jobjectArray() const { return nullptr; }
jbooleanArray()61     operator jbooleanArray() const { return nullptr; }
jbyteArray()62     operator jbyteArray() const { return nullptr; }
jcharArray()63     operator jcharArray() const { return nullptr; }
jshortArray()64     operator jshortArray() const { return nullptr; }
jintArray()65     operator jintArray() const { return nullptr; }
jlongArray()66     operator jlongArray() const { return nullptr; }
jfloatArray()67     operator jfloatArray() const { return nullptr; }
jdoubleArray()68     operator jdoubleArray() const { return nullptr; }
jthrowable()69     operator jthrowable() const { return nullptr; }
70 };
71 
72 // Gets `ScopedUtfChars` from a `jstring` expression.
73 //
74 // Throws `NullPointerException` and returns the default value if the given `jstring` is a null
75 // pointer.
76 //
77 // Examples:
78 //
79 // - If the function returns a value:
80 //
81 // jobject MyJniMethod(JNIEnv* env, jstring j_str) {
82 //   ScopedUtfChars str = GET_UTF_OR_RETURN(env, j_str);
83 //   // Safely use `str` here...
84 // }
85 //
86 // - If the function returns void:
87 //
88 // void MyJniMethod(JNIEnv* env, jstring j_str) {
89 //   ScopedUtfChars str = GET_UTF_OR_RETURN_VOID(env, j_str);
90 //   // Safely use `str` here...
91 // }
92 //
93 // The idiomatic way to construct an `std::string` using this macro (an additional string copy is
94 // performed):
95 //
96 // jobject MyJniMethod(JNIEnv* env, jstring j_str) {
97 //   std::string str(GET_UTF_OR_RETURN(env, j_str));
98 //   // Safely use `str` here...
99 // }
100 #define GET_UTF_OR_RETURN(env, expr) \
101     GET_UTF_OR_RETURN_IMPL_((env), (expr), android::jnihelp::JniDefaultValue())
102 #define GET_UTF_OR_RETURN_VOID(env, expr) GET_UTF_OR_RETURN_IMPL_((env), (expr))
103 
104 #define GET_UTF_OR_RETURN_IMPL_(env, expr, ...)                          \
105     ({                                                                   \
106         ScopedUtfChars __or_return_scoped_utf_chars(env, expr);          \
107         if (__or_return_scoped_utf_chars.c_str() == nullptr) {           \
108             /* Return with a pending exception from `ScopedUtfChars`. */ \
109             return __VA_ARGS__;                                          \
110         }                                                                \
111         std::move(__or_return_scoped_utf_chars);                         \
112     })
113 
114 // Creates `ScopedLocalRef<jstring>` from a `const char*` or `std::string` expression using
115 // NewStringUTF.
116 //
117 // Throws `OutOfMemoryError` and returns the default value if the system runs out of memory.
118 //
119 // Examples:
120 //
121 // - If the function returns a value:
122 //
123 // jobject MyJniMethod(JNIEnv* env) {
124 //   std::string str = "foo";
125 //   ScopedLocalRef<jstring> j_str = CREATE_UTF_OR_RETURN(env, str);
126 //   // Safely use `j_str` here...
127 // }
128 //
129 // - If the function returns void:
130 //
131 // void MyJniMethod(JNIEnv* env) {
132 //   std::string str = "foo";
133 //   ScopedLocalRef<jstring> j_str = CREATE_UTF_OR_RETURN_VOID(env, str);
134 //   // Safely use `j_str` here...
135 // }
136 #define CREATE_UTF_OR_RETURN(env, expr) \
137     CREATE_UTF_OR_RETURN_IMPL_((env), (expr), android::jnihelp::JniDefaultValue())
138 #define CREATE_UTF_OR_RETURN_VOID(env, expr) CREATE_UTF_OR_RETURN_IMPL_((env), (expr))
139 
140 #define CREATE_UTF_OR_RETURN_IMPL_(env, expr, ...)                                             \
141     ({                                                                                         \
142         const char* __or_return_c_str;                                                         \
143         ScopedLocalRef<jstring> __or_return_local_ref(                                         \
144             env,                                                                               \
145             env->NewStringUTF(__or_return_c_str = android::jnihelp::internal::GetCStr(expr))); \
146         /* `*__or_return_c_str` may be freed here, but we only compare the pointer against     \
147          * nullptr. DO NOT DEREFERENCE `*__or_return_c_str` after this point. */               \
148         /* `NewStringUTF` returns nullptr when OOM or the input is nullptr, but only throws an \
149          * exception when OOM. */                                                              \
150         if (__or_return_local_ref == nullptr && __or_return_c_str != nullptr) {                \
151             /* Return with a pending exception from `NewStringUTF`. */                         \
152             return __VA_ARGS__;                                                                \
153         }                                                                                      \
154         std::move(__or_return_local_ref);                                                      \
155     })
156 
157 }  // namespace jnihelp
158 }  // namespace android
159