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