Skip to content

Commit 667478f

Browse files
wilhuffa-maurice
authored andcommitted
Add support for calling methods and getting fields.
PiperOrigin-RevId: 322398533
1 parent 3ac9e71 commit 667478f

File tree

7 files changed

+442
-0
lines changed

7 files changed

+442
-0
lines changed

firestore/src/jni/call_traits.h

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
#ifndef FIREBASE_FIRESTORE_CLIENT_CPP_SRC_JNI_CALL_TRAITS_H_
2+
#define FIREBASE_FIRESTORE_CLIENT_CPP_SRC_JNI_CALL_TRAITS_H_
3+
4+
#include <jni.h>
5+
6+
#include "firestore/src/jni/traits.h"
7+
8+
namespace firebase {
9+
namespace firestore {
10+
namespace jni {
11+
12+
/**
13+
* Traits describing how to invoke JNI methods uniformly for various JNI return
14+
* types.
15+
*
16+
* By default, uses Object variants (e.g. `CallObjectMethod`), since most types
17+
* will use this form. Only primitives need special forms.
18+
*/
19+
template <typename T>
20+
struct CallTraits {
21+
static constexpr auto kCall = &JNIEnv::CallObjectMethod;
22+
static constexpr auto kGetStaticField = &JNIEnv::GetStaticObjectField;
23+
static constexpr auto kCallStatic = &JNIEnv::CallStaticObjectMethod;
24+
};
25+
26+
template <>
27+
struct CallTraits<jboolean> {
28+
static constexpr auto kCall = &JNIEnv::CallBooleanMethod;
29+
static constexpr auto kGetStaticField = &JNIEnv::GetStaticBooleanField;
30+
static constexpr auto kCallStatic = &JNIEnv::CallStaticBooleanMethod;
31+
};
32+
33+
template <>
34+
struct CallTraits<jbyte> {
35+
static constexpr auto kCall = &JNIEnv::CallByteMethod;
36+
static constexpr auto kGetStaticField = &JNIEnv::GetStaticByteField;
37+
static constexpr auto kCallStatic = &JNIEnv::CallStaticByteMethod;
38+
};
39+
40+
template <>
41+
struct CallTraits<jchar> {
42+
static constexpr auto kCall = &JNIEnv::CallCharMethod;
43+
static constexpr auto kGetStaticField = &JNIEnv::GetStaticCharField;
44+
static constexpr auto kCallStatic = &JNIEnv::CallStaticCharMethod;
45+
};
46+
47+
template <>
48+
struct CallTraits<jshort> {
49+
static constexpr auto kCall = &JNIEnv::CallShortMethod;
50+
static constexpr auto kGetStaticField = &JNIEnv::GetStaticShortField;
51+
static constexpr auto kCallStatic = &JNIEnv::CallStaticShortMethod;
52+
};
53+
54+
template <>
55+
struct CallTraits<jint> {
56+
static constexpr auto kCall = &JNIEnv::CallIntMethod;
57+
static constexpr auto kGetStaticField = &JNIEnv::GetStaticIntField;
58+
static constexpr auto kCallStatic = &JNIEnv::CallStaticIntMethod;
59+
};
60+
61+
template <>
62+
struct CallTraits<jlong> {
63+
static constexpr auto kCall = &JNIEnv::CallLongMethod;
64+
static constexpr auto kGetStaticField = &JNIEnv::GetStaticLongField;
65+
static constexpr auto kCallStatic = &JNIEnv::CallStaticLongMethod;
66+
};
67+
68+
template <>
69+
struct CallTraits<jfloat> {
70+
static constexpr auto kCall = &JNIEnv::CallFloatMethod;
71+
static constexpr auto kGetStaticField = &JNIEnv::GetStaticFloatField;
72+
static constexpr auto kCallStatic = &JNIEnv::CallStaticFloatMethod;
73+
};
74+
75+
template <>
76+
struct CallTraits<jdouble> {
77+
static constexpr auto kCall = &JNIEnv::CallDoubleMethod;
78+
static constexpr auto kGetStaticField = &JNIEnv::GetStaticDoubleField;
79+
static constexpr auto kCallStatic = &JNIEnv::CallStaticDoubleMethod;
80+
};
81+
82+
template <>
83+
struct CallTraits<void> {
84+
static constexpr auto kCall = &JNIEnv::CallVoidMethod;
85+
static constexpr auto kCallStatic = &JNIEnv::CallStaticVoidMethod;
86+
};
87+
88+
/**
89+
* The type of the result of a JNI function. For reference types, it's always
90+
* a `Local` wrapper of the type. For primitive types, it's just the type
91+
* itself.
92+
*/
93+
template <typename T, bool = IsPrimitive<JniType<T>>::value>
94+
struct ResultTypeMap {
95+
using type = T;
96+
};
97+
98+
template <typename T>
99+
struct ResultTypeMap<T, false> {
100+
using type = Local<T>;
101+
};
102+
103+
template <>
104+
struct ResultTypeMap<void, false> {
105+
using type = void;
106+
};
107+
108+
template <typename T>
109+
using ResultType = typename ResultTypeMap<T>::type;
110+
111+
} // namespace jni
112+
} // namespace firestore
113+
} // namespace firebase
114+
115+
#endif // FIREBASE_FIRESTORE_CLIENT_CPP_SRC_JNI_CALL_TRAITS_H_

firestore/src/jni/class.h

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#ifndef FIREBASE_FIRESTORE_CLIENT_CPP_SRC_JNI_CLASS_H_
2+
#define FIREBASE_FIRESTORE_CLIENT_CPP_SRC_JNI_CLASS_H_
3+
4+
#include "firestore/src/jni/object.h"
5+
6+
namespace firebase {
7+
namespace firestore {
8+
namespace jni {
9+
10+
/**
11+
* A wrapper for a JNI `jclass` that adds additional behavior. This is a proxy
12+
* for a Java Class in the JVM.
13+
*
14+
* `Class` merely holds values with `jclass` type, see `Local` and `Global`
15+
* template subclasses for reference-type-aware wrappers that automatically
16+
* manage the lifetime of JNI objects.
17+
*/
18+
class Class : public Object {
19+
public:
20+
Class() = default;
21+
explicit Class(jclass clazz) : Object(clazz) {}
22+
23+
jclass get() const override { return static_cast<jclass>(object_); }
24+
};
25+
26+
} // namespace jni
27+
} // namespace firestore
28+
} // namespace firebase
29+
30+
#endif // FIREBASE_FIRESTORE_CLIENT_CPP_SRC_JNI_CLASS_H_

firestore/src/jni/env.cc

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,39 @@ namespace firebase {
44
namespace firestore {
55
namespace jni {
66

7+
Local<Class> Env::FindClass(const char* name) {
8+
jclass result = env_->FindClass(name);
9+
RecordException();
10+
return Local<Class>(env_, result);
11+
}
12+
13+
jmethodID Env::GetMethodId(const Class& clazz, const char* name,
14+
const char* sig) {
15+
if (!ok()) return nullptr;
16+
17+
jmethodID result = env_->GetMethodID(clazz.get(), name, sig);
18+
RecordException();
19+
return result;
20+
}
21+
22+
jfieldID Env::GetStaticFieldId(const Class& clazz, const char* name,
23+
const char* sig) {
24+
if (!ok()) return nullptr;
25+
26+
jfieldID result = env_->GetStaticFieldID(ToJni(clazz), name, sig);
27+
RecordException();
28+
return result;
29+
}
30+
31+
jmethodID Env::GetStaticMethodId(const Class& clazz, const char* name,
32+
const char* sig) {
33+
if (!ok()) return nullptr;
34+
35+
jmethodID result = env_->GetStaticMethodID(ToJni(clazz), name, sig);
36+
RecordException();
37+
return result;
38+
}
39+
740
Local<String> Env::NewStringUtf(const char* bytes) {
841
if (!ok()) return {};
942

firestore/src/jni/env.h

Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55

66
#include <string>
77

8+
#include "app/meta/move.h"
9+
#include "firestore/src/jni/call_traits.h"
10+
#include "firestore/src/jni/class.h"
811
#include "firestore/src/jni/object.h"
912
#include "firestore/src/jni/ownership.h"
1013
#include "firestore/src/jni/string.h"
@@ -14,6 +17,9 @@ namespace firebase {
1417
namespace firestore {
1518
namespace jni {
1619

20+
// Since we're targeting STLPort, `std::invoke` is not available.
21+
#define INVOKE(env, method, ...) ((env)->*(method))(__VA_ARGS__);
22+
1723
/**
1824
* A wrapper around a JNIEnv pointer that makes dealing with JNI simpler in C++,
1925
* by:
@@ -42,6 +48,119 @@ class Env {
4248
/** Returns the underlying JNIEnv pointer. */
4349
JNIEnv* get() const { return env_; }
4450

51+
// MARK: Class Operations
52+
53+
/**
54+
* Finds the java class associated with the given name which should be
55+
* formatted like "java/lang/Object".
56+
*/
57+
Local<Class> FindClass(const char* name);
58+
59+
// MARK: Object Operations
60+
61+
/**
62+
* Creates a new Java object of the given class, returning the result in a
63+
* reference wrapper of type T.
64+
*
65+
* @tparam T The C++ type to which the method should be coerced.
66+
* @tparam Args The C++ types of the arguments to the method.
67+
* @param clazz The Java class of the resulting object.
68+
* @param method The constructor method to invoke.
69+
* @param args The C++ arguments of the constructor. These will be converted
70+
* to their JNI equivalent value with a call to ToJni before invocation.
71+
* @return a local reference to the newly-created object.
72+
*/
73+
template <typename T = Object, typename... Args>
74+
Local<T> New(const Class& clazz, jmethodID method, Args&&... args) {
75+
if (!ok()) return {};
76+
77+
jobject result =
78+
env_->NewObject(clazz.get(), method, ToJni(Forward<Args>(args))...);
79+
RecordException();
80+
return MakeResult<T>(result);
81+
}
82+
83+
// MARK: Calling Instance Methods
84+
85+
/**
86+
* Finds the method on the given class that's associated with the method name
87+
* and signature.
88+
*/
89+
jmethodID GetMethodId(const Class& clazz, const char* name, const char* sig);
90+
91+
/**
92+
* Invokes the JNI instance method using the `Call*Method` appropriate to the
93+
* return type T.
94+
*
95+
* @tparam T The C++ return type to which the method should be coerced.
96+
* @param object The object to use as `this` for the invocation.
97+
* @param method The method to invoke.
98+
* @param args The C++ arguments of the method. These will be converted to
99+
* their JNI equivalent value with a call to ToJni before invocation.
100+
* @return The primitive result if T is a primitive, nothing if T is `void`,
101+
* or a local reference to the returned object.
102+
*/
103+
template <typename T, typename... Args>
104+
ResultType<T> Call(const Object& object, jmethodID method, Args&&... args) {
105+
auto env_method = CallTraits<JniType<T>>::kCall;
106+
return CallHelper<T>(env_method, object.get(), method,
107+
ToJni(Forward<Args>(args))...);
108+
}
109+
110+
// MARK: Accessing Static Fields
111+
112+
jfieldID GetStaticFieldId(const Class& clazz, const char* name,
113+
const char* sig);
114+
115+
/**
116+
* Returns the value of the given field using the GetStatic*Field method
117+
* appropriate to the field type T.
118+
*
119+
* @tparam T The C++ type to which the field value should be coerced.
120+
* @param clazz The class to use as the container for the field.
121+
* @param field The field to get.
122+
* @return a local reference to the field value.
123+
*/
124+
template <typename T = Object>
125+
ResultType<T> GetStaticField(const Class& clazz, jfieldID field) {
126+
if (!ok()) return {};
127+
128+
auto env_method = CallTraits<JniType<T>>::kGetStaticField;
129+
auto result = INVOKE(env_, env_method, clazz.get(), field);
130+
RecordException();
131+
return MakeResult<T>(result);
132+
}
133+
134+
// MARK: Calling Static Methods
135+
136+
/**
137+
* Finds the method on the given class that's associated with the method name
138+
* and signature.
139+
*/
140+
jmethodID GetStaticMethodId(const Class& clazz, const char* name,
141+
const char* sig);
142+
143+
/**
144+
* Invokes the JNI static method using the CallStatic*Method appropriate to
145+
* the return type T.
146+
*
147+
* @tparam T The C++ return type to which the method should be coerced.
148+
* @tparam Args The C++ types of the arguments to the method.
149+
* @param clazz The class against which to invoke the static method.
150+
* @param method The method to invoke.
151+
* @param args The C++ arguments of the method. These will be converted to
152+
* their JNI equivalent value with a call to ToJni before invocation.
153+
* @return The primitive result if T is a primitive, nothing if T is `void`,
154+
* or a local reference to the returned object.
155+
*/
156+
template <typename T, typename... Args>
157+
ResultType<T> CallStatic(const Class& clazz, jmethodID method,
158+
Args&&... args) {
159+
auto env_method = CallTraits<JniType<T>>::kCallStatic;
160+
return CallHelper<T>(env_method, clazz.get(), method,
161+
ToJni(Forward<Args>(args))...);
162+
}
163+
45164
// MARK: String Operations
46165

47166
/**
@@ -74,12 +193,66 @@ class Env {
74193
}
75194

76195
private:
196+
/**
197+
* Invokes the JNI instance method using the given method reference on JNIEnv.
198+
*
199+
* @tparam T The non-void C++ return type to which the method's result should
200+
* be coerced.
201+
* @param env_method A method reference from JNIEnv, appropriate for the
202+
* return type T, and the kind of method being invoked (instance or
203+
* static). Use `CallTraits<JniType<T>>::kCall` or `kCallStatic` to find
204+
* the right method.
205+
* @param args The method and JNI arguments of the JNI method, including the
206+
* class or object, jmethodID, and any arguments to pass.
207+
* @return The primitive result if T is a primitive or a local reference to
208+
* the returned object.
209+
*/
210+
template <typename T, typename M, typename... Args>
211+
typename enable_if<!is_same<T, void>::value, ResultType<T>>::type CallHelper(
212+
M&& env_method, Args&&... args) {
213+
if (!ok()) return {};
214+
215+
auto result = INVOKE(env_, env_method, Forward<Args>(args)...);
216+
RecordException();
217+
return MakeResult<T>(result);
218+
}
219+
220+
/**
221+
* Invokes a JNI call method if the return type is `void`.
222+
*
223+
* If `T` is anything but `void`, the overload is disabled.
224+
*/
225+
template <typename T, typename M, typename... Args>
226+
typename enable_if<is_same<T, void>::value, void>::type CallHelper(
227+
M&& env_method, Args&&... args) {
228+
if (!ok()) return;
229+
230+
INVOKE(env_, env_method, Forward<Args>(args)...);
231+
RecordException();
232+
}
233+
77234
void RecordException();
78235

236+
template <typename T>
237+
EnableForPrimitive<T, T> MakeResult(JniType<T> value) {
238+
return static_cast<T>(value);
239+
}
240+
241+
template <typename T>
242+
EnableForReference<T, Local<T>> MakeResult(jobject object) {
243+
// JNI object method results are always jobject, even when the actual type
244+
// is jstring or jclass. Cast to the correct type here so that Local<T>
245+
// doesn't have to account for this.
246+
auto typed_object = static_cast<JniType<T>>(object);
247+
return Local<T>(env_, typed_object);
248+
}
249+
79250
JNIEnv* env_ = nullptr;
80251
jthrowable last_exception_ = nullptr;
81252
};
82253

254+
#undef INVOKE
255+
83256
} // namespace jni
84257
} // namespace firestore
85258
} // namespace firebase

0 commit comments

Comments
 (0)