1 // Copyright 2024 The Chromium Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef JNI_ZERO_JAVA_REFS_H_ 6 #define JNI_ZERO_JAVA_REFS_H_ 7 8 #include <jni.h> 9 10 #include <type_traits> 11 #include <utility> 12 13 #include "third_party/jni_zero/jni_export.h" 14 #include "third_party/jni_zero/logging.h" 15 16 namespace jni_zero { 17 18 // Creates a new local reference frame, in which at least a given number of 19 // local references can be created. Note that local references already created 20 // in previous local frames are still valid in the current local frame. 21 class JNI_ZERO_COMPONENT_BUILD_EXPORT ScopedJavaLocalFrame { 22 public: 23 explicit ScopedJavaLocalFrame(JNIEnv* env); 24 ScopedJavaLocalFrame(JNIEnv* env, int capacity); 25 26 ScopedJavaLocalFrame(const ScopedJavaLocalFrame&) = delete; 27 ScopedJavaLocalFrame& operator=(const ScopedJavaLocalFrame&) = delete; 28 29 ~ScopedJavaLocalFrame(); 30 31 private: 32 // This class is only good for use on the thread it was created on so 33 // it's safe to cache the non-threadsafe JNIEnv* inside this object. 34 JNIEnv* env_; 35 }; 36 37 // Forward declare the generic java reference template class. 38 template <typename T> 39 class JavaRef; 40 41 // Template specialization of JavaRef, which acts as the base class for all 42 // other JavaRef<> template types. This allows you to e.g. pass 43 // ScopedJavaLocalRef<jstring> into a function taking const JavaRef<jobject>& 44 template <> 45 class JNI_ZERO_COMPONENT_BUILD_EXPORT JavaRef<jobject> { 46 public: 47 // Initializes a null reference. JavaRef()48 constexpr JavaRef() {} 49 50 // Allow nullptr to be converted to JavaRef. This avoids having to declare an 51 // empty JavaRef just to pass null to a function, and makes C++ "nullptr" and 52 // Java "null" equivalent. JavaRef(std::nullptr_t)53 constexpr JavaRef(std::nullptr_t) {} 54 55 JavaRef(const JavaRef&) = delete; 56 JavaRef& operator=(const JavaRef&) = delete; 57 58 // Public to allow destruction of null JavaRef objects. ~JavaRef()59 ~JavaRef() {} 60 61 // TODO(torne): maybe rename this to get() for consistency with unique_ptr 62 // once there's fewer unnecessary uses of it in the codebase. obj()63 jobject obj() const { return obj_; } 64 65 explicit operator bool() const { return obj_ != nullptr; } 66 67 // Deprecated. Just use bool conversion. 68 // TODO(torne): replace usage and remove this. is_null()69 bool is_null() const { return obj_ == nullptr; } 70 71 protected: 72 // Takes ownership of the |obj| reference passed; requires it to be a local 73 // reference type. 74 #if JNI_ZERO_DCHECK_IS_ON() 75 // Implementation contains a DCHECK; implement out-of-line when DCHECK_IS_ON. 76 JavaRef(JNIEnv* env, jobject obj); 77 #else 78 JavaRef(JNIEnv* env, jobject obj) : obj_(obj) {} 79 #endif 80 81 // Used for move semantics. obj_ must have been released first if non-null. steal(JavaRef && other)82 void steal(JavaRef&& other) { 83 obj_ = other.obj_; 84 other.obj_ = nullptr; 85 } 86 87 // The following are implementation detail convenience methods, for 88 // use by the sub-classes. 89 JNIEnv* SetNewLocalRef(JNIEnv* env, jobject obj); 90 void SetNewGlobalRef(JNIEnv* env, jobject obj); 91 void ResetLocalRef(JNIEnv* env); 92 void ResetGlobalRef(); 93 ReleaseInternal()94 jobject ReleaseInternal() { 95 jobject obj = obj_; 96 obj_ = nullptr; 97 return obj; 98 } 99 100 private: 101 jobject obj_ = nullptr; 102 }; 103 104 // Forward declare the object array reader for the convenience function. 105 template <typename T> 106 class JavaObjectArrayReader; 107 108 // Generic base class for ScopedJavaLocalRef and ScopedJavaGlobalRef. Useful 109 // for allowing functions to accept a reference without having to mandate 110 // whether it is a local or global type. 111 template <typename T> 112 class JavaRef : public JavaRef<jobject> { 113 public: JavaRef()114 constexpr JavaRef() {} JavaRef(std::nullptr_t)115 constexpr JavaRef(std::nullptr_t) {} 116 117 JavaRef(const JavaRef&) = delete; 118 JavaRef& operator=(const JavaRef&) = delete; 119 ~JavaRef()120 ~JavaRef() {} 121 obj()122 T obj() const { return static_cast<T>(JavaRef<jobject>::obj()); } 123 124 // Get a JavaObjectArrayReader for the array pointed to by this reference. 125 // Only defined for JavaRef<jobjectArray>. 126 // You must pass the type of the array elements (usually jobject) as the 127 // template parameter. 128 template <typename ElementType, 129 typename T_ = T, 130 typename = std::enable_if_t<std::is_same_v<T_, jobjectArray>>> ReadElements()131 JavaObjectArrayReader<ElementType> ReadElements() const { 132 return JavaObjectArrayReader<ElementType>(*this); 133 } 134 135 protected: JavaRef(JNIEnv * env,T obj)136 JavaRef(JNIEnv* env, T obj) : JavaRef<jobject>(env, obj) {} 137 }; 138 139 // Holds a local reference to a JNI method parameter. 140 // Method parameters should not be deleted, and so this class exists purely to 141 // wrap them as a JavaRef<T> in the JNI binding generator. Do not use in new 142 // code. 143 // TODO(crbug.com/40425392): Remove all uses of JavaParamRef to use JavaRef 144 // instead. 145 template <typename T> 146 class JavaParamRef : public JavaRef<T> { 147 public: 148 // Assumes that |obj| is a parameter passed to a JNI method from Java. 149 // Does not assume ownership as parameters should not be deleted. JavaParamRef(JNIEnv * env,T obj)150 JavaParamRef(JNIEnv* env, T obj) : JavaRef<T>(env, obj) {} 151 152 // Allow nullptr to be converted to JavaParamRef. Some unit tests call JNI 153 // methods directly from C++ and pass null for objects which are not actually 154 // used by the implementation (e.g. the caller object); allow this to keep 155 // working. JavaParamRef(std::nullptr_t)156 JavaParamRef(std::nullptr_t) {} 157 158 JavaParamRef(const JavaParamRef&) = delete; 159 JavaParamRef& operator=(const JavaParamRef&) = delete; 160 ~JavaParamRef()161 ~JavaParamRef() {} 162 T()163 operator T() const { return JavaRef<T>::obj(); } 164 }; 165 166 // Holds a local reference to a Java object. The local reference is scoped 167 // to the lifetime of this object. 168 // Instances of this class may hold onto any JNIEnv passed into it until 169 // destroyed. Therefore, since a JNIEnv is only suitable for use on a single 170 // thread, objects of this class must be created, used, and destroyed, on a 171 // single thread. 172 // Therefore, this class should only be used as a stack-based object and from a 173 // single thread. If you wish to have the reference outlive the current 174 // callstack (e.g. as a class member) or you wish to pass it across threads, 175 // use a ScopedJavaGlobalRef instead. 176 template <typename T> 177 class ScopedJavaLocalRef : public JavaRef<T> { 178 public: 179 // Take ownership of a bare jobject. This does not create a new reference. 180 // This should only be used by JNI helper functions, or in cases where code 181 // must call JNIEnv methods directly. Adopt(JNIEnv * env,T obj)182 static ScopedJavaLocalRef Adopt(JNIEnv* env, T obj) { 183 return ScopedJavaLocalRef(env, obj); 184 } 185 ScopedJavaLocalRef()186 constexpr ScopedJavaLocalRef() {} ScopedJavaLocalRef(std::nullptr_t)187 constexpr ScopedJavaLocalRef(std::nullptr_t) {} 188 189 // Copy constructor. This is required in addition to the copy conversion 190 // constructor below. ScopedJavaLocalRef(const ScopedJavaLocalRef & other)191 ScopedJavaLocalRef(const ScopedJavaLocalRef& other) : env_(other.env_) { 192 JavaRef<T>::SetNewLocalRef(env_, other.obj()); 193 } 194 195 // Copy conversion constructor. 196 template <typename U, 197 typename = std::enable_if_t<std::is_convertible_v<U, T>>> ScopedJavaLocalRef(const ScopedJavaLocalRef<U> & other)198 ScopedJavaLocalRef(const ScopedJavaLocalRef<U>& other) : env_(other.env_) { 199 JavaRef<T>::SetNewLocalRef(env_, other.obj()); 200 } 201 202 // Move constructor. This is required in addition to the move conversion 203 // constructor below. ScopedJavaLocalRef(ScopedJavaLocalRef && other)204 ScopedJavaLocalRef(ScopedJavaLocalRef&& other) : env_(other.env_) { 205 JavaRef<T>::steal(std::move(other)); 206 } 207 208 // Move conversion constructor. 209 template <typename U, 210 typename = std::enable_if_t<std::is_convertible_v<U, T>>> ScopedJavaLocalRef(ScopedJavaLocalRef<U> && other)211 ScopedJavaLocalRef(ScopedJavaLocalRef<U>&& other) : env_(other.env_) { 212 JavaRef<T>::steal(std::move(other)); 213 } 214 215 // Constructor for other JavaRef types. ScopedJavaLocalRef(const JavaRef<T> & other)216 explicit ScopedJavaLocalRef(const JavaRef<T>& other) { Reset(other); } 217 ScopedJavaLocalRef(JNIEnv * env,const JavaRef<T> & other)218 ScopedJavaLocalRef(JNIEnv* env, const JavaRef<T>& other) { Reset(other); } 219 220 // Assumes that |obj| is a local reference to a Java object and takes 221 // ownership of this local reference. 222 // TODO(torne): make legitimate uses call Adopt() instead, and make this 223 // private. ScopedJavaLocalRef(JNIEnv * env,T obj)224 ScopedJavaLocalRef(JNIEnv* env, T obj) : JavaRef<T>(env, obj), env_(env) {} 225 ~ScopedJavaLocalRef()226 ~ScopedJavaLocalRef() { Reset(); } 227 228 // Null assignment, for disambiguation. 229 ScopedJavaLocalRef& operator=(std::nullptr_t) { 230 Reset(); 231 return *this; 232 } 233 234 // Copy assignment. 235 ScopedJavaLocalRef& operator=(const ScopedJavaLocalRef& other) { 236 Reset(other); 237 return *this; 238 } 239 240 // Copy conversion assignment. 241 template <typename U, 242 typename = std::enable_if_t<std::is_convertible_v<U, T>>> 243 ScopedJavaLocalRef& operator=(const ScopedJavaLocalRef<U>& other) { 244 Reset(other); 245 return *this; 246 } 247 248 // Move assignment. 249 template <typename U, 250 typename = std::enable_if_t<std::is_convertible_v<U, T>>> 251 ScopedJavaLocalRef& operator=(ScopedJavaLocalRef<U>&& other) { 252 env_ = other.env_; 253 Reset(); 254 JavaRef<T>::steal(std::move(other)); 255 return *this; 256 } 257 258 // Assignment for other JavaRef types. 259 ScopedJavaLocalRef& operator=(const JavaRef<T>& other) { 260 Reset(other); 261 return *this; 262 } 263 Reset()264 void Reset() { JavaRef<T>::ResetLocalRef(env_); } 265 266 template <typename U, 267 typename = std::enable_if_t<std::is_convertible_v<U, T>>> Reset(const ScopedJavaLocalRef<U> & other)268 void Reset(const ScopedJavaLocalRef<U>& other) { 269 // We can copy over env_ here as |other| instance must be from the same 270 // thread as |this| local ref. (See class comment for multi-threading 271 // limitations, and alternatives). 272 env_ = JavaRef<T>::SetNewLocalRef(other.env_, other.obj()); 273 } 274 Reset(const JavaRef<T> & other)275 void Reset(const JavaRef<T>& other) { 276 // If |env_| was not yet set (is still null) it will be attached to the 277 // current thread in SetNewLocalRef(). 278 env_ = JavaRef<T>::SetNewLocalRef(env_, other.obj()); 279 } 280 281 // Releases the local reference to the caller. The caller *must* delete the 282 // local reference when it is done with it. Note that calling a Java method 283 // is *not* a transfer of ownership and Release() should not be used. Release()284 T Release() { return static_cast<T>(JavaRef<T>::ReleaseInternal()); } 285 286 // Alias for Release(). For use in templates when global refs are invalid. ReleaseLocal()287 T ReleaseLocal() { return static_cast<T>(JavaRef<T>::ReleaseInternal()); } 288 289 private: 290 // This class is only good for use on the thread it was created on so 291 // it's safe to cache the non-threadsafe JNIEnv* inside this object. 292 JNIEnv* env_ = nullptr; 293 294 // Prevent ScopedJavaLocalRef(JNIEnv*, T obj) from being used to take 295 // ownership of a JavaParamRef's underlying object - parameters are not 296 // allowed to be deleted and so should not be owned by ScopedJavaLocalRef. 297 // TODO(torne): this can be removed once JavaParamRef no longer has an 298 // implicit conversion back to T. 299 ScopedJavaLocalRef(JNIEnv* env, const JavaParamRef<T>& other); 300 301 // Friend required to get env_ from conversions. 302 template <typename U> 303 friend class ScopedJavaLocalRef; 304 305 // Avoids JavaObjectArrayReader having to accept and store its own env. 306 template <typename U> 307 friend class JavaObjectArrayReader; 308 }; 309 310 // Holds a global reference to a Java object. The global reference is scoped 311 // to the lifetime of this object. This class does not hold onto any JNIEnv* 312 // passed to it, hence it is safe to use across threads (within the constraints 313 // imposed by the underlying Java object that it references). 314 template <typename T> 315 class ScopedJavaGlobalRef : public JavaRef<T> { 316 public: ScopedJavaGlobalRef()317 constexpr ScopedJavaGlobalRef() {} ScopedJavaGlobalRef(std::nullptr_t)318 constexpr ScopedJavaGlobalRef(std::nullptr_t) {} 319 320 // Copy constructor. This is required in addition to the copy conversion 321 // constructor below. ScopedJavaGlobalRef(const ScopedJavaGlobalRef & other)322 ScopedJavaGlobalRef(const ScopedJavaGlobalRef& other) { Reset(other); } 323 324 // Copy conversion constructor. 325 template <typename U, 326 typename = std::enable_if_t<std::is_convertible_v<U, T>>> ScopedJavaGlobalRef(const ScopedJavaGlobalRef<U> & other)327 ScopedJavaGlobalRef(const ScopedJavaGlobalRef<U>& other) { 328 Reset(other); 329 } 330 331 // Move constructor. This is required in addition to the move conversion 332 // constructor below. ScopedJavaGlobalRef(ScopedJavaGlobalRef && other)333 ScopedJavaGlobalRef(ScopedJavaGlobalRef&& other) { 334 JavaRef<T>::steal(std::move(other)); 335 } 336 337 // Move conversion constructor. 338 template <typename U, 339 typename = std::enable_if_t<std::is_convertible_v<U, T>>> ScopedJavaGlobalRef(ScopedJavaGlobalRef<U> && other)340 ScopedJavaGlobalRef(ScopedJavaGlobalRef<U>&& other) { 341 JavaRef<T>::steal(std::move(other)); 342 } 343 344 // Conversion constructor for other JavaRef types. ScopedJavaGlobalRef(const JavaRef<T> & other)345 explicit ScopedJavaGlobalRef(const JavaRef<T>& other) { Reset(other); } 346 ScopedJavaGlobalRef(JNIEnv * env,const JavaRef<T> & other)347 ScopedJavaGlobalRef(JNIEnv* env, const JavaRef<T>& other) { 348 JavaRef<T>::SetNewGlobalRef(env, other.obj()); 349 } 350 351 // Create a new global reference to the object. 352 // Deprecated. Don't use bare jobjects; use a JavaRef as the input. ScopedJavaGlobalRef(JNIEnv * env,T obj)353 ScopedJavaGlobalRef(JNIEnv* env, T obj) { Reset(env, obj); } 354 ~ScopedJavaGlobalRef()355 ~ScopedJavaGlobalRef() { Reset(); } 356 357 // Null assignment, for disambiguation. 358 ScopedJavaGlobalRef& operator=(std::nullptr_t) { 359 Reset(); 360 return *this; 361 } 362 363 // Copy assignment. 364 ScopedJavaGlobalRef& operator=(const ScopedJavaGlobalRef& other) { 365 Reset(other); 366 return *this; 367 } 368 369 // Copy conversion assignment. 370 template <typename U, 371 typename = std::enable_if_t<std::is_convertible_v<U, T>>> 372 ScopedJavaGlobalRef& operator=(const ScopedJavaGlobalRef<U>& other) { 373 Reset(other); 374 return *this; 375 } 376 377 // Move assignment. 378 template <typename U, 379 typename = std::enable_if_t<std::is_convertible_v<U, T>>> 380 ScopedJavaGlobalRef& operator=(ScopedJavaGlobalRef<U>&& other) { 381 Reset(); 382 JavaRef<T>::steal(std::move(other)); 383 return *this; 384 } 385 386 // Assignment for other JavaRef types. 387 ScopedJavaGlobalRef& operator=(const JavaRef<T>& other) { 388 Reset(other); 389 return *this; 390 } 391 Reset()392 void Reset() { JavaRef<T>::ResetGlobalRef(); } 393 394 template <typename U, 395 typename = std::enable_if_t<std::is_convertible_v<U, T>>> Reset(const ScopedJavaGlobalRef<U> & other)396 void Reset(const ScopedJavaGlobalRef<U>& other) { 397 Reset(nullptr, other.obj()); 398 } 399 Reset(const JavaRef<T> & other)400 void Reset(const JavaRef<T>& other) { Reset(nullptr, other.obj()); } 401 402 // Deprecated. You can just use Reset(const JavaRef&). Reset(JNIEnv * env,const JavaParamRef<T> & other)403 void Reset(JNIEnv* env, const JavaParamRef<T>& other) { 404 Reset(env, other.obj()); 405 } 406 407 // Deprecated. Don't use bare jobjects; use a JavaRef as the input. Reset(JNIEnv * env,T obj)408 void Reset(JNIEnv* env, T obj) { JavaRef<T>::SetNewGlobalRef(env, obj); } 409 410 // Releases the global reference to the caller. The caller *must* delete the 411 // global reference when it is done with it. Note that calling a Java method 412 // is *not* a transfer of ownership and Release() should not be used. Release()413 T Release() { return static_cast<T>(JavaRef<T>::ReleaseInternal()); } 414 415 // Create a local reference. AsLocalRef(JNIEnv * env)416 ScopedJavaLocalRef<T> AsLocalRef(JNIEnv* env) const { 417 T j_obj = JavaRef<T>::obj(); 418 if (!j_obj) { 419 return nullptr; 420 } 421 return ScopedJavaLocalRef<T>::Adopt( 422 env, static_cast<T>(env->NewLocalRef(j_obj))); 423 } 424 }; 425 426 // Wrapper for working with weak references. 427 class JNI_ZERO_COMPONENT_BUILD_EXPORT ScopedJavaGlobalWeakRef { 428 public: 429 ScopedJavaGlobalWeakRef() = default; 430 ScopedJavaGlobalWeakRef(const ScopedJavaGlobalWeakRef& orig); ScopedJavaGlobalWeakRef(ScopedJavaGlobalWeakRef && orig)431 ScopedJavaGlobalWeakRef(ScopedJavaGlobalWeakRef&& orig) : obj_(orig.obj_) { 432 orig.obj_ = nullptr; 433 } 434 ScopedJavaGlobalWeakRef(JNIEnv* env, const JavaRef<jobject>& obj); ~ScopedJavaGlobalWeakRef()435 ~ScopedJavaGlobalWeakRef() { reset(); } 436 437 void operator=(const ScopedJavaGlobalWeakRef& rhs) { Assign(rhs); } 438 void operator=(ScopedJavaGlobalWeakRef&& rhs) { std::swap(obj_, rhs.obj_); } 439 440 ScopedJavaLocalRef<jobject> get(JNIEnv* env) const; 441 442 // Returns true if the weak reference has not been initialized to point at 443 // an object (or ḣas had reset() called). 444 // Do not call this to test if the object referred to still exists! The weak 445 // reference remains initialized even if the target object has been collected. is_uninitialized()446 bool is_uninitialized() const { return obj_ == nullptr; } 447 448 void reset(); 449 450 private: 451 void Assign(const ScopedJavaGlobalWeakRef& rhs); 452 453 jweak obj_ = nullptr; 454 }; 455 456 // A global JavaRef that will never be released. 457 template <typename T> 458 class JNI_ZERO_COMPONENT_BUILD_EXPORT LeakedJavaGlobalRef : public JavaRef<T> { 459 public: 460 constexpr LeakedJavaGlobalRef() = default; LeakedJavaGlobalRef(std::nullptr_t)461 constexpr LeakedJavaGlobalRef(std::nullptr_t) {} 462 463 LeakedJavaGlobalRef(const LeakedJavaGlobalRef& other) = delete; 464 LeakedJavaGlobalRef(const LeakedJavaGlobalRef&& other) = delete; 465 ~LeakedJavaGlobalRef() = default; 466 Reset(JNIEnv * env,const JavaRef<T> & j_object)467 void Reset(JNIEnv* env, const JavaRef<T>& j_object) { 468 JNI_ZERO_DCHECK(JavaRef<T>::obj() == nullptr); 469 JavaRef<T>::SetNewGlobalRef(env, j_object.obj()); 470 } 471 472 // Create a local reference. AsLocalRef(JNIEnv * env)473 ScopedJavaLocalRef<T> AsLocalRef(JNIEnv* env) const { 474 T j_obj = JavaRef<T>::obj(); 475 if (!j_obj) { 476 return nullptr; 477 } 478 return ScopedJavaLocalRef<T>::Adopt( 479 env, static_cast<T>(env->NewLocalRef(j_obj))); 480 } 481 }; 482 } // namespace jni_zero 483 484 #endif // JNI_ZERO_JAVA_REFS_H_ 485