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/. */
6 
7 #ifndef mozilla_jni_Refs_h__
8 #define mozilla_jni_Refs_h__
9 
10 #include <jni.h>
11 
12 #include <utility>
13 
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"
20 
21 namespace mozilla {
22 namespace jni {
23 
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;
42 
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 {};
48 
49 class Object;
50 
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;
56 
57   using Self = Ref<Cls, Type>;
58   using bool_type = void (Self::*)() const;
non_null_reference()59   void non_null_reference() const {}
60 
61   // A Cls-derivative that allows copying
62   // (e.g. when acting as a return value).
63   struct CopyableCtx : public Context<Cls, Type> {
CopyableCtxCopyableCtx64     CopyableCtx(JNIEnv* env, Type instance)
65         : Context<Cls, Type>(env, instance) {}
66 
CopyableCtxCopyableCtx67     CopyableCtx(const CopyableCtx& cls)
68         : Context<Cls, Type>(cls.Env(), cls.Get()) {}
69   };
70 
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;
75 
76  protected:
FindEnv()77   static JNIEnv* FindEnv() {
78     return Cls::callingThread == CallingThread::GECKO ? GetGeckoThreadEnv()
79                                                       : GetEnvForThread();
80   }
81 
82   Type mInstance;
83 
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.
Ref(Type instance)87   explicit Ref(Type instance) : mInstance(instance) {}
88 
89  public:
90   using JNIType = Type;
91 
92   class AutoLock {
93     friend class Ref<Cls, Type>;
94 
95     JNIEnv* const mEnv;
96     Type mInstance;
97 
AutoLock(Type aInstance)98     explicit AutoLock(Type aInstance)
99         : mEnv(FindEnv()), mInstance(mEnv->NewLocalRef(aInstance)) {
100       mEnv->MonitorEnter(mInstance);
101       MOZ_CATCH_JNI_EXCEPTION(mEnv);
102     }
103 
104    public:
AutoLock(AutoLock && aOther)105     AutoLock(AutoLock&& aOther)
106         : mEnv(aOther.mEnv), mInstance(aOther.mInstance) {
107       aOther.mInstance = nullptr;
108     }
109 
~AutoLock()110     ~AutoLock() { Unlock(); }
111 
Unlock()112     void Unlock() {
113       if (mInstance) {
114         mEnv->MonitorExit(mInstance);
115         mEnv->DeleteLocalRef(mInstance);
116         MOZ_CATCH_JNI_EXCEPTION(mEnv);
117         mInstance = nullptr;
118       }
119     }
120   };
121 
122   // Construct a Ref form a raw JNI reference.
From(JNIType obj)123   static Ref<Cls, Type> From(JNIType obj) { return Ref<Cls, Type>(obj); }
124 
125   // Construct a Ref form a generic object reference.
From(const Ref<Object,jobject> & obj)126   static Ref<Cls, Type> From(const Ref<Object, jobject>& obj) {
127     return Ref<Cls, Type>(JNIType(obj.Get()));
128   }
129 
Ref(decltype (nullptr))130   MOZ_IMPLICIT Ref(decltype(nullptr)) : mInstance(nullptr) {}
131 
132   // Get the raw JNI reference.
Get()133   JNIType Get() const { return mInstance; }
134 
135   template <class T>
IsInstanceOf()136   bool IsInstanceOf() const {
137     return FindEnv()->IsInstanceOf(mInstance, typename T::Context().ClassRef());
138   }
139 
140   template <class T>
Cast()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);
147   }
148 
Lock()149   AutoLock Lock() const { return AutoLock(mInstance); }
150 
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);
155   }
156 
157   bool operator!=(const Ref& other) const { return !operator==(other); }
158 
159   bool operator==(decltype(nullptr)) const { return !mInstance; }
160 
161   bool operator!=(decltype(nullptr)) const { return !!mInstance; }
162 
163   CopyableCtx operator->() const { return CopyableCtx(FindEnv(), mInstance); }
164 
165   CopyableCtx operator*() const { return operator->(); }
166 
167   // Any ref can be cast to an object ref.
168   operator Ref<Object, jobject>() const {
169     return Ref<Object, jobject>(mInstance);
170   }
171 
172   // Null checking (e.g. !!ref) using the safe-bool idiom.
bool_type()173   operator bool_type() const {
174     return mInstance ? &Self::non_null_reference : nullptr;
175   }
176 
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.
180 
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.
184 
185   // operator JNIType() const = delete;
186 };
187 
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>;
192 
193   static jclass sClassRef;  // global reference
194 
195  protected:
196   JNIEnv* const mEnv;
197 
198  public:
Context()199   Context() : Ref(nullptr), mEnv(Ref::FindEnv()) {}
200 
Context(JNIEnv * env,Type instance)201   Context(JNIEnv* env, Type instance) : Ref(instance), mEnv(env) {}
202 
ClassRef()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);
208     }
209     return sClassRef;
210   }
211 
Env()212   JNIEnv* Env() const { return mEnv; }
213 
214   template <class T>
IsInstanceOf()215   bool IsInstanceOf() const {
216     return mEnv->IsInstanceOf(Ref::mInstance,
217                               typename T::Context(mEnv, nullptr).ClassRef());
218   }
219 
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());
224   }
225 
226   bool operator!=(const Ref& other) const { return !operator==(other); }
227 
228   bool operator==(decltype(nullptr)) const { return !Ref::mInstance; }
229 
230   bool operator!=(decltype(nullptr)) const { return !!Ref::mInstance; }
231 
232   Cls operator->() const {
233     MOZ_ASSERT(Ref::mInstance, "Null jobject");
234     return Cls(*this);
235   }
236 
237   const Context<Cls, Type>& operator*() const { return *this; }
238 };
239 
240 template <class C, typename T>
241 jclass Context<C, T>::sClassRef;
242 
243 template <class Cls, typename Type = jobject>
244 class ObjectBase {
245  protected:
246   const jni::Context<Cls, Type>& mCtx;
247 
ClassRef()248   jclass ClassRef() const { return mCtx.ClassRef(); }
Env()249   JNIEnv* Env() const { return mCtx.Env(); }
Instance()250   Type Instance() const { return mCtx.Get(); }
251 
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&;
259 
260   static const CallingThread callingThread = CallingThread::ANY;
261   static const char name[];
262 
ObjectBase(const Context & ctx)263   explicit ObjectBase(const Context& ctx) : mCtx(ctx) {}
264 
265   Cls* operator->() { return static_cast<Cls*>(this); }
266 };
267 
268 // Binding for a plain jobject.
269 class Object : public ObjectBase<Object, jobject> {
270  public:
Object(const Context & ctx)271   explicit Object(const Context& ctx) : ObjectBase<Object, jobject>(ctx) {}
272 };
273 
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:
TypedObject(const Context<TypedObject<T>,T> & ctx)278   explicit TypedObject(const Context<TypedObject<T>, T>& ctx)
279       : ObjectBase<TypedObject<T>, T>(ctx) {}
280 };
281 
282 // Binding for a boxed primitive object.
283 template <typename T>
284 class BoxedObject : public ObjectBase<BoxedObject<T>, jobject> {
285  public:
BoxedObject(const Context<BoxedObject<T>,jobject> & ctx)286   explicit BoxedObject(const Context<BoxedObject<T>, jobject>& ctx)
287       : ObjectBase<BoxedObject<T>, jobject>(ctx) {}
288 };
289 
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[];
332 
333 // Define bindings for built-in types.
334 using String = TypedObject<jstring>;
335 using Class = TypedObject<jclass>;
336 using Throwable = TypedObject<jthrowable>;
337 
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>;
346 
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>;
356 
357 namespace detail {
358 
359 // See explanation in LocalRef.
360 template <class Cls>
361 struct GenericObject {
362   using Type = Object;
363 };
364 template <>
365 struct GenericObject<Object> {
366   struct Type {
367     using Ref = jni::Ref<Type, jobject>;
368     using Context = jni::Context<Type, jobject>;
369   };
370 };
371 template <class Cls>
372 struct GenericLocalRef {
373   template <class C>
374   struct Type : jni::Object {};
375 };
376 template <>
377 struct GenericLocalRef<Object> {
378   template <class C>
379   using Type = jni::LocalRef<C>;
380 };
381 
382 }  // namespace detail
383 
384 template <class Cls>
385 class LocalRef : public Cls::Context {
386   template <class C>
387   friend class LocalRef;
388 
389   using Ctx = typename Cls::Context;
390   using Ref = typename Cls::Ref;
391   using JNIType = typename Ref::JNIType;
392 
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;
400 
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>;
407 
408   static JNIType NewLocalRef(JNIEnv* env, JNIType obj) {
409     return JNIType(obj ? env->NewLocalRef(obj) : nullptr);
410   }
411 
412   LocalRef(JNIEnv* env, JNIType instance) : Ctx(env, instance) {}
413 
414   LocalRef& swap(LocalRef& other) {
415     auto instance = other.mInstance;
416     other.mInstance = Ctx::mInstance;
417     Ctx::mInstance = instance;
418     return *this;
419   }
420 
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);
427   }
428 
429   static LocalRef Adopt(JNIEnv* env, JNIType instance) {
430     return LocalRef(env, instance);
431   }
432 
433   // Copy constructor.
434   LocalRef(const LocalRef<Cls>& ref)
435       : Ctx(ref.mEnv, NewLocalRef(ref.mEnv, ref.mInstance)) {}
436 
437   // Move constructor.
438   LocalRef(LocalRef<Cls>&& ref) : Ctx(ref.mEnv, ref.mInstance) {
439     ref.mInstance = nullptr;
440   }
441 
442   explicit LocalRef(JNIEnv* env = Ref::FindEnv()) : Ctx(env, nullptr) {}
443 
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());
448   }
449 
450   LocalRef(JNIEnv* env, const Ref& ref)
451       : Ctx(env, NewLocalRef(env, ref.Get())) {}
452 
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;
458   }
459 
460   template <class C>
461   MOZ_IMPLICIT LocalRef(GenericLocalRef<C>&& ref)
462       : Ctx(ref.mEnv, ref.mInstance) {
463     ref.mInstance = nullptr;
464   }
465 
466   // Implicitly converts nullptr to LocalRef.
467   MOZ_IMPLICIT LocalRef(decltype(nullptr)) : Ctx(Ref::FindEnv(), nullptr) {}
468 
469   ~LocalRef() {
470     if (Ctx::mInstance) {
471       Ctx::mEnv->DeleteLocalRef(Ctx::mInstance);
472       Ctx::mInstance = nullptr;
473     }
474   }
475 
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;
482   }
483 
484   LocalRef<Cls>& operator=(LocalRef<Cls> ref) & { return swap(ref); }
485 
486   LocalRef<Cls>& operator=(const Ref& ref) & {
487     LocalRef<Cls> newRef(Ctx::mEnv, ref);
488     return swap(newRef);
489   }
490 
491   LocalRef<Cls>& operator=(LocalRef<GenericObject>&& ref) & {
492     LocalRef<Cls> newRef(std::move(ref));
493     return swap(newRef);
494   }
495 
496   template <class C>
497   LocalRef<Cls>& operator=(GenericLocalRef<C>&& ref) & {
498     LocalRef<Cls> newRef(std::move(ref));
499     return swap(newRef);
500   }
501 
502   LocalRef<Cls>& operator=(decltype(nullptr)) & {
503     LocalRef<Cls> newRef(Ctx::mEnv, nullptr);
504     return swap(newRef);
505   }
506 };
507 
508 template <class Cls>
509 class GlobalRef : public Cls::Ref {
510   using Ref = typename Cls::Ref;
511   using JNIType = typename Ref::JNIType;
512 
513   static JNIType NewGlobalRef(JNIEnv* env, JNIType instance) {
514     return JNIType(instance ? env->NewGlobalRef(instance) : nullptr);
515   }
516 
517   GlobalRef& swap(GlobalRef& other) {
518     auto instance = other.mInstance;
519     other.mInstance = Ref::mInstance;
520     Ref::mInstance = instance;
521     return *this;
522   }
523 
524  public:
525   GlobalRef() : Ref(nullptr) {}
526 
527   // Copy constructor
528   GlobalRef(const GlobalRef& ref)
529       : Ref(NewGlobalRef(GetEnvForThread(), ref.mInstance)) {}
530 
531   // Move constructor
532   GlobalRef(GlobalRef&& ref) : Ref(ref.mInstance) { ref.mInstance = nullptr; }
533 
534   MOZ_IMPLICIT GlobalRef(const Ref& ref)
535       : Ref(NewGlobalRef(GetEnvForThread(), ref.Get())) {}
536 
537   GlobalRef(JNIEnv* env, const Ref& ref) : Ref(NewGlobalRef(env, ref.Get())) {}
538 
539   MOZ_IMPLICIT GlobalRef(const LocalRef<Cls>& ref)
540       : Ref(NewGlobalRef(ref.Env(), ref.Get())) {}
541 
542   // Implicitly converts nullptr to GlobalRef.
543   MOZ_IMPLICIT GlobalRef(decltype(nullptr)) : Ref(nullptr) {}
544 
545   ~GlobalRef() {
546     if (Ref::mInstance) {
547       Clear(GetEnvForThread());
548     }
549   }
550 
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;
557   }
558 
559   void Clear(JNIEnv* env) {
560     if (Ref::mInstance) {
561       env->DeleteGlobalRef(Ref::mInstance);
562       Ref::mInstance = nullptr;
563     }
564   }
565 
566   GlobalRef<Cls>& operator=(GlobalRef<Cls> ref) & { return swap(ref); }
567 
568   GlobalRef<Cls>& operator=(const Ref& ref) & {
569     GlobalRef<Cls> newRef(ref);
570     return swap(newRef);
571   }
572 
573   GlobalRef<Cls>& operator=(const LocalRef<Cls>& ref) & {
574     GlobalRef<Cls> newRef(ref);
575     return swap(newRef);
576   }
577 
578   GlobalRef<Cls>& operator=(decltype(nullptr)) & {
579     GlobalRef<Cls> newRef(nullptr);
580     return swap(newRef);
581   }
582 };
583 
584 template <class Cls>
585 class WeakRef : public Ref<Cls, jweak> {
586   using Ref = Ref<Cls, jweak>;
587   using JNIType = typename Ref::JNIType;
588 
589   static JNIType NewWeakRef(JNIEnv* env, JNIType instance) {
590     return JNIType(instance ? env->NewWeakGlobalRef(instance) : nullptr);
591   }
592 
593   WeakRef& swap(WeakRef& other) {
594     auto instance = other.mInstance;
595     other.mInstance = Ref::mInstance;
596     Ref::mInstance = instance;
597     return *this;
598   }
599 
600  public:
601   WeakRef() : Ref(nullptr) {}
602 
603   // Copy constructor
604   WeakRef(const WeakRef& ref)
605       : Ref(NewWeakRef(GetEnvForThread(), ref.mInstance)) {}
606 
607   // Move constructor
608   WeakRef(WeakRef&& ref) : Ref(ref.mInstance) { ref.mInstance = nullptr; }
609 
610   MOZ_IMPLICIT WeakRef(const Ref& ref)
611       : Ref(NewWeakRef(GetEnvForThread(), ref.Get())) {}
612 
613   WeakRef(JNIEnv* env, const Ref& ref) : Ref(NewWeakRef(env, ref.Get())) {}
614 
615   MOZ_IMPLICIT WeakRef(const LocalRef<Cls>& ref)
616       : Ref(NewWeakRef(ref.Env(), ref.Get())) {}
617 
618   // Implicitly converts nullptr to WeakRef.
619   MOZ_IMPLICIT WeakRef(decltype(nullptr)) : Ref(nullptr) {}
620 
621   ~WeakRef() {
622     if (Ref::mInstance) {
623       Clear(GetEnvForThread());
624     }
625   }
626 
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;
633   }
634 
635   void Clear(JNIEnv* env) {
636     if (Ref::mInstance) {
637       env->DeleteWeakGlobalRef(Ref::mInstance);
638       Ref::mInstance = nullptr;
639     }
640   }
641 
642   WeakRef<Cls>& operator=(WeakRef<Cls> ref) & { return swap(ref); }
643 
644   WeakRef<Cls>& operator=(const Ref& ref) & {
645     WeakRef<Cls> newRef(ref);
646     return swap(newRef);
647   }
648 
649   WeakRef<Cls>& operator=(const LocalRef<Cls>& ref) & {
650     WeakRef<Cls> newRef(ref);
651     return swap(newRef);
652   }
653 
654   WeakRef<Cls>& operator=(decltype(nullptr)) & {
655     WeakRef<Cls> newRef(nullptr);
656     return swap(newRef);
657   }
658 
659   void operator->() const = delete;
660   void operator*() const = delete;
661 };
662 
663 template <class Cls>
664 class DependentRef : public Cls::Ref {
665   using Ref = typename Cls::Ref;
666 
667  public:
668   explicit DependentRef(typename Ref::JNIType instance) : Ref(instance) {}
669 
670   DependentRef(const DependentRef& ref) : Ref(ref.Get()) {}
671 };
672 
673 class StringParam;
674 
675 template <>
676 class TypedObject<jstring> : public ObjectBase<TypedObject<jstring>, jstring> {
677   using Base = ObjectBase<TypedObject<jstring>, jstring>;
678 
679  public:
680   using Param = const StringParam&;
681 
682   explicit TypedObject(const Context& ctx) : Base(ctx) {}
683 
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;
688   }
689 
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());
694 
695     nsString result(reinterpret_cast<const char16_t*>(str), len);
696     Base::Env()->ReleaseStringChars(Base::Instance(), str);
697     return result;
698   }
699 
700   nsCString ToCString() const { return NS_ConvertUTF16toUTF8(ToString()); }
701 
702   // Convert jstring to a nsString.
703   operator nsString() const { return ToString(); }
704 
705   // Convert jstring to a nsCString.
706   operator nsCString() const { return ToCString(); }
707 };
708 
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;
713 
714  private:
715   // Not null if we should delete ref on destruction.
716   JNIEnv* const mEnv;
717 
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));
723     }
724     MOZ_CATCH_JNI_EXCEPTION(env);
725     return result;
726   }
727 
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();
737     }
738     return result;
739   }
740 
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;
746     }
747     return GetString(env, utf16, aFallible);
748   }
749 
750  public:
751   MOZ_IMPLICIT StringParam(decltype(nullptr)) : Ref(nullptr), mEnv(nullptr) {}
752 
753   MOZ_IMPLICIT StringParam(const Ref& ref) : Ref(ref.Get()), mEnv(nullptr) {}
754 
755   MOZ_IMPLICIT StringParam(const nsAString& str, JNIEnv* env = Ref::FindEnv())
756       : Ref(GetString(env, str)), mEnv(env) {}
757 
758   MOZ_IMPLICIT StringParam(const nsAString& str, JNIEnv* env,
759                            const fallible_t& aFallible)
760       : Ref(GetString(env, str, aFallible)), mEnv(env) {}
761 
762   MOZ_IMPLICIT StringParam(const nsLiteralString& str,
763                            JNIEnv* env = Ref::FindEnv())
764       : Ref(GetString(env, str)), mEnv(env) {}
765 
766   MOZ_IMPLICIT StringParam(const char16_t* str, JNIEnv* env = Ref::FindEnv())
767       : Ref(GetString(env, nsDependentString(str))), mEnv(env) {}
768 
769   MOZ_IMPLICIT StringParam(const nsACString& str, JNIEnv* env = Ref::FindEnv())
770       : Ref(GetString(env, NS_ConvertUTF8toUTF16(str))), mEnv(env) {}
771 
772   MOZ_IMPLICIT StringParam(const nsACString& str, JNIEnv* env,
773                            const fallible_t& aFallible)
774       : Ref(GetString(env, str, aFallible)), mEnv(env) {}
775 
776   MOZ_IMPLICIT StringParam(const nsLiteralCString& str,
777                            JNIEnv* env = Ref::FindEnv())
778       : Ref(GetString(env, NS_ConvertUTF8toUTF16(str))), mEnv(env) {}
779 
780   MOZ_IMPLICIT StringParam(const char* str, JNIEnv* env = Ref::FindEnv())
781       : Ref(GetString(env, NS_ConvertUTF8toUTF16(str))), mEnv(env) {}
782 
783   StringParam(StringParam&& other) : Ref(other.Get()), mEnv(other.mEnv) {
784     other.mInstance = nullptr;
785   }
786 
787   ~StringParam() {
788     if (mEnv && Get()) {
789       mEnv->DeleteLocalRef(Get());
790     }
791   }
792 
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);
797   }
798 };
799 
800 namespace detail {
801 template <typename T>
802 struct TypeAdapter;
803 }
804 
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>;
810 
811  public:
812   explicit ArrayRefBase(const Context<TypedObject<JNIType>, JNIType>& ctx)
813       : Base(ctx) {}
814 
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);
826   }
827 
828   size_t Length() const {
829     const size_t ret = Base::Env()->GetArrayLength(Base::Instance());
830     MOZ_CATCH_JNI_EXCEPTION(Base::Env());
831     return ret;
832   }
833 
834   ElementType GetElement(size_t index) const {
835     using JNIElemType = typename detail::TypeAdapter<ElementType>::JNIType;
836     static_assert(sizeof(ElementType) == sizeof(JNIElemType),
837                   "Size of native type must match size of JNI type");
838 
839     ElementType ret;
840     (Base::Env()->*detail::TypeAdapter<ElementType>::GetArray)(
841         Base::Instance(), jsize(index), 1,
842         reinterpret_cast<JNIElemType*>(&ret));
843     MOZ_CATCH_JNI_EXCEPTION(Base::Env());
844     return ret;
845   }
846 
847   nsTArray<ElementType> GetElements() const {
848     using JNIElemType = typename detail::TypeAdapter<ElementType>::JNIType;
849     static_assert(sizeof(ElementType) == sizeof(JNIElemType),
850                   "Size of native type must match size of JNI type");
851 
852     const size_t len = size_t(Base::Env()->GetArrayLength(Base::Instance()));
853 
854     nsTArray<ElementType> array(len);
855     array.SetLength(len);
856     CopyTo(array.Elements(), len);
857     return array;
858   }
859 
860   // returns number of elements copied
861   size_t CopyTo(ElementType* buffer, size_t size) const {
862     using JNIElemType = typename detail::TypeAdapter<ElementType>::JNIType;
863     static_assert(sizeof(ElementType) == sizeof(JNIElemType),
864                   "Size of native type must match size of JNI type");
865 
866     const size_t len = size_t(Base::Env()->GetArrayLength(Base::Instance()));
867     const size_t amountToCopy = (len > size ? size : len);
868     (Base::Env()->*detail::TypeAdapter<ElementType>::GetArray)(
869         Base::Instance(), 0, jsize(amountToCopy),
870         reinterpret_cast<JNIElemType*>(buffer));
871     return amountToCopy;
872   }
873 
874   ElementType operator[](size_t index) const { return GetElement(index); }
875 
876   operator nsTArray<ElementType>() const { return GetElements(); }
877 };
878 
879 #define DEFINE_PRIMITIVE_ARRAY_REF_HEADER(JNIType, ElementType)                \
880   template <>                                                                  \
881   class TypedObject<JNIType> : public ArrayRefBase<JNIType, ElementType> {     \
882    public:                                                                     \
883     explicit TypedObject(const Context& ctx)                                   \
884         : ArrayRefBase<JNIType, ElementType>(ctx) {}                           \
885     static typename Base::LocalRef From(const nsTArray<ElementType>& aArray) { \
886       return New(aArray.Elements(), aArray.Length());                          \
887     }
888 
889 #define DEFINE_PRIMITIVE_ARRAY_REF_FOOTER }
890 
891 #define DEFINE_PRIMITIVE_ARRAY_REF_FROM_IMPLICIT_CONVERSION(ElementType,     \
892                                                             ConvertFromType) \
893   static typename Base::LocalRef From(                                       \
894       const nsTArray<ConvertFromType>& aArray) {                             \
895     return New(reinterpret_cast<const ElementType*>(aArray.Elements()),      \
896                aArray.Length());                                             \
897   }
898 
899 #define DEFINE_PRIMITIVE_ARRAY_REF(JNIType, ElementType)  \
900   DEFINE_PRIMITIVE_ARRAY_REF_HEADER(JNIType, ElementType) \
901   DEFINE_PRIMITIVE_ARRAY_REF_FOOTER
902 
903 #define DEFINE_PRIMITIVE_ARRAY_REF_WITH_IMPLICIT_CONVERSION(           \
904     JNIType, ElementType, ConvertFromType)                             \
905   DEFINE_PRIMITIVE_ARRAY_REF_HEADER(JNIType, ElementType)              \
906   DEFINE_PRIMITIVE_ARRAY_REF_FROM_IMPLICIT_CONVERSION(ElementType,     \
907                                                       ConvertFromType) \
908   DEFINE_PRIMITIVE_ARRAY_REF_FOOTER
909 
910 DEFINE_PRIMITIVE_ARRAY_REF(jbooleanArray, bool);
911 DEFINE_PRIMITIVE_ARRAY_REF_WITH_IMPLICIT_CONVERSION(jbyteArray, int8_t,
912                                                     uint8_t);
913 DEFINE_PRIMITIVE_ARRAY_REF(jcharArray, char16_t);
914 DEFINE_PRIMITIVE_ARRAY_REF_WITH_IMPLICIT_CONVERSION(jshortArray, int16_t,
915                                                     uint16_t);
916 DEFINE_PRIMITIVE_ARRAY_REF_WITH_IMPLICIT_CONVERSION(jintArray, int32_t,
917                                                     uint32_t);
918 DEFINE_PRIMITIVE_ARRAY_REF(jfloatArray, float);
919 DEFINE_PRIMITIVE_ARRAY_REF_WITH_IMPLICIT_CONVERSION(jlongArray, int64_t,
920                                                     uint64_t);
921 DEFINE_PRIMITIVE_ARRAY_REF(jdoubleArray, double);
922 
923 #undef DEFINE_PRIMITIVE_ARRAY_REF
924 #undef DEFINE_PRIMITIVE_ARRAY_REF_WITH_IMPLICIT_CONVERSION
925 #undef DEFINE_PRIMITIVE_ARRAY_HEADER
926 #undef DEFINE_PRIMITIVE_ARRAY_FROM_IMPLICIT_CONVERSION
927 #undef DEFINE_PRIMITIVE_ARRAY_FOOTER
928 
929 class ByteBuffer : public ObjectBase<ByteBuffer, jobject> {
930  public:
931   explicit ByteBuffer(const Context& ctx)
932       : ObjectBase<ByteBuffer, jobject>(ctx) {}
933 
934   static LocalRef New(void* data, size_t capacity) {
935     JNIEnv* const env = GetEnvForThread();
936     const auto ret =
937         LocalRef::Adopt(env, env->NewDirectByteBuffer(data, jlong(capacity)));
938     MOZ_CATCH_JNI_EXCEPTION(env);
939     return ret;
940   }
941 
942   void* Address() {
943     void* const ret = Env()->GetDirectBufferAddress(Instance());
944     MOZ_CATCH_JNI_EXCEPTION(Env());
945     return ret;
946   }
947 
948   size_t Capacity() {
949     const size_t ret = size_t(Env()->GetDirectBufferCapacity(Instance()));
950     MOZ_CATCH_JNI_EXCEPTION(Env());
951     return ret;
952   }
953 };
954 
955 template <>
956 const char ObjectBase<ByteBuffer, jobject>::name[];
957 
958 template <>
959 class TypedObject<jobjectArray>
960     : public ObjectBase<TypedObject<jobjectArray>, jobjectArray> {
961   using Base = ObjectBase<TypedObject<jobjectArray>, jobjectArray>;
962 
963  public:
964   template <class Cls = Object>
965   static Base::LocalRef New(size_t length,
966                             typename Cls::Param initialElement = nullptr) {
967     JNIEnv* const env = GetEnvForThread();
968     jobjectArray array = env->NewObjectArray(
969         jsize(length), typename Cls::Context(env, nullptr).ClassRef(),
970         initialElement.Get());
971     MOZ_CATCH_JNI_EXCEPTION(env);
972     return Base::LocalRef::Adopt(env, array);
973   }
974 
975   explicit TypedObject(const Context& ctx) : Base(ctx) {}
976 
977   size_t Length() const {
978     const size_t ret = Base::Env()->GetArrayLength(Base::Instance());
979     MOZ_CATCH_JNI_EXCEPTION(Base::Env());
980     return ret;
981   }
982 
983   Object::LocalRef GetElement(size_t index) const {
984     auto ret = Object::LocalRef::Adopt(
985         Base::Env(),
986         Base::Env()->GetObjectArrayElement(Base::Instance(), jsize(index)));
987     MOZ_CATCH_JNI_EXCEPTION(Base::Env());
988     return ret;
989   }
990 
991   nsTArray<Object::LocalRef> GetElements() const {
992     const jsize len = size_t(Base::Env()->GetArrayLength(Base::Instance()));
993 
994     nsTArray<Object::LocalRef> array((size_t(len)));
995     for (jsize i = 0; i < len; i++) {
996       array.AppendElement(Object::LocalRef::Adopt(
997           Base::Env(),
998           Base::Env()->GetObjectArrayElement(Base::Instance(), i)));
999       MOZ_CATCH_JNI_EXCEPTION(Base::Env());
1000     }
1001     return array;
1002   }
1003 
1004   Object::LocalRef operator[](size_t index) const { return GetElement(index); }
1005 
1006   operator nsTArray<Object::LocalRef>() const { return GetElements(); }
1007 
1008   void SetElement(size_t index, Object::Param element) const {
1009     Base::Env()->SetObjectArrayElement(Base::Instance(), jsize(index),
1010                                        element.Get());
1011     MOZ_CATCH_JNI_EXCEPTION(Base::Env());
1012   }
1013 };
1014 
1015 // Support conversion from LocalRef<T>* to LocalRef<Object>*:
1016 //   LocalRef<Foo> foo;
1017 //   Foo::GetFoo(&foo); // error because parameter type is LocalRef<Object>*.
1018 //   Foo::GetFoo(ReturnTo(&foo)); // OK because ReturnTo converts the argument.
1019 template <class Cls>
1020 class ReturnToLocal {
1021  private:
1022   LocalRef<Cls>* const localRef;
1023   LocalRef<Object> objRef;
1024 
1025  public:
1026   explicit ReturnToLocal(LocalRef<Cls>* ref) : localRef(ref) {}
1027   operator LocalRef<Object>*() { return &objRef; }
1028 
1029   ~ReturnToLocal() {
1030     if (objRef) {
1031       *localRef = std::move(objRef);
1032     }
1033   }
1034 };
1035 
1036 template <class Cls>
1037 ReturnToLocal<Cls> ReturnTo(LocalRef<Cls>* ref) {
1038   return ReturnToLocal<Cls>(ref);
1039 }
1040 
1041 // Support conversion from GlobalRef<T>* to LocalRef<Object/T>*:
1042 //   GlobalRef<Foo> foo;
1043 //   Foo::GetFoo(&foo); // error because parameter type is LocalRef<Foo>*.
1044 //   Foo::GetFoo(ReturnTo(&foo)); // OK because ReturnTo converts the argument.
1045 template <class Cls>
1046 class ReturnToGlobal {
1047  private:
1048   GlobalRef<Cls>* const globalRef;
1049   LocalRef<Object> objRef;
1050   LocalRef<Cls> clsRef;
1051 
1052  public:
1053   explicit ReturnToGlobal(GlobalRef<Cls>* ref) : globalRef(ref) {}
1054   operator LocalRef<Object>*() { return &objRef; }
1055   operator LocalRef<Cls>*() { return &clsRef; }
1056 
1057   ~ReturnToGlobal() {
1058     if (objRef) {
1059       *globalRef = (clsRef = std::move(objRef));
1060     } else if (clsRef) {
1061       *globalRef = clsRef;
1062     }
1063   }
1064 };
1065 
1066 template <class Cls>
1067 ReturnToGlobal<Cls> ReturnTo(GlobalRef<Cls>* ref) {
1068   return ReturnToGlobal<Cls>(ref);
1069 }
1070 
1071 // Make a LocalRef<T> from any other Ref<T>
1072 template <typename Cls, typename JNIType>
1073 LocalRef<Cls> ToLocalRef(const Ref<Cls, JNIType>& aRef) {
1074   return LocalRef<Cls>(aRef);
1075 }
1076 
1077 }  // namespace jni
1078 }  // namespace mozilla
1079 
1080 #endif  // mozilla_jni_Refs_h__
1081