Generate C++ thunks for Rust protos

In this CL I'd like to call existing C++ Protobuf API from the V0 Rust API. Since parts of the C++ API are defined inline and using (obviously) C++ name mangling, we need to create a "thunks.cc" file that:

1) Generates code for C++ API function we use from Rust
2) Exposes these functions without any name mangling (meaning using `extern "C"`)

In this CL we add Bazel logic to generate "thunks" file, compile it, and propagate its object to linking. We also add logic to protoc to generate this "thunks" file.

The protoc logic is rather rudimentary still. I hope to focus on protoc code quality in my followup work on V0 Rust API using C++ kernel.

PiperOrigin-RevId: 523479839
pull/12367/head
Marcel Hlopko 2 years ago committed by Copybara-Service
parent c23f226585
commit 5814f6c977
  1. 107
      rust/aspects.bzl
  2. 18
      rust/cpp_kernel/BUILD
  3. 58
      rust/cpp_kernel/cpp.rs
  4. 80
      rust/cpp_kernel/cpp_api.h
  5. 37
      rust/cpp_kernel/rust_alloc_for_cpp_api.rs
  6. 53
      rust/shared.rs
  7. 12
      rust/test/BUILD
  8. 30
      rust/test/cpp/interop/BUILD
  9. 47
      rust/test/cpp/interop/main.rs
  10. 43
      rust/test/cpp/interop/test_utils.cc
  11. 48
      rust/upb_kernel/upb.rs
  12. 6
      src/google/protobuf/BUILD.bazel
  13. 1
      src/google/protobuf/compiler/rust/BUILD.bazel
  14. 79
      src/google/protobuf/compiler/rust/generator.cc

@ -10,6 +10,7 @@ load("@rules_rust//rust/private:providers.bzl", "CrateInfo", "DepInfo", "DepVari
load("@rules_rust//rust/private:rustc.bzl", "rustc_compile_action")
load("@rules_rust//rust:defs.bzl", "rust_common")
load("@upb//bazel:upb_proto_library.bzl", "UpbWrappedCcInfo", "upb_proto_library_aspect")
load("@bazel_tools//tools/cpp:toolchain_utils.bzl", "find_cpp_toolchain")
proto_common = proto_common_do_not_use
@ -27,7 +28,7 @@ def _generate_rust_gencode(
ctx,
proto_info,
proto_lang_toolchain,
ext):
is_upb):
"""Generates Rust gencode
This function uses proto_common APIs and a ProtoLangToolchain to register an action
@ -37,25 +38,33 @@ def _generate_rust_gencode(
proto_info (ProtoInfo): ProtoInfo of the proto_library target for which we are generating
gencode
proto_lang_toolchain (ProtoLangToolchainInfo): proto lang toolchain for Rust
ext: the file extension to use for generated files
is_upb (Bool): True when generating gencode for UPB, False otherwise.
Returns:
rs_outputs ([File]): generated Rust source files
rs_outputs (([File], [File]): tuple(generated Rust files, generated C++ thunks).
"""
actions = ctx.actions
rs_outputs = proto_common.declare_generated_files(
actions = actions,
proto_info = proto_info,
extension = ext,
extension = ".{}.pb.rs".format("u" if is_upb else "c"),
)
if is_upb:
cc_outputs = []
else:
cc_outputs = proto_common.declare_generated_files(
actions = actions,
proto_info = proto_info,
extension = ".pb.thunks.cc",
)
proto_common.compile(
actions = ctx.actions,
proto_info = proto_info,
generated_files = rs_outputs,
generated_files = rs_outputs + cc_outputs,
proto_lang_toolchain_info = proto_lang_toolchain,
plugin_output = ctx.bin_dir.path,
)
return rs_outputs
return (rs_outputs, cc_outputs)
def _get_crate_info(providers):
for provider in providers:
@ -75,6 +84,53 @@ def _get_cc_info(providers):
return provider
fail("Couldn't find a CcInfo in the list of providers")
def _compile_cc(
ctx,
attr,
cc_toolchain,
feature_configuration,
src,
cc_infos):
"""Compiles a C++ source file.
Args:
ctx: The rule context.
attr: The current rule's attributes.
cc_toolchain: A cc_toolchain.
feature_configuration: A feature configuration.
src: The source file to be compiled.
cc_infos: List[CcInfo]: A list of CcInfo dependencies.
Returns:
A CcInfo provider.
"""
cc_info = cc_common.merge_cc_infos(direct_cc_infos = cc_infos)
(compilation_context, compilation_outputs) = cc_common.compile(
name = src.basename,
actions = ctx.actions,
feature_configuration = feature_configuration,
cc_toolchain = cc_toolchain,
srcs = [src],
grep_includes = ctx.file._grep_includes,
user_compile_flags = attr.copts if hasattr(attr, "copts") else [],
compilation_contexts = [cc_info.compilation_context],
)
(linking_context, _) = cc_common.create_linking_context_from_compilation_outputs(
name = src.basename,
actions = ctx.actions,
feature_configuration = feature_configuration,
cc_toolchain = cc_toolchain,
compilation_outputs = compilation_outputs,
linking_contexts = [cc_info.linking_context],
)
return CcInfo(
compilation_context = compilation_context,
linking_context = linking_context,
)
def _compile_rust(ctx, attr, src, extra_srcs, deps):
"""Compiles a Rust source file.
@ -167,8 +223,33 @@ def _rust_proto_aspect_common(target, ctx, is_upb):
return [ctx.rule.attr.deps[0][RustProtoInfo]]
proto_lang_toolchain = ctx.attr._proto_lang_toolchain[proto_common.ProtoLangToolchainInfo]
cc_toolchain = find_cpp_toolchain(ctx)
feature_configuration = cc_common.configure_features(
ctx = ctx,
cc_toolchain = cc_toolchain,
requested_features = ctx.features,
unsupported_features = ctx.disabled_features,
)
gencode = _generate_rust_gencode(ctx, target[ProtoInfo], proto_lang_toolchain, (".u.pb.rs" if is_upb else ".c.pb.rs"))
(gencode, thunks) = _generate_rust_gencode(
ctx,
target[ProtoInfo],
proto_lang_toolchain,
is_upb,
)
if is_upb:
thunks_cc_info = target[UpbWrappedCcInfo].cc_info_with_thunks
else:
thunks_cc_info = cc_common.merge_cc_infos(cc_infos = [_compile_cc(
feature_configuration = feature_configuration,
src = thunk,
ctx = ctx,
attr = attr,
cc_toolchain = cc_toolchain,
cc_infos = [target[CcInfo], ctx.attr._cpp_thunks_deps[CcInfo]],
) for thunk in thunks])
runtime = proto_lang_toolchain.runtime
dep_variant_info_for_runtime = DepVariantInfo(
@ -177,9 +258,7 @@ def _rust_proto_aspect_common(target, ctx, is_upb):
cc_info = runtime[CcInfo] if CcInfo in runtime else None,
build_info = None,
)
dep_variant_info_for_native_gencode = DepVariantInfo(
cc_info = target[UpbWrappedCcInfo].cc_info_with_thunks if is_upb else target[CcInfo],
)
dep_variant_info_for_native_gencode = DepVariantInfo(cc_info = thunks_cc_info)
proto_dep = getattr(ctx.rule.attr, "deps", [])
dep_variant_info = _compile_rust(
@ -215,6 +294,14 @@ def _make_proto_library_aspect(is_upb):
executable = True,
cfg = "exec",
),
"_cpp_thunks_deps": attr.label(
default = Label("//rust/cpp_kernel:cpp_api"),
),
"_grep_includes": attr.label(
allow_single_file = True,
default = Label("@bazel_tools//tools/cpp:grep-includes"),
cfg = "exec",
),
"_error_format": attr.label(
default = Label("@rules_rust//:error_format"),
),

@ -23,3 +23,21 @@ rust_test(
"nomsan",
],
)
cc_library(
name = "cpp_api",
hdrs = ["cpp_api.h"],
visibility = [
"//src/google/protobuf:__subpackages__",
"//rust:__subpackages__",
],
deps = [
":rust_alloc_for_cpp_api",
"//src/google/protobuf",
],
)
rust_library(
name = "rust_alloc_for_cpp_api",
srcs = ["rust_alloc_for_cpp_api.rs"],
)

@ -30,7 +30,11 @@
// Rust Protobuf runtime using the C++ kernel.
use std::alloc::{dealloc, Layout};
use std::boxed::Box;
use std::ops::Deref;
use std::ptr::NonNull;
use std::slice;
/// TODO(b/272728844): Replace this placeholder code with a real implementation.
#[repr(C)]
@ -49,3 +53,57 @@ impl Arena {
std::mem::drop(arena);
}
}
/// Represents serialized Protobuf wire format data. It's typically produced by
/// `<Message>.serialize()`.
///
/// This struct is ABI compatible with the equivalent struct on the C++ side. It
/// owns (and drops) its data.
// copybara:strip_begin
// LINT.IfChange
// copybara:strip_end
#[repr(C)]
#[derive(Debug)]
pub struct SerializedData {
/// Owns the memory.
data: NonNull<u8>,
len: usize,
}
// copybara:strip_begin
// LINT.ThenChange(//depot/google3/third_party/protobuf/rust/cpp_kernel/cpp_api.
// h) copybara:strip_end
impl Deref for SerializedData {
type Target = [u8];
fn deref(&self) -> &Self::Target {
unsafe { slice::from_raw_parts(self.data.as_ptr(), self.len) }
}
}
impl Drop for SerializedData {
fn drop(&mut self) {
unsafe {
dealloc(self.data.as_ptr(), Layout::array::<u8>(self.len).unwrap());
};
}
}
#[cfg(test)]
mod tests {
use super::*;
// We need to allocate the byte array so SerializedData can own it and
// deallocate it in its drop. This function makes it easier to do so for our
// tests.
fn allocate_byte_array(content: &'static [u8]) -> (*mut u8, usize) {
let content: &mut [u8] = Box::leak(content.into());
(content.as_mut_ptr(), content.len())
}
#[test]
fn test_serialized_data_roundtrip() {
let (ptr, len) = allocate_byte_array(b"Hello world");
let serialized_data = SerializedData { data: NonNull::new(ptr).unwrap(), len: len };
assert_eq!(&*serialized_data, b"Hello world");
}
}

@ -0,0 +1,80 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google Inc. 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 Inc. 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.
// This file contains support code for generated C++ thunks.
#ifndef GOOGLE_PROTOBUF_RUST_CPP_KERNEL_CPP_H__
#define GOOGLE_PROTOBUF_RUST_CPP_KERNEL_CPP_H__
#include <cstddef>
#include <iostream>
#include <string>
#include "google/protobuf/message.h"
namespace google {
namespace protobuf {
namespace rust_internal {
// Represents serialized Protobuf wire format data.
//
// Only to be used to transfer serialized data from C++ to Rust under these
// assumptions:
// * Rust and C++ versions of this struct are ABI compatible.
// * Rust version owns and frees its data.
// * The data were allocated using the Rust allocator.
//
extern "C" struct SerializedData {
/// Owns the memory.
const char* data;
size_t len;
SerializedData(const char* data, size_t len) : data(data), len(len) {}
};
// Allocates memory using the current Rust global allocator.
//
// This function is defined in `rust_alloc_for_cpp_api.rs`.
extern "C" void* __pb_rust_alloc(size_t size, size_t align);
inline SerializedData SerializeMsg(const google::protobuf::Message* msg) {
size_t len = msg->ByteSizeLong();
void* bytes = __pb_rust_alloc(len, alignof(char));
if (!msg->SerializeToArray(bytes, static_cast<int>(len))) {
ABSL_LOG(FATAL) << "Couldn't serialize the message.";
}
return SerializedData(static_cast<char*>(bytes), len);
}
} // namespace rust_internal
} // namespace protobuf
} // namespace google
#endif // GOOGLE_PROTOBUF_RUST_CPP_KERNEL_CPP_H__

@ -0,0 +1,37 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google Inc. 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 Inc. 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.
use std::alloc::{alloc, Layout};
#[no_mangle]
extern "C" fn __pb_rust_alloc(size: usize, align: usize) -> *mut u8 {
let layout = Layout::from_size_align(size, align).unwrap();
unsafe { alloc(layout) }
}

@ -39,55 +39,4 @@ pub extern crate cpp as __runtime;
pub extern crate upb as __runtime;
pub use __runtime::Arena;
use std::ops::Deref;
use std::ptr::NonNull;
use std::slice;
/// Represents serialized Protobuf wire format data. It's typically produced by
/// `<Message>.serialize()`.
pub struct SerializedData {
data: NonNull<u8>,
len: usize,
arena: *mut Arena,
}
impl SerializedData {
pub unsafe fn from_raw_parts(arena: *mut Arena, data: NonNull<u8>, len: usize) -> Self {
SerializedData { arena, data, len }
}
}
impl Deref for SerializedData {
type Target = [u8];
fn deref(&self) -> &Self::Target {
unsafe { slice::from_raw_parts(self.data.as_ptr() as *const _, self.len) }
}
}
impl Drop for SerializedData {
fn drop(&mut self) {
unsafe { Arena::free(self.arena) };
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_serialized_data_roundtrip() {
let arena = unsafe { Arena::new() };
let original_data = b"Hello world";
let len = original_data.len();
let serialized_data = unsafe {
SerializedData::from_raw_parts(
arena,
NonNull::new(original_data as *const _ as *mut _).unwrap(),
len,
)
};
assert_eq!(&*serialized_data, b"Hello world");
}
}
pub use __runtime::SerializedData;

@ -10,6 +10,16 @@ UNITTEST_PROTO_TARGET = (
"//src/google/protobuf:test_protos"
)
UNITTEST_CC_PROTO_TARGET = (
"//src/google/protobuf:cc_test_protos"
)
alias(
name = "unittest_cc_proto",
actual = UNITTEST_CC_PROTO_TARGET,
visibility = ["//rust/test:__subpackages__"],
)
rust_proto_library(
name = "unittest_rust_proto",
testonly = True,
@ -34,7 +44,7 @@ rust_cc_proto_library(
"//rust/test/cpp:__subpackages__",
"//rust/test/shared:__subpackages__",
],
deps = [":unittest_cc_proto"],
deps = [UNITTEST_CC_PROTO_TARGET],
)
proto_library(

@ -0,0 +1,30 @@
# This package contains tests exercising C++/Rust interop in the cpp kernel.
load("@rules_rust//rust:defs.bzl", "rust_test")
cc_library(
name = "test_utils",
srcs = ["test_utils.cc"],
deps = [
"//rust/cpp_kernel:cpp_api",
"//rust/test:unittest_cc_proto",
],
)
rust_test(
name = "interop_test",
srcs = ["main.rs"],
tags = [
# TODO(b/270274576): Enable testing on arm once we have a Rust Arm toolchain.
"not_build:arm",
# TODO(b/243126140): Enable tsan once we support sanitizers with Rust.
"notsan",
# TODO(b/243126140): Enable msan once we support sanitizers with Rust.
"nomsan",
],
deps = [
":test_utils",
"//rust:protobuf_cpp",
"//rust/test:unittest_cc_rust_proto",
],
)

@ -0,0 +1,47 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google Inc. 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 Inc. 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.
use std::ptr::NonNull;
#[test]
fn mutate_message_in_cpp() {
let mut msg = unittest_proto::TestAllTypes::new();
unsafe { MutateInt32Field(msg.__unstable_cpp_repr_grant_permission_to_break()) };
let serialized_msg_from_cpp =
unsafe { Serialize(msg.__unstable_cpp_repr_grant_permission_to_break()) };
assert_eq!(*msg.serialize(), *serialized_msg_from_cpp);
}
// Helper functions invoking C++ Protobuf APIs directly in C++. Defined in
// `//third_party/protobuf/rust/test/cpp/interop:test_utils`.
extern "C" {
fn MutateInt32Field(msg: NonNull<u8>);
fn Serialize(msg: NonNull<u8>) -> protobuf_cpp::SerializedData;
}

@ -0,0 +1,43 @@
// Protocol Buffers - Google's data interchange format
// Copyright 2023 Google Inc. 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 Inc. 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 <iostream>
#include "google/protobuf/rust/cpp_kernel/cpp_api.h"
#include "google/protobuf/unittest.pb.h"
extern "C" void MutateInt32Field(protobuf_unittest::TestAllTypes* msg) {
msg->set_optional_int32(42);
}
extern "C" google::protobuf::rust_internal::SerializedData Serialize(
const protobuf_unittest::TestAllTypes* msg) {
return google::protobuf::rust_internal::SerializeMsg(msg);
}

@ -31,6 +31,10 @@
// Rust Protobuf runtime using the UPB kernel.
/// Represents UPB's upb_Arena.
use std::ops::Deref;
use std::ptr::NonNull;
use std::slice;
#[repr(C)]
pub struct Arena {
_data: [u8; 0],
@ -52,6 +56,34 @@ extern "C" {
pub fn upb_Arena_Free(arena: *mut Arena);
}
/// Represents serialized Protobuf wire format data. It's typically produced by
/// `<Message>.serialize()`.
#[derive(Debug)]
pub struct SerializedData {
data: NonNull<u8>,
len: usize,
arena: *mut Arena,
}
impl SerializedData {
pub unsafe fn from_raw_parts(arena: *mut Arena, data: NonNull<u8>, len: usize) -> Self {
SerializedData { arena, data, len }
}
}
impl Deref for SerializedData {
type Target = [u8];
fn deref(&self) -> &Self::Target {
unsafe { slice::from_raw_parts(self.data.as_ptr() as *const _, self.len) }
}
}
impl Drop for SerializedData {
fn drop(&mut self) {
unsafe { Arena::free(self.arena) };
}
}
#[cfg(test)]
mod tests {
use super::*;
@ -61,4 +93,20 @@ mod tests {
let arena = unsafe { Arena::new() };
unsafe { Arena::free(arena) };
}
#[test]
fn test_serialized_data_roundtrip() {
let arena = unsafe { Arena::new() };
let original_data = b"Hello world";
let len = original_data.len();
let serialized_data = unsafe {
SerializedData::from_raw_parts(
arena,
NonNull::new(original_data as *const _ as *mut _).unwrap(),
len,
)
};
assert_eq!(&*serialized_data, b"Hello world");
}
}

@ -515,6 +515,7 @@ cc_library(
visibility = [
"//:__pkg__",
"//pkg:__pkg__",
"//rust:__subpackages__",
"//src/google/protobuf:__subpackages__",
],
deps = [
@ -771,7 +772,10 @@ cc_proto_library(
cc_proto_library(
name = "cc_test_protos",
visibility = ["//src/google/protobuf:__subpackages__"],
visibility = [
"//rust/test:__subpackages__",
"//src/google/protobuf:__subpackages__",
],
deps = [":test_protos"],
)

@ -18,6 +18,7 @@ cc_library(
deps = [
"//src/google/protobuf:protobuf_nowkt",
"//src/google/protobuf/compiler:code_generator",
"//src/google/protobuf/compiler/cpp:names",
"@com_google_absl//absl/log:absl_check",
"@com_google_absl//absl/types:optional",
],

@ -39,6 +39,7 @@
#include "absl/strings/str_replace.h"
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "google/protobuf/compiler/cpp/names.h"
#include "google/protobuf/descriptor.h"
#include "google/protobuf/io/printer.h"
@ -156,20 +157,66 @@ void GenerateForUpb(const FileDescriptor* file, google::protobuf::io::Printer& p
}
}
std::string GetUnderscoreDelimitedFullName(const Descriptor* msg) {
std::string result = msg->full_name();
absl::StrReplaceAll({{".", "_"}}, &result);
return result;
}
void GenerateForCpp(const FileDescriptor* file, google::protobuf::io::Printer& p) {
for (int i = 0; i < file->message_type_count(); ++i) {
// TODO(b/272728844): Implement real logic
p.Emit({{"Msg", file->message_type(i)->name()}},
R"rs(
pub struct $Msg$ {
msg: ::__std::ptr::NonNull<u8>,
}
const Descriptor* msg = file->message_type(i);
p.Emit(
{
{"Msg", msg->name()},
{"pkg_Msg", GetUnderscoreDelimitedFullName(msg)},
},
R"rs(
pub struct $Msg$ {
msg: ::__std::ptr::NonNull<u8>,
}
impl $Msg$ {
pub fn new() -> Self { Self { msg: ::__std::ptr::NonNull::dangling() }}
pub fn serialize(&self) -> Vec<u8> { vec![] }
}
)rs");
impl $Msg$ {
pub fn new() -> Self {
Self {
msg: unsafe { __rust_proto_thunk__$pkg_Msg$__new() }
}
}
pub fn serialize(&self) -> ::__pb::SerializedData {
return unsafe { __rust_proto_thunk__$pkg_Msg$__serialize(self.msg) };
}
pub fn __unstable_cpp_repr_grant_permission_to_break(&mut self) -> ::__std::ptr::NonNull<u8> {
self.msg
}
}
extern "C" {
fn __rust_proto_thunk__$pkg_Msg$__new() -> ::__std::ptr::NonNull<u8>;
fn __rust_proto_thunk__$pkg_Msg$__serialize(raw_msg: ::__std::ptr::NonNull<u8>) -> ::__pb::SerializedData;
}
)rs");
}
}
void GenerateThunksForCpp(const FileDescriptor* file, google::protobuf::io::Printer& p) {
for (int i = 0; i < file->message_type_count(); ++i) {
const Descriptor* msg = file->message_type(i);
p.Emit(
{
{"Msg", msg->name()},
{"pkg_Msg", GetUnderscoreDelimitedFullName(msg)},
{"namespace", cpp::Namespace(msg)},
},
R"cc(
extern "C" {
void* __rust_proto_thunk__$pkg_Msg$__new() { return new $namespace$::$Msg$(); }
google::protobuf::rust_internal::SerializedData
__rust_proto_thunk__$pkg_Msg$__serialize($namespace$::$Msg$* msg) {
return google::protobuf::rust_internal::SerializeMsg(msg);
}
}
)cc");
}
}
@ -240,6 +287,16 @@ bool RustGenerator::Generate(const FileDescriptor* file,
break;
case Kernel::kCpp:
GenerateForCpp(file, p);
auto thunksfile = absl::WrapUnique(
generator_context->Open(absl::StrCat(basename, ".pb.thunks.cc")));
google::protobuf::io::Printer thunks(thunksfile.get());
thunks.Emit({{"basename", basename}},
R"cc(
#include "$basename$.pb.h"
#include "google/protobuf/rust/cpp_kernel/cpp_api.h"
)cc");
GenerateThunksForCpp(file, thunks);
break;
}
return true;

Loading…
Cancel
Save