1 #ifndef mozilla_jni_Natives_h__
2 #define mozilla_jni_Natives_h__
3 
4 #include <jni.h>
5 
6 #include "mozilla/IndexSequence.h"
7 #include "mozilla/Move.h"
8 #include "mozilla/Tuple.h"
9 #include "mozilla/TypeTraits.h"
10 #include "mozilla/UniquePtr.h"
11 #include "mozilla/WeakPtr.h"
12 #include "mozilla/Unused.h"
13 #include "mozilla/jni/Accessors.h"
14 #include "mozilla/jni/Refs.h"
15 #include "mozilla/jni/Types.h"
16 #include "mozilla/jni/Utils.h"
17 
18 namespace mozilla {
19 namespace jni {
20 
21 /**
22  * C++ classes implementing instance (non-static) native methods can choose
23  * from one of two ownership models, when associating a C++ object with a Java
24  * instance.
25  *
26  * * If the C++ class inherits from mozilla::SupportsWeakPtr, weak pointers
27  *   will be used. The Java instance will store and own the pointer to a
28  *   WeakPtr object. The C++ class itself is otherwise not owned or directly
29  *   referenced. To attach a Java instance to a C++ instance, pass in a pointer
30  *   to the C++ class (i.e. MyClass*).
31  *
32  *   class MyClass : public SupportsWeakPtr<MyClass>
33  *                 , public MyJavaClass::Natives<MyClass>
34  *   {
35  *       // ...
36  *
37  *   public:
38  *       MOZ_DECLARE_WEAKREFERENCE_TYPENAME(MyClass)
39  *       using MyJavaClass::Natives<MyClass>::Dispose;
40  *
41  *       void AttachTo(const MyJavaClass::LocalRef& instance)
42  *       {
43  *           MyJavaClass::Natives<MyClass>::AttachInstance(instance, this);
44  *
45  *           // "instance" does NOT own "this", so the C++ object
46  *           // lifetime is separate from the Java object lifetime.
47  *       }
48  *   };
49  *
50  * * If the C++ class doesn't inherit from mozilla::SupportsWeakPtr, the Java
51  *   instance will store and own a pointer to the C++ object itself. This
52  *   pointer must not be stored or deleted elsewhere. To attach a Java instance
53  *   to a C++ instance, pass in a reference to a UniquePtr of the C++ class
54  *   (i.e. UniquePtr<MyClass>).
55  *
56  *   class MyClass : public MyJavaClass::Natives<MyClass>
57  *   {
58  *       // ...
59  *
60  *   public:
61  *       using MyJavaClass::Natives<MyClass>::Dispose;
62  *
63  *       static void AttachTo(const MyJavaClass::LocalRef& instance)
64  *       {
65  *           MyJavaClass::Natives<MyClass>::AttachInstance(
66  *                   instance, mozilla::MakeUnique<MyClass>());
67  *
68  *           // "instance" owns the newly created C++ object, so the C++
69  *           // object is destroyed as soon as instance.dispose() is called.
70  *       }
71  *   };
72  */
73 
74 namespace detail {
75 
CheckNativeHandle(JNIEnv * env,uintptr_t handle)76 inline uintptr_t CheckNativeHandle(JNIEnv* env, uintptr_t handle)
77 {
78     if (!handle) {
79         if (!env->ExceptionCheck()) {
80             ThrowException(env, "java/lang/NullPointerException",
81                            "Null native pointer");
82         }
83         return 0;
84     }
85     return handle;
86 }
87 
88 template<class Impl, bool UseWeakPtr = mozilla::IsBaseOf<
89                          SupportsWeakPtr<Impl>, Impl>::value /* = false */>
90 struct NativePtr
91 {
GetNativePtr92     static Impl* Get(JNIEnv* env, jobject instance)
93     {
94         return reinterpret_cast<Impl*>(CheckNativeHandle(
95                 env, GetNativeHandle(env, instance)));
96     }
97 
98     template<class LocalRef>
GetNativePtr99     static Impl* Get(const LocalRef& instance)
100     {
101         return Get(instance.Env(), instance.Get());
102     }
103 
104     template<class LocalRef>
SetNativePtr105     static void Set(const LocalRef& instance, UniquePtr<Impl>&& ptr)
106     {
107         Clear(instance);
108         SetNativeHandle(instance.Env(), instance.Get(),
109                           reinterpret_cast<uintptr_t>(ptr.release()));
110         MOZ_CATCH_JNI_EXCEPTION(instance.Env());
111     }
112 
113     template<class LocalRef>
ClearNativePtr114     static void Clear(const LocalRef& instance)
115     {
116         UniquePtr<Impl> ptr(reinterpret_cast<Impl*>(
117                 GetNativeHandle(instance.Env(), instance.Get())));
118         MOZ_CATCH_JNI_EXCEPTION(instance.Env());
119 
120         if (ptr) {
121             SetNativeHandle(instance.Env(), instance.Get(), 0);
122             MOZ_CATCH_JNI_EXCEPTION(instance.Env());
123         }
124     }
125 };
126 
127 template<class Impl>
128 struct NativePtr<Impl, /* UseWeakPtr = */ true>
129 {
130     static Impl* Get(JNIEnv* env, jobject instance)
131     {
132         const auto ptr = reinterpret_cast<WeakPtr<Impl>*>(
133                 CheckNativeHandle(env, GetNativeHandle(env, instance)));
134         if (!ptr) {
135             return nullptr;
136         }
137 
138         Impl* const impl = *ptr;
139         if (!impl) {
140             ThrowException(env, "java/lang/NullPointerException",
141                            "Native object already released");
142         }
143         return impl;
144     }
145 
146     template<class LocalRef>
147     static Impl* Get(const LocalRef& instance)
148     {
149         return Get(instance.Env(), instance.Get());
150     }
151 
152     template<class LocalRef>
153     static void Set(const LocalRef& instance, Impl* ptr)
154     {
155         Clear(instance);
156         SetNativeHandle(instance.Env(), instance.Get(),
157                           reinterpret_cast<uintptr_t>(new WeakPtr<Impl>(ptr)));
158         MOZ_CATCH_JNI_EXCEPTION(instance.Env());
159     }
160 
161     template<class LocalRef>
162     static void Clear(const LocalRef& instance)
163     {
164         const auto ptr = reinterpret_cast<WeakPtr<Impl>*>(
165                 GetNativeHandle(instance.Env(), instance.Get()));
166         MOZ_CATCH_JNI_EXCEPTION(instance.Env());
167 
168         if (ptr) {
169             SetNativeHandle(instance.Env(), instance.Get(), 0);
170             MOZ_CATCH_JNI_EXCEPTION(instance.Env());
171             delete ptr;
172         }
173     }
174 };
175 
176 } // namespace detail
177 
178 using namespace detail;
179 
180 /**
181  * For JNI native methods that are dispatched to a proxy, i.e. using
182  * @WrapForJNI(dispatchTo = "proxy"), the implementing C++ class must provide a
183  * OnNativeCall member. Subsequently, every native call is automatically
184  * wrapped in a functor object, and the object is passed to OnNativeCall. The
185  * OnNativeCall implementation can choose to invoke the call, save it, dispatch
186  * it to a different thread, etc. Each copy of functor may only be invoked
187  * once.
188  *
189  * class MyClass : public MyJavaClass::Natives<MyClass>
190  * {
191  *     // ...
192  *
193  *     template<class Functor>
194  *     class ProxyRunnable final : public Runnable
195  *     {
196  *         Functor mCall;
197  *     public:
198  *         ProxyRunnable(Functor&& call) : mCall(mozilla::Move(call)) {}
199  *         virtual void run() override { mCall(); }
200  *     };
201  *
202  * public:
203  *     template<class Functor>
204  *     static void OnNativeCall(Functor&& call)
205  *     {
206  *         RunOnAnotherThread(new ProxyRunnable(mozilla::Move(call)));
207  *     }
208  * };
209  */
210 
211 namespace detail {
212 
213 // ProxyArg is used to handle JNI ref arguments for proxies. Because a proxied
214 // call may happen outside of the original JNI native call, we must save all
215 // JNI ref arguments as global refs to avoid the arguments going out of scope.
216 template<typename T>
217 struct ProxyArg
218 {
219     static_assert(mozilla::IsPod<T>::value, "T must be primitive type");
220 
221     // Primitive types can be saved by value.
222     typedef T Type;
223     typedef typename TypeAdapter<T>::JNIType JNIType;
224 
225     static void Clear(JNIEnv* env, Type&) {}
226 
227     static Type From(JNIEnv* env, JNIType val)
228     {
229         return TypeAdapter<T>::ToNative(env, val);
230     }
231 };
232 
233 template<class C, typename T>
234 struct ProxyArg<Ref<C, T>>
235 {
236     // Ref types need to be saved by global ref.
237     typedef typename C::GlobalRef Type;
238     typedef typename TypeAdapter<Ref<C, T>>::JNIType JNIType;
239 
240     static void Clear(JNIEnv* env, Type& ref) { ref.Clear(env); }
241 
242     static Type From(JNIEnv* env, JNIType val)
243     {
244         return Type(env, C::Ref::From(val));
245     }
246 };
247 
248 template<typename C> struct ProxyArg<const C&> : ProxyArg<C> {};
249 template<> struct ProxyArg<StringParam> : ProxyArg<String::Ref> {};
250 template<class C> struct ProxyArg<LocalRef<C>> : ProxyArg<typename C::Ref> {};
251 
252 // ProxyNativeCall implements the functor object that is passed to OnNativeCall
253 template<class Impl, class Owner, bool IsStatic,
254          bool HasThisArg /* has instance/class local ref in the call */,
255          typename... Args>
256 class ProxyNativeCall : public AbstractCall
257 {
258     // "this arg" refers to the Class::LocalRef (for static methods) or
259     // Owner::LocalRef (for instance methods) that we optionally (as indicated
260     // by HasThisArg) pass into the destination C++ function.
261     typedef typename mozilla::Conditional<IsStatic,
262             Class, Owner>::Type ThisArgClass;
263     typedef typename mozilla::Conditional<IsStatic,
264             jclass, jobject>::Type ThisArgJNIType;
265 
266     // Type signature of the destination C++ function, which matches the
267     // Method template parameter in NativeStubImpl::Wrap.
268     typedef typename mozilla::Conditional<IsStatic,
269             typename mozilla::Conditional<HasThisArg,
270                     void (*) (const Class::LocalRef&, Args...),
271                     void (*) (Args...)>::Type,
272             typename mozilla::Conditional<HasThisArg,
273                     void (Impl::*) (const typename Owner::LocalRef&, Args...),
274                     void (Impl::*) (Args...)>::Type>::Type NativeCallType;
275 
276     // Destination C++ function.
277     NativeCallType mNativeCall;
278     // Saved this arg.
279     typename ThisArgClass::GlobalRef mThisArg;
280     // Saved arguments.
281     mozilla::Tuple<typename ProxyArg<Args>::Type...> mArgs;
282 
283     // We cannot use IsStatic and HasThisArg directly (without going through
284     // extra hoops) because GCC complains about invalid overloads, so we use
285     // another pair of template parameters, Static and ThisArg.
286 
287     template<bool Static, bool ThisArg, size_t... Indices>
288     typename mozilla::EnableIf<Static && ThisArg, void>::Type
289     Call(const Class::LocalRef& cls,
290          mozilla::IndexSequence<Indices...>) const
291     {
292         (*mNativeCall)(cls, mozilla::Get<Indices>(mArgs)...);
293     }
294 
295     template<bool Static, bool ThisArg, size_t... Indices>
296     typename mozilla::EnableIf<Static && !ThisArg, void>::Type
297     Call(const Class::LocalRef& cls,
298          mozilla::IndexSequence<Indices...>) const
299     {
300         (*mNativeCall)(mozilla::Get<Indices>(mArgs)...);
301     }
302 
303     template<bool Static, bool ThisArg, size_t... Indices>
304     typename mozilla::EnableIf<!Static && ThisArg, void>::Type
305     Call(const typename Owner::LocalRef& inst,
306          mozilla::IndexSequence<Indices...>) const
307     {
308         Impl* const impl = NativePtr<Impl>::Get(inst);
309         MOZ_CATCH_JNI_EXCEPTION(inst.Env());
310         (impl->*mNativeCall)(inst, mozilla::Get<Indices>(mArgs)...);
311     }
312 
313     template<bool Static, bool ThisArg, size_t... Indices>
314     typename mozilla::EnableIf<!Static && !ThisArg, void>::Type
315     Call(const typename Owner::LocalRef& inst,
316          mozilla::IndexSequence<Indices...>) const
317     {
318         Impl* const impl = NativePtr<Impl>::Get(inst);
319         MOZ_CATCH_JNI_EXCEPTION(inst.Env());
320         (impl->*mNativeCall)(mozilla::Get<Indices>(mArgs)...);
321     }
322 
323     template<size_t... Indices>
324     void Clear(JNIEnv* env, mozilla::IndexSequence<Indices...>)
325     {
326         int dummy[] = {
327             (ProxyArg<Args>::Clear(env, Get<Indices>(mArgs)), 0)...
328         };
329         mozilla::Unused << dummy;
330     }
331 
332 public:
333     // The class that implements the call target.
334     typedef Impl TargetClass;
335     typedef typename ThisArgClass::Param ThisArgType;
336 
337     static const bool isStatic = IsStatic;
338 
339     ProxyNativeCall(ThisArgJNIType thisArg,
340                     NativeCallType nativeCall,
341                     JNIEnv* env,
342                     typename ProxyArg<Args>::JNIType... args)
343         : mNativeCall(nativeCall)
344         , mThisArg(env, ThisArgClass::Ref::From(thisArg))
345         , mArgs(ProxyArg<Args>::From(env, args)...)
346     {}
347 
348     ProxyNativeCall(ProxyNativeCall&&) = default;
349     ProxyNativeCall(const ProxyNativeCall&) = default;
350 
351     // Get class ref for static calls or object ref for instance calls.
352     typename ThisArgClass::Param GetThisArg() const { return mThisArg; }
353 
354     // Return if target is the given function pointer / pointer-to-member.
355     // Because we can only compare pointers of the same type, we use a
356     // templated overload that is chosen only if given a different type of
357     // pointer than our target pointer type.
358     bool IsTarget(NativeCallType call) const { return call == mNativeCall; }
359     template<typename T> bool IsTarget(T&&) const { return false; }
360 
361     // Redirect the call to another function / class member with the same
362     // signature as the original target. Crash if given a wrong signature.
363     void SetTarget(NativeCallType call) { mNativeCall = call; }
364     template<typename T> void SetTarget(T&&) const { MOZ_CRASH(); }
365 
366     void operator()() override
367     {
368         JNIEnv* const env = GetEnvForThread();
369         typename ThisArgClass::LocalRef thisArg(env, mThisArg);
370         Call<IsStatic, HasThisArg>(
371                 thisArg, typename IndexSequenceFor<Args...>::Type());
372 
373         // Clear all saved global refs. We do this after the call is invoked,
374         // and not inside the destructor because we already have a JNIEnv here,
375         // so it's more efficient to clear out the saved args here. The
376         // downside is that the call can only be invoked once.
377         Clear(env, typename IndexSequenceFor<Args...>::Type());
378         mThisArg.Clear(env);
379     }
380 };
381 
382 template<class Impl, bool HasThisArg, typename... Args>
383 struct Dispatcher
384 {
385     template<class Traits, bool IsStatic = Traits::isStatic,
386              typename... ProxyArgs>
387     static typename EnableIf<
388             Traits::dispatchTarget == DispatchTarget::PROXY, void>::Type
389     Run(ProxyArgs&&... args)
390     {
391         Impl::OnNativeCall(ProxyNativeCall<
392                 Impl, typename Traits::Owner, IsStatic,
393                 HasThisArg, Args...>(Forward<ProxyArgs>(args)...));
394     }
395 
396     template<class Traits, bool IsStatic = Traits::isStatic,
397              typename ThisArg, typename... ProxyArgs>
398     static typename EnableIf<
399             Traits::dispatchTarget == DispatchTarget::GECKO, void>::Type
400     Run(ThisArg thisArg, ProxyArgs&&... args)
401     {
402         // For a static method, do not forward the "this arg" (i.e. the class
403         // local ref) if the implementation does not request it. This saves us
404         // a pair of calls to add/delete global ref.
405         DispatchToGeckoThread(MakeUnique<ProxyNativeCall<
406                 Impl, typename Traits::Owner, IsStatic, HasThisArg,
407                 Args...>>(HasThisArg || !IsStatic ? thisArg : nullptr,
408                           Forward<ProxyArgs>(args)...));
409     }
410 
411     template<class Traits, bool IsStatic = false, typename... ProxyArgs>
412     static typename EnableIf<
413             Traits::dispatchTarget == DispatchTarget::CURRENT, void>::Type
414     Run(ProxyArgs&&... args) {}
415 };
416 
417 } // namespace detail
418 
419 // Wrapper methods that convert arguments from the JNI types to the native
420 // types, e.g. from jobject to jni::Object::Ref. For instance methods, the
421 // wrapper methods also convert calls to calls on objects.
422 //
423 // We need specialization for static/non-static because the two have different
424 // signatures (jobject vs jclass and Impl::*Method vs *Method).
425 // We need specialization for return type, because void return type requires
426 // us to not deal with the return value.
427 
428 // Bug 1207642 - Work around Dalvik bug by realigning stack on JNI entry
429 #ifdef __i386__
430 #define MOZ_JNICALL JNICALL __attribute__((force_align_arg_pointer))
431 #else
432 #define MOZ_JNICALL JNICALL
433 #endif
434 
435 template<class Traits, class Impl, class Args = typename Traits::Args>
436 class NativeStub;
437 
438 template<class Traits, class Impl, typename... Args>
439 class NativeStub<Traits, Impl, jni::Args<Args...>>
440 {
441     using Owner = typename Traits::Owner;
442     using ReturnType = typename Traits::ReturnType;
443 
444     static constexpr bool isStatic = Traits::isStatic;
445     static constexpr bool isVoid = mozilla::IsVoid<ReturnType>::value;
446 
447     struct VoidType { using JNIType = void; };
448     using ReturnJNIType = typename Conditional<
449             isVoid, VoidType, TypeAdapter<ReturnType>>::Type::JNIType;
450 
451     using ReturnTypeForNonVoidInstance = typename Conditional<
452             !isStatic && !isVoid, ReturnType, VoidType>::Type;
453     using ReturnTypeForVoidInstance = typename Conditional<
454             !isStatic && isVoid, ReturnType, VoidType&>::Type;
455     using ReturnTypeForNonVoidStatic = typename Conditional<
456             isStatic && !isVoid, ReturnType, VoidType>::Type;
457     using ReturnTypeForVoidStatic = typename Conditional<
458             isStatic && isVoid, ReturnType, VoidType&>::Type;
459 
460     static_assert(Traits::dispatchTarget == DispatchTarget::CURRENT || isVoid,
461                   "Dispatched calls must have void return type");
462 
463 public:
464     // Non-void instance method
465     template<ReturnTypeForNonVoidInstance (Impl::*Method) (Args...)>
466     static MOZ_JNICALL ReturnJNIType
467     Wrap(JNIEnv* env, jobject instance,
468          typename TypeAdapter<Args>::JNIType... args)
469     {
470         MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
471 
472         Impl* const impl = NativePtr<Impl>::Get(env, instance);
473         if (!impl) {
474             // There is a pending JNI exception at this point.
475             return ReturnJNIType();
476         }
477         return TypeAdapter<ReturnType>::FromNative(env,
478                 (impl->*Method)(TypeAdapter<Args>::ToNative(env, args)...));
479     }
480 
481     // Non-void instance method with instance reference
482     template<ReturnTypeForNonVoidInstance (Impl::*Method)
483              (const typename Owner::LocalRef&, Args...)>
484     static MOZ_JNICALL ReturnJNIType
485     Wrap(JNIEnv* env, jobject instance,
486          typename TypeAdapter<Args>::JNIType... args)
487     {
488         MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
489 
490         Impl* const impl = NativePtr<Impl>::Get(env, instance);
491         if (!impl) {
492             // There is a pending JNI exception at this point.
493             return ReturnJNIType();
494         }
495         auto self = Owner::LocalRef::Adopt(env, instance);
496         const auto res = TypeAdapter<ReturnType>::FromNative(env,
497                 (impl->*Method)(self, TypeAdapter<Args>::ToNative(env, args)...));
498         self.Forget();
499         return res;
500     }
501 
502     // Void instance method
503     template<ReturnTypeForVoidInstance (Impl::*Method) (Args...)>
504     static MOZ_JNICALL void
505     Wrap(JNIEnv* env, jobject instance,
506          typename TypeAdapter<Args>::JNIType... args)
507     {
508         MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
509 
510         if (Traits::dispatchTarget != DispatchTarget::CURRENT) {
511             Dispatcher<Impl, /* HasThisArg */ false, Args...>::
512                     template Run<Traits>(instance, Method, env, args...);
513             return;
514         }
515 
516         Impl* const impl = NativePtr<Impl>::Get(env, instance);
517         if (!impl) {
518             // There is a pending JNI exception at this point.
519             return;
520         }
521         (impl->*Method)(TypeAdapter<Args>::ToNative(env, args)...);
522     }
523 
524     // Void instance method with instance reference
525     template<ReturnTypeForVoidInstance (Impl::*Method)
526              (const typename Owner::LocalRef&, Args...)>
527     static MOZ_JNICALL void
528     Wrap(JNIEnv* env, jobject instance,
529          typename TypeAdapter<Args>::JNIType... args)
530     {
531         MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
532 
533         if (Traits::dispatchTarget != DispatchTarget::CURRENT) {
534             Dispatcher<Impl, /* HasThisArg */ true, Args...>::
535                     template Run<Traits>(instance, Method, env, args...);
536             return;
537         }
538 
539         Impl* const impl = NativePtr<Impl>::Get(env, instance);
540         if (!impl) {
541             // There is a pending JNI exception at this point.
542             return;
543         }
544         auto self = Owner::LocalRef::Adopt(env, instance);
545         (impl->*Method)(self, TypeAdapter<Args>::ToNative(env, args)...);
546         self.Forget();
547     }
548 
549     // Overload for DisposeNative
550     template<ReturnTypeForVoidInstance (*DisposeNative)
551              (const typename Owner::LocalRef&)>
552     static MOZ_JNICALL void
553     Wrap(JNIEnv* env, jobject instance)
554     {
555         MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
556 
557         if (Traits::dispatchTarget != DispatchTarget::CURRENT) {
558             using LocalRef = typename Owner::LocalRef;
559             Dispatcher<Impl, /* HasThisArg */ false, const LocalRef&>::
560                     template Run<Traits, /* IsStatic */ true>(
561                     /* ThisArg */ nullptr, DisposeNative, env, instance);
562             return;
563         }
564 
565         auto self = Owner::LocalRef::Adopt(env, instance);
566         (Impl::DisposeNative)(self);
567         self.Forget();
568     }
569 
570     // Non-void static method
571     template<ReturnTypeForNonVoidStatic (*Method) (Args...)>
572     static MOZ_JNICALL ReturnJNIType
573     Wrap(JNIEnv* env, jclass, typename TypeAdapter<Args>::JNIType... args)
574     {
575         MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
576 
577         return TypeAdapter<ReturnType>::FromNative(env,
578                 (*Method)(TypeAdapter<Args>::ToNative(env, args)...));
579     }
580 
581     // Non-void static method with class reference
582     template<ReturnTypeForNonVoidStatic (*Method)
583              (const Class::LocalRef&, Args...)>
584     static MOZ_JNICALL ReturnJNIType
585     Wrap(JNIEnv* env, jclass cls, typename TypeAdapter<Args>::JNIType... args)
586     {
587         MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
588 
589         auto clazz = Class::LocalRef::Adopt(env, cls);
590         const auto res = TypeAdapter<ReturnType>::FromNative(env,
591                 (*Method)(clazz, TypeAdapter<Args>::ToNative(env, args)...));
592         clazz.Forget();
593         return res;
594     }
595 
596     // Void static method
597     template<ReturnTypeForVoidStatic (*Method) (Args...)>
598     static MOZ_JNICALL void
599     Wrap(JNIEnv* env, jclass cls, typename TypeAdapter<Args>::JNIType... args)
600     {
601         MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
602 
603         if (Traits::dispatchTarget != DispatchTarget::CURRENT) {
604             Dispatcher<Impl, /* HasThisArg */ false, Args...>::
605                     template Run<Traits>(cls, Method, env, args...);
606             return;
607         }
608 
609         (*Method)(TypeAdapter<Args>::ToNative(env, args)...);
610     }
611 
612     // Void static method with class reference
613     template<ReturnTypeForVoidStatic (*Method)
614              (const Class::LocalRef&, Args...)>
615     static MOZ_JNICALL void
616     Wrap(JNIEnv* env, jclass cls, typename TypeAdapter<Args>::JNIType... args)
617     {
618         MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
619 
620         if (Traits::dispatchTarget != DispatchTarget::CURRENT) {
621             Dispatcher<Impl, /* HasThisArg */ true, Args...>::
622                     template Run<Traits>(cls, Method, env, args...);
623             return;
624         }
625 
626         auto clazz = Class::LocalRef::Adopt(env, cls);
627         (*Method)(clazz, TypeAdapter<Args>::ToNative(env, args)...);
628         clazz.Forget();
629     }
630 };
631 
632 // Generate a JNINativeMethod from a native
633 // method's traits class and a wrapped stub.
634 template<class Traits, typename Ret, typename... Args>
635 constexpr JNINativeMethod MakeNativeMethod(MOZ_JNICALL Ret (*stub)(JNIEnv*, Args...))
636 {
637     return {
638         Traits::name,
639         Traits::signature,
640         reinterpret_cast<void*>(stub)
641     };
642 }
643 
644 // Class inherited by implementing class.
645 template<class Cls, class Impl>
646 class NativeImpl
647 {
648     typedef typename Cls::template Natives<Impl> Natives;
649 
650     static bool sInited;
651 
652 public:
653     static void Init() {
654         if (sInited) {
655             return;
656         }
657         const auto& ctx = typename Cls::Context();
658         ctx.Env()->RegisterNatives(
659                 ctx.ClassRef(), Natives::methods,
660                 sizeof(Natives::methods) / sizeof(Natives::methods[0]));
661         MOZ_CATCH_JNI_EXCEPTION(ctx.Env());
662         sInited = true;
663     }
664 
665 protected:
666 
667     // Associate a C++ instance with a Java instance.
668     static void AttachNative(const typename Cls::LocalRef& instance,
669                              SupportsWeakPtr<Impl>* ptr)
670     {
671         static_assert(mozilla::IsBaseOf<SupportsWeakPtr<Impl>, Impl>::value,
672                       "Attach with UniquePtr&& when not using WeakPtr");
673         return NativePtr<Impl>::Set(instance, static_cast<Impl*>(ptr));
674     }
675 
676     static void AttachNative(const typename Cls::LocalRef& instance,
677                              UniquePtr<Impl>&& ptr)
678     {
679         static_assert(!mozilla::IsBaseOf<SupportsWeakPtr<Impl>, Impl>::value,
680                       "Attach with SupportsWeakPtr* when using WeakPtr");
681         return NativePtr<Impl>::Set(instance, mozilla::Move(ptr));
682     }
683 
684     // Get the C++ instance associated with a Java instance.
685     // There is always a pending exception if the return value is nullptr.
686     static Impl* GetNative(const typename Cls::LocalRef& instance) {
687         return NativePtr<Impl>::Get(instance);
688     }
689 
690     static void DisposeNative(const typename Cls::LocalRef& instance) {
691         NativePtr<Impl>::Clear(instance);
692     }
693 
694     NativeImpl() {
695         // Initialize on creation if not already initialized.
696         Init();
697     }
698 };
699 
700 // Define static member.
701 template<class C, class I>
702 bool NativeImpl<C, I>::sInited;
703 
704 } // namespace jni
705 } // namespace mozilla
706 
707 #endif // mozilla_jni_Natives_h__
708