Skip to content

Commit e5d5400

Browse files
committed
Proxying now returns RefBase objects.
Thrash test.
1 parent eefd622 commit e5d5400

File tree

14 files changed

+7831
-99
lines changed

14 files changed

+7831
-99
lines changed

implementation/BUILD

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ cc_library(
294294
name = "field_ref",
295295
hdrs = ["field_ref.h"],
296296
deps = [
297+
":proxy_temporary",
297298
":default_class_loader",
298299
":field_selection",
299300
":id",
@@ -788,6 +789,7 @@ cc_library(
788789
name = "method_ref",
789790
hdrs = ["overload_ref.h"],
790791
deps = [
792+
":proxy_temporary",
791793
":configuration",
792794
":id_type",
793795
":promotion_mechanics_tags",
@@ -1052,6 +1054,7 @@ cc_library(
10521054
name = "proxy_definitions_string",
10531055
hdrs = ["proxy_definitions_string.h"],
10541056
deps = [
1057+
":proxy_temporary",
10551058
":default_class_loader",
10561059
":forward_declarations",
10571060
":jvm",
@@ -1076,6 +1079,14 @@ cc_test(
10761079
],
10771080
)
10781081

1082+
################################################################################
1083+
# ProxyTemporary.
1084+
################################################################################
1085+
cc_library(
1086+
name = "proxy_temporary",
1087+
hdrs = ["proxy_temporary.h"],
1088+
)
1089+
10791090
################################################################################
10801091
# RefBase.
10811092
################################################################################

implementation/field_ref.h

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,10 @@
2222
#include <cstddef>
2323
#include <type_traits>
2424
#include <utility>
25+
#include <utility>
2526
#include <vector>
2627

28+
#include "implementation/proxy_temporary.h"
2729
#include "implementation/default_class_loader.h"
2830
#include "implementation/field_selection.h"
2931
#include "implementation/id.h"
@@ -115,8 +117,9 @@ class FieldRef {
115117
void Set(T&& value) {
116118
FieldHelper<CDecl_t<typename IdT::RawValT>, IdT::kRank,
117119
IdT::kIsStatic>::SetValue(SelfVal(), GetFieldID(class_ref_),
118-
Proxy_t<T>::ProxyAsArg(
119-
std::forward<T>(value)));
120+
ForwardWithProxyTemporaryStrip(
121+
Proxy_t<T>::ProxyAsArg(
122+
std::forward<T>(value))));
120123
}
121124

122125
private:

implementation/jni_helper/BUILD

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -26,22 +26,6 @@ cc_test(
2626
],
2727
)
2828

29-
################################################################################
30-
# DryRun.
31-
################################################################################
32-
cc_test(
33-
name = "dry_run_test",
34-
srcs = ["dry_run_test.cc"],
35-
defines = [
36-
"DRY_RUN",
37-
"ENABLE_DEBUG_OUTPUT",
38-
],
39-
deps = [
40-
"//:jni_bind",
41-
"@googletest//:gtest_main",
42-
],
43-
)
44-
4529
################################################################################
4630
# Fake Test Constants.
4731
################################################################################

implementation/jni_helper/dry_run_test.cc

Lines changed: 0 additions & 26 deletions
This file was deleted.

implementation/legacy/local_array_string_test.cc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,8 @@ TEST_F(JniTest, Array_LocalVanillaObjectRValuesCanBeSet) {
109109
// In the future, this should drop to 1.
110110
EXPECT_CALL(*env_, FindClass(StrEq("java/lang/String"))).Times(2);
111111

112-
EXPECT_CALL(*env_, DeleteLocalRef(_)).Times(2); // array, in place obj
112+
EXPECT_CALL(*env_, DeleteLocalRef(_)).Times(3); // array, in place obj
113113
EXPECT_CALL(*env_, DeleteLocalRef(Fake<jclass>())).Times(2); // FindClass
114-
EXPECT_CALL(*env_, DeleteLocalRef(Fake<jstring>())).Times(0);
115114

116115
LocalArray<jobject, 1, kJavaLangString> arr{
117116
3, LocalObject<kJavaLangString>{"Foo"}};

implementation/legacy/string_ref_test.cc

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -111,11 +111,10 @@ TEST_F(JniTest, LocalString_CreatesFromStringView) {
111111

112112
// jclass for temp String class reference.
113113
EXPECT_CALL(*env_, DeleteLocalRef(Fake<jclass>()));
114+
// Temporary xref created during construction from NewStringUTF.
115+
EXPECT_CALL(*env_, DeleteLocalRef(Fake<jstring>()));
114116
// The variable str (which is itself an object).
115117
EXPECT_CALL(*env_, DeleteLocalRef(Fake<jobject>()));
116-
// TODO(b/143908983): Currently strings leak one local during proxying.
117-
// Temporary xref created during construction.
118-
// EXPECT_CALL(*env_, DeleteLocalRef(Fake<jstring>()));
119118

120119
LocalString str{std::string_view{char_ptr}};
121120
}
@@ -126,11 +125,11 @@ TEST_F(JniTest, LocalString_CreatesFromString) {
126125

127126
// jclass for temp String class reference.
128127
EXPECT_CALL(*env_, DeleteLocalRef(Fake<jclass>()));
128+
// Temporary xref created during construction from NewStringUTF.
129+
EXPECT_CALL(*env_, DeleteLocalRef(Fake<jstring>()));
129130
// The variable str (which is itself an object).
130131
EXPECT_CALL(*env_, DeleteLocalRef(Fake<jobject>()));
131-
// TODO(b/143908983): Currently strings leak one local during proxying.
132-
// Temporary xref created during construction.
133-
// EXPECT_CALL(*env_, DeleteLocalRef(Fake<jstring>()));
132+
134133
LocalString str{std::string{"TestString"}};
135134
}
136135

implementation/local_array_string_test.cc

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,8 @@ TEST_F(JniTest, Array_LocalVanillaObjectRValuesCanBeSet) {
107107
// In the future, this should drop to 1.
108108
EXPECT_CALL(*env_, FindClass(StrEq("java/lang/String"))).Times(2);
109109

110-
EXPECT_CALL(*env_, DeleteLocalRef(_)).Times(2); // array, in place obj
110+
EXPECT_CALL(*env_, DeleteLocalRef(_)).Times(3); // array, in place obj
111111
EXPECT_CALL(*env_, DeleteLocalRef(Fake<jclass>())).Times(2); // FindClass
112-
EXPECT_CALL(*env_, DeleteLocalRef(Fake<jstring>())).Times(0);
113112

114113
LocalArray<jobject, 1, kJavaLangString> arr{
115114
3, LocalObject<kJavaLangString>{"Foo"}};

implementation/overload_ref.h

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -102,25 +102,33 @@ struct OverloadRef {
102102
if constexpr (std::is_same_v<ReturnProxied, void>) {
103103
return InvokeHelper<void, kRank, kStatic>::Invoke(
104104
object, clazz, mthd,
105-
Proxy_t<Params>::ProxyAsArg(std::forward<Params>(params))...);
105+
ForwardWithProxyTemporaryStrip(Proxy_t<Params>::ProxyAsArg(
106+
std::forward<Params>(params)))...);
106107
} else if constexpr (IdT::kIsConstructor) {
107108
return ReturnProxied{
108109
AdoptLocal{},
109110
LifecycleHelper<jobject, LifecycleType::LOCAL>::Construct(
110111
clazz, mthd,
111-
Proxy_t<Params>::ProxyAsArg(std::forward<Params>(params))...)};
112+
ForwardWithProxyTemporaryStrip(
113+
Proxy_t<Params>::ProxyAsArg(
114+
std::forward<Params>(params)))...)};
112115
} else {
113116
if constexpr (std::is_base_of_v<RefBaseBase, ReturnProxied>) {
114-
return ReturnProxied{
115-
AdoptLocal{},
116-
InvokeHelper<typename ReturnIdT::CDecl, kRank, kStatic>::Invoke(
117-
object, clazz, mthd,
118-
Proxy_t<Params>::ProxyAsArg(std::forward<Params>(params))...)};
117+
return
118+
ReturnProxied{
119+
AdoptLocal{},
120+
InvokeHelper<typename ReturnIdT::CDecl, kRank, kStatic>::Invoke(
121+
object, clazz, mthd,
122+
ForwardWithProxyTemporaryStrip(
123+
Proxy_t<Params>::ProxyAsArg(std::forward<Params>(params)))
124+
...)};
119125
} else {
120126
return static_cast<ReturnProxied>(
121-
InvokeHelper<typename ReturnIdT::CDecl, kRank, kStatic>::Invoke(
122-
object, clazz, mthd,
123-
Proxy_t<Params>::ProxyAsArg(std::forward<Params>(params))...));
127+
InvokeHelper<typename ReturnIdT::CDecl, kRank, kStatic>::Invoke(
128+
object, clazz, mthd,
129+
ForwardWithProxyTemporaryStrip(
130+
Proxy_t<Params>::ProxyAsArg(
131+
std::forward<Params>(params)))...));
124132
}
125133
}
126134
}

implementation/proxy_definitions_string.h

Lines changed: 22 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,12 +32,12 @@
3232
#include "implementation/jni_helper/lifecycle_string.h"
3333
#include "implementation/jvm.h"
3434
#include "implementation/proxy.h"
35+
#include "implementation/proxy_temporary.h"
3536
#include "implementation/proxy_convenience_aliases.h"
3637
#include "implementation/ref_base.h"
3738
#include "jni_dep.h"
3839

3940
namespace jni {
40-
4141
template <typename JString>
4242
struct Proxy<JString,
4343
typename std::enable_if_t<std::is_same_v<JString, jstring>>>
@@ -68,22 +68,36 @@ struct Proxy<JString,
6868
IsConvertibleKey<T>::template value<std::string_view> ||
6969
std::is_same_v<T, LocalString> || std::is_same_v<T, GlobalString>;
7070

71+
static constexpr auto DeleteLambda = [](const jstring& s) {
72+
JniEnv::GetEnv()->DeleteLocalRef(static_cast<jobject>(s));
73+
};
74+
75+
struct DeleteLocalRef {
76+
static void Call(const jstring& s) {
77+
JniEnv::GetEnv()->DeleteLocalRef(static_cast<jobject>(s));
78+
}
79+
};
80+
7181
// These leak local instances of strings. Usually, RAII mechanisms would
7282
// correctly release local instances, but here we are stripping that so it can
7383
// be used in a method. This could be obviated by wrapping the calling scope
7484
// in a local stack frame.
75-
static jstring ProxyAsArg(jstring s) { return s; }
85+
static jstring ProxyAsArg(jstring s) {
86+
return s;
87+
}
7688

89+
// Note: Because a temporary is created `ProxyTemporary` is used to
90+
// guarantee the release of the underlying local after use in `ProxyAsArg`.
7791
template <typename T,
7892
typename = std::enable_if_t<std::is_same_v<T, const char*> ||
7993
std::is_same_v<T, std::string> ||
8094
std::is_same_v<T, std::string_view>>>
81-
static jstring ProxyAsArg(T s) {
95+
static ProxyTemporary<jstring, DeleteLocalRef> ProxyAsArg(T s) {
8296
if constexpr (std::is_same_v<T, const char*>) {
83-
return LifecycleHelper<jstring, LifecycleType::LOCAL>::Construct(s);
97+
return {LifecycleHelper<jstring, LifecycleType::LOCAL>::Construct(s)};
8498
} else {
85-
return LifecycleHelper<jstring, LifecycleType::LOCAL>::Construct(
86-
s.data());
99+
return {LifecycleHelper<jstring, LifecycleType::LOCAL>::Construct(
100+
s.data())};
87101
}
88102
}
89103

@@ -101,7 +115,6 @@ struct Proxy<JString,
101115
return t.Release();
102116
}
103117
};
118+
} // namespace jni
104119

105-
} // namespace jni
106-
107-
#endif // JNI_BIND_IMPLEMENTATION_PROXY_DEFINITIONS_STRING_H_
120+
#endif // JNI_BIND_IMPLEMENTATION_PROXY_DEFINITIONS_STRING_H_

implementation/proxy_temporary.h

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
#ifndef JNI_BIND_IMPLEMENTATION_PROXY_TEMPORARY_H_
2+
#define JNI_BIND_IMPLEMENTATION_PROXY_TEMPORARY_H_
3+
4+
namespace jni {
5+
6+
struct ProxyTemporaryBase {};
7+
8+
// Some values built in proxy_definitions are intended to be ephemeral.
9+
// Because the full decorated LocalObject is not defined, we use this
10+
// class to proxy the value for the duration of `ProxyAsArg`, but allow it to
11+
// immediately be destroyed after the call.
12+
//
13+
// See https://github.com/google/jni-bind/issues/414.
14+
template <typename T, typename DtorLambda>
15+
struct ProxyTemporary : ProxyTemporaryBase {
16+
ProxyTemporary(T t) : t_(t) { }
17+
18+
~ProxyTemporary() {
19+
DtorLambda::Call(t_);
20+
}
21+
22+
const T& Get() const { return t_; }
23+
24+
T t_;
25+
};
26+
27+
28+
namespace detail {
29+
30+
template <typename T>
31+
const auto& ForwardWithProxyTemporaryStripImpl(T&& t, std::true_type /* is_ref_base */) {
32+
return t.Get();
33+
}
34+
35+
template <typename T>
36+
decltype(auto) ForwardWithProxyTemporaryStripImpl(
37+
T&& t, std::false_type /* not ref base */) {
38+
return std::forward<T>(t); // perfect forward
39+
}
40+
41+
} // namespace detail
42+
43+
template <typename T>
44+
decltype(auto) ForwardWithProxyTemporaryStrip(T&& t) {
45+
using U = std::remove_reference_t<T>;
46+
return detail::ForwardWithProxyTemporaryStripImpl(
47+
std::forward<T>(t),
48+
std::is_base_of<ProxyTemporaryBase, U>{});
49+
}
50+
51+
} // namespace jni
52+
53+
#endif // JNI_BIND_IMPLEMENTATION_PROXY_TEMPORARY_H_

0 commit comments

Comments
 (0)