• 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/default_value.h"
15 #include "google/protobuf/compiler/rust/accessors/generator.h"
16 #include "google/protobuf/compiler/rust/accessors/with_presence.h"
17 #include "google/protobuf/compiler/rust/context.h"
18 #include "google/protobuf/compiler/rust/naming.h"
19 #include "google/protobuf/compiler/rust/upb_helpers.h"
20 #include "google/protobuf/descriptor.h"
21 
22 namespace google {
23 namespace protobuf {
24 namespace compiler {
25 namespace rust {
26 
27 namespace {
28 
29 // The upb function to use for the get/set functions, eg `Int32` for the
30 // functions `upb_Message_GetInt32` and upb_Message_SetInt32`.
UpbCTypeNameForFunctions(const FieldDescriptor & field)31 std::string UpbCTypeNameForFunctions(const FieldDescriptor& field) {
32   switch (field.cpp_type()) {
33     case FieldDescriptor::CPPTYPE_INT32:
34       return "Int32";
35     case FieldDescriptor::CPPTYPE_INT64:
36       return "Int64";
37     case FieldDescriptor::CPPTYPE_UINT32:
38       return "UInt32";
39     case FieldDescriptor::CPPTYPE_UINT64:
40       return "UInt64";
41     case FieldDescriptor::CPPTYPE_DOUBLE:
42       return "Double";
43     case FieldDescriptor::CPPTYPE_FLOAT:
44       return "Float";
45     case FieldDescriptor::CPPTYPE_BOOL:
46       return "Bool";
47     case FieldDescriptor::CPPTYPE_ENUM:
48       return "Int32";
49     case FieldDescriptor::CPPTYPE_STRING:
50     case FieldDescriptor::CPPTYPE_MESSAGE:
51       // Handled by a different file.
52       break;
53   }
54   ABSL_CHECK(false) << "Unexpected field type: " << field.cpp_type_name();
55   return "";
56 }
57 
58 }  // namespace
59 
InMsgImpl(Context & ctx,const FieldDescriptor & field,AccessorCase accessor_case) const60 void SingularScalar::InMsgImpl(Context& ctx, const FieldDescriptor& field,
61                                AccessorCase accessor_case) const {
62   if (field.has_presence()) {
63     WithPresenceAccessorsInMsgImpl(ctx, field, accessor_case);
64   }
65 
66   std::string field_name = FieldNameWithCollisionAvoidance(field);
67 
68   ctx.Emit(
69       {
70           {"field", RsSafeName(field_name)},
71           {"raw_field_name", field_name},  // Never r# prefixed
72           {"view_self", ViewReceiver(accessor_case)},
73           {"Scalar", RsTypePath(ctx, field)},
74           {"default_value", DefaultValue(ctx, field)},
75           {"upb_mt_field_index", UpbMiniTableFieldIndex(field)},
76           {"upb_fn_type_name", UpbCTypeNameForFunctions(field)},
77           {"getter",
78            [&] {
79              if (ctx.is_cpp()) {
80                ctx.Emit({{"getter_thunk", ThunkName(ctx, field, "get")}}, R"rs(
81                     pub fn $field$($view_self$) -> $Scalar$ {
82                       unsafe { $getter_thunk$(self.raw_msg()) }
83                     }
84                   )rs");
85              } else {
86                ctx.Emit(
87                    R"rs(
88                     pub fn $field$($view_self$) -> $Scalar$ {
89                       unsafe {
90                         let mt = <Self as $pbr$::AssociatedMiniTable>::mini_table();
91                         let f = $pbr$::upb_MiniTable_GetFieldByIndex(
92                             mt, $upb_mt_field_index$);
93 
94                         // TODO: b/361751487: This .into() and .try_into() is only
95                         // here for the enum<->i32 case, we should avoid it for
96                         // other primitives where the types naturally match
97                         // perfectly (and do an unchecked conversion for
98                         // i32->enum types, since even for closed enums we trust
99                         // upb to only return one of the named values).
100                         $pbr$::upb_Message_Get$upb_fn_type_name$(
101                             self.raw_msg(), f, ($default_value$).into()).try_into().unwrap()
102                       }
103                     }
104                   )rs");
105              }
106            }},
107           {"setter",
108            [&] {
109              if (accessor_case == AccessorCase::VIEW) return;
110              if (ctx.is_cpp()) {
111                ctx.Emit({{"setter_thunk", ThunkName(ctx, field, "set")}}, R"rs(
112                   pub fn set_$raw_field_name$(&mut self, val: $Scalar$) {
113                     unsafe { $setter_thunk$(self.raw_msg(), val) }
114                   }
115                 )rs");
116              } else {
117                ctx.Emit(R"rs(
118                   pub fn set_$raw_field_name$(&mut self, val: $Scalar$) {
119                     unsafe {
120                       let mt = <Self as $pbr$::AssociatedMiniTable>::mini_table();
121                       let f = $pbr$::upb_MiniTable_GetFieldByIndex(
122                           mt, $upb_mt_field_index$);
123                       // TODO: b/361751487: This .into() is only here
124                       // here for the enum<->i32 case, we should avoid it for
125                       // other primitives where the types naturally match
126                       // perfectly.
127                       $pbr$::upb_Message_SetBaseField$upb_fn_type_name$(
128                           self.raw_msg(), f, val.into());
129                     }
130                   }
131                 )rs");
132              }
133            }},
134       },
135       R"rs(
136           $getter$
137           $setter$
138         )rs");
139 }
140 
InExternC(Context & ctx,const FieldDescriptor & field) const141 void SingularScalar::InExternC(Context& ctx,
142                                const FieldDescriptor& field) const {
143   ABSL_CHECK(ctx.is_cpp());
144 
145   if (field.has_presence()) {
146     WithPresenceAccessorsInExternC(ctx, field);
147   }
148 
149   // In order to soundly pass a Rust type to C/C++ as a function argument,
150   // the types must be FFI-compatible.
151   // This requires special consideration for enums, which aren't trivial
152   // primitive types. Rust protobuf enums are defined as `#[repr(transparent)]`
153   // over `i32`, making them ABI-compatible with `int32_t`.
154   // Upb defines enum thunks as taking `int32_t`, and so we can pass Rust enums
155   // directly to thunks without any cast.
156   ctx.Emit({{"Scalar", RsTypePath(ctx, field)},
157             {"getter_thunk", ThunkName(ctx, field, "get")},
158             {"setter_thunk", ThunkName(ctx, field, "set")}},
159            R"rs(
160           fn $getter_thunk$(raw_msg: $pbr$::RawMessage) -> $Scalar$;
161           fn $setter_thunk$(raw_msg: $pbr$::RawMessage, val: $Scalar$);
162         )rs");
163 }
164 
InThunkCc(Context & ctx,const FieldDescriptor & field) const165 void SingularScalar::InThunkCc(Context& ctx,
166                                const FieldDescriptor& field) const {
167   ABSL_CHECK(ctx.is_cpp());
168 
169   if (field.has_presence()) {
170     WithPresenceAccessorsInThunkCc(ctx, field);
171   }
172 
173   std::string scalar;
174   auto enum_ = field.enum_type();
175   if (enum_ != nullptr) {
176     // The C++ runtime defines its thunks as receiving enum types.
177     // This is fine since:
178     // - the C++ runtime represents enums as `int`
179     // - the C++ runtime guarantees `int` is a `int32_t`.
180     // - Rust gencode defines enums as `#[repr(transparent)]` over `i32`.
181     scalar = cpp::QualifiedClassName(enum_);
182   } else {
183     scalar = cpp::PrimitiveTypeName(field.cpp_type());
184   }
185 
186   ctx.Emit({{"field", cpp::FieldName(&field)},
187             {"Scalar", scalar},
188             {"QualifiedMsg", cpp::QualifiedClassName(field.containing_type())},
189             {"getter_thunk", ThunkName(ctx, field, "get")},
190             {"setter_thunk", ThunkName(ctx, field, "set")}},
191            R"cc(
192              $Scalar$ $getter_thunk$($QualifiedMsg$* msg) {
193                return msg->$field$();
194              }
195              void $setter_thunk$($QualifiedMsg$* msg, $Scalar$ val) {
196                msg->set_$field$(val);
197              }
198            )cc");
199 }
200 
201 }  // namespace rust
202 }  // namespace compiler
203 }  // namespace protobuf
204 }  // namespace google
205