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