• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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