1 #ifndef mozilla_jni_Accessors_h__
2 #define mozilla_jni_Accessors_h__
3 
4 #include <jni.h>
5 
6 #include "mozilla/jni/Refs.h"
7 #include "mozilla/jni/Types.h"
8 #include "mozilla/jni/Utils.h"
9 #include "AndroidBridge.h"
10 
11 namespace mozilla {
12 namespace jni {
13 
14 namespace detail {
15 
16 // Helper class to convert an arbitrary type to a jvalue, e.g. Value(123).val.
17 struct Value
18 {
ValueValue19     Value(jboolean z) { val.z = z; }
ValueValue20     Value(jbyte b)    { val.b = b; }
ValueValue21     Value(jchar c)    { val.c = c; }
ValueValue22     Value(jshort s)   { val.s = s; }
ValueValue23     Value(jint i)     { val.i = i; }
ValueValue24     Value(jlong j)    { val.j = j; }
ValueValue25     Value(jfloat f)   { val.f = f; }
ValueValue26     Value(jdouble d)  { val.d = d; }
ValueValue27     Value(jobject l)  { val.l = l; }
28 
29     jvalue val;
30 };
31 
32 } // namespace detail
33 
34 using namespace detail;
35 
36 // Base class for Method<>, Field<>, and Constructor<>.
37 class Accessor
38 {
GetNsresult(JNIEnv * env,nsresult * rv)39     static void GetNsresult(JNIEnv* env, nsresult* rv)
40     {
41         if (env->ExceptionCheck()) {
42 #ifdef MOZ_CHECK_JNI
43             env->ExceptionDescribe();
44 #endif
45             env->ExceptionClear();
46             *rv = NS_ERROR_FAILURE;
47         } else {
48             *rv = NS_OK;
49         }
50     }
51 
52 protected:
53     // Called after making a JNIEnv call.
54     template<class Traits>
EndAccess(const typename Traits::Owner::Context & ctx,nsresult * rv)55     static void EndAccess(const typename Traits::Owner::Context& ctx,
56                           nsresult* rv)
57     {
58         if (Traits::exceptionMode == ExceptionMode::ABORT) {
59             MOZ_CATCH_JNI_EXCEPTION(ctx.Env());
60 
61         } else if (Traits::exceptionMode == ExceptionMode::NSRESULT) {
62             GetNsresult(ctx.Env(), rv);
63         }
64     }
65 };
66 
67 
68 // Member<> is used to call a JNI method given a traits class.
69 template<class Traits, typename ReturnType = typename Traits::ReturnType>
70 class Method : public Accessor
71 {
72     typedef Accessor Base;
73     typedef typename Traits::Owner::Context Context;
74 
75 protected:
76     static jmethodID sID;
77 
BeginAccess(const Context & ctx)78     static void BeginAccess(const Context& ctx)
79     {
80         MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
81         static_assert(Traits::dispatchTarget == DispatchTarget::CURRENT,
82                       "Dispatching not supported for method call");
83 
84         if (sID) {
85             return;
86         }
87 
88         if (Traits::isStatic) {
89             MOZ_ALWAYS_TRUE(sID = AndroidBridge::GetStaticMethodID(
90                 ctx.Env(), ctx.ClassRef(), Traits::name, Traits::signature));
91         } else {
92             MOZ_ALWAYS_TRUE(sID = AndroidBridge::GetMethodID(
93                 ctx.Env(), ctx.ClassRef(), Traits::name, Traits::signature));
94         }
95     }
96 
EndAccess(const Context & ctx,nsresult * rv)97     static void EndAccess(const Context& ctx, nsresult* rv)
98     {
99         return Base::EndAccess<Traits>(ctx, rv);
100     }
101 
102 public:
103     template<typename... Args>
Call(const Context & ctx,nsresult * rv,const Args &...args)104     static ReturnType Call(const Context& ctx, nsresult* rv, const Args&... args)
105     {
106         JNIEnv* const env = ctx.Env();
107         BeginAccess(ctx);
108 
109         jvalue jargs[] = {
110             Value(TypeAdapter<Args>::FromNative(env, args)).val ...
111         };
112 
113         auto result = TypeAdapter<ReturnType>::ToNative(env,
114                 Traits::isStatic ?
115                 (env->*TypeAdapter<ReturnType>::StaticCall)(
116                         ctx.RawClassRef(), sID, jargs) :
117                 (env->*TypeAdapter<ReturnType>::Call)(
118                         ctx.Get(), sID, jargs));
119 
120         EndAccess(ctx, rv);
121         return result;
122     }
123 };
124 
125 // Define sID member.
126 template<class T, typename R> jmethodID Method<T, R>::sID;
127 
128 
129 // Specialize void because C++ forbids us from
130 // using a "void" temporary result variable.
131 template<class Traits>
132 class Method<Traits, void> : public Method<Traits, bool>
133 {
134     typedef Method<Traits, bool> Base;
135     typedef typename Traits::Owner::Context Context;
136 
137 public:
138     template<typename... Args>
Call(const Context & ctx,nsresult * rv,const Args &...args)139     static void Call(const Context& ctx, nsresult* rv,
140                      const Args&... args)
141     {
142         JNIEnv* const env = ctx.Env();
143         Base::BeginAccess(ctx);
144 
145         jvalue jargs[] = {
146             Value(TypeAdapter<Args>::FromNative(env, args)).val ...
147         };
148 
149         if (Traits::isStatic) {
150             env->CallStaticVoidMethodA(ctx.RawClassRef(), Base::sID, jargs);
151         } else {
152             env->CallVoidMethodA(ctx.Get(), Base::sID, jargs);
153         }
154 
155         Base::EndAccess(ctx, rv);
156     }
157 };
158 
159 
160 // Constructor<> is used to construct a JNI instance given a traits class.
161 template<class Traits>
162 class Constructor : protected Method<Traits, typename Traits::ReturnType> {
163     typedef typename Traits::Owner::Context Context;
164     typedef typename Traits::ReturnType ReturnType;
165     typedef Method<Traits, ReturnType> Base;
166 
167 public:
168     template<typename... Args>
Call(const Context & ctx,nsresult * rv,const Args &...args)169     static ReturnType Call(const Context& ctx, nsresult* rv,
170                            const Args&... args)
171     {
172         JNIEnv* const env = ctx.Env();
173         Base::BeginAccess(ctx);
174 
175         jvalue jargs[] = {
176             Value(TypeAdapter<Args>::FromNative(env, args)).val ...
177         };
178 
179         auto result = TypeAdapter<ReturnType>::ToNative(
180                 env, env->NewObjectA(ctx.RawClassRef(), Base::sID, jargs));
181 
182         Base::EndAccess(ctx, rv);
183         return result;
184     }
185 };
186 
187 
188 // Field<> is used to access a JNI field given a traits class.
189 template<class Traits>
190 class Field : public Accessor
191 {
192     typedef Accessor Base;
193     typedef typename Traits::Owner::Context Context;
194     typedef typename Traits::ReturnType GetterType;
195     typedef typename Traits::SetterType SetterType;
196 
197 private:
198 
199     static jfieldID sID;
200 
BeginAccess(const Context & ctx)201     static void BeginAccess(const Context& ctx)
202     {
203         MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
204         static_assert(Traits::dispatchTarget == DispatchTarget::CURRENT,
205                       "Dispatching not supported for field access");
206 
207         if (sID) {
208             return;
209         }
210 
211         if (Traits::isStatic) {
212             MOZ_ALWAYS_TRUE(sID = AndroidBridge::GetStaticFieldID(
213                 ctx.Env(), ctx.ClassRef(), Traits::name, Traits::signature));
214         } else {
215             MOZ_ALWAYS_TRUE(sID = AndroidBridge::GetFieldID(
216                 ctx.Env(), ctx.ClassRef(), Traits::name, Traits::signature));
217         }
218     }
219 
EndAccess(const Context & ctx,nsresult * rv)220     static void EndAccess(const Context& ctx, nsresult* rv)
221     {
222         return Base::EndAccess<Traits>(ctx, rv);
223     }
224 
225 public:
Get(const Context & ctx,nsresult * rv)226     static GetterType Get(const Context& ctx, nsresult* rv)
227     {
228         JNIEnv* const env = ctx.Env();
229         BeginAccess(ctx);
230 
231         auto result = TypeAdapter<GetterType>::ToNative(
232                 env, Traits::isStatic ?
233 
234                 (env->*TypeAdapter<GetterType>::StaticGet)
235                         (ctx.RawClassRef(), sID) :
236 
237                 (env->*TypeAdapter<GetterType>::Get)
238                         (ctx.Get(), sID));
239 
240         EndAccess(ctx, rv);
241         return result;
242     }
243 
Set(const Context & ctx,nsresult * rv,SetterType val)244     static void Set(const Context& ctx, nsresult* rv, SetterType val)
245     {
246         JNIEnv* const env = ctx.Env();
247         BeginAccess(ctx);
248 
249         if (Traits::isStatic) {
250             (env->*TypeAdapter<SetterType>::StaticSet)(
251                     ctx.RawClassRef(), sID,
252                     TypeAdapter<SetterType>::FromNative(env, val));
253         } else {
254             (env->*TypeAdapter<SetterType>::Set)(
255                     ctx.Get(), sID,
256                     TypeAdapter<SetterType>::FromNative(env, val));
257         }
258 
259         EndAccess(ctx, rv);
260     }
261 };
262 
263 // Define sID member.
264 template<class T> jfieldID Field<T>::sID;
265 
266 
267 // Define the sClassRef member declared in Refs.h and
268 // used by Method and Field above.
269 template<class C, typename T> jclass Context<C, T>::sClassRef;
270 
271 } // namespace jni
272 } // namespace mozilla
273 
274 #endif // mozilla_jni_Accessors_h__
275