#ifndef mozilla_jni_Natives_h__ #define mozilla_jni_Natives_h__ #include #include "mozilla/WeakPtr.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 { // Get the native pointer stored in a Java instance. template Impl* GetNativePtr(JNIEnv* env, jobject instance) { const auto ptr = reinterpret_cast*>( 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 Impl* GetNativePtr(const LocalRef& instance) { return GetNativePtr(instance.Env(), instance.Get()); } template void ClearNativePtr(const LocalRef& instance) { JNIEnv* const env = instance.Env(); const auto ptr = reinterpret_cast*>( GetNativeHandle(env, instance.Get())); if (ptr) { SetNativeHandle(env, instance.Get(), 0); delete ptr; } else { // GetNativeHandle throws an exception when returning null. MOZ_ASSERT(env->ExceptionCheck()); env->ExceptionClear(); } } template void SetNativePtr(const LocalRef& instance, Impl* ptr) { ClearNativePtr(instance); SetNativeHandle(instance.Env(), instance.Get(), reinterpret_cast(new WeakPtr(ptr))); } 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. template class NativeStubImpl; // Specialization for instance methods with non-void return type template class NativeStubImpl> { typedef typename Traits::Owner Owner; typedef typename TypeAdapter::JNIType ReturnJNIType; public: // Instance method template static ReturnJNIType Wrap(JNIEnv* env, jobject instance, typename TypeAdapter::JNIType... args) { Impl* const impl = GetNativePtr(env, instance); if (!impl) { return ReturnJNIType(); } return TypeAdapter::FromNative(env, (impl->*Method)(TypeAdapter::ToNative(env, args)...)); } // Instance method with instance reference template static ReturnJNIType Wrap(JNIEnv* env, jobject instance, typename TypeAdapter::JNIType... args) { Impl* const impl = GetNativePtr(env, instance); if (!impl) { return ReturnJNIType(); } auto self = Owner::LocalRef::Adopt(env, instance); const auto res = TypeAdapter::FromNative(env, (impl->*Method)(self, TypeAdapter::ToNative(env, args)...)); self.Forget(); return res; } }; // Specialization for instance methods with void return type template class NativeStubImpl> { typedef typename Traits::Owner Owner; public: // Instance method template static void Wrap(JNIEnv* env, jobject instance, typename TypeAdapter::JNIType... args) { Impl* const impl = GetNativePtr(env, instance); if (!impl) { return; } (impl->*Method)(TypeAdapter::ToNative(env, args)...); } // Instance method with instance reference template static void Wrap(JNIEnv* env, jobject instance, typename TypeAdapter::JNIType... args) { Impl* const impl = GetNativePtr(env, instance); if (!impl) { return; } auto self = Owner::LocalRef::Adopt(env, instance); (impl->*Method)(self, TypeAdapter::ToNative(env, args)...); self.Forget(); } }; // Specialization for static methods with non-void return type template class NativeStubImpl> { typedef typename TypeAdapter::JNIType ReturnJNIType; public: // Static method template static ReturnJNIType Wrap(JNIEnv* env, jclass, typename TypeAdapter::JNIType... args) { return TypeAdapter::FromNative(env, (*Method)(TypeAdapter::ToNative(env, args)...)); } // Static method with class reference template static ReturnJNIType Wrap(JNIEnv* env, jclass cls, typename TypeAdapter::JNIType... args) { auto clazz = ClassObject::LocalRef::Adopt(env, cls); const auto res = TypeAdapter::FromNative(env, (*Method)(clazz, TypeAdapter::ToNative(env, args)...)); clazz.Forget(); return res; } }; // Specialization for static methods with void return type template class NativeStubImpl> { public: // Static method template static void Wrap(JNIEnv* env, jclass, typename TypeAdapter::JNIType... args) { (*Method)(TypeAdapter::ToNative(env, args)...); } // Static method with class reference template static void Wrap(JNIEnv* env, jclass cls, typename TypeAdapter::JNIType... args) { auto clazz = ClassObject::LocalRef::Adopt(env, cls); (*Method)(clazz, TypeAdapter::ToNative(env, args)...); clazz.Forget(); } }; } // namespace detail // Form a stub wrapper from a native method's traits class and an implementing // class. The stub wrapper has a Wrap function that will form a wrapped stub. template struct NativeStub : detail::NativeStubImpl { }; // Generate a JNINativeMethod from a native // method's traits class and a wrapped stub. template constexpr JNINativeMethod MakeNativeMethod(Ret (*stub)(JNIEnv*, Args...)) { return { Traits::name, Traits::signature, reinterpret_cast(stub) }; } // Class inherited by implementing class. template class NativeImpl { typedef typename Cls::template Natives Natives; static bool sInited; public: static void Init() { if (sInited) { return; } JNIEnv* const env = GetJNIForThread(); MOZ_ALWAYS_TRUE(!env->RegisterNatives( Accessor::EnsureClassRef(env), Natives::methods, sizeof(Natives::methods) / sizeof(Natives::methods[0]))); sInited = true; } protected: static Impl* GetNative(const typename Cls::LocalRef& instance) { return GetNativePtr(instance); } NativeImpl() { // Initialize on creation if not already initialized. Init(); } void AttachNative(const typename Cls::LocalRef& instance) { SetNativePtr<>(instance, static_cast(this)); } void DisposeNative(const typename Cls::LocalRef& instance) { ClearNativePtr(instance); } }; // Define static member. template bool NativeImpl::sInited; } // namespace jni } // namespace mozilla #endif // mozilla_jni_Natives_h__