1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2023 Google LLC. All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7
8 #include <string>
9
10 #include "absl/log/absl_check.h"
11 #include "absl/strings/string_view.h"
12 #include "google/protobuf/compiler/cpp/helpers.h"
13 #include "google/protobuf/compiler/rust/accessors/accessor_case.h"
14 #include "google/protobuf/compiler/rust/accessors/generator.h"
15 #include "google/protobuf/compiler/rust/accessors/with_presence.h"
16 #include "google/protobuf/compiler/rust/context.h"
17 #include "google/protobuf/compiler/rust/naming.h"
18 #include "google/protobuf/compiler/rust/upb_helpers.h"
19 #include "google/protobuf/descriptor.h"
20
21 namespace google {
22 namespace protobuf {
23 namespace compiler {
24 namespace rust {
25
InMsgImpl(Context & ctx,const FieldDescriptor & field,AccessorCase accessor_case) const26 void SingularMessage::InMsgImpl(Context& ctx, const FieldDescriptor& field,
27 AccessorCase accessor_case) const {
28 if (field.has_presence()) {
29 WithPresenceAccessorsInMsgImpl(ctx, field, accessor_case);
30 }
31
32 // fully qualified message name with modules prefixed
33 std::string msg_type = RsTypePath(ctx, field);
34 std::string field_name = FieldNameWithCollisionAvoidance(field);
35 ctx.Emit(
36 {
37 {"msg_type", msg_type},
38 {"field", RsSafeName(field_name)},
39 {"raw_field_name", field_name},
40 {"view_lifetime", ViewLifetime(accessor_case)},
41 {"view_self", ViewReceiver(accessor_case)},
42 {"upb_mt_field_index", UpbMiniTableFieldIndex(field)},
43 {
44 "getter_body",
45 [&] {
46 if (ctx.is_upb()) {
47 ctx.Emit(R"rs(
48 let submsg = unsafe {
49 let f = $pbr$::upb_MiniTable_GetFieldByIndex(
50 <Self as $pbr$::AssociatedMiniTable>::mini_table(),
51 $upb_mt_field_index$);
52 $pbr$::upb_Message_GetMessage(self.raw_msg(), f)
53 };
54 //~ For upb, getters return null if the field is unset, so we need
55 //~ to check for null and return the default instance manually.
56 //~ Note that a nullptr received from upb manifests as Option::None
57 match submsg {
58 //~ TODO:(b/304357029)
59 None => $msg_type$View::new($pbi$::Private, $pbr$::ScratchSpace::zeroed_block()),
60 Some(sub_raw_msg) => $msg_type$View::new($pbi$::Private, sub_raw_msg),
61 }
62 )rs");
63 } else {
64 ctx.Emit({{"getter_thunk", ThunkName(ctx, field, "get")}},
65 R"rs(
66 //~ For C++ kernel, getters automatically return the
67 //~ default_instance if the field is unset.
68 let submsg = unsafe { $getter_thunk$(self.raw_msg()) };
69 $msg_type$View::new($pbi$::Private, submsg)
70 )rs");
71 }
72 },
73 },
74 {"getter",
75 [&] {
76 ctx.Emit(R"rs(
77 pub fn $field$($view_self$) -> $msg_type$View<$view_lifetime$> {
78 $getter_body$
79 }
80 )rs");
81 }},
82 {"getter_mut_body",
83 [&] {
84 if (ctx.is_cpp()) {
85 ctx.Emit(
86 {{"getter_mut_thunk", ThunkName(ctx, field, "get_mut")}},
87 R"rs(
88 let raw_msg = unsafe { $getter_mut_thunk$(self.raw_msg()) };
89 $msg_type$Mut::from_parent($pbi$::Private,
90 self.as_mutator_message_ref($pbi$::Private), raw_msg)
91 )rs");
92 } else {
93 ctx.Emit({}, R"rs(
94 let raw_msg = unsafe {
95 let mt = <Self as $pbr$::AssociatedMiniTable>::mini_table();
96 let f = $pbr$::upb_MiniTable_GetFieldByIndex(mt, $upb_mt_field_index$);
97 $pbr$::upb_Message_GetOrCreateMutableMessage(
98 self.raw_msg(), mt, f, self.arena().raw()).unwrap()
99 };
100 $msg_type$Mut::from_parent($pbi$::Private,
101 self.as_mutator_message_ref($pbi$::Private), raw_msg)
102 )rs");
103 }
104 }},
105 {"getter_mut",
106 [&] {
107 if (accessor_case == AccessorCase::VIEW) {
108 return;
109 }
110
111 ctx.Emit({}, R"rs(
112 pub fn $raw_field_name$_mut(&mut self) -> $msg_type$Mut<'_> {
113 $getter_mut_body$
114 })rs");
115 }},
116 {"setter_body",
117 [&] {
118 if (accessor_case == AccessorCase::VIEW) return;
119 if (ctx.is_upb()) {
120 ctx.Emit(R"rs(
121 // The message and arena are dropped after the setter. The
122 // memory remains allocated as we fuse the arena with the
123 // parent message's arena.
124 let mut msg = val.into_proxied($pbi$::Private);
125 self.as_mutator_message_ref($pbi$::Private)
126 .arena()
127 .fuse(msg.as_mutator_message_ref($pbi$::Private).arena());
128
129 unsafe {
130 let f = $pbr$::upb_MiniTable_GetFieldByIndex(
131 <Self as $pbr$::AssociatedMiniTable>::mini_table(),
132 $upb_mt_field_index$);
133 $pbr$::upb_Message_SetBaseFieldMessage(
134 self.as_mutator_message_ref($pbi$::Private).msg(),
135 f,
136 msg.as_mutator_message_ref($pbi$::Private).msg());
137 }
138 )rs");
139 } else {
140 ctx.Emit({{"set_allocated_thunk", ThunkName(ctx, field, "set")}},
141 R"rs(
142 // Prevent the memory from being deallocated. The setter
143 // transfers ownership of the memory to the parent message.
144 let mut msg = std::mem::ManuallyDrop::new(val.into_proxied($pbi$::Private));
145 unsafe {
146 $set_allocated_thunk$(self.as_mutator_message_ref($pbi$::Private).msg(),
147 msg.as_mutator_message_ref($pbi$::Private).msg());
148 }
149 )rs");
150 }
151 }},
152 {"setter",
153 [&] {
154 if (accessor_case == AccessorCase::VIEW) return;
155 ctx.Emit(R"rs(
156 pub fn set_$raw_field_name$(&mut self,
157 val: impl $pb$::IntoProxied<$msg_type$>) {
158
159 $setter_body$
160 }
161 )rs");
162 }},
163 },
164 R"rs(
165 $getter$
166 $getter_mut$
167 $setter$
168 )rs");
169 }
170
InExternC(Context & ctx,const FieldDescriptor & field) const171 void SingularMessage::InExternC(Context& ctx,
172 const FieldDescriptor& field) const {
173 ABSL_CHECK(ctx.is_cpp());
174
175 if (field.has_presence()) {
176 WithPresenceAccessorsInExternC(ctx, field);
177 }
178
179 ctx.Emit(
180 {
181 {"getter_thunk", ThunkName(ctx, field, "get")},
182 {"getter_mut_thunk", ThunkName(ctx, field, "get_mut")},
183 {"set_allocated_thunk", ThunkName(ctx, field, "set")},
184 {"getter_mut",
185 [&] {
186 if (ctx.is_cpp()) {
187 ctx.Emit(
188 R"rs(
189 fn $getter_mut_thunk$(raw_msg: $pbr$::RawMessage)
190 -> $pbr$::RawMessage;)rs");
191 } else {
192 ctx.Emit(
193 R"rs(fn $getter_mut_thunk$(raw_msg: $pbr$::RawMessage,
194 arena: $pbr$::RawArena)
195 -> $pbr$::RawMessage;)rs");
196 }
197 }},
198 {"ReturnType",
199 [&] {
200 if (ctx.is_cpp()) {
201 // guaranteed to have a nonnull submsg for the cpp kernel
202 ctx.Emit({}, "$pbr$::RawMessage;");
203 } else {
204 // upb kernel may return NULL for a submsg, we can detect this
205 // in terra rust if the option returned is None
206 ctx.Emit({}, "$Option$<$pbr$::RawMessage>;");
207 }
208 }},
209 },
210 R"rs(
211 fn $getter_thunk$(raw_msg: $pbr$::RawMessage) -> $ReturnType$;
212 $getter_mut$
213 fn $set_allocated_thunk$(raw_msg: $pbr$::RawMessage,
214 field_msg: $pbr$::RawMessage);
215 )rs");
216 }
217
InThunkCc(Context & ctx,const FieldDescriptor & field) const218 void SingularMessage::InThunkCc(Context& ctx,
219 const FieldDescriptor& field) const {
220 ABSL_CHECK(ctx.is_cpp());
221 if (field.has_presence()) {
222 WithPresenceAccessorsInThunkCc(ctx, field);
223 }
224
225 ctx.Emit({{"QualifiedMsg", cpp::QualifiedClassName(field.containing_type())},
226 {"FieldMsg", cpp::QualifiedClassName(field.message_type())},
227 {"set_allocated_thunk", ThunkName(ctx, field, "set")},
228 {"getter_thunk", ThunkName(ctx, field, "get")},
229 {"getter_mut_thunk", ThunkName(ctx, field, "get_mut")},
230 {"field", cpp::FieldName(&field)}},
231 R"cc(
232 const void* $getter_thunk$($QualifiedMsg$* msg) {
233 return static_cast<const void*>(&msg->$field$());
234 }
235 void* $getter_mut_thunk$($QualifiedMsg$* msg) {
236 return static_cast<void*>(msg->mutable_$field$());
237 }
238 void $set_allocated_thunk$($QualifiedMsg$* msg, $FieldMsg$* sub_msg) {
239 msg->set_allocated_$field$(sub_msg);
240 }
241 )cc");
242 }
243
244 } // namespace rust
245 } // namespace compiler
246 } // namespace protobuf
247 } // namespace google
248