1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #ifndef mozilla_jni_Accessors_h__
8 #define mozilla_jni_Accessors_h__
9 
10 #include <jni.h>
11 
12 #include "mozilla/jni/Refs.h"
13 #include "mozilla/jni/Types.h"
14 #include "mozilla/jni/Utils.h"
15 #include "AndroidBridge.h"
16 
17 namespace mozilla {
18 namespace jni {
19 
20 namespace detail {
21 
22 // Helper class to convert an arbitrary type to a jvalue, e.g. Value(123).val.
23 struct Value {
ValueValue24   explicit Value(jboolean z) { val.z = z; }
ValueValue25   explicit Value(jbyte b) { val.b = b; }
ValueValue26   explicit Value(jchar c) { val.c = c; }
ValueValue27   explicit Value(jshort s) { val.s = s; }
ValueValue28   explicit Value(jint i) { val.i = i; }
ValueValue29   explicit Value(jlong j) { val.j = j; }
ValueValue30   explicit Value(jfloat f) { val.f = f; }
ValueValue31   explicit Value(jdouble d) { val.d = d; }
ValueValue32   explicit Value(jobject l) { val.l = l; }
33 
34   jvalue val;
35 };
36 
37 }  // namespace detail
38 
39 using namespace detail;
40 
41 // Base class for Method<>, Field<>, and Constructor<>.
42 class Accessor {
GetNsresult(JNIEnv * env,nsresult * rv)43   static void GetNsresult(JNIEnv* env, nsresult* rv) {
44     if (env->ExceptionCheck()) {
45 #ifdef MOZ_CHECK_JNI
46       env->ExceptionDescribe();
47 #endif
48       env->ExceptionClear();
49       *rv = NS_ERROR_FAILURE;
50     } else {
51       *rv = NS_OK;
52     }
53   }
54 
55  protected:
56   // Called after making a JNIEnv call.
57   template <class Traits>
EndAccess(const typename Traits::Owner::Context & ctx,nsresult * rv)58   static void EndAccess(const typename Traits::Owner::Context& ctx,
59                         nsresult* rv) {
60     if (Traits::exceptionMode == ExceptionMode::ABORT) {
61       MOZ_CATCH_JNI_EXCEPTION(ctx.Env());
62 
63     } else if (Traits::exceptionMode == ExceptionMode::NSRESULT) {
64       GetNsresult(ctx.Env(), rv);
65     }
66   }
67 };
68 
69 // Member<> is used to call a JNI method given a traits class.
70 template <class Traits, typename ReturnType = typename Traits::ReturnType>
71 class Method : public Accessor {
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     MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
80     static_assert(Traits::dispatchTarget == DispatchTarget::CURRENT,
81                   "Dispatching not supported for method call");
82 
83     if (sID) {
84       return;
85     }
86 
87     if (Traits::isStatic) {
88       MOZ_ALWAYS_TRUE(
89           sID = AndroidBridge::GetStaticMethodID(
90               ctx.Env(), ctx.ClassRef(), Traits::name, Traits::signature));
91     } else {
92       MOZ_ALWAYS_TRUE(
93           sID = AndroidBridge::GetMethodID(ctx.Env(), ctx.ClassRef(),
94                                            Traits::name, Traits::signature));
95     }
96   }
97 
EndAccess(const Context & ctx,nsresult * rv)98   static void EndAccess(const Context& ctx, nsresult* rv) {
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,
105                          const Args&... args) {
106     JNIEnv* const env = ctx.Env();
107     BeginAccess(ctx);
108 
109     jvalue jargs[] = {Value(TypeAdapter<Args>::FromNative(env, args)).val...};
110 
111     auto result = TypeAdapter<ReturnType>::ToNative(
112         env, Traits::isStatic ? (env->*TypeAdapter<ReturnType>::StaticCall)(
113                                     ctx.ClassRef(), sID, jargs)
114                               : (env->*TypeAdapter<ReturnType>::Call)(
115                                     ctx.Get(), sID, jargs));
116 
117     EndAccess(ctx, rv);
118     return result;
119   }
120 };
121 
122 // Define sID member.
123 template <class T, typename R>
124 jmethodID Method<T, R>::sID;
125 
126 // Specialize void because C++ forbids us from
127 // using a "void" temporary result variable.
128 template <class Traits>
129 class Method<Traits, void> : public Method<Traits, bool> {
130   typedef Method<Traits, bool> Base;
131   typedef typename Traits::Owner::Context Context;
132 
133  public:
134   template <typename... Args>
Call(const Context & ctx,nsresult * rv,const Args &...args)135   static void Call(const Context& ctx, nsresult* rv, const Args&... args) {
136     JNIEnv* const env = ctx.Env();
137     Base::BeginAccess(ctx);
138 
139     jvalue jargs[] = {Value(TypeAdapter<Args>::FromNative(env, args)).val...};
140 
141     if (Traits::isStatic) {
142       env->CallStaticVoidMethodA(ctx.ClassRef(), Base::sID, jargs);
143     } else {
144       env->CallVoidMethodA(ctx.Get(), Base::sID, jargs);
145     }
146 
147     Base::EndAccess(ctx, rv);
148   }
149 };
150 
151 // Constructor<> is used to construct a JNI instance given a traits class.
152 template <class Traits>
153 class Constructor : protected Method<Traits, typename Traits::ReturnType> {
154   typedef typename Traits::Owner::Context Context;
155   typedef typename Traits::ReturnType ReturnType;
156   typedef Method<Traits, ReturnType> Base;
157 
158  public:
159   template <typename... Args>
Call(const Context & ctx,nsresult * rv,const Args &...args)160   static ReturnType Call(const Context& ctx, nsresult* rv,
161                          const Args&... args) {
162     JNIEnv* const env = ctx.Env();
163     Base::BeginAccess(ctx);
164 
165     jvalue jargs[] = {Value(TypeAdapter<Args>::FromNative(env, args)).val...};
166 
167     auto result = TypeAdapter<ReturnType>::ToNative(
168         env, env->NewObjectA(ctx.ClassRef(), Base::sID, jargs));
169 
170     Base::EndAccess(ctx, rv);
171     return result;
172   }
173 };
174 
175 // Field<> is used to access a JNI field given a traits class.
176 template <class Traits>
177 class Field : public Accessor {
178   typedef Accessor Base;
179   typedef typename Traits::Owner::Context Context;
180   typedef typename Traits::ReturnType GetterType;
181   typedef typename Traits::SetterType SetterType;
182 
183  private:
184   static jfieldID sID;
185 
BeginAccess(const Context & ctx)186   static void BeginAccess(const Context& ctx) {
187     MOZ_ASSERT_JNI_THREAD(Traits::callingThread);
188     static_assert(Traits::dispatchTarget == DispatchTarget::CURRENT,
189                   "Dispatching not supported for field access");
190 
191     if (sID) {
192       return;
193     }
194 
195     if (Traits::isStatic) {
196       MOZ_ALWAYS_TRUE(
197           sID = AndroidBridge::GetStaticFieldID(
198               ctx.Env(), ctx.ClassRef(), Traits::name, Traits::signature));
199     } else {
200       MOZ_ALWAYS_TRUE(sID = AndroidBridge::GetFieldID(ctx.Env(), ctx.ClassRef(),
201                                                       Traits::name,
202                                                       Traits::signature));
203     }
204   }
205 
EndAccess(const Context & ctx,nsresult * rv)206   static void EndAccess(const Context& ctx, nsresult* rv) {
207     return Base::EndAccess<Traits>(ctx, rv);
208   }
209 
210  public:
Get(const Context & ctx,nsresult * rv)211   static GetterType Get(const Context& ctx, nsresult* rv) {
212     JNIEnv* const env = ctx.Env();
213     BeginAccess(ctx);
214 
215     auto result = TypeAdapter<GetterType>::ToNative(
216         env, Traits::isStatic
217                  ?
218 
219                  (env->*TypeAdapter<GetterType>::StaticGet)(ctx.ClassRef(), sID)
220                  :
221 
222                  (env->*TypeAdapter<GetterType>::Get)(ctx.Get(), sID));
223 
224     EndAccess(ctx, rv);
225     return result;
226   }
227 
Set(const Context & ctx,nsresult * rv,SetterType val)228   static void Set(const Context& ctx, nsresult* rv, SetterType val) {
229     JNIEnv* const env = ctx.Env();
230     BeginAccess(ctx);
231 
232     if (Traits::isStatic) {
233       (env->*TypeAdapter<SetterType>::StaticSet)(
234           ctx.ClassRef(), sID, TypeAdapter<SetterType>::FromNative(env, val));
235     } else {
236       (env->*TypeAdapter<SetterType>::Set)(
237           ctx.Get(), sID, TypeAdapter<SetterType>::FromNative(env, val));
238     }
239 
240     EndAccess(ctx, rv);
241   }
242 };
243 
244 // Define sID member.
245 template <class T>
246 jfieldID Field<T>::sID;
247 
248 }  // namespace jni
249 }  // namespace mozilla
250 
251 #endif  // mozilla_jni_Accessors_h__
252