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