• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 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 BASE_ANDROID_SCOPED_JAVA_REF_H_
6 #define BASE_ANDROID_SCOPED_JAVA_REF_H_
7 
8 #include <jni.h>
9 #include <stddef.h>
10 
11 #include <type_traits>
12 #include <utility>
13 
14 #include "base/base_export.h"
15 #include "base/check_op.h"
16 #include "base/memory/raw_ptr.h"
17 
18 namespace base {
19 namespace android {
20 
21 // Creates a new local reference frame, in which at least a given number of
22 // local references can be created. Note that local references already created
23 // in previous local frames are still valid in the current local frame.
24 class BASE_EXPORT ScopedJavaLocalFrame {
25  public:
26   explicit ScopedJavaLocalFrame(JNIEnv* env);
27   ScopedJavaLocalFrame(JNIEnv* env, int capacity);
28 
29   ScopedJavaLocalFrame(const ScopedJavaLocalFrame&) = delete;
30   ScopedJavaLocalFrame& operator=(const ScopedJavaLocalFrame&) = delete;
31 
32   ~ScopedJavaLocalFrame();
33 
34  private:
35   // This class is only good for use on the thread it was created on so
36   // it's safe to cache the non-threadsafe JNIEnv* inside this object.
37   raw_ptr<JNIEnv> env_;
38 };
39 
40 // Forward declare the generic java reference template class.
41 template <typename T>
42 class JavaRef;
43 
44 // Template specialization of JavaRef, which acts as the base class for all
45 // other JavaRef<> template types. This allows you to e.g. pass
46 // ScopedJavaLocalRef<jstring> into a function taking const JavaRef<jobject>&
47 template <>
48 class BASE_EXPORT JavaRef<jobject> {
49  public:
50   // Initializes a null reference.
JavaRef()51   constexpr JavaRef() {}
52 
53   // Allow nullptr to be converted to JavaRef. This avoids having to declare an
54   // empty JavaRef just to pass null to a function, and makes C++ "nullptr" and
55   // Java "null" equivalent.
JavaRef(std::nullptr_t)56   constexpr JavaRef(std::nullptr_t) {}
57 
58   JavaRef(const JavaRef&) = delete;
59   JavaRef& operator=(const JavaRef&) = delete;
60 
61   // Public to allow destruction of null JavaRef objects.
~JavaRef()62   ~JavaRef() {}
63 
64   // TODO(torne): maybe rename this to get() for consistency with unique_ptr
65   // once there's fewer unnecessary uses of it in the codebase.
obj()66   jobject obj() const { return obj_; }
67 
68   explicit operator bool() const { return obj_ != nullptr; }
69 
70   // Deprecated. Just use bool conversion.
71   // TODO(torne): replace usage and remove this.
is_null()72   bool is_null() const { return obj_ == nullptr; }
73 
74  protected:
75 // Takes ownership of the |obj| reference passed; requires it to be a local
76 // reference type.
77 #if DCHECK_IS_ON()
78   // Implementation contains a DCHECK; implement out-of-line when DCHECK_IS_ON.
79   JavaRef(JNIEnv* env, jobject obj);
80 #else
81   JavaRef(JNIEnv* env, jobject obj) : obj_(obj) {}
82 #endif
83 
84   // Used for move semantics. obj_ must have been released first if non-null.
steal(JavaRef && other)85   void steal(JavaRef&& other) {
86     obj_ = other.obj_;
87     other.obj_ = nullptr;
88   }
89 
90   // The following are implementation detail convenience methods, for
91   // use by the sub-classes.
92   JNIEnv* SetNewLocalRef(JNIEnv* env, jobject obj);
93   void SetNewGlobalRef(JNIEnv* env, jobject obj);
94   void ResetLocalRef(JNIEnv* env);
95   void ResetGlobalRef();
96   jobject ReleaseInternal();
97 
98  private:
99   jobject obj_ = nullptr;
100 };
101 
102 // Forward declare the object array reader for the convenience function.
103 template <typename T>
104 class JavaObjectArrayReader;
105 
106 // Generic base class for ScopedJavaLocalRef and ScopedJavaGlobalRef. Useful
107 // for allowing functions to accept a reference without having to mandate
108 // whether it is a local or global type.
109 template <typename T>
110 class JavaRef : public JavaRef<jobject> {
111  public:
JavaRef()112   constexpr JavaRef() {}
JavaRef(std::nullptr_t)113   constexpr JavaRef(std::nullptr_t) {}
114 
115   JavaRef(const JavaRef&) = delete;
116   JavaRef& operator=(const JavaRef&) = delete;
117 
~JavaRef()118   ~JavaRef() {}
119 
obj()120   T obj() const { return static_cast<T>(JavaRef<jobject>::obj()); }
121 
122   // Get a JavaObjectArrayReader for the array pointed to by this reference.
123   // Only defined for JavaRef<jobjectArray>.
124   // You must pass the type of the array elements (usually jobject) as the
125   // template parameter.
126   template <typename ElementType,
127             typename T_ = T,
128             typename = std::enable_if_t<std::is_same_v<T_, jobjectArray>>>
ReadElements()129   JavaObjectArrayReader<ElementType> ReadElements() const {
130     return JavaObjectArrayReader<ElementType>(*this);
131   }
132 
133  protected:
JavaRef(JNIEnv * env,T obj)134   JavaRef(JNIEnv* env, T obj) : JavaRef<jobject>(env, obj) {}
135 };
136 
137 // Holds a local reference to a JNI method parameter.
138 // Method parameters should not be deleted, and so this class exists purely to
139 // wrap them as a JavaRef<T> in the JNI binding generator. Do not create
140 // instances manually.
141 template <typename T>
142 class JavaParamRef : public JavaRef<T> {
143  public:
144   // Assumes that |obj| is a parameter passed to a JNI method from Java.
145   // Does not assume ownership as parameters should not be deleted.
JavaParamRef(JNIEnv * env,T obj)146   JavaParamRef(JNIEnv* env, T obj) : JavaRef<T>(env, obj) {}
147 
148   // Allow nullptr to be converted to JavaParamRef. Some unit tests call JNI
149   // methods directly from C++ and pass null for objects which are not actually
150   // used by the implementation (e.g. the caller object); allow this to keep
151   // working.
JavaParamRef(std::nullptr_t)152   JavaParamRef(std::nullptr_t) {}
153 
154   JavaParamRef(const JavaParamRef&) = delete;
155   JavaParamRef& operator=(const JavaParamRef&) = delete;
156 
~JavaParamRef()157   ~JavaParamRef() {}
158 
159   // TODO(torne): remove this cast once we're using JavaRef consistently.
160   // http://crbug.com/506850
T()161   operator T() const { return JavaRef<T>::obj(); }
162 };
163 
164 // Holds a local reference to a Java object. The local reference is scoped
165 // to the lifetime of this object.
166 // Instances of this class may hold onto any JNIEnv passed into it until
167 // destroyed. Therefore, since a JNIEnv is only suitable for use on a single
168 // thread, objects of this class must be created, used, and destroyed, on a
169 // single thread.
170 // Therefore, this class should only be used as a stack-based object and from a
171 // single thread. If you wish to have the reference outlive the current
172 // callstack (e.g. as a class member) or you wish to pass it across threads,
173 // use a ScopedJavaGlobalRef instead.
174 template <typename T>
175 class ScopedJavaLocalRef : public JavaRef<T> {
176  public:
177   // Take ownership of a bare jobject. This does not create a new reference.
178   // This should only be used by JNI helper functions, or in cases where code
179   // must call JNIEnv methods directly.
Adopt(JNIEnv * env,T obj)180   static ScopedJavaLocalRef Adopt(JNIEnv* env, T obj) {
181     return ScopedJavaLocalRef(env, obj);
182   }
183 
ScopedJavaLocalRef()184   constexpr ScopedJavaLocalRef() {}
ScopedJavaLocalRef(std::nullptr_t)185   constexpr ScopedJavaLocalRef(std::nullptr_t) {}
186 
187   // Copy constructor. This is required in addition to the copy conversion
188   // constructor below.
ScopedJavaLocalRef(const ScopedJavaLocalRef & other)189   ScopedJavaLocalRef(const ScopedJavaLocalRef& other) : env_(other.env_) {
190     JavaRef<T>::SetNewLocalRef(env_, other.obj());
191   }
192 
193   // Copy conversion constructor.
194   template <typename U,
195             typename = std::enable_if_t<std::is_convertible_v<U, T>>>
ScopedJavaLocalRef(const ScopedJavaLocalRef<U> & other)196   ScopedJavaLocalRef(const ScopedJavaLocalRef<U>& other) : env_(other.env_) {
197     JavaRef<T>::SetNewLocalRef(env_, other.obj());
198   }
199 
200   // Move constructor. This is required in addition to the move conversion
201   // constructor below.
ScopedJavaLocalRef(ScopedJavaLocalRef && other)202   ScopedJavaLocalRef(ScopedJavaLocalRef&& other) : env_(other.env_) {
203     JavaRef<T>::steal(std::move(other));
204   }
205 
206   // Move conversion constructor.
207   template <typename U,
208             typename = std::enable_if_t<std::is_convertible_v<U, T>>>
ScopedJavaLocalRef(ScopedJavaLocalRef<U> && other)209   ScopedJavaLocalRef(ScopedJavaLocalRef<U>&& other) : env_(other.env_) {
210     JavaRef<T>::steal(std::move(other));
211   }
212 
213   // Constructor for other JavaRef types.
ScopedJavaLocalRef(const JavaRef<T> & other)214   explicit ScopedJavaLocalRef(const JavaRef<T>& other) { Reset(other); }
215 
216   // Assumes that |obj| is a local reference to a Java object and takes
217   // ownership of this local reference.
218   // TODO(torne): make legitimate uses call Adopt() instead, and make this
219   // private.
ScopedJavaLocalRef(JNIEnv * env,T obj)220   ScopedJavaLocalRef(JNIEnv* env, T obj) : JavaRef<T>(env, obj), env_(env) {}
221 
~ScopedJavaLocalRef()222   ~ScopedJavaLocalRef() { Reset(); }
223 
224   // Null assignment, for disambiguation.
225   ScopedJavaLocalRef& operator=(std::nullptr_t) {
226     Reset();
227     return *this;
228   }
229 
230   // Copy assignment.
231   ScopedJavaLocalRef& operator=(const ScopedJavaLocalRef& other) {
232     Reset(other);
233     return *this;
234   }
235 
236   // Copy conversion assignment.
237   template <typename U,
238             typename = std::enable_if_t<std::is_convertible_v<U, T>>>
239   ScopedJavaLocalRef& operator=(const ScopedJavaLocalRef<U>& other) {
240     Reset(other);
241     return *this;
242   }
243 
244   // Move assignment.
245   template <typename U,
246             typename = std::enable_if_t<std::is_convertible_v<U, T>>>
247   ScopedJavaLocalRef& operator=(ScopedJavaLocalRef<U>&& other) {
248     env_ = other.env_;
249     Reset();
250     JavaRef<T>::steal(std::move(other));
251     return *this;
252   }
253 
254   // Assignment for other JavaRef types.
255   ScopedJavaLocalRef& operator=(const JavaRef<T>& other) {
256     Reset(other);
257     return *this;
258   }
259 
Reset()260   void Reset() { JavaRef<T>::ResetLocalRef(env_); }
261 
262   template <typename U,
263             typename = std::enable_if_t<std::is_convertible_v<U, T>>>
Reset(const ScopedJavaLocalRef<U> & other)264   void Reset(const ScopedJavaLocalRef<U>& other) {
265     // We can copy over env_ here as |other| instance must be from the same
266     // thread as |this| local ref. (See class comment for multi-threading
267     // limitations, and alternatives).
268     env_ = JavaRef<T>::SetNewLocalRef(other.env_, other.obj());
269   }
270 
Reset(const JavaRef<T> & other)271   void Reset(const JavaRef<T>& other) {
272     // If |env_| was not yet set (is still null) it will be attached to the
273     // current thread in SetNewLocalRef().
274     env_ = JavaRef<T>::SetNewLocalRef(env_, other.obj());
275   }
276 
277   // Releases the local reference to the caller. The caller *must* delete the
278   // local reference when it is done with it. Note that calling a Java method
279   // is *not* a transfer of ownership and Release() should not be used.
Release()280   T Release() { return static_cast<T>(JavaRef<T>::ReleaseInternal()); }
281 
282  private:
283   // This class is only good for use on the thread it was created on so
284   // it's safe to cache the non-threadsafe JNIEnv* inside this object.
285   raw_ptr<JNIEnv> env_ = nullptr;
286 
287   // Prevent ScopedJavaLocalRef(JNIEnv*, T obj) from being used to take
288   // ownership of a JavaParamRef's underlying object - parameters are not
289   // allowed to be deleted and so should not be owned by ScopedJavaLocalRef.
290   // TODO(torne): this can be removed once JavaParamRef no longer has an
291   // implicit conversion back to T.
292   ScopedJavaLocalRef(JNIEnv* env, const JavaParamRef<T>& other);
293 
294   // Friend required to get env_ from conversions.
295   template <typename U>
296   friend class ScopedJavaLocalRef;
297 
298   // Avoids JavaObjectArrayReader having to accept and store its own env.
299   template <typename U>
300   friend class JavaObjectArrayReader;
301 };
302 
303 // Holds a global reference to a Java object. The global reference is scoped
304 // to the lifetime of this object. This class does not hold onto any JNIEnv*
305 // passed to it, hence it is safe to use across threads (within the constraints
306 // imposed by the underlying Java object that it references).
307 template <typename T>
308 class ScopedJavaGlobalRef : public JavaRef<T> {
309  public:
ScopedJavaGlobalRef()310   constexpr ScopedJavaGlobalRef() {}
ScopedJavaGlobalRef(std::nullptr_t)311   constexpr ScopedJavaGlobalRef(std::nullptr_t) {}
312 
313   // Copy constructor. This is required in addition to the copy conversion
314   // constructor below.
ScopedJavaGlobalRef(const ScopedJavaGlobalRef & other)315   ScopedJavaGlobalRef(const ScopedJavaGlobalRef& other) { Reset(other); }
316 
317   // Copy conversion constructor.
318   template <typename U,
319             typename = std::enable_if_t<std::is_convertible_v<U, T>>>
ScopedJavaGlobalRef(const ScopedJavaGlobalRef<U> & other)320   ScopedJavaGlobalRef(const ScopedJavaGlobalRef<U>& other) {
321     Reset(other);
322   }
323 
324   // Move constructor. This is required in addition to the move conversion
325   // constructor below.
ScopedJavaGlobalRef(ScopedJavaGlobalRef && other)326   ScopedJavaGlobalRef(ScopedJavaGlobalRef&& other) {
327     JavaRef<T>::steal(std::move(other));
328   }
329 
330   // Move conversion constructor.
331   template <typename U,
332             typename = std::enable_if_t<std::is_convertible_v<U, T>>>
ScopedJavaGlobalRef(ScopedJavaGlobalRef<U> && other)333   ScopedJavaGlobalRef(ScopedJavaGlobalRef<U>&& other) {
334     JavaRef<T>::steal(std::move(other));
335   }
336 
337   // Conversion constructor for other JavaRef types.
ScopedJavaGlobalRef(const JavaRef<T> & other)338   explicit ScopedJavaGlobalRef(const JavaRef<T>& other) { Reset(other); }
339 
340   // Create a new global reference to the object.
341   // Deprecated. Don't use bare jobjects; use a JavaRef as the input.
ScopedJavaGlobalRef(JNIEnv * env,T obj)342   ScopedJavaGlobalRef(JNIEnv* env, T obj) { Reset(env, obj); }
343 
~ScopedJavaGlobalRef()344   ~ScopedJavaGlobalRef() { Reset(); }
345 
346   // Null assignment, for disambiguation.
347   ScopedJavaGlobalRef& operator=(std::nullptr_t) {
348     Reset();
349     return *this;
350   }
351 
352   // Copy assignment.
353   ScopedJavaGlobalRef& operator=(const ScopedJavaGlobalRef& other) {
354     Reset(other);
355     return *this;
356   }
357 
358   // Copy conversion assignment.
359   template <typename U,
360             typename = std::enable_if_t<std::is_convertible_v<U, T>>>
361   ScopedJavaGlobalRef& operator=(const ScopedJavaGlobalRef<U>& other) {
362     Reset(other);
363     return *this;
364   }
365 
366   // Move assignment.
367   template <typename U,
368             typename = std::enable_if_t<std::is_convertible_v<U, T>>>
369   ScopedJavaGlobalRef& operator=(ScopedJavaGlobalRef<U>&& other) {
370     Reset();
371     JavaRef<T>::steal(std::move(other));
372     return *this;
373   }
374 
375   // Assignment for other JavaRef types.
376   ScopedJavaGlobalRef& operator=(const JavaRef<T>& other) {
377     Reset(other);
378     return *this;
379   }
380 
Reset()381   void Reset() { JavaRef<T>::ResetGlobalRef(); }
382 
383   template <typename U,
384             typename = std::enable_if_t<std::is_convertible_v<U, T>>>
Reset(const ScopedJavaGlobalRef<U> & other)385   void Reset(const ScopedJavaGlobalRef<U>& other) {
386     Reset(nullptr, other.obj());
387   }
388 
Reset(const JavaRef<T> & other)389   void Reset(const JavaRef<T>& other) { Reset(nullptr, other.obj()); }
390 
391   // Deprecated. You can just use Reset(const JavaRef&).
Reset(JNIEnv * env,const JavaParamRef<T> & other)392   void Reset(JNIEnv* env, const JavaParamRef<T>& other) {
393     Reset(env, other.obj());
394   }
395 
396   // Deprecated. Don't use bare jobjects; use a JavaRef as the input.
Reset(JNIEnv * env,T obj)397   void Reset(JNIEnv* env, T obj) { JavaRef<T>::SetNewGlobalRef(env, obj); }
398 
399   // Releases the global reference to the caller. The caller *must* delete the
400   // global reference when it is done with it. Note that calling a Java method
401   // is *not* a transfer of ownership and Release() should not be used.
Release()402   T Release() { return static_cast<T>(JavaRef<T>::ReleaseInternal()); }
403 };
404 
405 // Wrapper for a jobjectArray which supports input iteration, allowing Java
406 // arrays to be iterated over with a range-based for loop, or used with
407 // <algorithm> functions that accept input iterators.
408 //
409 // The iterator returns each object in the array in turn, wrapped in a
410 // ScopedJavaLocalRef<T>. T will usually be jobject, but if you know that the
411 // array contains a more specific type (such as jstring) you can use that
412 // instead. This does not check the type at runtime!
413 //
414 // The wrapper holds a local reference to the array and only queries the size of
415 // the array once, so must only be used as a stack-based object from the current
416 // thread.
417 //
418 // Note that this does *not* update the contents of the array if you mutate the
419 // returned ScopedJavaLocalRef.
420 template <typename T>
421 class JavaObjectArrayReader {
422  public:
423   class iterator {
424    public:
425     // We can only be an input iterator, as all richer iterator types must
426     // implement the multipass guarantee (always returning the same object for
427     // the same iterator position), which is not practical when returning
428     // temporary objects.
429     using iterator_category = std::input_iterator_tag;
430 
431     using difference_type = ptrdiff_t;
432     using value_type = ScopedJavaLocalRef<T>;
433 
434     // It doesn't make sense to return a reference type as the iterator creates
435     // temporary wrapper objects when dereferenced. Fortunately, it's not
436     // required that input iterators actually use references, and defining it
437     // as value_type is valid.
438     using reference = value_type;
439 
440     // This exists to make operator-> work as expected: its return value must
441     // resolve to an actual pointer (otherwise the compiler just keeps calling
442     // operator-> on the return value until it does), so we need an extra level
443     // of indirection. This is sometimes called an "arrow proxy" or similar, and
444     // this version is adapted from base/value_iterators.h.
445     class pointer {
446      public:
pointer(const reference & ref)447       explicit pointer(const reference& ref) : ref_(ref) {}
448       pointer(const pointer& ptr) = default;
449       pointer& operator=(const pointer& ptr) = delete;
450       reference* operator->() { return &ref_; }
451 
452      private:
453       reference ref_;
454     };
455 
456     iterator(const iterator&) = default;
457     ~iterator() = default;
458 
459     iterator& operator=(const iterator&) = default;
460 
461     bool operator==(const iterator& other) const {
462       DCHECK(reader_ == other.reader_);
463       return i_ == other.i_;
464     }
465 
466     bool operator!=(const iterator& other) const {
467       DCHECK(reader_ == other.reader_);
468       return i_ != other.i_;
469     }
470 
471     reference operator*() const {
472       DCHECK(i_ < reader_->size_);
473       // JNIEnv functions return unowned local references; take ownership with
474       // Adopt so that ~ScopedJavaLocalRef will release it automatically later.
475       return value_type::Adopt(
476           reader_->array_.env_,
477           static_cast<T>(reader_->array_.env_->GetObjectArrayElement(
478               reader_->array_.obj(), i_)));
479     }
480 
481     pointer operator->() const { return pointer(operator*()); }
482 
483     iterator& operator++() {
484       DCHECK(i_ < reader_->size_);
485       ++i_;
486       return *this;
487     }
488 
489     iterator operator++(int) {
490       iterator old = *this;
491       ++*this;
492       return old;
493     }
494 
495    private:
iterator(const JavaObjectArrayReader * reader,jsize i)496     iterator(const JavaObjectArrayReader* reader, jsize i)
497         : reader_(reader), i_(i) {}
498     raw_ptr<const JavaObjectArrayReader<T>> reader_;
499     jsize i_;
500 
501     friend JavaObjectArrayReader;
502   };
503 
JavaObjectArrayReader(const JavaRef<jobjectArray> & array)504   JavaObjectArrayReader(const JavaRef<jobjectArray>& array) : array_(array) {
505     size_ = array_.env_->GetArrayLength(array_.obj());
506   }
507 
508   // Copy constructor to allow returning it from JavaRef::ReadElements().
509   JavaObjectArrayReader(const JavaObjectArrayReader& other) = default;
510 
511   // Assignment operator for consistency with copy constructor.
512   JavaObjectArrayReader& operator=(const JavaObjectArrayReader& other) =
513       default;
514 
515   // Allow move constructor and assignment since this owns a local ref.
516   JavaObjectArrayReader(JavaObjectArrayReader&& other) = default;
517   JavaObjectArrayReader& operator=(JavaObjectArrayReader&& other) = default;
518 
empty()519   bool empty() const { return size_ == 0; }
520 
size()521   jsize size() const { return size_; }
522 
begin()523   iterator begin() const { return iterator(this, 0); }
524 
end()525   iterator end() const { return iterator(this, size_); }
526 
527  private:
528   ScopedJavaLocalRef<jobjectArray> array_;
529   jsize size_;
530 
531   friend iterator;
532 };
533 
534 }  // namespace android
535 }  // namespace base
536 
537 #endif  // BASE_ANDROID_SCOPED_JAVA_REF_H_
538