Bug 1178850 - Add supporting classes for native JNI calls; r=snorp
☠☠ backed out by 0084c286fd30 ☠ ☠
authorJim Chen <nchen@mozilla.com>
Fri, 10 Jul 2015 16:52:52 -0400
changeset 284046 d6dab7810669a68e75869da07a1218af3b6c49c5
parent 284045 8ee5809f349b4af29450007855a82ac724a4dc38
child 284047 c02b603104eaabeae566c9ccb179a35d1fe043bc
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssnorp
bugs1178850
milestone42.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1178850 - Add supporting classes for native JNI calls; r=snorp
widget/android/jni/Accessors.h
widget/android/jni/Natives.h
widget/android/jni/Refs.h
widget/android/jni/Types.h
widget/android/jni/moz.build
--- a/widget/android/jni/Accessors.h
+++ b/widget/android/jni/Accessors.h
@@ -28,26 +28,28 @@ struct Value
 
     jvalue val;
 };
 
 }
 
 // Base class for Method<>, Field<>, and Constructor<>.
 class Accessor {
-private:
+public:
     template<class Cls>
-    static void EnsureClassRef(JNIEnv* env)
+    static jclass EnsureClassRef(JNIEnv* env)
     {
         if (!Cls::sClassRef) {
             MOZ_ALWAYS_TRUE(Cls::sClassRef =
                 AndroidBridge::GetClassGlobalRef(env, Cls::name));
         }
+        return Cls::sClassRef;
     }
 
+private:
     static void GetNsresult(JNIEnv* env, nsresult* rv)
     {
         if (env->ExceptionCheck()) {
             env->ExceptionClear();
             *rv = NS_ERROR_FAILURE;
         } else {
             *rv = NS_OK;
         }
new file mode 100644
--- /dev/null
+++ b/widget/android/jni/Natives.h
@@ -0,0 +1,212 @@
+#ifndef mozilla_jni_Natives_h__
+#define mozilla_jni_Natives_h__
+
+#include <jni.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<class Impl>
+Impl* GetInstancePtr(JNIEnv* env, jobject instance)
+{
+    // TODO: implement instance native pointers.
+    return nullptr;
+}
+
+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<bool IsStatic, typename ReturnType,
+         class Traits, class Impl, class Args>
+class NativeStubImpl;
+
+// Specialization for instance methods with non-void return type
+template<typename ReturnType, class Traits, class Impl, typename... Args>
+class NativeStubImpl<false, ReturnType, Traits, Impl, jni::Args<Args...>>
+{
+    typedef typename Traits::Owner Owner;
+    typedef typename TypeAdapter<ReturnType>::JNIType ReturnJNIType;
+
+public:
+    // Instance method
+    template<ReturnType (Impl::*Method) (Args...)>
+    static ReturnJNIType Wrap(JNIEnv* env, jobject instance,
+                              typename TypeAdapter<Args>::JNIType... args)
+    {
+        Impl* const impl = GetInstancePtr<Impl>(env, instance);
+        if (!impl) {
+            return ReturnJNIType();
+        }
+        return TypeAdapter<ReturnType>::FromNative(env,
+                (impl->*Method)(TypeAdapter<Args>::ToNative(env, args)...));
+    }
+
+    // Instance method with instance reference
+    template<ReturnType (Impl::*Method) (typename Owner::Param, Args...)>
+    static ReturnJNIType Wrap(JNIEnv* env, jobject instance,
+                              typename TypeAdapter<Args>::JNIType... args)
+    {
+        Impl* const impl = GetInstancePtr<Impl>(env, instance);
+        if (!impl) {
+            return ReturnJNIType();
+        }
+        return TypeAdapter<ReturnType>::FromNative(env,
+                (impl->*Method)(Owner::Ref::From(instance),
+                                TypeAdapter<Args>::ToNative(env, args)...));
+    }
+};
+
+// Specialization for instance methods with void return type
+template<class Traits, class Impl, typename... Args>
+class NativeStubImpl<false, void, Traits, Impl, jni::Args<Args...>>
+{
+    typedef typename Traits::Owner Owner;
+
+public:
+    // Instance method
+    template<void (Impl::*Method) (Args...)>
+    static void Wrap(JNIEnv* env, jobject instance,
+                     typename TypeAdapter<Args>::JNIType... args)
+    {
+        Impl* const impl = GetInstancePtr<Impl>(env, instance);
+        if (!impl) {
+            return;
+        }
+        (impl->*Method)(TypeAdapter<Args>::ToNative(env, args)...);
+    }
+
+    // Instance method with instance reference
+    template<void (Impl::*Method) (typename Owner::Param, Args...)>
+    static void Wrap(JNIEnv* env, jobject instance,
+                     typename TypeAdapter<Args>::JNIType... args)
+    {
+        Impl* const impl = GetInstancePtr<Impl>(env, instance);
+        if (!impl) {
+            return;
+        }
+        (impl->*Method)(Owner::Ref::From(instance),
+                        TypeAdapter<Args>::ToNative(env, args)...);
+    }
+};
+
+// Specialization for static methods with non-void return type
+template<typename ReturnType, class Traits, class Impl, typename... Args>
+class NativeStubImpl<true, ReturnType, Traits, Impl, jni::Args<Args...>>
+{
+    typedef typename TypeAdapter<ReturnType>::JNIType ReturnJNIType;
+
+public:
+    // Static method
+    template<ReturnType (*Method) (Args...)>
+    static ReturnJNIType Wrap(JNIEnv* env, jclass,
+                              typename TypeAdapter<Args>::JNIType... args)
+    {
+        return TypeAdapter<ReturnType>::FromNative(env,
+                (*Method)(TypeAdapter<Args>::ToNative(env, args)...));
+    }
+
+    // Static method with class reference
+    template<ReturnType (*Method) (ClassObject::Param, Args...)>
+    static ReturnJNIType Wrap(JNIEnv* env, jclass cls,
+                              typename TypeAdapter<Args>::JNIType... args)
+    {
+        return TypeAdapter<ReturnType>::FromNative(env,
+                (*Method)(ClassObject::Ref::From(cls),
+                          TypeAdapter<Args>::ToNative(env, args)...));
+    }
+};
+
+// Specialization for static methods with void return type
+template<class Traits, class Impl, typename... Args>
+class NativeStubImpl<true, void, Traits, Impl, jni::Args<Args...>>
+{
+public:
+    // Static method
+    template<void (*Method) (Args...)>
+    static void Wrap(JNIEnv* env, jclass,
+                     typename TypeAdapter<Args>::JNIType... args)
+    {
+        (*Method)(TypeAdapter<Args>::ToNative(env, args)...);
+    }
+
+    // Static method with class reference
+    template<void (*Method) (ClassObject::Param, Args...)>
+    static void Wrap(JNIEnv* env, jclass cls,
+                     typename TypeAdapter<Args>::JNIType... args)
+    {
+        (*Method)(ClassObject::Ref::From(cls),
+                  TypeAdapter<Args>::ToNative(env, args)...);
+    }
+};
+
+} // 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<class Traits, class Impl>
+struct NativeStub : detail::NativeStubImpl<Traits::isStatic,
+                                           typename Traits::ReturnType,
+                                           Traits, Impl, typename Traits::Args>
+{
+};
+
+// Generate a JNINativeMethod from a native
+// method's traits class and a wrapped stub.
+template<class Traits, typename Ret, typename... Args>
+constexpr JNINativeMethod MakeNativeMethod(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;
+        }
+        JNIEnv* const env = GetJNIForThread();
+        env->RegisterNatives(Accessor::EnsureClassRef<Cls>(env),
+                             Natives::methods,
+                             sizeof(Natives::methods) / sizeof(JNINativeMethod));
+        sInited = true;
+    }
+
+    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__
--- a/widget/android/jni/Refs.h
+++ b/widget/android/jni/Refs.h
@@ -39,16 +39,23 @@ enum class ExceptionMode
     ABORT,
     // Ignore the exception and return to caller.
     IGNORE,
     // Catch any exception and return a nsresult.
     NSRESULT,
 };
 
 
+// Class to hold the native types of a method's arguments.
+// For example, if a method has signature (ILjava/lang/String;)V,
+// its arguments class would be jni::Args<int32_t, jni::String::Param>
+template<typename...>
+struct Args {};
+
+
 // Base class for all JNI binding classes.
 // Templated so that we have one sClassRef for each class.
 template<class Cls>
 class Class
 {
     friend class Accessor;
     template<class T> friend class Constructor;
     template<class T> friend class Field;
--- a/widget/android/jni/Types.h
+++ b/widget/android/jni/Types.h
@@ -23,42 +23,54 @@ namespace {
 //  * ToNative: static function that converts the JNI type to the native type.
 //  * FromNative: static function that converts the native type to the JNI type.
 
 template<typename T> struct TypeAdapter;
 
 
 // TypeAdapter<LocalRef<Cls>> applies when jobject is a return value.
 template<class Cls> struct TypeAdapter<LocalRef<Cls>> {
+    typedef decltype(Ref<Cls>(nullptr).Get()) JNIType;
+
     static constexpr auto Call = &JNIEnv::CallObjectMethodA;
     static constexpr auto StaticCall = &JNIEnv::CallStaticObjectMethodA;
     static constexpr auto Get = &JNIEnv::GetObjectField;
     static constexpr auto StaticGet = &JNIEnv::GetStaticObjectField;
 
     static LocalRef<Cls> ToNative(JNIEnv* env, jobject instance) {
         return LocalRef<Cls>::Adopt(env, instance);
     }
+
+    static JNIType FromNative(JNIEnv*, LocalRef<Cls>&& instance) {
+        return instance.Forget();
+    }
 };
 
 template<class Cls> constexpr jobject
     (JNIEnv::*TypeAdapter<LocalRef<Cls>>::Call)(jobject, jmethodID, jvalue*);
 template<class Cls> constexpr jobject
     (JNIEnv::*TypeAdapter<LocalRef<Cls>>::StaticCall)(jclass, jmethodID, jvalue*);
 template<class Cls> constexpr jobject
     (JNIEnv::*TypeAdapter<LocalRef<Cls>>::Get)(jobject, jfieldID);
 template<class Cls> constexpr jobject
     (JNIEnv::*TypeAdapter<LocalRef<Cls>>::StaticGet)(jclass, jfieldID);
 
 
 // TypeAdapter<Ref<Cls>> applies when jobject is a parameter value.
 template<class Cls> struct TypeAdapter<Ref<Cls>> {
+    typedef decltype(Ref<Cls>(nullptr).Get()) JNIType;
+
     static constexpr auto Set = &JNIEnv::SetObjectField;
     static constexpr auto StaticSet = &JNIEnv::SetStaticObjectField;
 
-    static jobject FromNative(JNIEnv*, const Ref<Cls>& instance) {
+    static Ref<Cls> ToNative(JNIEnv* env, jobject instance) {
+        return Ref<Cls>::From(instance);
+    }
+
+    static JNIType FromNative(JNIEnv*, const Ref<Cls>& instance) {
         return instance.Get();
     }
 };
 
 template<class Cls> constexpr void
     (JNIEnv::*TypeAdapter<Ref<Cls>>::Set)(jobject, jfieldID, jobject);
 template<class Cls> constexpr void
     (JNIEnv::*TypeAdapter<Ref<Cls>>::StaticSet)(jclass, jfieldID, jobject);
@@ -68,16 +80,18 @@ template<class Cls> constexpr void
 template<> struct TypeAdapter<Param<String>>
         : public TypeAdapter<String::Ref>
 {};
 
 
 #define DEFINE_PRIMITIVE_TYPE_ADAPTER(NativeType, JNIType, JNIName) \
     \
     template<> struct TypeAdapter<NativeType> { \
+        typedef JNIType JNI##Type; \
+    \
         static constexpr auto Call = &JNIEnv::Call ## JNIName ## MethodA; \
         static constexpr auto StaticCall = &JNIEnv::CallStatic ## JNIName ## MethodA; \
         static constexpr auto Get = &JNIEnv::Get ## JNIName ## Field; \
         static constexpr auto StaticGet = &JNIEnv::GetStatic ## JNIName ## Field; \
         static constexpr auto Set = &JNIEnv::Set ## JNIName ## Field; \
         static constexpr auto StaticSet = &JNIEnv::SetStatic ## JNIName ## Field; \
     \
         static JNIType FromNative(JNIEnv*, NativeType val) { \
--- a/widget/android/jni/moz.build
+++ b/widget/android/jni/moz.build
@@ -1,16 +1,17 @@
 # -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
 # vim: set filetype=python:
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXPORTS.mozilla.jni += [
     'Accessors.h',
+    'Natives.h',
     'Refs.h',
     'Types.h',
     'Utils.h',
 ]
 
 SOURCES += [
     'Utils.cpp',
 ]