Skip to content

Commit cb36be5

Browse files
wilhuffa-maurice
authored andcommitted
Add JNI Object wrapper and type conversion traits
This is the first in a series of commits that aims to convert our JNI usage to a more modern approach while still remaining STLPort compatible. PiperOrigin-RevId: 321059370
1 parent 0490b4b commit cb36be5

File tree

6 files changed

+273
-0
lines changed

6 files changed

+273
-0
lines changed

app/src/include/firebase/internal/type_traits.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,9 @@ struct is_lvalue_reference<T&> {
8585
template <typename T>
8686
using decay = FIREBASE_TYPE_TRAITS_NS::decay<T>;
8787

88+
template <typename T>
89+
using decay_t = typename decay<T>::type;
90+
8891
template <bool value, typename T = void>
8992
using enable_if = FIREBASE_TYPE_TRAITS_NS::enable_if<value, T>;
9093

@@ -100,6 +103,9 @@ using is_same = FIREBASE_TYPE_TRAITS_NS::is_same<T, U>;
100103
template <typename T, T value>
101104
using integral_constant = FIREBASE_TYPE_TRAITS_NS::integral_constant<T, value>;
102105

106+
using true_type = FIREBASE_TYPE_TRAITS_NS::true_type;
107+
using false_type = FIREBASE_TYPE_TRAITS_NS::false_type;
108+
103109
#undef FIREBASE_TYPE_TRAITS_NS
104110

105111
// `is_char<T>::value` is true iff `T` is a character type (including `wchar_t`

firestore/src/jni/object.cc

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#include "firestore/src/jni/object.h"
2+
3+
#include "app/src/util_android.h"
4+
5+
namespace firebase {
6+
namespace firestore {
7+
namespace jni {
8+
9+
std::string Object::ToString(JNIEnv* env) const {
10+
return util::JniObjectToString(env, object_);
11+
}
12+
13+
} // namespace jni
14+
} // namespace firestore
15+
} // namespace firebase

firestore/src/jni/object.h

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#ifndef FIREBASE_FIRESTORE_CLIENT_CPP_SRC_JNI_OBJECT_H_
2+
#define FIREBASE_FIRESTORE_CLIENT_CPP_SRC_JNI_OBJECT_H_
3+
4+
#include <jni.h>
5+
6+
#include <string>
7+
8+
namespace firebase {
9+
namespace firestore {
10+
namespace jni {
11+
12+
/**
13+
* A wrapper for a JNI `jobject` that adds additional behavior.
14+
*
15+
* `Object` merely holds values with `jobject` types, see `Local` and `Global`
16+
* template subclasses for reference-type-aware wrappers that automatically
17+
* manage the lifetime of JNI objects.
18+
*/
19+
class Object {
20+
public:
21+
Object() = default;
22+
explicit Object(jobject object) : object_(object) {}
23+
virtual ~Object() = default;
24+
25+
explicit operator bool() const { return object_ != nullptr; }
26+
27+
virtual jobject get() const { return object_; }
28+
29+
/**
30+
* Converts this object to a C++ String by calling the Java `toString` method
31+
* on it.
32+
*/
33+
std::string ToString(JNIEnv* env) const;
34+
35+
protected:
36+
jobject object_ = nullptr;
37+
};
38+
39+
inline bool operator==(const Object& lhs, const Object& rhs) {
40+
return lhs.get() == rhs.get();
41+
}
42+
43+
inline bool operator!=(const Object& lhs, const Object& rhs) {
44+
return !(lhs == rhs);
45+
}
46+
47+
} // namespace jni
48+
} // namespace firestore
49+
} // namespace firebase
50+
51+
#endif // FIREBASE_FIRESTORE_CLIENT_CPP_SRC_JNI_OBJECT_H_

firestore/src/jni/traits.h

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
#ifndef FIREBASE_FIRESTORE_CLIENT_CPP_SRC_JNI_TRAITS_H_
2+
#define FIREBASE_FIRESTORE_CLIENT_CPP_SRC_JNI_TRAITS_H_
3+
4+
#include <jni.h>
5+
6+
#include "app/src/include/firebase/internal/type_traits.h"
7+
8+
namespace firebase {
9+
namespace firestore {
10+
namespace jni {
11+
12+
class Object;
13+
14+
// clang-format off
15+
16+
// MARK: Primitive and Reference Type traits
17+
18+
template <typename T> struct IsPrimitive : public false_type {};
19+
template <> struct IsPrimitive<jboolean> : public true_type {};
20+
template <> struct IsPrimitive<jbyte> : public true_type {};
21+
template <> struct IsPrimitive<jchar> : public true_type {};
22+
template <> struct IsPrimitive<jshort> : public true_type {};
23+
template <> struct IsPrimitive<jint> : public true_type {};
24+
template <> struct IsPrimitive<jlong> : public true_type {};
25+
template <> struct IsPrimitive<jfloat> : public true_type {};
26+
template <> struct IsPrimitive<jdouble> : public true_type {};
27+
28+
template <typename T> struct IsReference : public false_type {};
29+
template <> struct IsReference<jclass> : public true_type {};
30+
template <> struct IsReference<jobject> : public true_type {};
31+
template <> struct IsReference<jstring> : public true_type {};
32+
template <> struct IsReference<jthrowable> : public true_type {};
33+
template <> struct IsReference<jbyteArray> : public true_type {};
34+
template <> struct IsReference<jobjectArray> : public true_type {};
35+
36+
37+
// MARK: Type mapping
38+
39+
// A compile-time map from C++ types to their JNI equivalents.
40+
template <typename T> struct JniTypeMap {};
41+
template <> struct JniTypeMap<bool> { using type = jboolean; };
42+
template <> struct JniTypeMap<uint8_t> { using type = jbyte; };
43+
template <> struct JniTypeMap<uint16_t> { using type = jchar; };
44+
template <> struct JniTypeMap<int16_t> { using type = jshort; };
45+
template <> struct JniTypeMap<int32_t> { using type = jint; };
46+
template <> struct JniTypeMap<int64_t> { using type = jlong; };
47+
template <> struct JniTypeMap<float> { using type = jfloat; };
48+
template <> struct JniTypeMap<double> { using type = jdouble; };
49+
template <> struct JniTypeMap<size_t> { using type = jsize; };
50+
51+
template <> struct JniTypeMap<jobject> { using type = jobject; };
52+
53+
template <> struct JniTypeMap<Object> { using type = jobject; };
54+
55+
template <typename T>
56+
using JniType = typename JniTypeMap<decay_t<T>>::type;
57+
58+
// clang-format on
59+
60+
// MARK: Enable If Helpers
61+
62+
template <typename T, typename R = T>
63+
using EnableForPrimitive =
64+
typename enable_if<IsPrimitive<JniType<T>>::value, R>::type;
65+
66+
template <typename T, typename R = T>
67+
using EnableForReference =
68+
typename enable_if<IsReference<JniType<T>>::value, R>::type;
69+
70+
// MARK: Type converters
71+
72+
// Converts C++ primitives to their equivalent JNI primitive types by casting.
73+
template <typename T>
74+
EnableForPrimitive<T, JniType<T>> ToJni(const T& value) {
75+
return static_cast<JniType<T>>(value);
76+
}
77+
78+
// Converts JNI wrapper reference types (like `const Object&`) and any ownership
79+
// wrappers of those types to their underlying `jobject`-derived reference.
80+
template <typename T>
81+
EnableForReference<T, JniType<T>> ToJni(const T& value) {
82+
return value.get();
83+
}
84+
template <typename T, typename J = typename T::jni_type>
85+
J ToJni(const T& value) {
86+
return value.get();
87+
}
88+
89+
// Preexisting JNI types can be passed directly. This makes incremental
90+
// migration possible. Ideally this could eventually be removed.
91+
inline jobject ToJni(jobject value) { return value; }
92+
93+
} // namespace jni
94+
} // namespace firestore
95+
} // namespace firebase
96+
97+
#endif // FIREBASE_FIRESTORE_CLIENT_CPP_SRC_JNI_TRAITS_H_
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#include "firestore/src/jni/object.h"
2+
3+
#include <jni.h>
4+
5+
#include "firestore/src/tests/firestore_integration_test.h"
6+
#include "gtest/gtest.h"
7+
8+
namespace firebase {
9+
namespace firestore {
10+
namespace jni {
11+
12+
class ObjectTest : public FirestoreIntegrationTest {
13+
public:
14+
ObjectTest() : env_(app()->GetJNIEnv()) {}
15+
16+
protected:
17+
JNIEnv* env_ = nullptr;
18+
};
19+
20+
TEST_F(ObjectTest, ToString) {
21+
jclass string_class = env_->FindClass("java/lang/String");
22+
Object wrapper(string_class);
23+
24+
// java.lang.Class defines its toString output as having this form.
25+
EXPECT_EQ("class java.lang.String", wrapper.ToString(env_));
26+
env_->DeleteLocalRef(string_class);
27+
}
28+
29+
} // namespace jni
30+
} // namespace firestore
31+
} // namespace firebase
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#include "firestore/src/jni/traits.h"
2+
3+
#include <jni.h>
4+
5+
#include <limits>
6+
7+
#include "firestore/src/jni/object.h"
8+
#include "firestore/src/tests/firestore_integration_test.h"
9+
#include "gtest/gtest.h"
10+
11+
namespace firebase {
12+
namespace firestore {
13+
namespace jni {
14+
15+
using testing::StaticAssertTypeEq;
16+
17+
using TraitsTest = FirestoreIntegrationTest;
18+
19+
template <typename C, typename J>
20+
void ExpectConvertsPrimitive() {
21+
static_assert(sizeof(C) >= sizeof(J),
22+
"Java types should never be bigger than C++ equivalents");
23+
24+
// Some C++ types (notably size_t) have a size that is not fixed. Use the
25+
// maximum value supported by the Java type for testing.
26+
C cpp_value = static_cast<C>(std::numeric_limits<J>::max());
27+
J jni_value = ToJni(cpp_value);
28+
EXPECT_EQ(jni_value, static_cast<J>(cpp_value));
29+
}
30+
31+
TEST_F(TraitsTest, ConvertsPrimitives) {
32+
ExpectConvertsPrimitive<bool, jboolean>();
33+
ExpectConvertsPrimitive<uint8_t, jbyte>();
34+
ExpectConvertsPrimitive<uint16_t, jchar>();
35+
ExpectConvertsPrimitive<int16_t, jshort>();
36+
ExpectConvertsPrimitive<int32_t, jint>();
37+
ExpectConvertsPrimitive<int64_t, jlong>();
38+
ExpectConvertsPrimitive<float, jfloat>();
39+
ExpectConvertsPrimitive<double, jdouble>();
40+
ExpectConvertsPrimitive<size_t, jsize>();
41+
}
42+
43+
TEST_F(TraitsTest, ConvertsObjects) {
44+
Object cpp_value;
45+
jobject jni_value = ToJni(cpp_value);
46+
EXPECT_EQ(jni_value, nullptr);
47+
48+
jobject jobject_value = nullptr;
49+
jni_value = ToJni(jobject_value);
50+
EXPECT_EQ(jni_value, nullptr);
51+
52+
jni_value = ToJni(nullptr);
53+
EXPECT_EQ(jni_value, nullptr);
54+
}
55+
56+
// Conversion implicitly tests type mapping. Additionally test variations of
57+
// types that should be equivalent.
58+
TEST_F(TraitsTest, DecaysBeforeMappingTypes) {
59+
StaticAssertTypeEq<JniType<int32_t>, jint>();
60+
StaticAssertTypeEq<JniType<const int32_t>, jint>();
61+
62+
StaticAssertTypeEq<JniType<jobject>, jobject>();
63+
StaticAssertTypeEq<JniType<const jobject>, jobject>();
64+
65+
StaticAssertTypeEq<JniType<Object>, jobject>();
66+
StaticAssertTypeEq<JniType<const Object>, jobject>();
67+
StaticAssertTypeEq<JniType<const Object&>, jobject>();
68+
StaticAssertTypeEq<JniType<Object&&>, jobject>();
69+
}
70+
71+
} // namespace jni
72+
} // namespace firestore
73+
} // namespace firebase

0 commit comments

Comments
 (0)