Bug 1793629 - Implement attention indicator for the unified extensions button, r...
[gecko.git] / widget / android / jni / Refs.h
blob9b507034910080cc92992894bfdabdb04c52a338
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef mozilla_jni_Refs_h__
8 #define mozilla_jni_Refs_h__
10 #include <jni.h>
12 #include <utility>
14 #include "mozilla/fallible.h"
15 #include "mozilla/jni/Utils.h"
16 #include "mozilla/jni/TypeAdapter.h"
17 #include "nsError.h" // for nsresult
18 #include "nsString.h"
19 #include "nsTArray.h"
21 namespace mozilla {
22 namespace jni {
24 // Wrapped object reference (e.g. jobject, jclass, etc...)
25 template <class Cls, typename JNIType>
26 class Ref;
27 // Represents a calling context for JNI methods.
28 template <class Cls, typename JNIType>
29 class Context;
30 // Wrapped local reference that inherits from Ref.
31 template <class Cls>
32 class LocalRef;
33 // Wrapped global reference that inherits from Ref.
34 template <class Cls>
35 class GlobalRef;
36 // Wrapped weak reference that inherits from Ref.
37 template <class Cls>
38 class WeakRef;
39 // Wrapped dangling reference that's owned by someone else.
40 template <class Cls>
41 class DependentRef;
43 // Class to hold the native types of a method's arguments.
44 // For example, if a method has signature (ILjava/lang/String;)V,
45 // its arguments class would be jni::Args<int32_t, jni::String::Param>
46 template <typename...>
47 struct Args {};
49 class Object;
51 // Base class for Ref and its specializations.
52 template <class Cls, typename Type>
53 class Ref {
54 template <class C, typename T>
55 friend class Ref;
57 using Self = Ref<Cls, Type>;
58 using bool_type = void (Self::*)() const;
59 void non_null_reference() const {}
61 // A Cls-derivative that allows copying
62 // (e.g. when acting as a return value).
63 struct CopyableCtx : public Context<Cls, Type> {
64 CopyableCtx(JNIEnv* env, Type instance)
65 : Context<Cls, Type>(env, instance) {}
67 CopyableCtx(const CopyableCtx& cls)
68 : Context<Cls, Type>(cls.Env(), cls.Get()) {}
71 // Private copy constructor so that there's no danger of assigning a
72 // temporary LocalRef/GlobalRef to a Ref, and potentially use the Ref
73 // after the source had been freed.
74 Ref(const Ref&) = default;
76 protected:
77 static JNIEnv* FindEnv() {
78 return Cls::callingThread == CallingThread::GECKO ? GetGeckoThreadEnv()
79 : GetEnvForThread();
82 Type mInstance;
84 // Protected jobject constructor because outside code should be using
85 // Ref::From. Using Ref::From makes it very easy to see which code is using
86 // raw JNI types for future refactoring.
87 explicit Ref(Type instance) : mInstance(instance) {}
89 public:
90 using JNIType = Type;
92 class AutoLock {
93 friend class Ref<Cls, Type>;
95 JNIEnv* const mEnv;
96 Type mInstance;
98 explicit AutoLock(Type aInstance)
99 : mEnv(FindEnv()), mInstance(mEnv->NewLocalRef(aInstance)) {
100 mEnv->MonitorEnter(mInstance);
101 MOZ_CATCH_JNI_EXCEPTION(mEnv);
104 public:
105 AutoLock(AutoLock&& aOther)
106 : mEnv(aOther.mEnv), mInstance(aOther.mInstance) {
107 aOther.mInstance = nullptr;
110 ~AutoLock() { Unlock(); }
112 void Unlock() {
113 if (mInstance) {
114 mEnv->MonitorExit(mInstance);
115 mEnv->DeleteLocalRef(mInstance);
116 MOZ_CATCH_JNI_EXCEPTION(mEnv);
117 mInstance = nullptr;
122 // Construct a Ref form a raw JNI reference.
123 static Ref<Cls, Type> From(JNIType obj) { return Ref<Cls, Type>(obj); }
125 // Construct a Ref form a generic object reference.
126 static Ref<Cls, Type> From(const Ref<Object, jobject>& obj) {
127 return Ref<Cls, Type>(JNIType(obj.Get()));
130 MOZ_IMPLICIT Ref(decltype(nullptr)) : mInstance(nullptr) {}
132 // Get the raw JNI reference.
133 JNIType Get() const { return mInstance; }
135 template <class T>
136 bool IsInstanceOf() const {
137 return FindEnv()->IsInstanceOf(mInstance, typename T::Context().ClassRef());
140 template <class T>
141 typename T::Ref Cast() const {
142 #ifdef MOZ_CHECK_JNI
143 MOZ_RELEASE_ASSERT(FindEnv()->IsAssignableFrom(
144 Context<Cls, Type>().ClassRef(), typename T::Context().ClassRef()));
145 #endif
146 return T::Ref::From(*this);
149 AutoLock Lock() const { return AutoLock(mInstance); }
151 bool operator==(const Ref& other) const {
152 // Treat two references of the same object as being the same.
153 return mInstance == other.mInstance ||
154 JNI_FALSE != FindEnv()->IsSameObject(mInstance, other.mInstance);
157 bool operator!=(const Ref& other) const { return !operator==(other); }
159 bool operator==(decltype(nullptr)) const { return !mInstance; }
161 bool operator!=(decltype(nullptr)) const { return !!mInstance; }
163 CopyableCtx operator->() const { return CopyableCtx(FindEnv(), mInstance); }
165 CopyableCtx operator*() const { return operator->(); }
167 // Any ref can be cast to an object ref.
168 operator Ref<Object, jobject>() const {
169 return Ref<Object, jobject>(mInstance);
172 // Null checking (e.g. !!ref) using the safe-bool idiom.
173 operator bool_type() const {
174 return mInstance ? &Self::non_null_reference : nullptr;
177 // We don't allow implicit conversion to jobject because that can lead
178 // to easy mistakes such as assigning a temporary LocalRef to a jobject,
179 // and using the jobject after the LocalRef has been freed.
181 // We don't allow explicit conversion, to make outside code use Ref::Get.
182 // Using Ref::Get makes it very easy to see which code is using raw JNI
183 // types to make future refactoring easier.
185 // operator JNIType() const = delete;
188 // Represents a calling context for JNI methods.
189 template <class Cls, typename Type>
190 class Context : public Ref<Cls, Type> {
191 using Ref = jni::Ref<Cls, Type>;
193 static jclass sClassRef; // global reference
195 protected:
196 JNIEnv* const mEnv;
198 public:
199 Context() : Ref(nullptr), mEnv(Ref::FindEnv()) {}
201 Context(JNIEnv* env, Type instance) : Ref(instance), mEnv(env) {}
203 jclass ClassRef() const {
204 if (!sClassRef) {
205 const jclass cls = GetClassRef(mEnv, Cls::name);
206 sClassRef = jclass(mEnv->NewGlobalRef(cls));
207 mEnv->DeleteLocalRef(cls);
209 return sClassRef;
212 JNIEnv* Env() const { return mEnv; }
214 template <class T>
215 bool IsInstanceOf() const {
216 return mEnv->IsInstanceOf(Ref::mInstance,
217 typename T::Context(mEnv, nullptr).ClassRef());
220 bool operator==(const Ref& other) const {
221 // Treat two references of the same object as being the same.
222 return Ref::mInstance == other.Get() ||
223 JNI_FALSE != mEnv->IsSameObject(Ref::mInstance, other.Get());
226 bool operator!=(const Ref& other) const { return !operator==(other); }
228 bool operator==(decltype(nullptr)) const { return !Ref::mInstance; }
230 bool operator!=(decltype(nullptr)) const { return !!Ref::mInstance; }
232 Cls operator->() const {
233 MOZ_ASSERT(Ref::mInstance, "Null jobject");
234 return Cls(*this);
237 const Context<Cls, Type>& operator*() const { return *this; }
240 template <class C, typename T>
241 jclass Context<C, T>::sClassRef;
243 template <class Cls, typename Type = jobject>
244 class ObjectBase {
245 protected:
246 const jni::Context<Cls, Type>& mCtx;
248 jclass ClassRef() const { return mCtx.ClassRef(); }
249 JNIEnv* Env() const { return mCtx.Env(); }
250 Type Instance() const { return mCtx.Get(); }
252 public:
253 using Ref = jni::Ref<Cls, Type>;
254 using Context = jni::Context<Cls, Type>;
255 using LocalRef = jni::LocalRef<Cls>;
256 using GlobalRef = jni::GlobalRef<Cls>;
257 using WeakRef = jni::WeakRef<Cls>;
258 using Param = const Ref&;
260 static const CallingThread callingThread = CallingThread::ANY;
261 static const char name[];
263 explicit ObjectBase(const Context& ctx) : mCtx(ctx) {}
265 Cls* operator->() { return static_cast<Cls*>(this); }
268 // Binding for a plain jobject.
269 class Object : public ObjectBase<Object, jobject> {
270 public:
271 explicit Object(const Context& ctx) : ObjectBase<Object, jobject>(ctx) {}
274 // Binding for a built-in object reference other than jobject.
275 template <typename T>
276 class TypedObject : public ObjectBase<TypedObject<T>, T> {
277 public:
278 explicit TypedObject(const Context<TypedObject<T>, T>& ctx)
279 : ObjectBase<TypedObject<T>, T>(ctx) {}
282 // Binding for a boxed primitive object.
283 template <typename T>
284 class BoxedObject : public ObjectBase<BoxedObject<T>, jobject> {
285 public:
286 explicit BoxedObject(const Context<BoxedObject<T>, jobject>& ctx)
287 : ObjectBase<BoxedObject<T>, jobject>(ctx) {}
290 template <>
291 const char ObjectBase<Object, jobject>::name[];
292 template <>
293 const char ObjectBase<TypedObject<jstring>, jstring>::name[];
294 template <>
295 const char ObjectBase<TypedObject<jclass>, jclass>::name[];
296 template <>
297 const char ObjectBase<TypedObject<jthrowable>, jthrowable>::name[];
298 template <>
299 const char ObjectBase<BoxedObject<jboolean>, jobject>::name[];
300 template <>
301 const char ObjectBase<BoxedObject<jbyte>, jobject>::name[];
302 template <>
303 const char ObjectBase<BoxedObject<jchar>, jobject>::name[];
304 template <>
305 const char ObjectBase<BoxedObject<jshort>, jobject>::name[];
306 template <>
307 const char ObjectBase<BoxedObject<jint>, jobject>::name[];
308 template <>
309 const char ObjectBase<BoxedObject<jlong>, jobject>::name[];
310 template <>
311 const char ObjectBase<BoxedObject<jfloat>, jobject>::name[];
312 template <>
313 const char ObjectBase<BoxedObject<jdouble>, jobject>::name[];
314 template <>
315 const char ObjectBase<TypedObject<jbooleanArray>, jbooleanArray>::name[];
316 template <>
317 const char ObjectBase<TypedObject<jbyteArray>, jbyteArray>::name[];
318 template <>
319 const char ObjectBase<TypedObject<jcharArray>, jcharArray>::name[];
320 template <>
321 const char ObjectBase<TypedObject<jshortArray>, jshortArray>::name[];
322 template <>
323 const char ObjectBase<TypedObject<jintArray>, jintArray>::name[];
324 template <>
325 const char ObjectBase<TypedObject<jlongArray>, jlongArray>::name[];
326 template <>
327 const char ObjectBase<TypedObject<jfloatArray>, jfloatArray>::name[];
328 template <>
329 const char ObjectBase<TypedObject<jdoubleArray>, jdoubleArray>::name[];
330 template <>
331 const char ObjectBase<TypedObject<jobjectArray>, jobjectArray>::name[];
333 // Define bindings for built-in types.
334 using String = TypedObject<jstring>;
335 using Class = TypedObject<jclass>;
336 using Throwable = TypedObject<jthrowable>;
338 using Boolean = BoxedObject<jboolean>;
339 using Byte = BoxedObject<jbyte>;
340 using Character = BoxedObject<jchar>;
341 using Short = BoxedObject<jshort>;
342 using Integer = BoxedObject<jint>;
343 using Long = BoxedObject<jlong>;
344 using Float = BoxedObject<jfloat>;
345 using Double = BoxedObject<jdouble>;
347 using BooleanArray = TypedObject<jbooleanArray>;
348 using ByteArray = TypedObject<jbyteArray>;
349 using CharArray = TypedObject<jcharArray>;
350 using ShortArray = TypedObject<jshortArray>;
351 using IntArray = TypedObject<jintArray>;
352 using LongArray = TypedObject<jlongArray>;
353 using FloatArray = TypedObject<jfloatArray>;
354 using DoubleArray = TypedObject<jdoubleArray>;
355 using ObjectArray = TypedObject<jobjectArray>;
357 namespace detail {
359 // See explanation in LocalRef.
360 template <class Cls>
361 struct GenericObject {
362 using Type = Object;
364 template <>
365 struct GenericObject<Object> {
366 struct Type {
367 using Ref = jni::Ref<Type, jobject>;
368 using Context = jni::Context<Type, jobject>;
371 template <class Cls>
372 struct GenericLocalRef {
373 template <class C>
374 struct Type : jni::Object {};
376 template <>
377 struct GenericLocalRef<Object> {
378 template <class C>
379 using Type = jni::LocalRef<C>;
382 } // namespace detail
384 template <class Cls>
385 class LocalRef : public Cls::Context {
386 template <class C>
387 friend class LocalRef;
389 using Ctx = typename Cls::Context;
390 using Ref = typename Cls::Ref;
391 using JNIType = typename Ref::JNIType;
393 // In order to be able to convert LocalRef<Object> to LocalRef<Cls>, we
394 // need constructors and copy assignment operators that take in a
395 // LocalRef<Object> argument. However, if Cls *is* Object, we would have
396 // duplicated constructors and operators with LocalRef<Object> arguments. To
397 // avoid this conflict, we use GenericObject, which is defined as Object for
398 // LocalRef<non-Object> and defined as a dummy class for LocalRef<Object>.
399 using GenericObject = typename detail::GenericObject<Cls>::Type;
401 // Similarly, GenericLocalRef is useed to convert LocalRef<Cls> to,
402 // LocalRef<Object>. It's defined as LocalRef<C> for Cls == Object,
403 // and defined as a dummy template class for Cls != Object.
404 template <class C>
405 using GenericLocalRef =
406 typename detail::GenericLocalRef<Cls>::template Type<C>;
408 static JNIType NewLocalRef(JNIEnv* env, JNIType obj) {
409 return JNIType(obj ? env->NewLocalRef(obj) : nullptr);
412 LocalRef(JNIEnv* env, JNIType instance) : Ctx(env, instance) {}
414 LocalRef& swap(LocalRef& other) {
415 auto instance = other.mInstance;
416 other.mInstance = Ctx::mInstance;
417 Ctx::mInstance = instance;
418 return *this;
421 public:
422 // Construct a LocalRef from a raw JNI local reference. Unlike Ref::From,
423 // LocalRef::Adopt returns a LocalRef that will delete the local reference
424 // when going out of scope.
425 static LocalRef Adopt(JNIType instance) {
426 return LocalRef(Ref::FindEnv(), instance);
429 static LocalRef Adopt(JNIEnv* env, JNIType instance) {
430 return LocalRef(env, instance);
433 // Copy constructor.
434 LocalRef(const LocalRef<Cls>& ref)
435 : Ctx(ref.mEnv, NewLocalRef(ref.mEnv, ref.mInstance)) {}
437 // Move constructor.
438 LocalRef(LocalRef<Cls>&& ref) : Ctx(ref.mEnv, ref.mInstance) {
439 ref.mInstance = nullptr;
442 explicit LocalRef(JNIEnv* env = Ref::FindEnv()) : Ctx(env, nullptr) {}
444 // Construct a LocalRef from any Ref,
445 // which means creating a new local reference.
446 MOZ_IMPLICIT LocalRef(const Ref& ref) : Ctx(Ref::FindEnv(), nullptr) {
447 Ctx::mInstance = NewLocalRef(Ctx::mEnv, ref.Get());
450 LocalRef(JNIEnv* env, const Ref& ref)
451 : Ctx(env, NewLocalRef(env, ref.Get())) {}
453 // Move a LocalRef<Object> into a LocalRef<Cls> without
454 // creating/deleting local references.
455 MOZ_IMPLICIT LocalRef(LocalRef<GenericObject>&& ref)
456 : Ctx(ref.mEnv, JNIType(ref.mInstance)) {
457 ref.mInstance = nullptr;
460 template <class C>
461 MOZ_IMPLICIT LocalRef(GenericLocalRef<C>&& ref)
462 : Ctx(ref.mEnv, ref.mInstance) {
463 ref.mInstance = nullptr;
466 // Implicitly converts nullptr to LocalRef.
467 MOZ_IMPLICIT LocalRef(decltype(nullptr)) : Ctx(Ref::FindEnv(), nullptr) {}
469 ~LocalRef() {
470 if (Ctx::mInstance) {
471 Ctx::mEnv->DeleteLocalRef(Ctx::mInstance);
472 Ctx::mInstance = nullptr;
476 // Get the raw JNI reference that can be used as a return value.
477 // Returns the same JNI type (jobject, jstring, etc.) as the underlying Ref.
478 typename Ref::JNIType Forget() {
479 const auto obj = Ctx::Get();
480 Ctx::mInstance = nullptr;
481 return obj;
484 LocalRef<Cls>& operator=(LocalRef<Cls> ref) & { return swap(ref); }
486 LocalRef<Cls>& operator=(const Ref& ref) & {
487 LocalRef<Cls> newRef(Ctx::mEnv, ref);
488 return swap(newRef);
491 LocalRef<Cls>& operator=(LocalRef<GenericObject>&& ref) & {
492 LocalRef<Cls> newRef(std::move(ref));
493 return swap(newRef);
496 template <class C>
497 LocalRef<Cls>& operator=(GenericLocalRef<C>&& ref) & {
498 LocalRef<Cls> newRef(std::move(ref));
499 return swap(newRef);
502 LocalRef<Cls>& operator=(decltype(nullptr)) & {
503 LocalRef<Cls> newRef(Ctx::mEnv, nullptr);
504 return swap(newRef);
508 template <class Cls>
509 class GlobalRef : public Cls::Ref {
510 using Ref = typename Cls::Ref;
511 using JNIType = typename Ref::JNIType;
513 static JNIType NewGlobalRef(JNIEnv* env, JNIType instance) {
514 return JNIType(instance ? env->NewGlobalRef(instance) : nullptr);
517 GlobalRef& swap(GlobalRef& other) {
518 auto instance = other.mInstance;
519 other.mInstance = Ref::mInstance;
520 Ref::mInstance = instance;
521 return *this;
524 public:
525 GlobalRef() : Ref(nullptr) {}
527 // Copy constructor
528 GlobalRef(const GlobalRef& ref)
529 : Ref(NewGlobalRef(GetEnvForThread(), ref.mInstance)) {}
531 // Move constructor
532 GlobalRef(GlobalRef&& ref) : Ref(ref.mInstance) { ref.mInstance = nullptr; }
534 MOZ_IMPLICIT GlobalRef(const Ref& ref)
535 : Ref(NewGlobalRef(GetEnvForThread(), ref.Get())) {}
537 GlobalRef(JNIEnv* env, const Ref& ref) : Ref(NewGlobalRef(env, ref.Get())) {}
539 MOZ_IMPLICIT GlobalRef(const LocalRef<Cls>& ref)
540 : Ref(NewGlobalRef(ref.Env(), ref.Get())) {}
542 // Implicitly converts nullptr to GlobalRef.
543 MOZ_IMPLICIT GlobalRef(decltype(nullptr)) : Ref(nullptr) {}
545 ~GlobalRef() {
546 if (Ref::mInstance) {
547 Clear(GetEnvForThread());
551 // Get the raw JNI reference that can be used as a return value.
552 // Returns the same JNI type (jobject, jstring, etc.) as the underlying Ref.
553 typename Ref::JNIType Forget() {
554 const auto obj = Ref::Get();
555 Ref::mInstance = nullptr;
556 return obj;
559 void Clear(JNIEnv* env) {
560 if (Ref::mInstance) {
561 env->DeleteGlobalRef(Ref::mInstance);
562 Ref::mInstance = nullptr;
566 GlobalRef<Cls>& operator=(GlobalRef<Cls> ref) & { return swap(ref); }
568 GlobalRef<Cls>& operator=(const Ref& ref) & {
569 GlobalRef<Cls> newRef(ref);
570 return swap(newRef);
573 GlobalRef<Cls>& operator=(const LocalRef<Cls>& ref) & {
574 GlobalRef<Cls> newRef(ref);
575 return swap(newRef);
578 GlobalRef<Cls>& operator=(decltype(nullptr)) & {
579 GlobalRef<Cls> newRef(nullptr);
580 return swap(newRef);
584 template <class Cls>
585 class WeakRef : public Ref<Cls, jweak> {
586 using Ref = Ref<Cls, jweak>;
587 using JNIType = typename Ref::JNIType;
589 static JNIType NewWeakRef(JNIEnv* env, JNIType instance) {
590 return JNIType(instance ? env->NewWeakGlobalRef(instance) : nullptr);
593 WeakRef& swap(WeakRef& other) {
594 auto instance = other.mInstance;
595 other.mInstance = Ref::mInstance;
596 Ref::mInstance = instance;
597 return *this;
600 public:
601 WeakRef() : Ref(nullptr) {}
603 // Copy constructor
604 WeakRef(const WeakRef& ref)
605 : Ref(NewWeakRef(GetEnvForThread(), ref.mInstance)) {}
607 // Move constructor
608 WeakRef(WeakRef&& ref) : Ref(ref.mInstance) { ref.mInstance = nullptr; }
610 MOZ_IMPLICIT WeakRef(const Ref& ref)
611 : Ref(NewWeakRef(GetEnvForThread(), ref.Get())) {}
613 WeakRef(JNIEnv* env, const Ref& ref) : Ref(NewWeakRef(env, ref.Get())) {}
615 MOZ_IMPLICIT WeakRef(const LocalRef<Cls>& ref)
616 : Ref(NewWeakRef(ref.Env(), ref.Get())) {}
618 // Implicitly converts nullptr to WeakRef.
619 MOZ_IMPLICIT WeakRef(decltype(nullptr)) : Ref(nullptr) {}
621 ~WeakRef() {
622 if (Ref::mInstance) {
623 Clear(GetEnvForThread());
627 // Get the raw JNI reference that can be used as a return value.
628 // Returns the same JNI type (jobject, jstring, etc.) as the underlying Ref.
629 typename Ref::JNIType Forget() {
630 const auto obj = Ref::Get();
631 Ref::mInstance = nullptr;
632 return obj;
635 void Clear(JNIEnv* env) {
636 if (Ref::mInstance) {
637 env->DeleteWeakGlobalRef(Ref::mInstance);
638 Ref::mInstance = nullptr;
642 WeakRef<Cls>& operator=(WeakRef<Cls> ref) & { return swap(ref); }
644 WeakRef<Cls>& operator=(const Ref& ref) & {
645 WeakRef<Cls> newRef(ref);
646 return swap(newRef);
649 WeakRef<Cls>& operator=(const LocalRef<Cls>& ref) & {
650 WeakRef<Cls> newRef(ref);
651 return swap(newRef);
654 WeakRef<Cls>& operator=(decltype(nullptr)) & {
655 WeakRef<Cls> newRef(nullptr);
656 return swap(newRef);
659 void operator->() const = delete;
660 void operator*() const = delete;
663 template <class Cls>
664 class DependentRef : public Cls::Ref {
665 using Ref = typename Cls::Ref;
667 public:
668 explicit DependentRef(typename Ref::JNIType instance) : Ref(instance) {}
670 DependentRef(const DependentRef& ref) : Ref(ref.Get()) {}
673 class StringParam;
675 template <>
676 class TypedObject<jstring> : public ObjectBase<TypedObject<jstring>, jstring> {
677 using Base = ObjectBase<TypedObject<jstring>, jstring>;
679 public:
680 using Param = const StringParam&;
682 explicit TypedObject(const Context& ctx) : Base(ctx) {}
684 size_t Length() const {
685 const size_t ret = Base::Env()->GetStringLength(Base::Instance());
686 MOZ_CATCH_JNI_EXCEPTION(Base::Env());
687 return ret;
690 nsString ToString() const {
691 const jchar* const str =
692 Base::Env()->GetStringChars(Base::Instance(), nullptr);
693 const jsize len = Base::Env()->GetStringLength(Base::Instance());
695 nsString result(reinterpret_cast<const char16_t*>(str), len);
696 Base::Env()->ReleaseStringChars(Base::Instance(), str);
697 return result;
700 nsCString ToCString() const { return NS_ConvertUTF16toUTF8(ToString()); }
702 // Convert jstring to a nsString.
703 operator nsString() const { return ToString(); }
705 // Convert jstring to a nsCString.
706 operator nsCString() const { return ToCString(); }
709 // Define a custom parameter type for String,
710 // which accepts both String::Ref and nsAString/nsACString
711 class StringParam : public String::Ref {
712 using Ref = String::Ref;
714 private:
715 // Not null if we should delete ref on destruction.
716 JNIEnv* const mEnv;
718 static jstring GetString(JNIEnv* env, const nsAString& str) {
719 const jstring result = env->NewString(
720 reinterpret_cast<const jchar*>(str.BeginReading()), str.Length());
721 if (!result) {
722 NS_ABORT_OOM(str.Length() * sizeof(char16_t));
724 MOZ_CATCH_JNI_EXCEPTION(env);
725 return result;
728 static jstring GetString(JNIEnv* env, const nsAString& str,
729 const fallible_t&) {
730 const jstring result = env->NewString(
731 reinterpret_cast<const jchar*>(str.BeginReading()), str.Length());
732 if (env->ExceptionCheck()) {
733 #ifdef MOZ_CHECK_JNI
734 env->ExceptionDescribe();
735 #endif
736 env->ExceptionClear();
738 return result;
741 static jstring GetString(JNIEnv* env, const nsACString& str,
742 const fallible_t& aFallible) {
743 nsAutoString utf16;
744 if (!CopyUTF8toUTF16(str, utf16, aFallible)) {
745 return nullptr;
747 return GetString(env, utf16, aFallible);
750 public:
751 MOZ_IMPLICIT StringParam(decltype(nullptr)) : Ref(nullptr), mEnv(nullptr) {}
753 MOZ_IMPLICIT StringParam(const Ref& ref) : Ref(ref.Get()), mEnv(nullptr) {}
755 MOZ_IMPLICIT StringParam(const nsAString& str, JNIEnv* env = Ref::FindEnv())
756 : Ref(GetString(env, str)), mEnv(env) {}
758 MOZ_IMPLICIT StringParam(const nsAString& str, JNIEnv* env,
759 const fallible_t& aFallible)
760 : Ref(GetString(env, str, aFallible)), mEnv(env) {}
762 MOZ_IMPLICIT StringParam(const nsLiteralString& str,
763 JNIEnv* env = Ref::FindEnv())
764 : Ref(GetString(env, str)), mEnv(env) {}
766 MOZ_IMPLICIT StringParam(const char16_t* str, JNIEnv* env = Ref::FindEnv())
767 : Ref(GetString(env, nsDependentString(str))), mEnv(env) {}
769 MOZ_IMPLICIT StringParam(const nsACString& str, JNIEnv* env = Ref::FindEnv())
770 : Ref(GetString(env, NS_ConvertUTF8toUTF16(str))), mEnv(env) {}
772 MOZ_IMPLICIT StringParam(const nsACString& str, JNIEnv* env,
773 const fallible_t& aFallible)
774 : Ref(GetString(env, str, aFallible)), mEnv(env) {}
776 MOZ_IMPLICIT StringParam(const nsLiteralCString& str,
777 JNIEnv* env = Ref::FindEnv())
778 : Ref(GetString(env, NS_ConvertUTF8toUTF16(str))), mEnv(env) {}
780 MOZ_IMPLICIT StringParam(const char* str, JNIEnv* env = Ref::FindEnv())
781 : Ref(GetString(env, NS_ConvertUTF8toUTF16(str))), mEnv(env) {}
783 StringParam(StringParam&& other) : Ref(other.Get()), mEnv(other.mEnv) {
784 other.mInstance = nullptr;
787 ~StringParam() {
788 if (mEnv && Get()) {
789 mEnv->DeleteLocalRef(Get());
793 operator String::LocalRef() const {
794 // We can't return our existing ref because the returned
795 // LocalRef could be freed first, so we need a new local ref.
796 return String::LocalRef(mEnv ? mEnv : Ref::FindEnv(), *this);
800 namespace detail {
801 template <typename T>
802 struct TypeAdapter;
805 // Ref specialization for arrays.
806 template <typename JNIType, class ElementType>
807 class ArrayRefBase : public ObjectBase<TypedObject<JNIType>, JNIType> {
808 protected:
809 using Base = ObjectBase<TypedObject<JNIType>, JNIType>;
811 public:
812 explicit ArrayRefBase(const Context<TypedObject<JNIType>, JNIType>& ctx)
813 : Base(ctx) {}
815 static typename Base::LocalRef New(const ElementType* data, size_t length) {
816 using JNIElemType = typename detail::TypeAdapter<ElementType>::JNIType;
817 static_assert(sizeof(ElementType) == sizeof(JNIElemType),
818 "Size of native type must match size of JNI type");
819 JNIEnv* const jenv = mozilla::jni::GetEnvForThread();
820 auto result = (jenv->*detail::TypeAdapter<ElementType>::NewArray)(length);
821 MOZ_CATCH_JNI_EXCEPTION(jenv);
822 (jenv->*detail::TypeAdapter<ElementType>::SetArray)(
823 result, jsize(0), length, reinterpret_cast<const JNIElemType*>(data));
824 MOZ_CATCH_JNI_EXCEPTION(jenv);
825 return Base::LocalRef::Adopt(jenv, result);
828 static typename Base::LocalRef New(const ElementType* data, size_t length,
829 const fallible_t&) {
830 using JNIElemType = typename detail::TypeAdapter<ElementType>::JNIType;
831 static_assert(sizeof(ElementType) == sizeof(JNIElemType),
832 "Size of native type must match size of JNI type");
833 JNIEnv* const jenv = mozilla::jni::GetEnvForThread();
834 auto result = (jenv->*detail::TypeAdapter<ElementType>::NewArray)(length);
835 if (jenv->ExceptionCheck()) {
836 if (!IsOOMException(jenv)) {
837 // This exception isn't excepted due not to OOM. This is unrecoverable
838 // error.
839 MOZ_CATCH_JNI_EXCEPTION(jenv);
841 #ifdef MOZ_CHECK_JNI
842 jenv->ExceptionDescribe();
843 #endif
844 jenv->ExceptionClear();
845 return Base::LocalRef::Adopt(jenv, nullptr);
847 (jenv->*detail::TypeAdapter<ElementType>::SetArray)(
848 result, jsize(0), length, reinterpret_cast<const JNIElemType*>(data));
849 if (jenv->ExceptionCheck()) {
850 if (!IsOOMException(jenv)) {
851 // This exception isn't excepted due not to OOM. This is unrecoverable
852 // error.
853 MOZ_CATCH_JNI_EXCEPTION(jenv);
855 #ifdef MOZ_CHECK_JNI
856 jenv->ExceptionDescribe();
857 #endif
858 jenv->ExceptionClear();
859 jenv->DeleteLocalRef(result);
860 return Base::LocalRef::Adopt(jenv, nullptr);
862 return Base::LocalRef::Adopt(jenv, result);
865 size_t Length() const {
866 const size_t ret = Base::Env()->GetArrayLength(Base::Instance());
867 MOZ_CATCH_JNI_EXCEPTION(Base::Env());
868 return ret;
871 ElementType GetElement(size_t index) const {
872 using JNIElemType = typename detail::TypeAdapter<ElementType>::JNIType;
873 static_assert(sizeof(ElementType) == sizeof(JNIElemType),
874 "Size of native type must match size of JNI type");
876 ElementType ret;
877 (Base::Env()->*detail::TypeAdapter<ElementType>::GetArray)(
878 Base::Instance(), jsize(index), 1,
879 reinterpret_cast<JNIElemType*>(&ret));
880 MOZ_CATCH_JNI_EXCEPTION(Base::Env());
881 return ret;
884 nsTArray<ElementType> GetElements() const {
885 using JNIElemType = typename detail::TypeAdapter<ElementType>::JNIType;
886 static_assert(sizeof(ElementType) == sizeof(JNIElemType),
887 "Size of native type must match size of JNI type");
889 const size_t len = size_t(Base::Env()->GetArrayLength(Base::Instance()));
891 nsTArray<ElementType> array(len);
892 array.SetLength(len);
893 CopyTo(array.Elements(), len);
894 return array;
897 // returns number of elements copied
898 size_t CopyTo(ElementType* buffer, size_t size) const {
899 using JNIElemType = typename detail::TypeAdapter<ElementType>::JNIType;
900 static_assert(sizeof(ElementType) == sizeof(JNIElemType),
901 "Size of native type must match size of JNI type");
903 const size_t len = size_t(Base::Env()->GetArrayLength(Base::Instance()));
904 const size_t amountToCopy = (len > size ? size : len);
905 (Base::Env()->*detail::TypeAdapter<ElementType>::GetArray)(
906 Base::Instance(), 0, jsize(amountToCopy),
907 reinterpret_cast<JNIElemType*>(buffer));
908 return amountToCopy;
911 ElementType operator[](size_t index) const { return GetElement(index); }
913 operator nsTArray<ElementType>() const { return GetElements(); }
916 #define DEFINE_PRIMITIVE_ARRAY_REF_HEADER(JNIType, ElementType) \
917 template <> \
918 class TypedObject<JNIType> : public ArrayRefBase<JNIType, ElementType> { \
919 public: \
920 explicit TypedObject(const Context& ctx) \
921 : ArrayRefBase<JNIType, ElementType>(ctx) {} \
922 static typename Base::LocalRef From(const nsTArray<ElementType>& aArray) { \
923 return New(aArray.Elements(), aArray.Length()); \
926 #define DEFINE_PRIMITIVE_ARRAY_REF_FOOTER }
928 #define DEFINE_PRIMITIVE_ARRAY_REF_FROM_IMPLICIT_CONVERSION(ElementType, \
929 ConvertFromType) \
930 static typename Base::LocalRef From( \
931 const nsTArray<ConvertFromType>& aArray) { \
932 return New(reinterpret_cast<const ElementType*>(aArray.Elements()), \
933 aArray.Length()); \
936 #define DEFINE_PRIMITIVE_ARRAY_REF(JNIType, ElementType) \
937 DEFINE_PRIMITIVE_ARRAY_REF_HEADER(JNIType, ElementType) \
938 DEFINE_PRIMITIVE_ARRAY_REF_FOOTER
940 #define DEFINE_PRIMITIVE_ARRAY_REF_WITH_IMPLICIT_CONVERSION( \
941 JNIType, ElementType, ConvertFromType) \
942 DEFINE_PRIMITIVE_ARRAY_REF_HEADER(JNIType, ElementType) \
943 DEFINE_PRIMITIVE_ARRAY_REF_FROM_IMPLICIT_CONVERSION(ElementType, \
944 ConvertFromType) \
945 DEFINE_PRIMITIVE_ARRAY_REF_FOOTER
947 DEFINE_PRIMITIVE_ARRAY_REF(jbooleanArray, bool);
948 DEFINE_PRIMITIVE_ARRAY_REF_WITH_IMPLICIT_CONVERSION(jbyteArray, int8_t,
949 uint8_t);
950 DEFINE_PRIMITIVE_ARRAY_REF(jcharArray, char16_t);
951 DEFINE_PRIMITIVE_ARRAY_REF_WITH_IMPLICIT_CONVERSION(jshortArray, int16_t,
952 uint16_t);
953 DEFINE_PRIMITIVE_ARRAY_REF_WITH_IMPLICIT_CONVERSION(jintArray, int32_t,
954 uint32_t);
955 DEFINE_PRIMITIVE_ARRAY_REF(jfloatArray, float);
956 DEFINE_PRIMITIVE_ARRAY_REF_WITH_IMPLICIT_CONVERSION(jlongArray, int64_t,
957 uint64_t);
958 DEFINE_PRIMITIVE_ARRAY_REF(jdoubleArray, double);
960 #undef DEFINE_PRIMITIVE_ARRAY_REF
961 #undef DEFINE_PRIMITIVE_ARRAY_REF_WITH_IMPLICIT_CONVERSION
962 #undef DEFINE_PRIMITIVE_ARRAY_HEADER
963 #undef DEFINE_PRIMITIVE_ARRAY_FROM_IMPLICIT_CONVERSION
964 #undef DEFINE_PRIMITIVE_ARRAY_FOOTER
966 class ByteBuffer : public ObjectBase<ByteBuffer, jobject> {
967 public:
968 explicit ByteBuffer(const Context& ctx)
969 : ObjectBase<ByteBuffer, jobject>(ctx) {}
971 static LocalRef New(void* data, size_t capacity) {
972 JNIEnv* const env = GetEnvForThread();
973 const auto ret =
974 LocalRef::Adopt(env, env->NewDirectByteBuffer(data, jlong(capacity)));
975 MOZ_CATCH_JNI_EXCEPTION(env);
976 return ret;
979 void* Address() {
980 void* const ret = Env()->GetDirectBufferAddress(Instance());
981 MOZ_CATCH_JNI_EXCEPTION(Env());
982 return ret;
985 size_t Capacity() {
986 const size_t ret = size_t(Env()->GetDirectBufferCapacity(Instance()));
987 MOZ_CATCH_JNI_EXCEPTION(Env());
988 return ret;
992 template <>
993 const char ObjectBase<ByteBuffer, jobject>::name[];
995 template <>
996 class TypedObject<jobjectArray>
997 : public ObjectBase<TypedObject<jobjectArray>, jobjectArray> {
998 using Base = ObjectBase<TypedObject<jobjectArray>, jobjectArray>;
1000 public:
1001 template <class Cls = Object>
1002 static Base::LocalRef New(size_t length,
1003 typename Cls::Param initialElement = nullptr) {
1004 JNIEnv* const env = GetEnvForThread();
1005 jobjectArray array = env->NewObjectArray(
1006 jsize(length), typename Cls::Context(env, nullptr).ClassRef(),
1007 initialElement.Get());
1008 MOZ_CATCH_JNI_EXCEPTION(env);
1009 return Base::LocalRef::Adopt(env, array);
1012 explicit TypedObject(const Context& ctx) : Base(ctx) {}
1014 size_t Length() const {
1015 const size_t ret = Base::Env()->GetArrayLength(Base::Instance());
1016 MOZ_CATCH_JNI_EXCEPTION(Base::Env());
1017 return ret;
1020 Object::LocalRef GetElement(size_t index) const {
1021 auto ret = Object::LocalRef::Adopt(
1022 Base::Env(),
1023 Base::Env()->GetObjectArrayElement(Base::Instance(), jsize(index)));
1024 MOZ_CATCH_JNI_EXCEPTION(Base::Env());
1025 return ret;
1028 nsTArray<Object::LocalRef> GetElements() const {
1029 const jsize len = size_t(Base::Env()->GetArrayLength(Base::Instance()));
1031 nsTArray<Object::LocalRef> array((size_t(len)));
1032 for (jsize i = 0; i < len; i++) {
1033 array.AppendElement(Object::LocalRef::Adopt(
1034 Base::Env(),
1035 Base::Env()->GetObjectArrayElement(Base::Instance(), i)));
1036 MOZ_CATCH_JNI_EXCEPTION(Base::Env());
1038 return array;
1041 Object::LocalRef operator[](size_t index) const { return GetElement(index); }
1043 operator nsTArray<Object::LocalRef>() const { return GetElements(); }
1045 void SetElement(size_t index, Object::Param element) const {
1046 Base::Env()->SetObjectArrayElement(Base::Instance(), jsize(index),
1047 element.Get());
1048 MOZ_CATCH_JNI_EXCEPTION(Base::Env());
1052 // Support conversion from LocalRef<T>* to LocalRef<Object>*:
1053 // LocalRef<Foo> foo;
1054 // Foo::GetFoo(&foo); // error because parameter type is LocalRef<Object>*.
1055 // Foo::GetFoo(ReturnTo(&foo)); // OK because ReturnTo converts the argument.
1056 template <class Cls>
1057 class ReturnToLocal {
1058 private:
1059 LocalRef<Cls>* const localRef;
1060 LocalRef<Object> objRef;
1062 public:
1063 explicit ReturnToLocal(LocalRef<Cls>* ref) : localRef(ref) {}
1064 operator LocalRef<Object>*() { return &objRef; }
1066 ~ReturnToLocal() {
1067 if (objRef) {
1068 *localRef = std::move(objRef);
1073 template <class Cls>
1074 ReturnToLocal<Cls> ReturnTo(LocalRef<Cls>* ref) {
1075 return ReturnToLocal<Cls>(ref);
1078 // Support conversion from GlobalRef<T>* to LocalRef<Object/T>*:
1079 // GlobalRef<Foo> foo;
1080 // Foo::GetFoo(&foo); // error because parameter type is LocalRef<Foo>*.
1081 // Foo::GetFoo(ReturnTo(&foo)); // OK because ReturnTo converts the argument.
1082 template <class Cls>
1083 class ReturnToGlobal {
1084 private:
1085 GlobalRef<Cls>* const globalRef;
1086 LocalRef<Object> objRef;
1087 LocalRef<Cls> clsRef;
1089 public:
1090 explicit ReturnToGlobal(GlobalRef<Cls>* ref) : globalRef(ref) {}
1091 operator LocalRef<Object>*() { return &objRef; }
1092 operator LocalRef<Cls>*() { return &clsRef; }
1094 ~ReturnToGlobal() {
1095 if (objRef) {
1096 *globalRef = (clsRef = std::move(objRef));
1097 } else if (clsRef) {
1098 *globalRef = clsRef;
1103 template <class Cls>
1104 ReturnToGlobal<Cls> ReturnTo(GlobalRef<Cls>* ref) {
1105 return ReturnToGlobal<Cls>(ref);
1108 // Make a LocalRef<T> from any other Ref<T>
1109 template <typename Cls, typename JNIType>
1110 LocalRef<Cls> ToLocalRef(const Ref<Cls, JNIType>& aRef) {
1111 return LocalRef<Cls>(aRef);
1114 } // namespace jni
1115 } // namespace mozilla
1117 #endif // mozilla_jni_Refs_h__