diff --git a/widget/android/jni/Natives.h b/widget/android/jni/Natives.h index 1647ee4131a1..abcfbb397694 100644 --- a/widget/android/jni/Natives.h +++ b/widget/android/jni/Natives.h @@ -3,6 +3,9 @@ #include +#include "mozilla/Move.h" +#include "mozilla/TypeTraits.h" +#include "mozilla/UniquePtr.h" #include "mozilla/WeakPtr.h" #include "mozilla/jni/Accessors.h" #include "mozilla/jni/Refs.h" @@ -12,53 +15,162 @@ namespace mozilla { namespace jni { -// Get the native pointer stored in a Java instance. +/** + * 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 + * , public MyJavaClass::Natives + * { + * // ... + * + * public: + * MOZ_DECLARE_WEAKREFERENCE_TYPENAME(MyClass) + * using MyJavaClass::Natives::Dispose; + * + * void AttachTo(const MyJavaClass::LocalRef& instance) + * { + * MyJavaClass::Natives::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). + * + * class MyClass : public MyJavaClass::Natives + * { + * // ... + * + * public: + * using MyJavaClass::Natives::Dispose; + * + * static void AttachTo(const MyJavaClass::LocalRef& instance) + * { + * MyJavaClass::Natives::AttachInstance( + * instance, mozilla::MakeUnique()); + * + * // "instance" owns the newly created C++ object, so the C++ + * // object is destroyed as soon as instance.dispose() is called. + * } + * }; + */ + +namespace { + +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, Impl>::value /* = false */> +struct NativePtr +{ + static Impl* Get(JNIEnv* env, jobject instance) + { + return reinterpret_cast(CheckNativeHandle( + env, GetNativeHandle(env, instance))); + } + + template + static Impl* Get(const LocalRef& instance) + { + return Get(instance.Env(), instance.Get()); + } + + template + static void Set(const LocalRef& instance, UniquePtr&& ptr) + { + Clear(instance); + SetNativeHandle(instance.Env(), instance.Get(), + reinterpret_cast(ptr.release())); + HandleUncaughtException(instance.Env()); + } + + template + static void Clear(const LocalRef& instance) + { + UniquePtr ptr(reinterpret_cast( + GetNativeHandle(instance.Env(), instance.Get()))); + HandleUncaughtException(instance.Env()); + + if (ptr) { + SetNativeHandle(instance.Env(), instance.Get(), 0); + HandleUncaughtException(instance.Env()); + } + } +}; + template -Impl* GetNativePtr(JNIEnv* env, jobject instance) +struct NativePtr { - const auto ptr = reinterpret_cast*>( - GetNativeHandle(env, instance)); - if (!ptr) { - return nullptr; + static Impl* Get(JNIEnv* env, jobject instance) + { + const auto ptr = reinterpret_cast*>( + 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; } - Impl* const impl = *ptr; - if (!impl) { - ThrowException(env, "java/lang/NullPointerException", - "Native object already released"); + template + static Impl* Get(const LocalRef& instance) + { + return Get(instance.Env(), instance.Get()); } - 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 + static void Set(const LocalRef& instance, SupportsWeakPtr* ptr) + { + Clear(instance); + SetNativeHandle(instance.Env(), instance.Get(), + reinterpret_cast(new WeakPtr(ptr))); + HandleUncaughtException(instance.Env()); } -} -template -void SetNativePtr(const LocalRef& instance, Impl* ptr) -{ - ClearNativePtr(instance); - SetNativeHandle(instance.Env(), instance.Get(), - reinterpret_cast(new WeakPtr(ptr))); -} + template + static void Clear(const LocalRef& instance) + { + const auto ptr = reinterpret_cast*>( + GetNativeHandle(instance.Env(), instance.Get())); + HandleUncaughtException(instance.Env()); + + if (ptr) { + SetNativeHandle(instance.Env(), instance.Get(), 0); + HandleUncaughtException(instance.Env()); + delete ptr; + } + } +}; + +} // namespace namespace detail { @@ -88,7 +200,7 @@ public: static ReturnJNIType Wrap(JNIEnv* env, jobject instance, typename TypeAdapter::JNIType... args) { - Impl* const impl = GetNativePtr(env, instance); + Impl* const impl = NativePtr::Get(env, instance); if (!impl) { return ReturnJNIType(); } @@ -101,7 +213,7 @@ public: static ReturnJNIType Wrap(JNIEnv* env, jobject instance, typename TypeAdapter::JNIType... args) { - Impl* const impl = GetNativePtr(env, instance); + Impl* const impl = NativePtr::Get(env, instance); if (!impl) { return ReturnJNIType(); } @@ -125,7 +237,7 @@ public: static void Wrap(JNIEnv* env, jobject instance, typename TypeAdapter::JNIType... args) { - Impl* const impl = GetNativePtr(env, instance); + Impl* const impl = NativePtr::Get(env, instance); if (!impl) { return; } @@ -137,7 +249,7 @@ public: static void Wrap(JNIEnv* env, jobject instance, typename TypeAdapter::JNIType... args) { - Impl* const impl = GetNativePtr(env, instance); + Impl* const impl = NativePtr::Get(env, instance); if (!impl) { return; } @@ -245,8 +357,28 @@ public: } protected: + + // Associate a C++ instance with a Java instance. + static void AttachNative(const typename Cls::LocalRef& instance, + SupportsWeakPtr* ptr) + { + static_assert(mozilla::IsBaseOf, Impl>::value, + "Attach with UniquePtr&& when not using WeakPtr"); + return NativePtr::Set(instance, ptr); + } + + static void AttachNative(const typename Cls::LocalRef& instance, + UniquePtr&& ptr) + { + static_assert(!mozilla::IsBaseOf, Impl>::value, + "Attach with SupportsWeakPtr* when using WeakPtr"); + return NativePtr::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 GetNativePtr(instance); + return NativePtr::Get(instance); } NativeImpl() { @@ -254,12 +386,8 @@ protected: Init(); } - void AttachNative(const typename Cls::LocalRef& instance) { - SetNativePtr<>(instance, static_cast(this)); - } - void DisposeNative(const typename Cls::LocalRef& instance) { - ClearNativePtr(instance); + NativePtr::Clear(instance); } }; diff --git a/widget/android/jni/Utils.cpp b/widget/android/jni/Utils.cpp index 16bf822fb2aa..33757a63b5a5 100644 --- a/widget/android/jni/Utils.cpp +++ b/widget/android/jni/Utils.cpp @@ -111,14 +111,8 @@ uintptr_t GetNativeHandle(JNIEnv* env, jobject instance) return 0; } - auto handle = static_cast( + return static_cast( env->GetLongField(instance, sJNIObjectHandleField)); - - if (!handle && !env->ExceptionCheck()) { - ThrowException(env, "java/lang/NullPointerException", - "Null native pointer"); - } - return handle; } void SetNativeHandle(JNIEnv* env, jobject instance, uintptr_t handle)