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