blob: a1cfb4e8c4089339f402f57813710e2c54d4628d [file] [log] [blame]
// 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