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