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