• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/context.h"
16 #include "google/protobuf/compiler/rust/naming.h"
17 #include "google/protobuf/compiler/rust/upb_helpers.h"
18 #include "google/protobuf/descriptor.h"
19 
20 namespace google {
21 namespace protobuf {
22 namespace compiler {
23 namespace rust {
24 
InMsgImpl(Context & ctx,const FieldDescriptor & field,AccessorCase accessor_case) const25 void RepeatedField::InMsgImpl(Context& ctx, const FieldDescriptor& field,
26                               AccessorCase accessor_case) const {
27   std::string field_name = FieldNameWithCollisionAvoidance(field);
28   ctx.Emit(
29       {
30           {"field", RsSafeName(field_name)},
31           {"raw_field_name", field_name},  // Never r# prefixed
32           {"RsType", RsTypePath(ctx, field)},
33           {"view_lifetime", ViewLifetime(accessor_case)},
34           {"view_self", ViewReceiver(accessor_case)},
35           {"upb_mt_field_index", UpbMiniTableFieldIndex(field)},
36           {"getter",
37            [&] {
38              if (ctx.is_upb()) {
39                ctx.Emit(R"rs(
40                     pub fn $field$($view_self$) -> $pb$::RepeatedView<$view_lifetime$, $RsType$> {
41                       unsafe {
42                         let f = $pbr$::upb_MiniTable_GetFieldByIndex(
43                           <Self as $pbr$::AssociatedMiniTable>::mini_table(),
44                           $upb_mt_field_index$);
45                         $pbr$::upb_Message_GetArray(
46                           self.raw_msg(), f)
47                       }.map_or_else(
48                           $pbr$::empty_array::<$RsType$>,
49                           |raw| unsafe {
50                             $pb$::RepeatedView::from_raw($pbi$::Private, raw)
51                           }
52                         )
53                     }
54                   )rs");
55              } else {
56                ctx.Emit({{"getter_thunk", ThunkName(ctx, field, "get")}}, R"rs(
57                     pub fn $field$($view_self$) -> $pb$::RepeatedView<$view_lifetime$, $RsType$> {
58                       unsafe {
59                         $pb$::RepeatedView::from_raw(
60                           $pbi$::Private,
61                           $getter_thunk$(self.raw_msg()),
62                         )
63                       }
64                     }
65                   )rs");
66              }
67            }},
68           {"getter_mut",
69            [&] {
70              if (accessor_case == AccessorCase::VIEW) {
71                return;
72              }
73              if (ctx.is_upb()) {
74                ctx.Emit({}, R"rs(
75                     pub fn $field$_mut(&mut self) -> $pb$::RepeatedMut<'_, $RsType$> {
76                       unsafe {
77                         let f = $pbr$::upb_MiniTable_GetFieldByIndex(
78                           <Self as $pbr$::AssociatedMiniTable>::mini_table(),
79                           $upb_mt_field_index$);
80                         let raw_array = $pbr$::upb_Message_GetOrCreateMutableArray(
81                               self.raw_msg(),
82                               f,
83                               self.arena().raw(),
84                             ).unwrap();
85                         $pb$::RepeatedMut::from_inner(
86                           $pbi$::Private,
87                           $pbr$::InnerRepeatedMut::new(
88                             raw_array, self.arena(),
89                           ),
90                         )
91                       }
92                     }
93                   )rs");
94              } else {
95                ctx.Emit(
96                    {{"getter_mut_thunk", ThunkName(ctx, field, "get_mut")}},
97                    R"rs(
98                       pub fn $field$_mut(&mut self) -> $pb$::RepeatedMut<'_, $RsType$> {
99                         unsafe {
100                           $pb$::RepeatedMut::from_inner(
101                             $pbi$::Private,
102                             $pbr$::InnerRepeatedMut::new(
103                               $getter_mut_thunk$(self.raw_msg()),
104                             ),
105                           )
106                         }
107                       }
108                     )rs");
109              }
110            }},
111           {"setter",
112            [&] {
113              if (accessor_case == AccessorCase::VIEW) {
114                return;
115              }
116              if (ctx.is_upb()) {
117                ctx.Emit(R"rs(
118                     pub fn set_$raw_field_name$(&mut self, src: impl $pb$::IntoProxied<$pb$::Repeated<$RsType$>>) {
119                       let minitable_field = unsafe {
120                         $pbr$::upb_MiniTable_GetFieldByIndex(
121                           <Self as $pbr$::AssociatedMiniTable>::mini_table(),
122                           $upb_mt_field_index$
123                         )
124                       };
125                       let val = src.into_proxied($pbi$::Private);
126                       let inner = val.inner($pbi$::Private);
127 
128                       self.arena().fuse(inner.arena());
129                       unsafe {
130                           let value_ptr: *const *const std::ffi::c_void =
131                               &(inner.raw().as_ptr() as *const std::ffi::c_void);
132                           $pbr$::upb_Message_SetBaseField(self.raw_msg(),
133                             minitable_field,
134                             value_ptr as *const std::ffi::c_void);
135                       }
136                     }
137                   )rs");
138              } else {
139                ctx.Emit(
140                    {{"move_setter_thunk", ThunkName(ctx, field, "move_set")}},
141                    R"rs(
142                       pub fn set_$raw_field_name$(&mut self, src: impl $pb$::IntoProxied<$pb$::Repeated<$RsType$>>) {
143                         // Prevent the memory from being deallocated. The setter
144                         // transfers ownership of the memory to the parent message.
145                         let val = std::mem::ManuallyDrop::new(src.into_proxied($pbi$::Private));
146                         unsafe {
147                           $move_setter_thunk$(self.raw_msg(),
148                             val.inner($pbi$::Private).raw());
149                         }
150                       }
151                     )rs");
152              }
153            }},
154       },
155       R"rs(
156           $getter$
157           $getter_mut$
158           $setter$
159         )rs");
160 }
161 
InExternC(Context & ctx,const FieldDescriptor & field) const162 void RepeatedField::InExternC(Context& ctx,
163                               const FieldDescriptor& field) const {
164   ABSL_CHECK(ctx.is_cpp());
165 
166   ctx.Emit({{"getter_thunk", ThunkName(ctx, field, "get")},
167             {"getter_mut_thunk", ThunkName(ctx, field, "get_mut")},
168             {"move_setter_thunk", ThunkName(ctx, field, "move_set")},
169             {"getter",
170              [&] {
171                if (ctx.is_upb()) {
172                  ctx.Emit(R"rs(
173                     fn $getter_mut_thunk$(
174                       raw_msg: $pbr$::RawMessage,
175                       size: *const usize,
176                       arena: $pbr$::RawArena,
177                     ) -> $pbr$::RawRepeatedField;
178                     //  Returns `None` when returned array pointer is NULL.
179                     fn $getter_thunk$(
180                       raw_msg: $pbr$::RawMessage,
181                       size: *const usize,
182                     ) -> $Option$<$pbr$::RawRepeatedField>;
183                   )rs");
184                } else {
185                  ctx.Emit(R"rs(
186                     fn $getter_mut_thunk$(raw_msg: $pbr$::RawMessage) -> $pbr$::RawRepeatedField;
187                     fn $getter_thunk$(raw_msg: $pbr$::RawMessage) -> $pbr$::RawRepeatedField;
188                     fn $move_setter_thunk$(raw_msg: $pbr$::RawMessage, value: $pbr$::RawRepeatedField);
189                   )rs");
190                }
191              }}},
192            R"rs(
193           $getter$
194         )rs");
195 }
196 
IsRepeatedPrimitive(const FieldDescriptor & field)197 bool IsRepeatedPrimitive(const FieldDescriptor& field) {
198   return field.cpp_type() == FieldDescriptor::CPPTYPE_ENUM ||
199          field.cpp_type() == FieldDescriptor::CPPTYPE_BOOL ||
200          field.cpp_type() == FieldDescriptor::CPPTYPE_DOUBLE ||
201          field.cpp_type() == FieldDescriptor::CPPTYPE_FLOAT ||
202          field.cpp_type() == FieldDescriptor::CPPTYPE_INT32 ||
203          field.cpp_type() == FieldDescriptor::CPPTYPE_INT64 ||
204          field.cpp_type() == FieldDescriptor::CPPTYPE_UINT32 ||
205          field.cpp_type() == FieldDescriptor::CPPTYPE_UINT64;
206 }
207 
IsRepeatedPtrPrimitive(const FieldDescriptor & field)208 bool IsRepeatedPtrPrimitive(const FieldDescriptor& field) {
209   return field.cpp_type() == FieldDescriptor::CPPTYPE_STRING;
210 }
211 
CppElementType(const FieldDescriptor & field)212 std::string CppElementType(const FieldDescriptor& field) {
213   if (IsRepeatedPrimitive(field) || IsRepeatedPtrPrimitive(field)) {
214     return cpp::PrimitiveTypeName(field.cpp_type());
215   } else {
216     return cpp::QualifiedClassName(field.message_type());
217   }
218 }
219 
CppRepeatedContainerType(const FieldDescriptor & field)220 const char* CppRepeatedContainerType(const FieldDescriptor& field) {
221   if (IsRepeatedPrimitive(field)) {
222     return "google::protobuf::RepeatedField";
223   } else {
224     return "google::protobuf::RepeatedPtrField";
225   }
226 }
227 
InThunkCc(Context & ctx,const FieldDescriptor & field) const228 void RepeatedField::InThunkCc(Context& ctx,
229                               const FieldDescriptor& field) const {
230   ABSL_CHECK(ctx.is_cpp());
231 
232   ctx.Emit({{"field", cpp::FieldName(&field)},
233             {"ElementType", CppElementType(field)},
234             {"ContainerType", CppRepeatedContainerType(field)},
235             {"QualifiedMsg", cpp::QualifiedClassName(field.containing_type())},
236             {"getter_thunk", ThunkName(ctx, field, "get")},
237             {"getter_mut_thunk", ThunkName(ctx, field, "get_mut")},
238             {"repeated_copy_from_thunk",
239              ThunkName(ctx, field, "repeated_copy_from")},
240             {"move_setter_thunk", ThunkName(ctx, field, "move_set")},
241             {"impls",
242              [&] {
243                ctx.Emit(
244                    R"cc(
245                      $ContainerType$<$ElementType$>* $getter_mut_thunk$(
246                          $QualifiedMsg$* msg) {
247                        return msg->mutable_$field$();
248                      }
249                      const $ContainerType$<$ElementType$>* $getter_thunk$(
250                          const $QualifiedMsg$* msg) {
251                        return &msg->$field$();
252                      }
253                      void $move_setter_thunk$(
254                          $QualifiedMsg$* msg,
255                          $ContainerType$<$ElementType$>* value) {
256                        *msg->mutable_$field$() = std::move(*value);
257                        delete value;
258                      }
259                    )cc");
260              }}},
261            "$impls$");
262 }
263 
264 }  // namespace rust
265 }  // namespace compiler
266 }  // namespace protobuf
267 }  // namespace google
268