2024-03-15 12:31:34 +08:00

406 lines
13 KiB
C++

// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google LLC. All rights reserved.
// https://developers.google.com/protocol-buffers/
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google LLC. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#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/descriptor.h"
namespace google {
namespace protobuf {
namespace compiler {
namespace rust {
namespace {
void MessageStructFields(Context<Descriptor> msg) {
switch (msg.opts().kernel) {
case Kernel::kCpp:
msg.Emit(R"rs(
msg: $NonNull$<u8>,
)rs");
return;
case Kernel::kUpb:
msg.Emit(R"rs(
msg: $NonNull$<u8>,
//~ rustc incorrectly thinks this field is never read, even though
//~ it has a destructor!
#[allow(dead_code)]
arena: $pbr$::Arena,
)rs");
return;
}
ABSL_LOG(FATAL) << "unreachable";
}
void MessageNew(Context<Descriptor> msg) {
switch (msg.opts().kernel) {
case Kernel::kCpp:
msg.Emit({{"new_thunk", Thunk(msg, "new")}}, R"rs(
Self { msg: unsafe { $new_thunk$() } }
)rs");
return;
case Kernel::kUpb:
msg.Emit({{"new_thunk", Thunk(msg, "new")}}, R"rs(
let arena = $pbr$::Arena::new();
Self {
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.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.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.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 {
$NonNull$::<u8>::new(
$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.msg.
self.arena = arena;
self.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$() -> $NonNull$<u8>;
fn $delete_thunk$(raw_msg: $NonNull$<u8>);
fn $serialize_thunk$(raw_msg: $NonNull$<u8>) -> $pbr$::SerializedData;
fn $deserialize_thunk$(raw_msg: $NonNull$<u8>, 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: $pbr$::RawArena) -> $NonNull$<u8>;
fn $serialize_thunk$(msg: $NonNull$<u8>, arena: $pbr$::RawArena, len: &mut usize) -> $NonNull$<u8>;
fn $deserialize_thunk$(data: *const u8, size: usize, arena: $pbr$::RawArena) -> *mut u8;
)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.msg); }
)rs");
}
} // namespace
MessageGenerator::MessageGenerator(Context<Descriptor> msg) {
accessors_.resize(msg.desc().field_count());
for (int i = 0; i < msg.desc().field_count(); ++i) {
auto field = msg.WithDesc(msg.desc().field(i));
accessors_[i] = AccessorGenerator::For(field);
if (accessors_[i] == nullptr) {
ABSL_LOG(WARNING) << "unsupported field: " << field.desc().full_name();
}
}
}
void MessageGenerator::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.fields", [&] { MessageStructFields(msg); }},
{"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& gen = accessors_[i];
auto field = msg.WithDesc(*msg.desc().field(i));
msg.Emit({{"comment", FieldInfoComment(field)}}, R"rs(
// $comment$
)rs");
if (gen == nullptr) {
msg.Emit({{"field", field.desc().full_name()}}, R"rs(
// Unsupported! :(
)rs");
msg.printer().PrintRaw("\n");
continue;
}
gen->GenerateMsgImpl(field);
msg.printer().PrintRaw("\n");
}
}},
{"accessor_externs",
[&] {
for (int i = 0; i < msg.desc().field_count(); ++i) {
auto& gen = accessors_[i];
if (gen == nullptr) continue;
gen->GenerateExternC(msg.WithDesc(*msg.desc().field(i)));
msg.printer().PrintRaw("\n");
}
}},
{"nested_msgs",
[&] {
if (msg.desc().nested_type_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));
MessageGenerator gen(nested_msg);
gen.GenerateRs(nested_msg);
}
}}},
R"rs(
pub mod $Msg$_ {
$nested_msgs$
} // mod $Msg$_
)rs");
}},
},
R"rs(
#[allow(non_camel_case_types)]
pub struct $Msg$ {
$Msg.fields$
}
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$
} // 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$
} // 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: $NonNull$<u8>) -> Self {
Self { msg }
}
pub fn __unstable_cpp_repr_grant_permission_to_break(&mut self) -> $NonNull$<u8> {
self.msg
}
}
)rs");
}
}
// Generates code for a particular message in `.pb.thunk.cc`.
void MessageGenerator::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));
MessageGenerator gen(nested_msg);
gen.GenerateThunksCc(nested_msg);
}
}},
{"accessor_thunks",
[&] {
for (int i = 0; i < msg.desc().field_count(); ++i) {
auto& gen = accessors_[i];
if (gen == nullptr) continue;
gen->GenerateThunkCc(msg.WithDesc(*msg.desc().field(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$
} // extern $abi$
// clang-format on
$nested_msg_thunks$
)cc");
}
} // namespace rust
} // namespace compiler
} // namespace protobuf
} // namespace google