1 /*
2 * Copyright (C) 2018 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 #ifndef LIBTEXTCLASSIFIER_UTILS_JAVA_JNI_BASE_H_
18 #define LIBTEXTCLASSIFIER_UTILS_JAVA_JNI_BASE_H_
19
20 #include <jni.h>
21
22 #include <string>
23
24 #include "utils/base/statusor.h"
25
26 // When we use a macro as an argument for a macro, an additional level of
27 // indirection is needed, if the macro argument is used with # or ##.
28 #define TC3_ADD_QUOTES_HELPER(TOKEN) #TOKEN
29 #define TC3_ADD_QUOTES(TOKEN) TC3_ADD_QUOTES_HELPER(TOKEN)
30
31 #ifndef TC3_PACKAGE_NAME
32 #define TC3_PACKAGE_NAME com_google_android_textclassifier
33 #endif
34
35 #ifndef TC3_PACKAGE_PATH
36 #define TC3_PACKAGE_PATH \
37 "com/google/android/textclassifier/"
38 #endif
39
40 #define TC3_JNI_METHOD_NAME_INTERNAL(package_name, class_name, method_name) \
41 Java_##package_name##_##class_name##_##method_name
42
43 #define TC3_JNI_METHOD_PRIMITIVE(return_type, package_name, class_name, \
44 method_name) \
45 JNIEXPORT return_type JNICALL TC3_JNI_METHOD_NAME_INTERNAL( \
46 package_name, class_name, method_name)
47
48 // The indirection is needed to correctly expand the TC3_PACKAGE_NAME macro.
49 // See the explanation near TC3_ADD_QUOTES macro.
50 #define TC3_JNI_METHOD2(return_type, package_name, class_name, method_name) \
51 TC3_JNI_METHOD_PRIMITIVE(return_type, package_name, class_name, method_name)
52
53 #define TC3_JNI_METHOD(return_type, class_name, method_name) \
54 TC3_JNI_METHOD2(return_type, TC3_PACKAGE_NAME, class_name, method_name)
55
56 #define TC3_JNI_METHOD_NAME2(package_name, class_name, method_name) \
57 TC3_JNI_METHOD_NAME_INTERNAL(package_name, class_name, method_name)
58
59 #define TC3_JNI_METHOD_NAME(class_name, method_name) \
60 TC3_JNI_METHOD_NAME2(TC3_PACKAGE_NAME, class_name, method_name)
61
62 namespace libtextclassifier3 {
63
64 // Returns true if the requested capacity is available.
65 bool EnsureLocalCapacity(JNIEnv* env, int capacity);
66
67 // Returns true if there was an exception. Also it clears the exception.
68 bool JniExceptionCheckAndClear(JNIEnv* env,
69 bool print_exception_on_error = true);
70
71 // A deleter to be used with std::unique_ptr to delete JNI global references.
72 class GlobalRefDeleter {
73 public:
GlobalRefDeleter(JavaVM * jvm)74 explicit GlobalRefDeleter(JavaVM* jvm) : jvm_(jvm) {}
75
76 GlobalRefDeleter(const GlobalRefDeleter& orig) = default;
77
78 // Copy assignment to allow move semantics in ScopedGlobalRef.
79 GlobalRefDeleter& operator=(const GlobalRefDeleter& rhs) {
80 TC3_CHECK_EQ(jvm_, rhs.jvm_);
81 return *this;
82 }
83
84 // The delete operator.
operator()85 void operator()(jobject object) const {
86 JNIEnv* env;
87 if (object != nullptr && jvm_ != nullptr &&
88 JNI_OK ==
89 jvm_->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_4)) {
90 env->DeleteGlobalRef(object);
91 }
92 }
93
94 private:
95 // The jvm_ stashed to use for deletion.
96 JavaVM* const jvm_;
97 };
98
99 // A deleter to be used with std::unique_ptr to delete JNI local references.
100 class LocalRefDeleter {
101 public:
LocalRefDeleter(JNIEnv * env)102 explicit LocalRefDeleter(JNIEnv* env)
103 : env_(env) {} // NOLINT(runtime/explicit)
104
105 LocalRefDeleter(const LocalRefDeleter& orig) = default;
106
107 // Copy assignment to allow move semantics in ScopedLocalRef.
108 LocalRefDeleter& operator=(const LocalRefDeleter& rhs) {
109 env_ = rhs.env_;
110 return *this;
111 }
112
113 // The delete operator.
operator()114 void operator()(jobject object) const {
115 if (env_) {
116 env_->DeleteLocalRef(object);
117 }
118 }
119
120 private:
121 // The env_ stashed to use for deletion. Thread-local, don't share!
122 JNIEnv* env_;
123 };
124
125 // A smart pointer that deletes a reference when it goes out of scope.
126 //
127 // Note that this class is not thread-safe since it caches JNIEnv in
128 // the deleter. Do not use the same jobject across different threads.
129 template <typename T, typename Env, typename Deleter>
130 class ScopedRef {
131 public:
ScopedRef()132 ScopedRef() : ptr_(nullptr, Deleter(nullptr)) {}
ScopedRef(T value,Env * env)133 ScopedRef(T value, Env* env) : ptr_(value, Deleter(env)) {}
134
get()135 T get() const { return ptr_.get(); }
136
release()137 T release() { return ptr_.release(); }
138
139 bool operator!() const { return !ptr_; }
140
141 bool operator==(void* value) const { return ptr_.get() == value; }
142
143 explicit operator bool() const { return ptr_ != nullptr; }
144
reset(T value,Env * env)145 void reset(T value, Env* env) {
146 ptr_.reset(value);
147 ptr_.get_deleter() = Deleter(env);
148 }
149
150 private:
151 std::unique_ptr<typename std::remove_pointer<T>::type, Deleter> ptr_;
152 };
153
154 template <typename T, typename U, typename Env, typename Deleter>
155 inline bool operator==(const ScopedRef<T, Env, Deleter>& x,
156 const ScopedRef<U, Env, Deleter>& y) {
157 return x.get() == y.get();
158 }
159
160 template <typename T, typename Env, typename Deleter>
161 inline bool operator==(const ScopedRef<T, Env, Deleter>& x, std::nullptr_t) {
162 return x.get() == nullptr;
163 }
164
165 template <typename T, typename Env, typename Deleter>
166 inline bool operator==(std::nullptr_t, const ScopedRef<T, Env, Deleter>& x) {
167 return nullptr == x.get();
168 }
169
170 template <typename T, typename U, typename Env, typename Deleter>
171 inline bool operator!=(const ScopedRef<T, Env, Deleter>& x,
172 const ScopedRef<U, Env, Deleter>& y) {
173 return x.get() != y.get();
174 }
175
176 template <typename T, typename Env, typename Deleter>
177 inline bool operator!=(const ScopedRef<T, Env, Deleter>& x, std::nullptr_t) {
178 return x.get() != nullptr;
179 }
180
181 template <typename T, typename Env, typename Deleter>
182 inline bool operator!=(std::nullptr_t, const ScopedRef<T, Env, Deleter>& x) {
183 return nullptr != x.get();
184 }
185
186 template <typename T, typename U, typename Env, typename Deleter>
187 inline bool operator<(const ScopedRef<T, Env, Deleter>& x,
188 const ScopedRef<U, Env, Deleter>& y) {
189 return x.get() < y.get();
190 }
191
192 template <typename T, typename U, typename Env, typename Deleter>
193 inline bool operator>(const ScopedRef<T, Env, Deleter>& x,
194 const ScopedRef<U, Env, Deleter>& y) {
195 return x.get() > y.get();
196 }
197
198 // A smart pointer that deletes a JNI global reference when it goes out
199 // of scope. Usage is:
200 // ScopedGlobalRef<jobject> scoped_global(env->JniFunction(), jvm);
201 template <typename T>
202 using ScopedGlobalRef = ScopedRef<T, JavaVM, GlobalRefDeleter>;
203
204 // Ditto, but usage is:
205 // ScopedLocalRef<jobject> scoped_local(env->JniFunction(), env);
206 template <typename T>
207 using ScopedLocalRef = ScopedRef<T, JNIEnv, LocalRefDeleter>;
208
209 // A helper to create global references.
210 template <typename T>
MakeGlobalRef(T object,JNIEnv * env,JavaVM * jvm)211 ScopedGlobalRef<T> MakeGlobalRef(T object, JNIEnv* env, JavaVM* jvm) {
212 const jobject global_object = env->NewGlobalRef(object);
213 return ScopedGlobalRef<T>(reinterpret_cast<T>(global_object), jvm);
214 }
215
216 } // namespace libtextclassifier3
217
218 #endif // LIBTEXTCLASSIFIER_UTILS_JAVA_JNI_BASE_H_
219