Skip to content

Commit c4731f4

Browse files
wilhuffa-maurice
authored andcommitted
Add initial Env with minimal support for Strings
PiperOrigin-RevId: 321475293
1 parent 44dbea4 commit c4731f4

File tree

9 files changed

+245
-0
lines changed

9 files changed

+245
-0
lines changed

firestore/src/jni/env.cc

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#include "firestore/src/jni/env.h"
2+
3+
namespace firebase {
4+
namespace firestore {
5+
namespace jni {
6+
7+
Local<String> Env::NewStringUtf(const char* bytes) {
8+
if (!ok()) return {};
9+
10+
jstring result = env_->NewStringUTF(bytes);
11+
RecordException();
12+
return Local<String>(env_, result);
13+
}
14+
15+
std::string Env::GetStringUtfRegion(jstring string, size_t start, size_t len) {
16+
if (!ok()) return "";
17+
18+
// Copy directly into the std::string buffer. This is guaranteed to work as
19+
// of C++11, and also happens to work with STLPort.
20+
std::string result;
21+
result.resize(len);
22+
23+
env_->GetStringUTFRegion(string, ToJni(start), ToJni(len), &result[0]);
24+
RecordException();
25+
26+
// Ensure that if there was an exception, the contents of the buffer are
27+
// disregarded.
28+
if (!ok()) return "";
29+
return result;
30+
}
31+
32+
void Env::RecordException() {
33+
if (last_exception_ || !env_->ExceptionCheck()) return;
34+
35+
env_->ExceptionDescribe();
36+
37+
last_exception_ = env_->ExceptionOccurred();
38+
env_->ExceptionClear();
39+
}
40+
41+
} // namespace jni
42+
} // namespace firestore
43+
} // namespace firebase

firestore/src/jni/env.h

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
#ifndef FIREBASE_FIRESTORE_CLIENT_CPP_SRC_JNI_ENV_H_
2+
#define FIREBASE_FIRESTORE_CLIENT_CPP_SRC_JNI_ENV_H_
3+
4+
#include <jni.h>
5+
6+
#include <string>
7+
8+
#include "firestore/src/jni/object.h"
9+
#include "firestore/src/jni/ownership.h"
10+
#include "firestore/src/jni/string.h"
11+
#include "firestore/src/jni/traits.h"
12+
13+
namespace firebase {
14+
namespace firestore {
15+
namespace jni {
16+
17+
/**
18+
* A wrapper around a JNIEnv pointer that makes dealing with JNI simpler in C++,
19+
* by:
20+
*
21+
* * Automatically converting arguments of C++ types to their JNI equivalents;
22+
* * handling C++ strings naturally;
23+
* * wrapping JNI references in `Local` RAII wrappers automatically; and
24+
* * simplifying error handling related to JNI calls (see below).
25+
*
26+
* Normally JNI requires that each call be followed by an explicit check to see
27+
* if an exception happened. This is tedious and clutters the code. Instead,
28+
* `Env` automatically checks for a JNI exception and short circuits any further
29+
* calls. This means that JNI-intensive code can be written straightforwardly
30+
* with a single, final check for errors. Exceptions can still be handled
31+
* inline if required.
32+
*/
33+
class Env {
34+
public:
35+
Env() : env_(GetEnv()) {}
36+
37+
explicit Env(JNIEnv* env) : env_(env) {}
38+
39+
/** Returns true if the Env has not encountered an exception. */
40+
bool ok() const { return last_exception_ == nullptr; }
41+
42+
/** Returns the underlying JNIEnv pointer. */
43+
JNIEnv* get() const { return env_; }
44+
45+
// MARK: String Operations
46+
47+
/**
48+
* Creates a new proxy for a Java String from a sequences of modified UTF-8
49+
* bytes.
50+
*/
51+
Local<String> NewStringUtf(const char* bytes);
52+
Local<String> NewStringUtf(const std::string& bytes) {
53+
return NewStringUtf(bytes.c_str());
54+
}
55+
56+
/** Returns the length of the string in modified UTF-8 bytes. */
57+
size_t GetStringUtfLength(jstring string) {
58+
jsize result = env_->GetStringUTFLength(string);
59+
RecordException();
60+
return static_cast<size_t>(result);
61+
}
62+
size_t GetStringUtfLength(const String& string) {
63+
return GetStringUtfLength(string.get());
64+
}
65+
66+
/**
67+
* Copies the contents of a region of a Java string to a C++ string. The
68+
* resulting string has a modified UTF-8 encoding.
69+
*/
70+
std::string GetStringUtfRegion(jstring string, size_t start, size_t len);
71+
std::string GetStringUtfRegion(const String& string, size_t start,
72+
size_t len) {
73+
return GetStringUtfRegion(string.get(), start, len);
74+
}
75+
76+
private:
77+
void RecordException();
78+
79+
JNIEnv* env_ = nullptr;
80+
jthrowable last_exception_ = nullptr;
81+
};
82+
83+
} // namespace jni
84+
} // namespace firestore
85+
} // namespace firebase
86+
87+
#endif // FIREBASE_FIRESTORE_CLIENT_CPP_SRC_JNI_ENV_H_

firestore/src/jni/jni_fwd.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ namespace jni {
1010
*/
1111
JNIEnv* GetEnv();
1212

13+
class Env;
14+
1315
// Reference types
1416
template <typename T>
1517
class Local;
@@ -19,6 +21,7 @@ template <typename T>
1921
class NonOwning;
2022

2123
class Object;
24+
class String;
2225

2326
} // namespace jni
2427
} // namespace firestore

firestore/src/jni/object.cc

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "firestore/src/jni/object.h"
22

33
#include "app/src/util_android.h"
4+
#include "firestore/src/jni/env.h"
45

56
namespace firebase {
67
namespace firestore {
@@ -10,6 +11,8 @@ std::string Object::ToString(JNIEnv* env) const {
1011
return util::JniObjectToString(env, object_);
1112
}
1213

14+
std::string Object::ToString(Env& env) const { return ToString(env.get()); }
15+
1316
} // namespace jni
1417
} // namespace firestore
1518
} // namespace firebase

firestore/src/jni/object.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ namespace firebase {
99
namespace firestore {
1010
namespace jni {
1111

12+
class Env;
13+
1214
/**
1315
* A wrapper for a JNI `jobject` that adds additional behavior.
1416
*
@@ -31,6 +33,7 @@ class Object {
3133
* on it.
3234
*/
3335
std::string ToString(JNIEnv* env) const;
36+
std::string ToString(Env& env) const;
3437

3538
protected:
3639
jobject object_ = nullptr;

firestore/src/jni/string.cc

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
#include "firestore/src/jni/string.h"
2+
3+
#include "firestore/src/jni/env.h"
4+
5+
namespace firebase {
6+
namespace firestore {
7+
namespace jni {
8+
9+
std::string String::ToString(Env& env) const {
10+
jstring str = get();
11+
size_t len = env.GetStringUtfLength(str);
12+
return env.GetStringUtfRegion(str, 0, len);
13+
}
14+
15+
} // namespace jni
16+
} // namespace firestore
17+
} // namespace firebase

firestore/src/jni/string.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#ifndef FIREBASE_FIRESTORE_CLIENT_CPP_SRC_JNI_STRING_H_
2+
#define FIREBASE_FIRESTORE_CLIENT_CPP_SRC_JNI_STRING_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 `jstring` that adds additional behavior. This is a proxy
12+
* for a Java String in the JVM.
13+
*
14+
* `String` merely holds values with `jstring` 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 String : public Object {
19+
public:
20+
String() = default;
21+
explicit String(jstring string) : Object(string) {}
22+
23+
jstring get() const override { return static_cast<jstring>(object_); }
24+
25+
/** Converts this Java String to a C++ string. */
26+
std::string ToString(Env& env) const;
27+
};
28+
29+
} // namespace jni
30+
} // namespace firestore
31+
} // namespace firebase
32+
33+
#endif // FIREBASE_FIRESTORE_CLIENT_CPP_SRC_JNI_STRING_H_

firestore/src/jni/traits.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,10 @@ template <> struct JniTypeMap<double> { using type = jdouble; };
4848
template <> struct JniTypeMap<size_t> { using type = jsize; };
4949

5050
template <> struct JniTypeMap<jobject> { using type = jobject; };
51+
template <> struct JniTypeMap<jstring> { using type = jstring; };
5152

5253
template <> struct JniTypeMap<Object> { using type = jobject; };
54+
template <> struct JniTypeMap<String> { using type = jstring; };
5355

5456
template <typename T>
5557
using JniType = typename JniTypeMap<decay_t<T>>::type;

firestore/src/tests/jni/env_test.cc

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#include "firestore/src/jni/env.h"
2+
3+
#include "firestore/src/tests/firestore_integration_test.h"
4+
#include "gtest/gtest.h"
5+
6+
namespace firebase {
7+
namespace firestore {
8+
namespace jni {
9+
10+
class EnvTest : public FirestoreIntegrationTest {
11+
public:
12+
EnvTest() : env_(GetEnv()) {}
13+
14+
protected:
15+
Env env_;
16+
};
17+
18+
#if __cpp_exceptions
19+
TEST_F(EnvTest, ToolchainSupportsThrowingFromDestructors) {
20+
class ThrowsInDestructor {
21+
public:
22+
~ThrowsInDestructor() noexcept(false) { throw std::exception(); }
23+
};
24+
25+
try {
26+
{ ThrowsInDestructor obj; }
27+
FAIL() << "Should have thrown";
28+
} catch (const std::exception& e) {
29+
SUCCEED() << "Caught exception";
30+
}
31+
}
32+
#endif // __cpp_exceptions
33+
34+
TEST_F(EnvTest, GetStringUtfLength) {
35+
Local<String> str = env_.NewStringUtf("Foo");
36+
size_t len = env_.GetStringUtfLength(str);
37+
EXPECT_EQ(3, len);
38+
}
39+
40+
TEST_F(EnvTest, GetStringUtfRegion) {
41+
Local<String> str = env_.NewStringUtf("Foo");
42+
std::string result = env_.GetStringUtfRegion(str, 1, 2);
43+
EXPECT_EQ("oo", result);
44+
}
45+
46+
TEST_F(EnvTest, ToString) {
47+
Local<String> str = env_.NewStringUtf("Foo");
48+
std::string result = str.ToString(env_);
49+
EXPECT_EQ("Foo", result);
50+
}
51+
52+
} // namespace jni
53+
} // namespace firestore
54+
} // namespace firebase

0 commit comments

Comments
 (0)