| // Protocol Buffers - Google's data interchange format |
| // Copyright 2023 Google LLC. All rights reserved. |
| // |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file or at |
| // https://developers.google.com/open-source/licenses/bsd |
| |
| #include "google/protobuf/compiler/rust/message.h" |
| |
| #include "absl/log/absl_check.h" |
| #include "absl/log/absl_log.h" |
| #include "absl/strings/string_view.h" |
| #include "google/protobuf/compiler/cpp/helpers.h" |
| #include "google/protobuf/compiler/cpp/names.h" |
| #include "google/protobuf/compiler/rust/accessors/accessors.h" |
| #include "google/protobuf/compiler/rust/context.h" |
| #include "google/protobuf/compiler/rust/naming.h" |
| #include "google/protobuf/compiler/rust/oneof.h" |
| #include "google/protobuf/descriptor.h" |
| |
| namespace google { |
| namespace protobuf { |
| namespace compiler { |
| namespace rust { |
| namespace { |
| |
| void MessageNew(Context<Descriptor> msg) { |
| switch (msg.opts().kernel) { |
| case Kernel::kCpp: |
| msg.Emit({{"new_thunk", Thunk(msg, "new")}}, R"rs( |
| Self { inner: $pbr$::MessageInner { msg: unsafe { $new_thunk$() } } } |
| )rs"); |
| return; |
| |
| case Kernel::kUpb: |
| msg.Emit({{"new_thunk", Thunk(msg, "new")}}, R"rs( |
| let arena = $pbr$::Arena::new(); |
| Self { |
| inner: $pbr$::MessageInner { |
| msg: unsafe { $new_thunk$(arena.raw()) }, |
| arena, |
| } |
| } |
| )rs"); |
| return; |
| } |
| |
| ABSL_LOG(FATAL) << "unreachable"; |
| } |
| |
| void MessageSerialize(Context<Descriptor> msg) { |
| switch (msg.opts().kernel) { |
| case Kernel::kCpp: |
| msg.Emit({{"serialize_thunk", Thunk(msg, "serialize")}}, R"rs( |
| unsafe { $serialize_thunk$(self.inner.msg) } |
| )rs"); |
| return; |
| |
| case Kernel::kUpb: |
| msg.Emit({{"serialize_thunk", Thunk(msg, "serialize")}}, R"rs( |
| let arena = $pbr$::Arena::new(); |
| let mut len = 0; |
| unsafe { |
| let data = $serialize_thunk$(self.inner.msg, arena.raw(), &mut len); |
| $pbr$::SerializedData::from_raw_parts(arena, data, len) |
| } |
| )rs"); |
| return; |
| } |
| |
| ABSL_LOG(FATAL) << "unreachable"; |
| } |
| |
| void MessageDeserialize(Context<Descriptor> msg) { |
| switch (msg.opts().kernel) { |
| case Kernel::kCpp: |
| msg.Emit( |
| { |
| {"deserialize_thunk", Thunk(msg, "deserialize")}, |
| }, |
| R"rs( |
| let success = unsafe { |
| let data = $pbr$::SerializedData::from_raw_parts( |
| $NonNull$::new(data.as_ptr() as *mut _).unwrap(), |
| data.len(), |
| ); |
| |
| $deserialize_thunk$(self.inner.msg, data) |
| }; |
| success.then_some(()).ok_or($pb$::ParseError) |
| )rs"); |
| return; |
| |
| case Kernel::kUpb: |
| msg.Emit({{"deserialize_thunk", Thunk(msg, "parse")}}, R"rs( |
| let arena = $pbr$::Arena::new(); |
| let msg = unsafe { |
| $deserialize_thunk$(data.as_ptr(), data.len(), arena.raw()) |
| }; |
| |
| match msg { |
| None => Err($pb$::ParseError), |
| Some(msg) => { |
| // This assignment causes self.arena to be dropped and to deallocate |
| // any previous message pointed/owned to by self.inner.msg. |
| self.inner.arena = arena; |
| self.inner.msg = msg; |
| Ok(()) |
| } |
| } |
| )rs"); |
| return; |
| } |
| |
| ABSL_LOG(FATAL) << "unreachable"; |
| } |
| |
| void MessageExterns(Context<Descriptor> msg) { |
| switch (msg.opts().kernel) { |
| case Kernel::kCpp: |
| msg.Emit( |
| { |
| {"new_thunk", Thunk(msg, "new")}, |
| {"delete_thunk", Thunk(msg, "delete")}, |
| {"serialize_thunk", Thunk(msg, "serialize")}, |
| {"deserialize_thunk", Thunk(msg, "deserialize")}, |
| }, |
| R"rs( |
| fn $new_thunk$() -> $pbi$::RawMessage; |
| fn $delete_thunk$(raw_msg: $pbi$::RawMessage); |
| fn $serialize_thunk$(raw_msg: $pbi$::RawMessage) -> $pbr$::SerializedData; |
| fn $deserialize_thunk$(raw_msg: $pbi$::RawMessage, data: $pbr$::SerializedData) -> bool; |
| )rs"); |
| return; |
| |
| case Kernel::kUpb: |
| msg.Emit( |
| { |
| {"new_thunk", Thunk(msg, "new")}, |
| {"serialize_thunk", Thunk(msg, "serialize")}, |
| {"deserialize_thunk", Thunk(msg, "parse")}, |
| }, |
| R"rs( |
| fn $new_thunk$(arena: $pbi$::RawArena) -> $pbi$::RawMessage; |
| fn $serialize_thunk$(msg: $pbi$::RawMessage, arena: $pbi$::RawArena, len: &mut usize) -> $NonNull$<u8>; |
| fn $deserialize_thunk$(data: *const u8, size: usize, arena: $pbi$::RawArena) -> Option<$pbi$::RawMessage>; |
| )rs"); |
| return; |
| } |
| |
| ABSL_LOG(FATAL) << "unreachable"; |
| } |
| |
| void MessageDrop(Context<Descriptor> msg) { |
| if (msg.is_upb()) { |
| // Nothing to do here; drop glue (which will run drop(self.arena) |
| // automatically) is sufficient. |
| return; |
| } |
| |
| msg.Emit({{"delete_thunk", Thunk(msg, "delete")}}, R"rs( |
| unsafe { $delete_thunk$(self.inner.msg); } |
| )rs"); |
| } |
| |
| void GetterForViewOrMut(Context<FieldDescriptor> field, bool is_mut) { |
| auto fieldName = field.desc().name(); |
| auto fieldType = field.desc().type(); |
| auto getter_thunk = Thunk(field, "get"); |
| auto setter_thunk = Thunk(field, "set"); |
| auto clearer_thunk = Thunk(field, "clear"); |
| // If we're dealing with a Mut, the getter must be supplied |
| // self.inner.msg() whereas a View has to be supplied self.msg |
| auto self = is_mut ? "self.inner.msg()" : "self.msg"; |
| if (fieldType == FieldDescriptor::TYPE_MESSAGE) { |
| Context<Descriptor> d = field.WithDesc(field.desc().message_type()); |
| // TODO: support messages which are defined in other crates. |
| if (!IsInCurrentlyGeneratingCrate(d)) { |
| return; |
| } |
| auto prefix = "crate::" + GetCrateRelativeQualifiedPath(d); |
| field.Emit( |
| { |
| {"prefix", prefix}, |
| {"field", fieldName}, |
| {"self", self}, |
| {"getter_thunk", getter_thunk}, |
| // TODO: dedupe with singular_message.cc |
| { |
| "view_body", |
| [&] { |
| if (field.is_upb()) { |
| field.Emit({}, R"rs( |
| let submsg = unsafe { $getter_thunk$($self$) }; |
| match submsg { |
| None => $prefix$View::new($pbi$::Private, |
| $pbr$::ScratchSpace::zeroed_block($pbi$::Private)), |
| Some(field) => $prefix$View::new($pbi$::Private, field), |
| } |
| )rs"); |
| } else { |
| field.Emit({}, R"rs( |
| let submsg = unsafe { $getter_thunk$($self$) }; |
| $prefix$View::new($pbi$::Private, submsg) |
| )rs"); |
| } |
| }, |
| }, |
| }, |
| R"rs( |
| pub fn r#$field$(&self) -> $prefix$View { |
| $view_body$ |
| } |
| )rs"); |
| return; |
| } |
| |
| auto rsType = PrimitiveRsTypeName(field.desc()); |
| if (fieldType == FieldDescriptor::TYPE_STRING) { |
| field.Emit( |
| { |
| {"field", fieldName}, |
| {"self", self}, |
| {"getter_thunk", getter_thunk}, |
| {"RsType", rsType}, |
| }, |
| R"rs( |
| pub fn r#$field$(&self) -> &$RsType$ { |
| let s = unsafe { $getter_thunk$($self$).as_ref() }; |
| unsafe { __pb::ProtoStr::from_utf8_unchecked(s) } |
| } |
| )rs"); |
| } else if (fieldType == FieldDescriptor::TYPE_BYTES) { |
| field.Emit( |
| { |
| {"field", fieldName}, |
| {"self", self}, |
| {"getter_thunk", getter_thunk}, |
| {"RsType", rsType}, |
| }, |
| R"rs( |
| pub fn r#$field$(&self) -> &$RsType$ { |
| unsafe { $getter_thunk$($self$).as_ref() } |
| } |
| )rs"); |
| } else { |
| field.Emit({{"field", fieldName}, |
| {"getter_thunk", getter_thunk}, |
| {"setter_thunk", setter_thunk}, |
| {"clearer_thunk", clearer_thunk}, |
| {"self", self}, |
| {"RsType", rsType}, |
| {"maybe_mutator", |
| [&] { |
| // TODO: once the rust public api is accessible, |
| // by tooling, ensure that this only appears for the |
| // mutational pathway |
| if (is_mut && fieldType) { |
| field.Emit({}, R"rs( |
| pub fn r#$field$_mut(&self) -> $pb$::Mut<'_, $RsType$> { |
| static VTABLE: $pbi$::PrimitiveVTable<$RsType$> = |
| $pbi$::PrimitiveVTable::new( |
| $pbi$::Private, |
| $getter_thunk$, |
| $setter_thunk$); |
| $pb$::PrimitiveMut::from_singular( |
| $pbi$::Private, |
| unsafe { |
| $pbi$::RawVTableMutator::new($pbi$::Private, |
| self.inner, |
| &VTABLE) |
| }) |
| } |
| )rs"); |
| } |
| }}}, |
| R"rs( |
| pub fn r#$field$(&self) -> $RsType$ { |
| unsafe { $getter_thunk$($self$) } |
| } |
| |
| $maybe_mutator$ |
| )rs"); |
| } |
| } |
| |
| void AccessorsForViewOrMut(Context<Descriptor> msg, bool is_mut) { |
| for (int i = 0; i < msg.desc().field_count(); ++i) { |
| auto field = msg.WithDesc(*msg.desc().field(i)); |
| if (field.desc().is_repeated()) continue; |
| // TODO - add cord support |
| if (field.desc().options().has_ctype()) continue; |
| // TODO |
| if (field.desc().type() == FieldDescriptor::TYPE_ENUM || |
| field.desc().type() == FieldDescriptor::TYPE_GROUP) |
| continue; |
| GetterForViewOrMut(field, is_mut); |
| msg.printer().PrintRaw("\n"); |
| } |
| } |
| |
| } // namespace |
| |
| void GenerateRs(Context<Descriptor> msg) { |
| if (msg.desc().map_key() != nullptr) { |
| ABSL_LOG(WARNING) << "unsupported map field: " << msg.desc().full_name(); |
| return; |
| } |
| msg.Emit( |
| {{"Msg", msg.desc().name()}, |
| {"Msg::new", [&] { MessageNew(msg); }}, |
| {"Msg::serialize", [&] { MessageSerialize(msg); }}, |
| {"Msg::deserialize", [&] { MessageDeserialize(msg); }}, |
| {"Msg::drop", [&] { MessageDrop(msg); }}, |
| {"Msg_externs", [&] { MessageExterns(msg); }}, |
| {"accessor_fns", |
| [&] { |
| for (int i = 0; i < msg.desc().field_count(); ++i) { |
| auto field = msg.WithDesc(*msg.desc().field(i)); |
| msg.Emit({{"comment", FieldInfoComment(field)}}, R"rs( |
| // $comment$ |
| )rs"); |
| GenerateAccessorMsgImpl(field); |
| msg.printer().PrintRaw("\n"); |
| } |
| }}, |
| {"oneof_accessor_fns", |
| [&] { |
| for (int i = 0; i < msg.desc().real_oneof_decl_count(); ++i) { |
| GenerateOneofAccessors( |
| msg.WithDesc(*msg.desc().real_oneof_decl(i))); |
| msg.printer().PrintRaw("\n"); |
| } |
| }}, |
| {"accessor_externs", |
| [&] { |
| for (int i = 0; i < msg.desc().field_count(); ++i) { |
| GenerateAccessorExternC(msg.WithDesc(*msg.desc().field(i))); |
| msg.printer().PrintRaw("\n"); |
| } |
| }}, |
| {"oneof_externs", |
| [&] { |
| for (int i = 0; i < msg.desc().real_oneof_decl_count(); ++i) { |
| GenerateOneofExternC(msg.WithDesc(*msg.desc().real_oneof_decl(i))); |
| msg.printer().PrintRaw("\n"); |
| } |
| }}, |
| {"nested_msgs", |
| [&] { |
| // If we have no nested types or oneofs, bail out without emitting |
| // an empty mod SomeMsg_. |
| if (msg.desc().nested_type_count() == 0 && |
| msg.desc().real_oneof_decl_count() == 0) { |
| return; |
| } |
| msg.Emit( |
| {{"Msg", msg.desc().name()}, |
| {"nested_msgs", |
| [&] { |
| for (int i = 0; i < msg.desc().nested_type_count(); ++i) { |
| auto nested_msg = msg.WithDesc(msg.desc().nested_type(i)); |
| GenerateRs(nested_msg); |
| } |
| }}, |
| {"oneofs", |
| [&] { |
| for (int i = 0; i < msg.desc().real_oneof_decl_count(); ++i) { |
| GenerateOneofDefinition( |
| msg.WithDesc(*msg.desc().real_oneof_decl(i))); |
| } |
| }}}, |
| R"rs( |
| #[allow(non_snake_case)] |
| pub mod $Msg$_ { |
| $nested_msgs$ |
| |
| $oneofs$ |
| } // mod $Msg$_ |
| )rs"); |
| }}, |
| {"accessor_fns_for_views", [&] { AccessorsForViewOrMut(msg, false); }}, |
| {"accessor_fns_for_muts", [&] { AccessorsForViewOrMut(msg, true); }}}, |
| R"rs( |
| #[allow(non_camel_case_types)] |
| // TODO: Implement support for debug redaction |
| #[derive(Debug)] |
| pub struct $Msg$ { |
| inner: $pbr$::MessageInner |
| } |
| |
| // SAFETY: |
| // - `$Msg$` does not provide shared mutation with its arena. |
| // - `$Msg$Mut` is not `Send`, and so even in the presence of mutator |
| // splitting, synchronous access of an arena that would conflict with |
| // field access is impossible. |
| unsafe impl Sync for $Msg$ {} |
| |
| impl $pb$::Proxied for $Msg$ { |
| type View<'a> = $Msg$View<'a>; |
| type Mut<'a> = $Msg$Mut<'a>; |
| } |
| |
| #[derive(Debug, Copy, Clone)] |
| #[allow(dead_code)] |
| pub struct $Msg$View<'a> { |
| msg: $pbi$::RawMessage, |
| _phantom: $Phantom$<&'a ()>, |
| } |
| |
| impl<'a> $Msg$View<'a> { |
| #[doc(hidden)] |
| pub fn new(_private: $pbi$::Private, msg: $pbi$::RawMessage) -> Self { |
| Self { msg, _phantom: std::marker::PhantomData } |
| } |
| $accessor_fns_for_views$ |
| } |
| |
| // SAFETY: |
| // - `$Msg$View` does not perform any mutation. |
| // - While a `$Msg$View` exists, a `$Msg$Mut` can't exist to mutate |
| // the arena that would conflict with field access. |
| // - `$Msg$Mut` is not `Send`, and so even in the presence of mutator |
| // splitting, synchronous access of an arena is impossible. |
| unsafe impl Sync for $Msg$View<'_> {} |
| unsafe impl Send for $Msg$View<'_> {} |
| |
| impl<'a> $pb$::ViewProxy<'a> for $Msg$View<'a> { |
| type Proxied = $Msg$; |
| |
| fn as_view(&self) -> $pb$::View<'a, $Msg$> { |
| *self |
| } |
| fn into_view<'shorter>(self) -> $pb$::View<'shorter, $Msg$> where 'a: 'shorter { |
| self |
| } |
| } |
| |
| impl<'a> $pb$::SettableValue<$Msg$> for $Msg$View<'a> { |
| fn set_on<'b>(self, _private: $pb$::__internal::Private, _mutator: $pb$::Mut<'b, $Msg$>) |
| where |
| $Msg$: 'b { |
| todo!() |
| } |
| } |
| |
| #[derive(Debug)] |
| #[allow(dead_code)] |
| #[allow(non_camel_case_types)] |
| pub struct $Msg$Mut<'a> { |
| inner: $pbr$::MutatorMessageRef<'a>, |
| } |
| |
| impl<'a> $Msg$Mut<'a> { |
| #[doc(hidden)] |
| pub fn new(_private: $pbi$::Private, |
| parent: &'a mut $pbr$::MessageInner, |
| msg: $pbi$::RawMessage) |
| -> Self { |
| Self { |
| inner: $pbr$::MutatorMessageRef::from_parent( |
| $pbi$::Private, parent, msg) |
| } |
| } |
| $accessor_fns_for_muts$ |
| } |
| |
| // SAFETY: |
| // - `$Msg$Mut` does not perform any shared mutation. |
| // - `$Msg$Mut` is not `Send`, and so even in the presence of mutator |
| // splitting, synchronous access of an arena is impossible. |
| unsafe impl Sync for $Msg$Mut<'_> {} |
| |
| impl<'a> $pb$::MutProxy<'a> for $Msg$Mut<'a> { |
| fn as_mut(&mut self) -> $pb$::Mut<'_, $Msg$> { |
| $Msg$Mut { inner: self.inner } |
| } |
| fn into_mut<'shorter>(self) -> $pb$::Mut<'shorter, $Msg$> where 'a : 'shorter { self } |
| } |
| |
| impl<'a> $pb$::ViewProxy<'a> for $Msg$Mut<'a> { |
| type Proxied = $Msg$; |
| fn as_view(&self) -> $pb$::View<'_, $Msg$> { |
| $Msg$View { msg: self.inner.msg(), _phantom: std::marker::PhantomData } |
| } |
| fn into_view<'shorter>(self) -> $pb$::View<'shorter, $Msg$> where 'a: 'shorter { |
| $Msg$View { msg: self.inner.msg(), _phantom: std::marker::PhantomData } |
| } |
| } |
| |
| impl $Msg$ { |
| pub fn new() -> Self { |
| $Msg::new$ |
| } |
| |
| pub fn serialize(&self) -> $pbr$::SerializedData { |
| $Msg::serialize$ |
| } |
| pub fn deserialize(&mut self, data: &[u8]) -> Result<(), $pb$::ParseError> { |
| $Msg::deserialize$ |
| } |
| |
| $accessor_fns$ |
| |
| $oneof_accessor_fns$ |
| } // impl $Msg$ |
| |
| //~ We implement drop unconditionally, so that `$Msg$: Drop` regardless |
| //~ of kernel. |
| impl $std$::ops::Drop for $Msg$ { |
| fn drop(&mut self) { |
| $Msg::drop$ |
| } |
| } |
| |
| extern "C" { |
| $Msg_externs$ |
| |
| $accessor_externs$ |
| |
| $oneof_externs$ |
| } // extern "C" for $Msg$ |
| |
| $nested_msgs$ |
| )rs"); |
| |
| if (msg.is_cpp()) { |
| msg.printer().PrintRaw("\n"); |
| msg.Emit({{"Msg", msg.desc().name()}}, R"rs( |
| impl $Msg$ { |
| pub fn __unstable_wrap_cpp_grant_permission_to_break(msg: $pbi$::RawMessage) -> Self { |
| Self { inner: $pbr$::MessageInner { msg } } |
| } |
| pub fn __unstable_cpp_repr_grant_permission_to_break(&mut self) -> $pbi$::RawMessage { |
| self.inner.msg |
| } |
| } |
| )rs"); |
| } |
| } |
| |
| // Generates code for a particular message in `.pb.thunk.cc`. |
| void GenerateThunksCc(Context<Descriptor> msg) { |
| ABSL_CHECK(msg.is_cpp()); |
| if (msg.desc().map_key() != nullptr) { |
| ABSL_LOG(WARNING) << "unsupported map field: " << msg.desc().full_name(); |
| return; |
| } |
| |
| msg.Emit( |
| {{"abi", "\"C\""}, // Workaround for syntax highlight bug in VSCode. |
| {"Msg", msg.desc().name()}, |
| {"QualifiedMsg", cpp::QualifiedClassName(&msg.desc())}, |
| {"new_thunk", Thunk(msg, "new")}, |
| {"delete_thunk", Thunk(msg, "delete")}, |
| {"serialize_thunk", Thunk(msg, "serialize")}, |
| {"deserialize_thunk", Thunk(msg, "deserialize")}, |
| {"nested_msg_thunks", |
| [&] { |
| for (int i = 0; i < msg.desc().nested_type_count(); ++i) { |
| Context<Descriptor> nested_msg = |
| msg.WithDesc(msg.desc().nested_type(i)); |
| GenerateThunksCc(nested_msg); |
| } |
| }}, |
| {"accessor_thunks", |
| [&] { |
| for (int i = 0; i < msg.desc().field_count(); ++i) { |
| GenerateAccessorThunkCc(msg.WithDesc(*msg.desc().field(i))); |
| } |
| }}, |
| {"oneof_thunks", |
| [&] { |
| for (int i = 0; i < msg.desc().real_oneof_decl_count(); ++i) { |
| GenerateOneofThunkCc(msg.WithDesc(*msg.desc().real_oneof_decl(i))); |
| } |
| }}}, |
| R"cc( |
| //~ $abi$ is a workaround for a syntax highlight bug in VSCode. However, |
| //~ that confuses clang-format (it refuses to keep the newline after |
| //~ `$abi${`). Disabling clang-format for the block. |
| // clang-format off |
| extern $abi$ { |
| void* $new_thunk$() { return new $QualifiedMsg$(); } |
| void $delete_thunk$(void* ptr) { delete static_cast<$QualifiedMsg$*>(ptr); } |
| google::protobuf::rust_internal::SerializedData $serialize_thunk$($QualifiedMsg$* msg) { |
| return google::protobuf::rust_internal::SerializeMsg(msg); |
| } |
| bool $deserialize_thunk$($QualifiedMsg$* msg, |
| google::protobuf::rust_internal::SerializedData data) { |
| return msg->ParseFromArray(data.data, data.len); |
| } |
| |
| $accessor_thunks$ |
| |
| $oneof_thunks$ |
| } // extern $abi$ |
| // clang-format on |
| |
| $nested_msg_thunks$ |
| )cc"); |
| } |
| |
| } // namespace rust |
| } // namespace compiler |
| } // namespace protobuf |
| } // namespace google |