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