Skip to content

Commit 10f6e78

Browse files
committed
Add simple trampoline state assertions.
1 parent eb1787c commit 10f6e78

File tree

2 files changed

+64
-6
lines changed

2 files changed

+64
-6
lines changed

tests/test_potentially_slicing_shared_ptr.cpp

+50-6
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,50 @@ struct VirtBase {
1818
using VirtBaseSH = VirtBase<0>; // for testing with py::smart_holder
1919
using VirtBaseSP = VirtBase<1>; // for testing with std::shared_ptr as holder
2020

21-
struct PyVirtBaseSH : VirtBaseSH, py::trampoline_self_life_support {
21+
// Similar to trampoline_self_life_support
22+
struct trampoline_is_alive_simple {
23+
std::uint64_t magic_token = 197001010000u;
24+
25+
trampoline_is_alive_simple() = default;
26+
27+
~trampoline_is_alive_simple() { magic_token = 20380118191407u; }
28+
29+
trampoline_is_alive_simple(const trampoline_is_alive_simple &other) {
30+
magic_token = other.magic_token;
31+
}
32+
trampoline_is_alive_simple(trampoline_is_alive_simple &&other) noexcept {
33+
magic_token = other.magic_token;
34+
other.magic_token = 20380118191407u;
35+
}
36+
37+
trampoline_is_alive_simple &operator=(const trampoline_is_alive_simple &) = delete;
38+
trampoline_is_alive_simple &operator=(trampoline_is_alive_simple &&) = delete;
39+
};
40+
41+
template <typename VB>
42+
const char *determine_trampoline_state(const std::shared_ptr<VB> &sp) {
43+
if (!sp) {
44+
return "sp nullptr";
45+
}
46+
auto *tias = dynamic_cast<trampoline_is_alive_simple *>(sp.get());
47+
if (!tias) {
48+
return "dynamic_cast failed";
49+
}
50+
if (tias->magic_token == 197001010000u) {
51+
return "trampoline alive";
52+
}
53+
if (tias->magic_token == 20380118191407u) {
54+
return "trampoline DEAD";
55+
}
56+
return "UNDEFINED BEHAVIOR";
57+
}
58+
59+
struct PyVirtBaseSH : VirtBaseSH, py::trampoline_self_life_support, trampoline_is_alive_simple {
2260
using VirtBaseSH::VirtBaseSH;
2361
int get_code() override { PYBIND11_OVERRIDE(int, VirtBaseSH, get_code); }
2462
};
2563

26-
struct PyVirtBaseSP : VirtBaseSP { // self-life-support not available
64+
struct PyVirtBaseSP : VirtBaseSP, trampoline_is_alive_simple { // self-life-support not available
2765
using VirtBaseSP::VirtBaseSP;
2866
int get_code() override { PYBIND11_OVERRIDE(int, VirtBaseSP, get_code); }
2967
};
@@ -42,13 +80,15 @@ template <typename VB>
4280
struct SpOwner {
4381
void set_sp(const std::shared_ptr<VB> &sp_) { sp = sp_; }
4482

45-
int get_code() {
83+
int get_code() const {
4684
if (!sp) {
4785
return -888;
4886
}
4987
return sp->get_code();
5088
}
5189

90+
const char *get_trampoline_state() const { return determine_trampoline_state(sp); }
91+
5292
private:
5393
std::shared_ptr<VB> sp;
5494
};
@@ -57,14 +97,16 @@ template <typename VB>
5797
struct WpOwner {
5898
void set_wp(const std::shared_ptr<VB> &sp) { wp = sp; }
5999

60-
int get_code() {
100+
int get_code() const {
61101
auto sp = wp.lock();
62102
if (!sp) {
63103
return -999;
64104
}
65105
return sp->get_code();
66106
}
67107

108+
const char *get_trampoline_state() const { return determine_trampoline_state(wp.lock()); }
109+
68110
private:
69111
std::weak_ptr<VB> wp;
70112
};
@@ -81,7 +123,8 @@ void wrap(py::module_ &m,
81123
py::classh<SpOwner<VB>>(m, spo_pyname)
82124
.def(py::init<>())
83125
.def("set_sp", &SpOwner<VB>::set_sp)
84-
.def("get_code", &SpOwner<VB>::get_code);
126+
.def("get_code", &SpOwner<VB>::get_code)
127+
.def("get_trampoline_state", &SpOwner<VB>::get_trampoline_state);
85128

86129
py::classh<WpOwner<VB>>(m, wpo_pyname)
87130
.def(py::init<>())
@@ -90,7 +133,8 @@ void wrap(py::module_ &m,
90133
[](WpOwner<VB> &self, py::handle obj) {
91134
self.set_wp(py::potentially_slicing_shared_ptr<VB>(obj));
92135
})
93-
.def("get_code", &WpOwner<VB>::get_code);
136+
.def("get_code", &WpOwner<VB>::get_code)
137+
.def("get_trampoline_state", &WpOwner<VB>::get_trampoline_state);
94138
}
95139

96140
} // namespace potentially_slicing_shared_ptr

tests/test_potentially_slicing_shared_ptr.py

+14
Original file line numberDiff line numberDiff line change
@@ -71,12 +71,17 @@ def test_rtrn_obj_cast_shared_ptr(holder_kind, rtrn_kind, expected_code):
7171
def test_with_sp_owner(holder_kind, expected_code):
7272
spo = SP_OWNER_TYPES[holder_kind]()
7373
assert spo.get_code() == -888
74+
assert spo.get_trampoline_state() == "sp nullptr"
7475

7576
obj = VIRT_BASE_TYPES[holder_kind][expected_code]()
7677
assert obj.get_code() == expected_code
7778

7879
spo.set_sp(obj)
7980
assert spo.get_code() == expected_code
81+
expected_trampoline_state = (
82+
"dynamic_cast failed" if expected_code == 100 else "trampoline alive"
83+
)
84+
assert spo.get_trampoline_state() == expected_trampoline_state
8085

8186
del obj
8287
gc.collect()
@@ -86,6 +91,7 @@ def test_with_sp_owner(holder_kind, expected_code):
8691
assert (
8792
spo.get_code() == 100
8893
) # see issue #1333 (inheritance slicing) and PR #5624
94+
assert spo.get_trampoline_state() == expected_trampoline_state
8995

9096

9197
@pytest.mark.parametrize("expected_code", [100, 200])
@@ -94,6 +100,7 @@ def test_with_sp_owner(holder_kind, expected_code):
94100
def test_with_wp_owner(holder_kind, set_meth, expected_code):
95101
wpo = WP_OWNER_TYPES[holder_kind]()
96102
assert wpo.get_code() == -999
103+
assert wpo.get_trampoline_state() == "sp nullptr"
97104

98105
obj = VIRT_BASE_TYPES[holder_kind][expected_code]()
99106
assert obj.get_code() == expected_code
@@ -107,6 +114,13 @@ def test_with_wp_owner(holder_kind, set_meth, expected_code):
107114
assert wpo.get_code() == expected_code
108115
else:
109116
assert wpo.get_code() == -999 # see issue #5623 (weak_ptr expired) and PR #5624
117+
if expected_code == 100:
118+
expected_trampoline_state = "dynamic_cast failed"
119+
elif holder_kind == "SH" and set_meth == "set_wp":
120+
expected_trampoline_state = "sp nullptr"
121+
else:
122+
expected_trampoline_state = "trampoline alive"
123+
assert wpo.get_trampoline_state() == expected_trampoline_state
110124

111125
del obj
112126
gc.collect()

0 commit comments

Comments
 (0)