xref: /aosp_15_r20/external/cronet/third_party/jni_zero/jni_zero.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2023 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_JNI_ZERO_H_
6 #define JNI_ZERO_JNI_ZERO_H_
7 
8 #include <jni.h>
9 
10 #include <atomic>
11 #include <string>
12 #include <type_traits>
13 #include <vector>
14 
15 #include "third_party/jni_zero/jni_export.h"
16 #include "third_party/jni_zero/logging.h"
17 
18 #if defined(__i386__)
19 // Dalvik JIT generated code doesn't guarantee 16-byte stack alignment on
20 // x86 - use force_align_arg_pointer to realign the stack at the JNI
21 // boundary. crbug.com/655248
22 #define JNI_BOUNDARY_EXPORT \
23   extern "C" __attribute__((visibility("default"), force_align_arg_pointer))
24 #else
25 #define JNI_BOUNDARY_EXPORT extern "C" __attribute__((visibility("default")))
26 #endif
27 
28 
29 // Wrapper used to receive int when calling Java from native.
30 // The wrapper disallows automatic conversion of long to int.
31 // This is to avoid a common anti-pattern where a Java int is used
32 // to receive a native pointer. Please use a Java long to receive
33 // native pointers, so that the code works on both 32-bit and 64-bit
34 // platforms. Note the wrapper allows other lossy conversions into
35 // jint that could be consider anti-patterns, such as from size_t.
36 
37 // Checking is only done in debugging builds.
38 
39 #ifdef NDEBUG
40 
41 typedef jint JniIntWrapper;
42 
43 // This inline is sufficiently trivial that it does not change the
44 // final code generated by g++.
as_jint(JniIntWrapper wrapper)45 inline jint as_jint(JniIntWrapper wrapper) {
46   return wrapper;
47 }
48 
49 #else
50 
51 class JniIntWrapper {
52  public:
JniIntWrapper()53   JniIntWrapper() : i_(0) {}
JniIntWrapper(int i)54   JniIntWrapper(int i) : i_(i) {}
JniIntWrapper(const JniIntWrapper & ji)55   JniIntWrapper(const JniIntWrapper& ji) : i_(ji.i_) {}
56   template <class T>
JniIntWrapper(const T & t)57   JniIntWrapper(const T& t) : i_(t) {}
as_jint()58   jint as_jint() const { return i_; }
59 
60  private:
61   // If you get an "is private" error at the line below it is because you used
62   // an implicit conversion to convert a long to an int when calling Java.
63   // We disallow this, as a common anti-pattern allows converting a native
64   // pointer (intptr_t) to a Java int. Please use a Java long to represent
65   // a native pointer. If you want a lossy conversion, please use an
66   // explicit conversion in your C++ code. Note an error is only seen when
67   // compiling on a 64-bit platform, as intptr_t is indistinguishable from
68   // int on 32-bit platforms.
69   JniIntWrapper(long);
70   jint i_;
71 };
72 
as_jint(const JniIntWrapper & wrapper)73 inline jint as_jint(const JniIntWrapper& wrapper) {
74   return wrapper.as_jint();
75 }
76 
77 #endif  // NDEBUG
78 
79 namespace jni_zero {
80 
81 // Commonly needed jclasses:
82 extern JNI_ZERO_COMPONENT_BUILD_EXPORT jclass g_object_class;
83 extern JNI_ZERO_COMPONENT_BUILD_EXPORT jclass g_string_class;
84 
85 // Creates a new local reference frame, in which at least a given number of
86 // local references can be created. Note that local references already created
87 // in previous local frames are still valid in the current local frame.
88 class JNI_ZERO_COMPONENT_BUILD_EXPORT ScopedJavaLocalFrame {
89  public:
90   explicit ScopedJavaLocalFrame(JNIEnv* env);
91   ScopedJavaLocalFrame(JNIEnv* env, int capacity);
92 
93   ScopedJavaLocalFrame(const ScopedJavaLocalFrame&) = delete;
94   ScopedJavaLocalFrame& operator=(const ScopedJavaLocalFrame&) = delete;
95 
96   ~ScopedJavaLocalFrame();
97 
98  private:
99   // This class is only good for use on the thread it was created on so
100   // it's safe to cache the non-threadsafe JNIEnv* inside this object.
101   JNIEnv* env_;
102 };
103 
104 // Forward declare the generic java reference template class.
105 template <typename T>
106 class JavaRef;
107 
108 // Template specialization of JavaRef, which acts as the base class for all
109 // other JavaRef<> template types. This allows you to e.g. pass
110 // ScopedJavaLocalRef<jstring> into a function taking const JavaRef<jobject>&
111 template <>
112 class JNI_ZERO_COMPONENT_BUILD_EXPORT JavaRef<jobject> {
113  public:
114   // Initializes a null reference.
JavaRef()115   constexpr JavaRef() {}
116 
117   // Allow nullptr to be converted to JavaRef. This avoids having to declare an
118   // empty JavaRef just to pass null to a function, and makes C++ "nullptr" and
119   // Java "null" equivalent.
JavaRef(std::nullptr_t)120   constexpr JavaRef(std::nullptr_t) {}
121 
122   JavaRef(const JavaRef&) = delete;
123   JavaRef& operator=(const JavaRef&) = delete;
124 
125   // Public to allow destruction of null JavaRef objects.
~JavaRef()126   ~JavaRef() {}
127 
128   // TODO(torne): maybe rename this to get() for consistency with unique_ptr
129   // once there's fewer unnecessary uses of it in the codebase.
obj()130   jobject obj() const { return obj_; }
131 
132   explicit operator bool() const { return obj_ != nullptr; }
133 
134   // Deprecated. Just use bool conversion.
135   // TODO(torne): replace usage and remove this.
is_null()136   bool is_null() const { return obj_ == nullptr; }
137 
138  protected:
139 // Takes ownership of the |obj| reference passed; requires it to be a local
140 // reference type.
141 #if JNI_ZERO_DCHECK_IS_ON()
142   // Implementation contains a DCHECK; implement out-of-line when DCHECK_IS_ON.
143   JavaRef(JNIEnv* env, jobject obj);
144 #else
145   JavaRef(JNIEnv* env, jobject obj) : obj_(obj) {}
146 #endif
147 
148   // Used for move semantics. obj_ must have been released first if non-null.
steal(JavaRef && other)149   void steal(JavaRef&& other) {
150     obj_ = other.obj_;
151     other.obj_ = nullptr;
152   }
153 
154   // The following are implementation detail convenience methods, for
155   // use by the sub-classes.
156   JNIEnv* SetNewLocalRef(JNIEnv* env, jobject obj);
157   void SetNewGlobalRef(JNIEnv* env, jobject obj);
158   void ResetLocalRef(JNIEnv* env);
159   void ResetGlobalRef();
160   jobject ReleaseInternal();
161 
162  private:
163   jobject obj_ = nullptr;
164 };
165 
166 // Forward declare the object array reader for the convenience function.
167 template <typename T>
168 class JavaObjectArrayReader;
169 
170 // Generic base class for ScopedJavaLocalRef and ScopedJavaGlobalRef. Useful
171 // for allowing functions to accept a reference without having to mandate
172 // whether it is a local or global type.
173 template <typename T>
174 class JavaRef : public JavaRef<jobject> {
175  public:
JavaRef()176   constexpr JavaRef() {}
JavaRef(std::nullptr_t)177   constexpr JavaRef(std::nullptr_t) {}
178 
179   JavaRef(const JavaRef&) = delete;
180   JavaRef& operator=(const JavaRef&) = delete;
181 
~JavaRef()182   ~JavaRef() {}
183 
obj()184   T obj() const { return static_cast<T>(JavaRef<jobject>::obj()); }
185 
186   // Get a JavaObjectArrayReader for the array pointed to by this reference.
187   // Only defined for JavaRef<jobjectArray>.
188   // You must pass the type of the array elements (usually jobject) as the
189   // template parameter.
190   template <typename ElementType,
191             typename T_ = T,
192             typename = std::enable_if_t<std::is_same_v<T_, jobjectArray>>>
ReadElements()193   JavaObjectArrayReader<ElementType> ReadElements() const {
194     return JavaObjectArrayReader<ElementType>(*this);
195   }
196 
197  protected:
JavaRef(JNIEnv * env,T obj)198   JavaRef(JNIEnv* env, T obj) : JavaRef<jobject>(env, obj) {}
199 };
200 
201 // Holds a local reference to a JNI method parameter.
202 // Method parameters should not be deleted, and so this class exists purely to
203 // wrap them as a JavaRef<T> in the JNI binding generator. Do not create
204 // instances manually.
205 template <typename T>
206 class JavaParamRef : public JavaRef<T> {
207  public:
208   // Assumes that |obj| is a parameter passed to a JNI method from Java.
209   // Does not assume ownership as parameters should not be deleted.
JavaParamRef(JNIEnv * env,T obj)210   JavaParamRef(JNIEnv* env, T obj) : JavaRef<T>(env, obj) {}
211 
212   // Allow nullptr to be converted to JavaParamRef. Some unit tests call JNI
213   // methods directly from C++ and pass null for objects which are not actually
214   // used by the implementation (e.g. the caller object); allow this to keep
215   // working.
JavaParamRef(std::nullptr_t)216   JavaParamRef(std::nullptr_t) {}
217 
218   JavaParamRef(const JavaParamRef&) = delete;
219   JavaParamRef& operator=(const JavaParamRef&) = delete;
220 
~JavaParamRef()221   ~JavaParamRef() {}
222 
223   // TODO(torne): remove this cast once we're using JavaRef consistently.
224   // http://crbug.com/506850
T()225   operator T() const { return JavaRef<T>::obj(); }
226 };
227 
228 // Holds a local reference to a Java object. The local reference is scoped
229 // to the lifetime of this object.
230 // Instances of this class may hold onto any JNIEnv passed into it until
231 // destroyed. Therefore, since a JNIEnv is only suitable for use on a single
232 // thread, objects of this class must be created, used, and destroyed, on a
233 // single thread.
234 // Therefore, this class should only be used as a stack-based object and from a
235 // single thread. If you wish to have the reference outlive the current
236 // callstack (e.g. as a class member) or you wish to pass it across threads,
237 // use a ScopedJavaGlobalRef instead.
238 template <typename T>
239 class ScopedJavaLocalRef : public JavaRef<T> {
240  public:
241   // Take ownership of a bare jobject. This does not create a new reference.
242   // This should only be used by JNI helper functions, or in cases where code
243   // must call JNIEnv methods directly.
Adopt(JNIEnv * env,T obj)244   static ScopedJavaLocalRef Adopt(JNIEnv* env, T obj) {
245     return ScopedJavaLocalRef(env, obj);
246   }
247 
ScopedJavaLocalRef()248   constexpr ScopedJavaLocalRef() {}
ScopedJavaLocalRef(std::nullptr_t)249   constexpr ScopedJavaLocalRef(std::nullptr_t) {}
250 
251   // Copy constructor. This is required in addition to the copy conversion
252   // constructor below.
ScopedJavaLocalRef(const ScopedJavaLocalRef & other)253   ScopedJavaLocalRef(const ScopedJavaLocalRef& other) : env_(other.env_) {
254     JavaRef<T>::SetNewLocalRef(env_, other.obj());
255   }
256 
257   // Copy conversion constructor.
258   template <typename U,
259             typename = std::enable_if_t<std::is_convertible_v<U, T>>>
ScopedJavaLocalRef(const ScopedJavaLocalRef<U> & other)260   ScopedJavaLocalRef(const ScopedJavaLocalRef<U>& other) : env_(other.env_) {
261     JavaRef<T>::SetNewLocalRef(env_, other.obj());
262   }
263 
264   // Move constructor. This is required in addition to the move conversion
265   // constructor below.
ScopedJavaLocalRef(ScopedJavaLocalRef && other)266   ScopedJavaLocalRef(ScopedJavaLocalRef&& other) : env_(other.env_) {
267     JavaRef<T>::steal(std::move(other));
268   }
269 
270   // Move conversion constructor.
271   template <typename U,
272             typename = std::enable_if_t<std::is_convertible_v<U, T>>>
ScopedJavaLocalRef(ScopedJavaLocalRef<U> && other)273   ScopedJavaLocalRef(ScopedJavaLocalRef<U>&& other) : env_(other.env_) {
274     JavaRef<T>::steal(std::move(other));
275   }
276 
277   // Constructor for other JavaRef types.
ScopedJavaLocalRef(const JavaRef<T> & other)278   explicit ScopedJavaLocalRef(const JavaRef<T>& other) { Reset(other); }
279 
ScopedJavaLocalRef(JNIEnv * env,const JavaRef<T> & other)280   ScopedJavaLocalRef(JNIEnv* env, const JavaRef<T>& other) { Reset(other); }
281 
282   // Assumes that |obj| is a local reference to a Java object and takes
283   // ownership of this local reference.
284   // TODO(torne): make legitimate uses call Adopt() instead, and make this
285   // private.
ScopedJavaLocalRef(JNIEnv * env,T obj)286   ScopedJavaLocalRef(JNIEnv* env, T obj) : JavaRef<T>(env, obj), env_(env) {}
287 
~ScopedJavaLocalRef()288   ~ScopedJavaLocalRef() { Reset(); }
289 
290   // Null assignment, for disambiguation.
291   ScopedJavaLocalRef& operator=(std::nullptr_t) {
292     Reset();
293     return *this;
294   }
295 
296   // Copy assignment.
297   ScopedJavaLocalRef& operator=(const ScopedJavaLocalRef& other) {
298     Reset(other);
299     return *this;
300   }
301 
302   // Copy conversion assignment.
303   template <typename U,
304             typename = std::enable_if_t<std::is_convertible_v<U, T>>>
305   ScopedJavaLocalRef& operator=(const ScopedJavaLocalRef<U>& other) {
306     Reset(other);
307     return *this;
308   }
309 
310   // Move assignment.
311   template <typename U,
312             typename = std::enable_if_t<std::is_convertible_v<U, T>>>
313   ScopedJavaLocalRef& operator=(ScopedJavaLocalRef<U>&& other) {
314     env_ = other.env_;
315     Reset();
316     JavaRef<T>::steal(std::move(other));
317     return *this;
318   }
319 
320   // Assignment for other JavaRef types.
321   ScopedJavaLocalRef& operator=(const JavaRef<T>& other) {
322     Reset(other);
323     return *this;
324   }
325 
Reset()326   void Reset() { JavaRef<T>::ResetLocalRef(env_); }
327 
328   template <typename U,
329             typename = std::enable_if_t<std::is_convertible_v<U, T>>>
Reset(const ScopedJavaLocalRef<U> & other)330   void Reset(const ScopedJavaLocalRef<U>& other) {
331     // We can copy over env_ here as |other| instance must be from the same
332     // thread as |this| local ref. (See class comment for multi-threading
333     // limitations, and alternatives).
334     env_ = JavaRef<T>::SetNewLocalRef(other.env_, other.obj());
335   }
336 
Reset(const JavaRef<T> & other)337   void Reset(const JavaRef<T>& other) {
338     // If |env_| was not yet set (is still null) it will be attached to the
339     // current thread in SetNewLocalRef().
340     env_ = JavaRef<T>::SetNewLocalRef(env_, other.obj());
341   }
342 
343   // Releases the local reference to the caller. The caller *must* delete the
344   // local reference when it is done with it. Note that calling a Java method
345   // is *not* a transfer of ownership and Release() should not be used.
Release()346   T Release() { return static_cast<T>(JavaRef<T>::ReleaseInternal()); }
347 
348  private:
349   // This class is only good for use on the thread it was created on so
350   // it's safe to cache the non-threadsafe JNIEnv* inside this object.
351   JNIEnv* env_ = nullptr;
352 
353   // Prevent ScopedJavaLocalRef(JNIEnv*, T obj) from being used to take
354   // ownership of a JavaParamRef's underlying object - parameters are not
355   // allowed to be deleted and so should not be owned by ScopedJavaLocalRef.
356   // TODO(torne): this can be removed once JavaParamRef no longer has an
357   // implicit conversion back to T.
358   ScopedJavaLocalRef(JNIEnv* env, const JavaParamRef<T>& other);
359 
360   // Friend required to get env_ from conversions.
361   template <typename U>
362   friend class ScopedJavaLocalRef;
363 
364   // Avoids JavaObjectArrayReader having to accept and store its own env.
365   template <typename U>
366   friend class JavaObjectArrayReader;
367 };
368 
369 // Holds a global reference to a Java object. The global reference is scoped
370 // to the lifetime of this object. This class does not hold onto any JNIEnv*
371 // passed to it, hence it is safe to use across threads (within the constraints
372 // imposed by the underlying Java object that it references).
373 template <typename T>
374 class ScopedJavaGlobalRef : public JavaRef<T> {
375  public:
ScopedJavaGlobalRef()376   constexpr ScopedJavaGlobalRef() {}
ScopedJavaGlobalRef(std::nullptr_t)377   constexpr ScopedJavaGlobalRef(std::nullptr_t) {}
378 
379   // Copy constructor. This is required in addition to the copy conversion
380   // constructor below.
ScopedJavaGlobalRef(const ScopedJavaGlobalRef & other)381   ScopedJavaGlobalRef(const ScopedJavaGlobalRef& other) { Reset(other); }
382 
383   // Copy conversion constructor.
384   template <typename U,
385             typename = std::enable_if_t<std::is_convertible_v<U, T>>>
ScopedJavaGlobalRef(const ScopedJavaGlobalRef<U> & other)386   ScopedJavaGlobalRef(const ScopedJavaGlobalRef<U>& other) {
387     Reset(other);
388   }
389 
390   // Move constructor. This is required in addition to the move conversion
391   // constructor below.
ScopedJavaGlobalRef(ScopedJavaGlobalRef && other)392   ScopedJavaGlobalRef(ScopedJavaGlobalRef&& other) {
393     JavaRef<T>::steal(std::move(other));
394   }
395 
396   // Move conversion constructor.
397   template <typename U,
398             typename = std::enable_if_t<std::is_convertible_v<U, T>>>
ScopedJavaGlobalRef(ScopedJavaGlobalRef<U> && other)399   ScopedJavaGlobalRef(ScopedJavaGlobalRef<U>&& other) {
400     JavaRef<T>::steal(std::move(other));
401   }
402 
403   // Conversion constructor for other JavaRef types.
ScopedJavaGlobalRef(const JavaRef<T> & other)404   explicit ScopedJavaGlobalRef(const JavaRef<T>& other) { Reset(other); }
405 
ScopedJavaGlobalRef(JNIEnv * env,const JavaRef<T> & other)406   ScopedJavaGlobalRef(JNIEnv* env, const JavaRef<T>& other) {
407     JavaRef<T>::SetNewGlobalRef(env, other.obj());
408   }
409 
410   // Create a new global reference to the object.
411   // Deprecated. Don't use bare jobjects; use a JavaRef as the input.
ScopedJavaGlobalRef(JNIEnv * env,T obj)412   ScopedJavaGlobalRef(JNIEnv* env, T obj) { Reset(env, obj); }
413 
~ScopedJavaGlobalRef()414   ~ScopedJavaGlobalRef() { Reset(); }
415 
416   // Null assignment, for disambiguation.
417   ScopedJavaGlobalRef& operator=(std::nullptr_t) {
418     Reset();
419     return *this;
420   }
421 
422   // Copy assignment.
423   ScopedJavaGlobalRef& operator=(const ScopedJavaGlobalRef& other) {
424     Reset(other);
425     return *this;
426   }
427 
428   // Copy conversion assignment.
429   template <typename U,
430             typename = std::enable_if_t<std::is_convertible_v<U, T>>>
431   ScopedJavaGlobalRef& operator=(const ScopedJavaGlobalRef<U>& other) {
432     Reset(other);
433     return *this;
434   }
435 
436   // Move assignment.
437   template <typename U,
438             typename = std::enable_if_t<std::is_convertible_v<U, T>>>
439   ScopedJavaGlobalRef& operator=(ScopedJavaGlobalRef<U>&& other) {
440     Reset();
441     JavaRef<T>::steal(std::move(other));
442     return *this;
443   }
444 
445   // Assignment for other JavaRef types.
446   ScopedJavaGlobalRef& operator=(const JavaRef<T>& other) {
447     Reset(other);
448     return *this;
449   }
450 
Reset()451   void Reset() { JavaRef<T>::ResetGlobalRef(); }
452 
453   template <typename U,
454             typename = std::enable_if_t<std::is_convertible_v<U, T>>>
Reset(const ScopedJavaGlobalRef<U> & other)455   void Reset(const ScopedJavaGlobalRef<U>& other) {
456     Reset(nullptr, other.obj());
457   }
458 
Reset(const JavaRef<T> & other)459   void Reset(const JavaRef<T>& other) { Reset(nullptr, other.obj()); }
460 
461   // Deprecated. You can just use Reset(const JavaRef&).
Reset(JNIEnv * env,const JavaParamRef<T> & other)462   void Reset(JNIEnv* env, const JavaParamRef<T>& other) {
463     Reset(env, other.obj());
464   }
465 
466   // Deprecated. Don't use bare jobjects; use a JavaRef as the input.
Reset(JNIEnv * env,T obj)467   void Reset(JNIEnv* env, T obj) { JavaRef<T>::SetNewGlobalRef(env, obj); }
468 
469   // Releases the global reference to the caller. The caller *must* delete the
470   // global reference when it is done with it. Note that calling a Java method
471   // is *not* a transfer of ownership and Release() should not be used.
Release()472   T Release() { return static_cast<T>(JavaRef<T>::ReleaseInternal()); }
473 };
474 
475 // Wrapper for a jobjectArray which supports input iteration, allowing Java
476 // arrays to be iterated over with a range-based for loop, or used with
477 // <algorithm> functions that accept input iterators.
478 //
479 // The iterator returns each object in the array in turn, wrapped in a
480 // ScopedJavaLocalRef<T>. T will usually be jobject, but if you know that the
481 // array contains a more specific type (such as jstring) you can use that
482 // instead. This does not check the type at runtime!
483 //
484 // The wrapper holds a local reference to the array and only queries the size of
485 // the array once, so must only be used as a stack-based object from the current
486 // thread.
487 //
488 // Note that this does *not* update the contents of the array if you mutate the
489 // returned ScopedJavaLocalRef.
490 template <typename T>
491 class JavaObjectArrayReader {
492  public:
493   class iterator {
494    public:
495     // We can only be an input iterator, as all richer iterator types must
496     // implement the multipass guarantee (always returning the same object for
497     // the same iterator position), which is not practical when returning
498     // temporary objects.
499     using iterator_category = std::input_iterator_tag;
500 
501     using difference_type = ptrdiff_t;
502     using value_type = ScopedJavaLocalRef<T>;
503 
504     // It doesn't make sense to return a reference type as the iterator creates
505     // temporary wrapper objects when dereferenced. Fortunately, it's not
506     // required that input iterators actually use references, and defining it
507     // as value_type is valid.
508     using reference = value_type;
509 
510     // This exists to make operator-> work as expected: its return value must
511     // resolve to an actual pointer (otherwise the compiler just keeps calling
512     // operator-> on the return value until it does), so we need an extra level
513     // of indirection. This is sometimes called an "arrow proxy" or similar, and
514     // this version is adapted from base/value_iterators.h.
515     class pointer {
516      public:
pointer(const reference & ref)517       explicit pointer(const reference& ref) : ref_(ref) {}
518       pointer(const pointer& ptr) = default;
519       pointer& operator=(const pointer& ptr) = delete;
520       reference* operator->() { return &ref_; }
521 
522      private:
523       reference ref_;
524     };
525 
526     iterator(const iterator&) = default;
527     ~iterator() = default;
528 
529     iterator& operator=(const iterator&) = default;
530 
531     bool operator==(const iterator& other) const {
532       JNI_ZERO_DCHECK(reader_ == other.reader_);
533       return i_ == other.i_;
534     }
535 
536     bool operator!=(const iterator& other) const {
537       JNI_ZERO_DCHECK(reader_ == other.reader_);
538       return i_ != other.i_;
539     }
540 
541     reference operator*() const {
542       JNI_ZERO_DCHECK(i_ < reader_->size_);
543       // JNIEnv functions return unowned local references; take ownership with
544       // Adopt so that ~ScopedJavaLocalRef will release it automatically later.
545       return value_type::Adopt(
546           reader_->array_.env_,
547           static_cast<T>(reader_->array_.env_->GetObjectArrayElement(
548               reader_->array_.obj(), i_)));
549     }
550 
551     pointer operator->() const { return pointer(operator*()); }
552 
553     iterator& operator++() {
554       JNI_ZERO_DCHECK(i_ < reader_->size_);
555       ++i_;
556       return *this;
557     }
558 
559     iterator operator++(int) {
560       iterator old = *this;
561       ++*this;
562       return old;
563     }
564 
565    private:
iterator(const JavaObjectArrayReader * reader,jsize i)566     iterator(const JavaObjectArrayReader* reader, jsize i)
567         : reader_(reader), i_(i) {}
568     const JavaObjectArrayReader<T>* reader_;
569     jsize i_;
570 
571     friend JavaObjectArrayReader;
572   };
573 
JavaObjectArrayReader(const JavaRef<jobjectArray> & array)574   JavaObjectArrayReader(const JavaRef<jobjectArray>& array) : array_(array) {
575     size_ = array_.env_->GetArrayLength(array_.obj());
576   }
577 
578   // Copy constructor to allow returning it from JavaRef::ReadElements().
579   JavaObjectArrayReader(const JavaObjectArrayReader& other) = default;
580 
581   // Assignment operator for consistency with copy constructor.
582   JavaObjectArrayReader& operator=(const JavaObjectArrayReader& other) =
583       default;
584 
585   // Allow move constructor and assignment since this owns a local ref.
586   JavaObjectArrayReader(JavaObjectArrayReader&& other) = default;
587   JavaObjectArrayReader& operator=(JavaObjectArrayReader&& other) = default;
588 
empty()589   bool empty() const { return size_ == 0; }
590 
size()591   jsize size() const { return size_; }
592 
begin()593   iterator begin() const { return iterator(this, 0); }
594 
end()595   iterator end() const { return iterator(this, size_); }
596 
597  private:
598   ScopedJavaLocalRef<jobjectArray> array_;
599   jsize size_;
600 
601   friend iterator;
602 };
603 
604 // Use as: @JniType("jni_zero::ByteArrayView") byte[].
605 //
606 // This requests a direct pointer to the array data rather than a copy of it,
607 // so can be more efficient than std::vector<uint8_t> for large arrays.
608 //
609 // This helper needs to release the array via its destructor, and as a result
610 // has more binary size overhead than using std::vector<uint8_t>. As such, you
611 // should prefer std::vector for small arrays.
612 //
613 // Callers must ensure that the passed in array reference outlives this wrapper
614 // (always the case when used with @JniType).
615 class ByteArrayView {
616  public:
ByteArrayView(JNIEnv * env,jbyteArray array)617   ByteArrayView(JNIEnv* env, jbyteArray array)
618       : env_(env),
619         array_(array),
620         length_(env->GetArrayLength(array)),
621         bytes_(env->GetByteArrayElements(array, nullptr)) {}
622 
~ByteArrayView()623   ~ByteArrayView() {
624     env_->ReleaseByteArrayElements(array_, bytes_, JNI_ABORT);
625   }
626 
627   ByteArrayView(const ByteArrayView&) = delete;
628   ByteArrayView(ByteArrayView&& other) = delete;
629   ByteArrayView& operator=(const ByteArrayView&) = delete;
630 
size()631   size_t size() const { return static_cast<size_t>(length_); }
empty()632   bool empty() const { return length_ == 0; }
bytes()633   const jbyte* bytes() const { return bytes_; }
data()634   const uint8_t* data() const { return reinterpret_cast<uint8_t*>(bytes_); }
chars()635   const char* chars() const { return reinterpret_cast<char*>(bytes_); }
string_view()636   std::string_view string_view() const {
637     return std::string_view(chars(), size());
638   }
639 
640  private:
641   JNIEnv* env_;
642   jbyteArray array_;
643   jsize length_;
644   jbyte* bytes_;
645 };
646 
647 // Attaches the current thread to the VM (if necessary) and return the JNIEnv*.
648 JNI_ZERO_COMPONENT_BUILD_EXPORT JNIEnv* AttachCurrentThread();
649 
650 // Same to AttachCurrentThread except that thread name will be set to
651 // |thread_name| if it is the first call. Otherwise, thread_name won't be
652 // changed. AttachCurrentThread() doesn't regard underlying platform thread
653 // name, but just resets it to "Thread-???". This function should be called
654 // right after new thread is created if it is important to keep thread name.
655 JNI_ZERO_COMPONENT_BUILD_EXPORT JNIEnv* AttachCurrentThreadWithName(
656     const std::string& thread_name);
657 
658 // Detaches the current thread from VM if it is attached.
659 JNI_ZERO_COMPONENT_BUILD_EXPORT void DetachFromVM();
660 
661 // Initializes the global JVM.
662 JNI_ZERO_COMPONENT_BUILD_EXPORT void InitVM(JavaVM* vm);
663 
664 // Returns true if the global JVM has been initialized.
665 JNI_ZERO_COMPONENT_BUILD_EXPORT bool IsVMInitialized();
666 
667 // Returns the global JVM, or nullptr if it has not been initialized.
668 JNI_ZERO_COMPONENT_BUILD_EXPORT JavaVM* GetVM();
669 
670 // Do not allow any future native->java calls.
671 // This is necessary in gtest DEATH_TESTS to prevent
672 // GetJavaStackTraceIfPresent() from accessing a defunct JVM (due to fork()).
673 // https://crbug.com/1484834
674 JNI_ZERO_COMPONENT_BUILD_EXPORT void DisableJvmForTesting();
675 
676 JNI_ZERO_COMPONENT_BUILD_EXPORT void SetExceptionHandler(
677     void (*callback)(JNIEnv*));
678 
679 // Returns true if an exception is pending in the provided JNIEnv*.
680 JNI_ZERO_COMPONENT_BUILD_EXPORT bool HasException(JNIEnv* env);
681 
682 // If an exception is pending in the provided JNIEnv*, this function clears it
683 // and returns true.
684 JNI_ZERO_COMPONENT_BUILD_EXPORT bool ClearException(JNIEnv* env);
685 
686 // If there's any pending exception, this function will call the set exception
687 // handler, or if none are set, it will fatally LOG.
688 JNI_ZERO_COMPONENT_BUILD_EXPORT void CheckException(JNIEnv* env);
689 
690 // Sets a function to call instead of just using JNIEnv.FindClass. Useful for
691 // chrome's "splits" which need to be resolved in special ClassLoaders. The
692 // class name parameter (first string) will be given in package.dot.Format. The
693 // second parameter is the split name, which will just be an empty string if not
694 // used.
695 JNI_ZERO_COMPONENT_BUILD_EXPORT void SetClassResolver(
696     jclass (*resolver)(JNIEnv*, const char*, const char*));
697 
698 // Finds the class named |class_name| and returns it.
699 // Use this method instead of invoking directly the JNI FindClass method (to
700 // prevent leaking local references).
701 // This method triggers a fatal assertion if the class could not be found.
702 // Use HasClass if you need to check whether the class exists.
703 JNI_ZERO_COMPONENT_BUILD_EXPORT ScopedJavaLocalRef<jclass>
704 GetClass(JNIEnv* env, const char* class_name, const char* split_name);
705 JNI_ZERO_COMPONENT_BUILD_EXPORT ScopedJavaLocalRef<jclass> GetClass(
706     JNIEnv* env,
707     const char* class_name);
708 
709 // Primary templates for non-Array conversion fuctions. Embedding application
710 // can specialize these functions for their own custom types in order to use
711 // custom types in @JniType.
712 template <typename T, typename J = jobject>
713 T FromJniType(JNIEnv*, const JavaRef<J>&);
714 
715 template <typename T, typename J = jobject>
716 ScopedJavaLocalRef<J> ToJniType(JNIEnv*, const T&);
717 
718 namespace internal {
719 template <typename T>
requires(T t)720 concept has_reserve = requires(T t) { t.reserve(0); };
721 
722 template <typename T>
requires(T t,T::value_type v)723 concept has_push_back = requires(T t, T::value_type v) { t.push_back(v); };
724 
725 template <typename T>
requires(T t,T::value_type v)726 concept has_insert = requires(T t, T::value_type v) { t.insert(v); };
727 
728 template <typename T>
requires(T t)729 concept is_range = requires(T t) {
730   T::value_type;
731   { t.begin() } -> std::same_as<typename T::const_iterator>;
732   { t.end() } -> std::same_as<typename T::const_iterator>;
733   { t.size() } -> std::same_as<size_t>;
734 };
735 
736 template <typename T>
requires(T t)737 concept is_container = requires(T t) {
738   is_range<T>;
739   requires has_push_back<T> || has_insert<T>;
740 };
741 }  // namespace internal
742 
743 // Primary template for Array conversion.
744 // This is in a struct so that we are able to write a default implementation for
745 // vector of any type as long as there is a conversion function from jobject to
746 // that type. Partial specialized template functions are not allowed, but
747 // functions inside a struct are.
748 template <typename ContainerType>
749 struct ConvertArray;
750 
751 // Partial specialization for converting java arrays into std::vector
752 template <typename ContainerType>
requires(ContainerType t)753   requires requires(ContainerType t) {
754     internal::is_container<ContainerType>;
755     not std::integral<typename ContainerType::value_type>;
756   }
757 struct ConvertArray<ContainerType> {
758  private:
759   using ElementType = ContainerType::value_type;
760 
761   template <typename JniType = jobject>
762   static ElementType ElementFromJniType(JNIEnv* env,
763                                         const JavaRef<JniType>& j_element) {
764     if constexpr (std::same_as<ElementType, ScopedJavaLocalRef<JniType>>) {
765       return j_element;
766     } else {
767       return jni_zero::FromJniType<ElementType, JniType>(env, j_element);
768     }
769   }
770 
771   template <typename JniType = jobject>
772   static ScopedJavaLocalRef<JniType> ElementToJniType(
773       JNIEnv* env,
774       const ElementType& element) {
775     if constexpr (std::same_as<ElementType, ScopedJavaLocalRef<JniType>>) {
776       return element;
777     } else if constexpr (std::is_pointer_v<ElementType> &&
778                          !std::is_fundamental_v<
779                              std::remove_pointer_t<ElementType>>) {
780       // Dereference object pointers to enable using vector<ContainerType*>
781       // in order to avoid copying objects for the sake of JNI.
782       return jni_zero::ToJniType<
783           std::remove_const_t<std::remove_pointer_t<ElementType>>, JniType>(
784           env, *element);
785     } else {
786       return jni_zero::ToJniType<ElementType, JniType>(env, element);
787     }
788   }
789 
790  public:
791   template <typename JniType = jobject>
792   static ContainerType FromJniType(JNIEnv* env,
793                                    const JavaRef<jobjectArray>& j_array) {
794     jsize array_jsize = env->GetArrayLength(j_array.obj());
795 
796     ContainerType ret;
797     if constexpr (internal::has_reserve<ContainerType>) {
798       size_t array_size = static_cast<size_t>(array_jsize);
799       ret.reserve(array_size);
800     }
801     for (jsize i = 0; i < array_jsize; ++i) {
802       ElementType element = ElementFromJniType<JniType>(
803           env, jni_zero::ScopedJavaLocalRef<JniType>::Adopt(
804                    env, static_cast<JniType>(
805                             env->GetObjectArrayElement(j_array.obj(), i))));
806       if constexpr (internal::has_push_back<ContainerType>) {
807         ret.push_back(std::move(element));
808       } else {
809         ret.insert(std::move(element));
810       }
811     }
812     return ret;
813   }
814 
815   template <typename JniType = jobject>
816   static ScopedJavaLocalRef<jobjectArray>
817   ToJniType(JNIEnv* env, const ContainerType& collection, jclass clazz) {
818     size_t array_size = collection.size();
819     jsize array_jsize = static_cast<jsize>(array_size);
820     jobjectArray j_array = env->NewObjectArray(array_jsize, clazz, nullptr);
821     CheckException(env);
822 
823     jsize i = 0;
824     for (auto& value : collection) {
825       // Do not call ToJniType for jobject->jobject.
826       if constexpr (std::same_as<ElementType, ScopedJavaLocalRef<JniType>>) {
827         env->SetObjectArrayElement(j_array, i, value.obj());
828       } else {
829         ScopedJavaLocalRef<jobject> element =
830             ElementToJniType<JniType>(env, value);
831         env->SetObjectArrayElement(j_array, i, element.obj());
832       }
833       ++i;
834     }
835     return ScopedJavaLocalRef<jobjectArray>(env, j_array);
836   }
837 };
838 
839 // Specialization for int64_t.
840 template <>
841 struct ConvertArray<std::vector<int64_t>> {
842   static std::vector<int64_t> FromJniType(JNIEnv* env,
843                                           const JavaRef<jlongArray>& j_array) {
844     jsize array_jsize = env->GetArrayLength(j_array.obj());
845     size_t array_size = static_cast<size_t>(array_jsize);
846     std::vector<int64_t> ret;
847     ret.resize(array_size);
848     env->GetLongArrayRegion(j_array.obj(), 0, array_jsize, ret.data());
849     return ret;
850   }
851 
852   static ScopedJavaLocalRef<jlongArray> ToJniType(
853       JNIEnv* env,
854       const std::vector<int64_t>& vec) {
855     jsize array_jsize = static_cast<jsize>(vec.size());
856     jlongArray jia = env->NewLongArray(array_jsize);
857     CheckException(env);
858     env->SetLongArrayRegion(jia, 0, array_jsize, vec.data());
859     return ScopedJavaLocalRef<jlongArray>(env, jia);
860   }
861 };
862 
863 // Specialization for int32_t.
864 template <>
865 struct ConvertArray<std::vector<int32_t>> {
866   static std::vector<int32_t> FromJniType(JNIEnv* env,
867                                           const JavaRef<jintArray>& j_array) {
868     jsize array_jsize = env->GetArrayLength(j_array.obj());
869     size_t array_size = static_cast<size_t>(array_jsize);
870     std::vector<int32_t> ret;
871     ret.resize(array_size);
872     env->GetIntArrayRegion(j_array.obj(), 0, array_jsize, ret.data());
873     return ret;
874   }
875 
876   static ScopedJavaLocalRef<jintArray> ToJniType(
877       JNIEnv* env,
878       const std::vector<int32_t>& vec) {
879     jsize array_jsize = static_cast<jsize>(vec.size());
880     jintArray jia = env->NewIntArray(array_jsize);
881     CheckException(env);
882     env->SetIntArrayRegion(jia, 0, array_jsize, vec.data());
883     return ScopedJavaLocalRef<jintArray>(env, jia);
884   }
885 };
886 
887 // Specialization for byte array.
888 template <>
889 struct ConvertArray<std::vector<uint8_t>> {
890   static std::vector<uint8_t> FromJniType(JNIEnv* env,
891                                           const JavaRef<jbyteArray>& j_array) {
892     jsize array_jsize = env->GetArrayLength(j_array.obj());
893     size_t array_size = static_cast<size_t>(array_jsize);
894     std::vector<uint8_t> ret;
895     ret.resize(array_size);
896     env->GetByteArrayRegion(j_array.obj(), 0, array_jsize,
897                             reinterpret_cast<jbyte*>(ret.data()));
898     return ret;
899   }
900 
901   static ScopedJavaLocalRef<jbyteArray> ToJniType(
902       JNIEnv* env,
903       const std::vector<uint8_t>& vec) {
904     jsize array_jsize = static_cast<jsize>(vec.size());
905     jbyteArray jia = env->NewByteArray(array_jsize);
906     CheckException(env);
907     env->SetByteArrayRegion(jia, 0, array_jsize,
908                             reinterpret_cast<const jbyte*>(vec.data()));
909     return ScopedJavaLocalRef<jbyteArray>(env, jia);
910   }
911 };
912 
913 // Specialization for ByteArrayView.
914 template <>
915 struct ConvertArray<ByteArrayView> {
916   static ByteArrayView FromJniType(JNIEnv* env,
917                                    const JavaRef<jbyteArray>& array) {
918     return ByteArrayView(env, array.obj());
919   }
920 };
921 
922 // This class is a wrapper for JNIEnv Get(Static)MethodID.
923 class JNI_ZERO_COMPONENT_BUILD_EXPORT MethodID {
924  public:
925   enum Type {
926     TYPE_STATIC,
927     TYPE_INSTANCE,
928   };
929 
930   // Returns the method ID for the method with the specified name and signature.
931   // This method triggers a fatal assertion if the method could not be found.
932   template <Type type>
933   static jmethodID Get(JNIEnv* env,
934                        jclass clazz,
935                        const char* method_name,
936                        const char* jni_signature);
937 
938   // The caller is responsible to zero-initialize |atomic_method_id|.
939   // It's fine to simultaneously call this on multiple threads referencing the
940   // same |atomic_method_id|.
941   template <Type type>
942   static jmethodID LazyGet(JNIEnv* env,
943                            jclass clazz,
944                            const char* method_name,
945                            const char* jni_signature,
946                            std::atomic<jmethodID>* atomic_method_id);
947 };
948 
949 }  // namespace jni_zero
950 
951 #endif  // JNI_ZERO_JNI_ZERO_H_
952