diff options
Diffstat (limited to 'widget/android/jni/Natives.h')
-rw-r--r-- | widget/android/jni/Natives.h | 707 |
1 files changed, 707 insertions, 0 deletions
diff --git a/widget/android/jni/Natives.h b/widget/android/jni/Natives.h new file mode 100644 index 000000000..511d96a87 --- /dev/null +++ b/widget/android/jni/Natives.h @@ -0,0 +1,707 @@ +#ifndef mozilla_jni_Natives_h__ +#define mozilla_jni_Natives_h__ + +#include <jni.h> + +#include "mozilla/IndexSequence.h" +#include "mozilla/Move.h" +#include "mozilla/Tuple.h" +#include "mozilla/TypeTraits.h" +#include "mozilla/UniquePtr.h" +#include "mozilla/WeakPtr.h" +#include "mozilla/Unused.h" +#include "mozilla/jni/Accessors.h" +#include "mozilla/jni/Refs.h" +#include "mozilla/jni/Types.h" +#include "mozilla/jni/Utils.h" + +namespace mozilla { +namespace jni { + +/** + * C++ classes implementing instance (non-static) native methods can choose + * from one of two ownership models, when associating a C++ object with a Java + * instance. + * + * * If the C++ class inherits from mozilla::SupportsWeakPtr, weak pointers + * will be used. The Java instance will store and own the pointer to a + * WeakPtr object. The C++ class itself is otherwise not owned or directly + * referenced. To attach a Java instance to a C++ instance, pass in a pointer + * to the C++ class (i.e. MyClass*). + * + * class MyClass : public SupportsWeakPtr<MyClass> + * , public MyJavaClass::Natives<MyClass> + * { + * // ... + * + * public: + * MOZ_DECLARE_WEAKREFERENCE_TYPENAME(MyClass) + * using MyJavaClass::Natives<MyClass>::Dispose; + * + * void AttachTo(const MyJavaClass::LocalRef& instance) + * { + * MyJavaClass::Natives<MyClass>::AttachInstance(instance, this); + * + * // "instance" does NOT own "this", so the C++ object + * // lifetime is separate from the Java object lifetime. + * } + * }; + * + * * If the C++ class doesn't inherit from mozilla::SupportsWeakPtr, the Java + * instance will store and own a pointer to the C++ object itself. This + * pointer must not be stored or deleted elsewhere. To attach a Java instance + * to a C++ instance, pass in a reference to a UniquePtr of the C++ class + * (i.e. UniquePtr<MyClass>). + * + * class MyClass : public MyJavaClass::Natives<MyClass> + * { + * // ... + * + * public: + * using MyJavaClass::Natives<MyClass>::Dispose; + * + * static void AttachTo(const MyJavaClass::LocalRef& instance) + * { + * MyJavaClass::Natives<MyClass>::AttachInstance( + * instance, mozilla::MakeUnique<MyClass>()); + * + * // "instance" owns the newly created C++ object, so the C++ + * // object is destroyed as soon as instance.dispose() is called. + * } + * }; + */ + +namespace detail { + +inline uintptr_t CheckNativeHandle(JNIEnv* env, uintptr_t handle) +{ + if (!handle) { + if (!env->ExceptionCheck()) { + ThrowException(env, "java/lang/NullPointerException", + "Null native pointer"); + } + return 0; + } + return handle; +} + +template<class Impl, bool UseWeakPtr = mozilla::IsBaseOf< + SupportsWeakPtr<Impl>, Impl>::value /* = false */> +struct NativePtr +{ + static Impl* Get(JNIEnv* env, jobject instance) + { + return reinterpret_cast<Impl*>(CheckNativeHandle( + env, GetNativeHandle(env, instance))); + } + + template<class LocalRef> + static Impl* Get(const LocalRef& instance) + { + return Get(instance.Env(), instance.Get()); + } + + template<class LocalRef> + static void Set(const LocalRef& instance, UniquePtr<Impl>&& ptr) + { + Clear(instance); + SetNativeHandle(instance.Env(), instance.Get(), + reinterpret_cast<uintptr_t>(ptr.release())); + MOZ_CATCH_JNI_EXCEPTION(instance.Env()); + } + + template<class LocalRef> + static void Clear(const LocalRef& instance) + { + UniquePtr<Impl> ptr(reinterpret_cast<Impl*>( + GetNativeHandle(instance.Env(), instance.Get()))); + MOZ_CATCH_JNI_EXCEPTION(instance.Env()); + + if (ptr) { + SetNativeHandle(instance.Env(), instance.Get(), 0); + MOZ_CATCH_JNI_EXCEPTION(instance.Env()); + } + } +}; + +template<class Impl> +struct NativePtr<Impl, /* UseWeakPtr = */ true> +{ + static Impl* Get(JNIEnv* env, jobject instance) + { + const auto ptr = reinterpret_cast<WeakPtr<Impl>*>( + CheckNativeHandle(env, GetNativeHandle(env, instance))); + if (!ptr) { + return nullptr; + } + + Impl* const impl = *ptr; + if (!impl) { + ThrowException(env, "java/lang/NullPointerException", + "Native object already released"); + } + return impl; + } + + template<class LocalRef> + static Impl* Get(const LocalRef& instance) + { + return Get(instance.Env(), instance.Get()); + } + + template<class LocalRef> + static void Set(const LocalRef& instance, Impl* ptr) + { + Clear(instance); + SetNativeHandle(instance.Env(), instance.Get(), + reinterpret_cast<uintptr_t>(new WeakPtr<Impl>(ptr))); + MOZ_CATCH_JNI_EXCEPTION(instance.Env()); + } + + template<class LocalRef> + static void Clear(const LocalRef& instance) + { + const auto ptr = reinterpret_cast<WeakPtr<Impl>*>( + GetNativeHandle(instance.Env(), instance.Get())); + MOZ_CATCH_JNI_EXCEPTION(instance.Env()); + + if (ptr) { + SetNativeHandle(instance.Env(), instance.Get(), 0); + MOZ_CATCH_JNI_EXCEPTION(instance.Env()); + delete ptr; + } + } +}; + +} // namespace detail + +using namespace detail; + +/** + * For JNI native methods that are dispatched to a proxy, i.e. using + * @WrapForJNI(dispatchTo = "proxy"), the implementing C++ class must provide a + * OnNativeCall member. Subsequently, every native call is automatically + * wrapped in a functor object, and the object is passed to OnNativeCall. The + * OnNativeCall implementation can choose to invoke the call, save it, dispatch + * it to a different thread, etc. Each copy of functor may only be invoked + * once. + * + * class MyClass : public MyJavaClass::Natives<MyClass> + * { + * // ... + * + * template<class Functor> + * class ProxyRunnable final : public Runnable + * { + * Functor mCall; + * public: + * ProxyRunnable(Functor&& call) : mCall(mozilla::Move(call)) {} + * virtual void run() override { mCall(); } + * }; + * + * public: + * template<class Functor> + * static void OnNativeCall(Functor&& call) + * { + * RunOnAnotherThread(new ProxyRunnable(mozilla::Move(call))); + * } + * }; + */ + +namespace detail { + +// ProxyArg is used to handle JNI ref arguments for proxies. Because a proxied +// call may happen outside of the original JNI native call, we must save all +// JNI ref arguments as global refs to avoid the arguments going out of scope. +template<typename T> +struct ProxyArg +{ + static_assert(mozilla::IsPod<T>::value, "T must be primitive type"); + + // Primitive types can be saved by value. + typedef T Type; + typedef typename TypeAdapter<T>::JNIType JNIType; + + static void Clear(JNIEnv* env, Type&) {} + + static Type From(JNIEnv* env, JNIType val) + { + return TypeAdapter<T>::ToNative(env, val); + } +}; + +template<class C, typename T> +struct ProxyArg<Ref<C, T>> +{ + // Ref types need to be saved by global ref. + typedef typename C::GlobalRef Type; + typedef typename TypeAdapter<Ref<C, T>>::JNIType JNIType; + + static void Clear(JNIEnv* env, Type& ref) { ref.Clear(env); } + + static Type From(JNIEnv* env, JNIType val) + { + return Type(env, C::Ref::From(val)); + } +}; + +template<typename C> struct ProxyArg<const C&> : ProxyArg<C> {}; +template<> struct ProxyArg<StringParam> : ProxyArg<String::Ref> {}; +template<class C> struct ProxyArg<LocalRef<C>> : ProxyArg<typename C::Ref> {}; + +// ProxyNativeCall implements the functor object that is passed to OnNativeCall +template<class Impl, class Owner, bool IsStatic, + bool HasThisArg /* has instance/class local ref in the call */, + typename... Args> +class ProxyNativeCall : public AbstractCall +{ + // "this arg" refers to the Class::LocalRef (for static methods) or + // Owner::LocalRef (for instance methods) that we optionally (as indicated + // by HasThisArg) pass into the destination C++ function. + typedef typename mozilla::Conditional<IsStatic, + Class, Owner>::Type ThisArgClass; + typedef typename mozilla::Conditional<IsStatic, + jclass, jobject>::Type ThisArgJNIType; + + // Type signature of the destination C++ function, which matches the + // Method template parameter in NativeStubImpl::Wrap. + typedef typename mozilla::Conditional<IsStatic, + typename mozilla::Conditional<HasThisArg, + void (*) (const Class::LocalRef&, Args...), + void (*) (Args...)>::Type, + typename mozilla::Conditional<HasThisArg, + void (Impl::*) (const typename Owner::LocalRef&, Args...), + void (Impl::*) (Args...)>::Type>::Type NativeCallType; + + // Destination C++ function. + NativeCallType mNativeCall; + // Saved this arg. + typename ThisArgClass::GlobalRef mThisArg; + // Saved arguments. + mozilla::Tuple<typename ProxyArg<Args>::Type...> mArgs; + + // We cannot use IsStatic and HasThisArg directly (without going through + // extra hoops) because GCC complains about invalid overloads, so we use + // another pair of template parameters, Static and ThisArg. + + template<bool Static, bool ThisArg, size_t... Indices> + typename mozilla::EnableIf<Static && ThisArg, void>::Type + Call(const Class::LocalRef& cls, + mozilla::IndexSequence<Indices...>) const + { + (*mNativeCall)(cls, mozilla::Get<Indices>(mArgs)...); + } + + template<bool Static, bool ThisArg, size_t... Indices> + typename mozilla::EnableIf<Static && !ThisArg, void>::Type + Call(const Class::LocalRef& cls, + mozilla::IndexSequence<Indices...>) const + { + (*mNativeCall)(mozilla::Get<Indices>(mArgs)...); + } + + template<bool Static, bool ThisArg, size_t... Indices> + typename mozilla::EnableIf<!Static && ThisArg, void>::Type + Call(const typename Owner::LocalRef& inst, + mozilla::IndexSequence<Indices...>) const + { + Impl* const impl = NativePtr<Impl>::Get(inst); + MOZ_CATCH_JNI_EXCEPTION(inst.Env()); + (impl->*mNativeCall)(inst, mozilla::Get<Indices>(mArgs)...); + } + + template<bool Static, bool ThisArg, size_t... Indices> + typename mozilla::EnableIf<!Static && !ThisArg, void>::Type + Call(const typename Owner::LocalRef& inst, + mozilla::IndexSequence<Indices...>) const + { + Impl* const impl = NativePtr<Impl>::Get(inst); + MOZ_CATCH_JNI_EXCEPTION(inst.Env()); + (impl->*mNativeCall)(mozilla::Get<Indices>(mArgs)...); + } + + template<size_t... Indices> + void Clear(JNIEnv* env, mozilla::IndexSequence<Indices...>) + { + int dummy[] = { + (ProxyArg<Args>::Clear(env, Get<Indices>(mArgs)), 0)... + }; + mozilla::Unused << dummy; + } + +public: + // The class that implements the call target. + typedef Impl TargetClass; + typedef typename ThisArgClass::Param ThisArgType; + + static const bool isStatic = IsStatic; + + ProxyNativeCall(ThisArgJNIType thisArg, + NativeCallType nativeCall, + JNIEnv* env, + typename ProxyArg<Args>::JNIType... args) + : mNativeCall(nativeCall) + , mThisArg(env, ThisArgClass::Ref::From(thisArg)) + , mArgs(ProxyArg<Args>::From(env, args)...) + {} + + ProxyNativeCall(ProxyNativeCall&&) = default; + ProxyNativeCall(const ProxyNativeCall&) = default; + + // Get class ref for static calls or object ref for instance calls. + typename ThisArgClass::Param GetThisArg() const { return mThisArg; } + + // Return if target is the given function pointer / pointer-to-member. + // Because we can only compare pointers of the same type, we use a + // templated overload that is chosen only if given a different type of + // pointer than our target pointer type. + bool IsTarget(NativeCallType call) const { return call == mNativeCall; } + template<typename T> bool IsTarget(T&&) const { return false; } + + // Redirect the call to another function / class member with the same + // signature as the original target. Crash if given a wrong signature. + void SetTarget(NativeCallType call) { mNativeCall = call; } + template<typename T> void SetTarget(T&&) const { MOZ_CRASH(); } + + void operator()() override + { + JNIEnv* const env = GetEnvForThread(); + typename ThisArgClass::LocalRef thisArg(env, mThisArg); + Call<IsStatic, HasThisArg>( + thisArg, typename IndexSequenceFor<Args...>::Type()); + + // Clear all saved global refs. We do this after the call is invoked, + // and not inside the destructor because we already have a JNIEnv here, + // so it's more efficient to clear out the saved args here. The + // downside is that the call can only be invoked once. + Clear(env, typename IndexSequenceFor<Args...>::Type()); + mThisArg.Clear(env); + } +}; + +template<class Impl, bool HasThisArg, typename... Args> +struct Dispatcher +{ + template<class Traits, bool IsStatic = Traits::isStatic, + typename... ProxyArgs> + static typename EnableIf< + Traits::dispatchTarget == DispatchTarget::PROXY, void>::Type + Run(ProxyArgs&&... args) + { + Impl::OnNativeCall(ProxyNativeCall< + Impl, typename Traits::Owner, IsStatic, + HasThisArg, Args...>(Forward<ProxyArgs>(args)...)); + } + + template<class Traits, bool IsStatic = Traits::isStatic, + typename ThisArg, typename... ProxyArgs> + static typename EnableIf< + Traits::dispatchTarget == DispatchTarget::GECKO, void>::Type + Run(ThisArg thisArg, ProxyArgs&&... args) + { + // For a static method, do not forward the "this arg" (i.e. the class + // local ref) if the implementation does not request it. This saves us + // a pair of calls to add/delete global ref. + DispatchToGeckoThread(MakeUnique<ProxyNativeCall< + Impl, typename Traits::Owner, IsStatic, HasThisArg, + Args...>>(HasThisArg || !IsStatic ? thisArg : nullptr, + Forward<ProxyArgs>(args)...)); + } + + template<class Traits, bool IsStatic = false, typename... ProxyArgs> + static typename EnableIf< + Traits::dispatchTarget == DispatchTarget::CURRENT, void>::Type + Run(ProxyArgs&&... args) {} +}; + +} // namespace detail + +// Wrapper methods that convert arguments from the JNI types to the native +// types, e.g. from jobject to jni::Object::Ref. For instance methods, the +// wrapper methods also convert calls to calls on objects. +// +// We need specialization for static/non-static because the two have different +// signatures (jobject vs jclass and Impl::*Method vs *Method). +// We need specialization for return type, because void return type requires +// us to not deal with the return value. + +// Bug 1207642 - Work around Dalvik bug by realigning stack on JNI entry +#ifdef __i386__ +#define MOZ_JNICALL JNICALL __attribute__((force_align_arg_pointer)) +#else +#define MOZ_JNICALL JNICALL +#endif + +template<class Traits, class Impl, class Args = typename Traits::Args> +class NativeStub; + +template<class Traits, class Impl, typename... Args> +class NativeStub<Traits, Impl, jni::Args<Args...>> +{ + using Owner = typename Traits::Owner; + using ReturnType = typename Traits::ReturnType; + + static constexpr bool isStatic = Traits::isStatic; + static constexpr bool isVoid = mozilla::IsVoid<ReturnType>::value; + + struct VoidType { using JNIType = void; }; + using ReturnJNIType = typename Conditional< + isVoid, VoidType, TypeAdapter<ReturnType>>::Type::JNIType; + + using ReturnTypeForNonVoidInstance = typename Conditional< + !isStatic && !isVoid, ReturnType, VoidType>::Type; + using ReturnTypeForVoidInstance = typename Conditional< + !isStatic && isVoid, ReturnType, VoidType&>::Type; + using ReturnTypeForNonVoidStatic = typename Conditional< + isStatic && !isVoid, ReturnType, VoidType>::Type; + using ReturnTypeForVoidStatic = typename Conditional< + isStatic && isVoid, ReturnType, VoidType&>::Type; + + static_assert(Traits::dispatchTarget == DispatchTarget::CURRENT || isVoid, + "Dispatched calls must have void return type"); + +public: + // Non-void instance method + template<ReturnTypeForNonVoidInstance (Impl::*Method) (Args...)> + static MOZ_JNICALL ReturnJNIType + Wrap(JNIEnv* env, jobject instance, + typename TypeAdapter<Args>::JNIType... args) + { + MOZ_ASSERT_JNI_THREAD(Traits::callingThread); + + Impl* const impl = NativePtr<Impl>::Get(env, instance); + if (!impl) { + // There is a pending JNI exception at this point. + return ReturnJNIType(); + } + return TypeAdapter<ReturnType>::FromNative(env, + (impl->*Method)(TypeAdapter<Args>::ToNative(env, args)...)); + } + + // Non-void instance method with instance reference + template<ReturnTypeForNonVoidInstance (Impl::*Method) + (const typename Owner::LocalRef&, Args...)> + static MOZ_JNICALL ReturnJNIType + Wrap(JNIEnv* env, jobject instance, + typename TypeAdapter<Args>::JNIType... args) + { + MOZ_ASSERT_JNI_THREAD(Traits::callingThread); + + Impl* const impl = NativePtr<Impl>::Get(env, instance); + if (!impl) { + // There is a pending JNI exception at this point. + return ReturnJNIType(); + } + auto self = Owner::LocalRef::Adopt(env, instance); + const auto res = TypeAdapter<ReturnType>::FromNative(env, + (impl->*Method)(self, TypeAdapter<Args>::ToNative(env, args)...)); + self.Forget(); + return res; + } + + // Void instance method + template<ReturnTypeForVoidInstance (Impl::*Method) (Args...)> + static MOZ_JNICALL void + Wrap(JNIEnv* env, jobject instance, + typename TypeAdapter<Args>::JNIType... args) + { + MOZ_ASSERT_JNI_THREAD(Traits::callingThread); + + if (Traits::dispatchTarget != DispatchTarget::CURRENT) { + Dispatcher<Impl, /* HasThisArg */ false, Args...>:: + template Run<Traits>(instance, Method, env, args...); + return; + } + + Impl* const impl = NativePtr<Impl>::Get(env, instance); + if (!impl) { + // There is a pending JNI exception at this point. + return; + } + (impl->*Method)(TypeAdapter<Args>::ToNative(env, args)...); + } + + // Void instance method with instance reference + template<ReturnTypeForVoidInstance (Impl::*Method) + (const typename Owner::LocalRef&, Args...)> + static MOZ_JNICALL void + Wrap(JNIEnv* env, jobject instance, + typename TypeAdapter<Args>::JNIType... args) + { + MOZ_ASSERT_JNI_THREAD(Traits::callingThread); + + if (Traits::dispatchTarget != DispatchTarget::CURRENT) { + Dispatcher<Impl, /* HasThisArg */ true, Args...>:: + template Run<Traits>(instance, Method, env, args...); + return; + } + + Impl* const impl = NativePtr<Impl>::Get(env, instance); + if (!impl) { + // There is a pending JNI exception at this point. + return; + } + auto self = Owner::LocalRef::Adopt(env, instance); + (impl->*Method)(self, TypeAdapter<Args>::ToNative(env, args)...); + self.Forget(); + } + + // Overload for DisposeNative + template<ReturnTypeForVoidInstance (*DisposeNative) + (const typename Owner::LocalRef&)> + static MOZ_JNICALL void + Wrap(JNIEnv* env, jobject instance) + { + MOZ_ASSERT_JNI_THREAD(Traits::callingThread); + + if (Traits::dispatchTarget != DispatchTarget::CURRENT) { + using LocalRef = typename Owner::LocalRef; + Dispatcher<Impl, /* HasThisArg */ false, const LocalRef&>:: + template Run<Traits, /* IsStatic */ true>( + /* ThisArg */ nullptr, DisposeNative, env, instance); + return; + } + + auto self = Owner::LocalRef::Adopt(env, instance); + (Impl::DisposeNative)(self); + self.Forget(); + } + + // Non-void static method + template<ReturnTypeForNonVoidStatic (*Method) (Args...)> + static MOZ_JNICALL ReturnJNIType + Wrap(JNIEnv* env, jclass, typename TypeAdapter<Args>::JNIType... args) + { + MOZ_ASSERT_JNI_THREAD(Traits::callingThread); + + return TypeAdapter<ReturnType>::FromNative(env, + (*Method)(TypeAdapter<Args>::ToNative(env, args)...)); + } + + // Non-void static method with class reference + template<ReturnTypeForNonVoidStatic (*Method) + (const Class::LocalRef&, Args...)> + static MOZ_JNICALL ReturnJNIType + Wrap(JNIEnv* env, jclass cls, typename TypeAdapter<Args>::JNIType... args) + { + MOZ_ASSERT_JNI_THREAD(Traits::callingThread); + + auto clazz = Class::LocalRef::Adopt(env, cls); + const auto res = TypeAdapter<ReturnType>::FromNative(env, + (*Method)(clazz, TypeAdapter<Args>::ToNative(env, args)...)); + clazz.Forget(); + return res; + } + + // Void static method + template<ReturnTypeForVoidStatic (*Method) (Args...)> + static MOZ_JNICALL void + Wrap(JNIEnv* env, jclass cls, typename TypeAdapter<Args>::JNIType... args) + { + MOZ_ASSERT_JNI_THREAD(Traits::callingThread); + + if (Traits::dispatchTarget != DispatchTarget::CURRENT) { + Dispatcher<Impl, /* HasThisArg */ false, Args...>:: + template Run<Traits>(cls, Method, env, args...); + return; + } + + (*Method)(TypeAdapter<Args>::ToNative(env, args)...); + } + + // Void static method with class reference + template<ReturnTypeForVoidStatic (*Method) + (const Class::LocalRef&, Args...)> + static MOZ_JNICALL void + Wrap(JNIEnv* env, jclass cls, typename TypeAdapter<Args>::JNIType... args) + { + MOZ_ASSERT_JNI_THREAD(Traits::callingThread); + + if (Traits::dispatchTarget != DispatchTarget::CURRENT) { + Dispatcher<Impl, /* HasThisArg */ true, Args...>:: + template Run<Traits>(cls, Method, env, args...); + return; + } + + auto clazz = Class::LocalRef::Adopt(env, cls); + (*Method)(clazz, TypeAdapter<Args>::ToNative(env, args)...); + clazz.Forget(); + } +}; + +// Generate a JNINativeMethod from a native +// method's traits class and a wrapped stub. +template<class Traits, typename Ret, typename... Args> +constexpr JNINativeMethod MakeNativeMethod(MOZ_JNICALL Ret (*stub)(JNIEnv*, Args...)) +{ + return { + Traits::name, + Traits::signature, + reinterpret_cast<void*>(stub) + }; +} + +// Class inherited by implementing class. +template<class Cls, class Impl> +class NativeImpl +{ + typedef typename Cls::template Natives<Impl> Natives; + + static bool sInited; + +public: + static void Init() { + if (sInited) { + return; + } + const auto& ctx = typename Cls::Context(); + ctx.Env()->RegisterNatives( + ctx.ClassRef(), Natives::methods, + sizeof(Natives::methods) / sizeof(Natives::methods[0])); + MOZ_CATCH_JNI_EXCEPTION(ctx.Env()); + sInited = true; + } + +protected: + + // Associate a C++ instance with a Java instance. + static void AttachNative(const typename Cls::LocalRef& instance, + SupportsWeakPtr<Impl>* ptr) + { + static_assert(mozilla::IsBaseOf<SupportsWeakPtr<Impl>, Impl>::value, + "Attach with UniquePtr&& when not using WeakPtr"); + return NativePtr<Impl>::Set(instance, static_cast<Impl*>(ptr)); + } + + static void AttachNative(const typename Cls::LocalRef& instance, + UniquePtr<Impl>&& ptr) + { + static_assert(!mozilla::IsBaseOf<SupportsWeakPtr<Impl>, Impl>::value, + "Attach with SupportsWeakPtr* when using WeakPtr"); + return NativePtr<Impl>::Set(instance, mozilla::Move(ptr)); + } + + // Get the C++ instance associated with a Java instance. + // There is always a pending exception if the return value is nullptr. + static Impl* GetNative(const typename Cls::LocalRef& instance) { + return NativePtr<Impl>::Get(instance); + } + + static void DisposeNative(const typename Cls::LocalRef& instance) { + NativePtr<Impl>::Clear(instance); + } + + NativeImpl() { + // Initialize on creation if not already initialized. + Init(); + } +}; + +// Define static member. +template<class C, class I> +bool NativeImpl<C, I>::sInited; + +} // namespace jni +} // namespace mozilla + +#endif // mozilla_jni_Natives_h__ |