From ab9f1ab58a4cab6e9bbdcd2f0ca6e38ea60566aa Mon Sep 17 00:00:00 2001 From: Marcel Hlopko Date: Thu, 16 Mar 2023 04:57:32 -0700 Subject: [PATCH] Configure the build for the Rust UPB backend In this CL we're adding the barebones infrastructure to generate Rust proto messages using UPB as a backend. The API is what we call a V0, not yet production-quality, not yet rigorously designed, just something to enable parallel work. The interesting part of switching backend between UPB and C++ will come in a followup. PiperOrigin-RevId: 517089760 --- rust/BUILD | 12 ++- rust/defs.bzl | 9 +- rust/lib.rs | 55 ++++++++++++- rust/test/BUILD | 10 ++- rust/test/child_parent_test.rs | 19 ++++- .../rust_proto_library_unit_test/empty.cc | 33 ++++++++ .../rust_proto_library_unit_test/empty.rs | 33 ++++++++ .../rust_proto_library_unit_test.bzl | 82 ++++++++++--------- rust/test/unittest_proto_test.rs | 8 +- rust/upb_backend/BUILD | 27 ++++++ rust/upb_backend/upb.rs | 53 ++++++++++++ rust/upb_backend/upb_api.c | 30 +++++++ .../protobuf/compiler/rust/generator.cc | 47 +++++++++-- 13 files changed, 356 insertions(+), 62 deletions(-) create mode 100644 rust/test/rust_proto_library_unit_test/empty.cc create mode 100644 rust/test/rust_proto_library_unit_test/empty.rs create mode 100644 rust/upb_backend/BUILD create mode 100644 rust/upb_backend/upb.rs create mode 100644 rust/upb_backend/upb_api.c diff --git a/rust/BUILD b/rust/BUILD index ac72f0781a..30dbd1e4c1 100644 --- a/rust/BUILD +++ b/rust/BUILD @@ -1,6 +1,6 @@ # Protobuf Rust runtime packages. -load("@rules_rust//rust:defs.bzl", "rust_library") +load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test") load("@rules_proto//proto:defs.bzl", "proto_lang_toolchain") package(default_visibility = ["//src/google/protobuf:__subpackages__"]) @@ -8,6 +8,16 @@ package(default_visibility = ["//src/google/protobuf:__subpackages__"]) rust_library( name = "protobuf", srcs = ["lib.rs"], + deps = ["//rust/upb_backend:upb"], +) + +rust_test( + name = "protobuf_test", + crate = ":protobuf", + tags = [ + "not_build:arm", + "notsan", + ], ) # TODO(b/270125787): Move to the right location once rust_proto_library is no longer experimental. diff --git a/rust/defs.bzl b/rust/defs.bzl index 8e7d47eaf3..af42f51fdf 100644 --- a/rust/defs.bzl +++ b/rust/defs.bzl @@ -9,6 +9,7 @@ load("@rules_rust//rust/private:providers.bzl", "CrateInfo", "DepInfo", "DepVari # buildifier: disable=bzl-visibility load("@rules_rust//rust/private:rustc.bzl", "rustc_compile_action") load("@rules_rust//rust:defs.bzl", "rust_common") +load("//third_party/upb/bazel:upb_proto_library.bzl", "UpbWrappedCcInfo", "upb_proto_library_aspect") proto_common = proto_common_do_not_use @@ -155,13 +156,18 @@ def _rust_proto_aspect_impl(target, ctx): build_info = None, ) + upb_gencode_cc_info = target[UpbWrappedCcInfo].cc_info_with_thunks + upb_gencode_dep_variant_info = DepVariantInfo(cc_info = upb_gencode_cc_info) + proto_dep = getattr(ctx.rule.attr, "deps", []) dep_variant_info = _compile_rust( ctx = ctx, attr = ctx.rule.attr, src = gencode[0], extra_srcs = gencode[1:], - deps = [dep_variant_info_for_runtime] + ([proto_dep[0][RustProtoInfo].dep_variant_info] if proto_dep else []), + deps = [dep_variant_info_for_runtime, upb_gencode_dep_variant_info] + ( + [proto_dep[0][RustProtoInfo].dep_variant_info] if proto_dep else [] + ), ) return [RustProtoInfo( dep_variant_info = dep_variant_info, @@ -170,6 +176,7 @@ def _rust_proto_aspect_impl(target, ctx): rust_proto_library_aspect = aspect( implementation = _rust_proto_aspect_impl, attr_aspects = ["deps"], + requires = [upb_proto_library_aspect], attrs = { "_cc_toolchain": attr.label( doc = ( diff --git a/rust/lib.rs b/rust/lib.rs index d860245a19..0aaeb2d97b 100644 --- a/rust/lib.rs +++ b/rust/lib.rs @@ -30,7 +30,56 @@ //! Rust Protobuf Runtime -// Not yet implemented. +pub use upb::*; -// TODO(b/270138878): Remove once we have real logic in the runtime. -pub fn do_nothing() {} +use std::ops::Deref; +use std::ptr::NonNull; +use std::slice; + +/// Represents serialized Protobuf wire format data. It's typically produced by +/// `.serialize()`. +pub struct SerializedData { + data: NonNull, + len: usize, + arena: *mut upb_Arena, +} + +impl SerializedData { + pub unsafe fn from_raw_parts(arena: *mut upb_Arena, data: NonNull, 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 { upb_Arena_Free(self.arena) }; + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_serialized_data_roundtrip() { + let arena = unsafe { upb_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"); + } +} diff --git a/rust/test/BUILD b/rust/test/BUILD index 95496f690d..780d3c67ed 100644 --- a/rust/test/BUILD +++ b/rust/test/BUILD @@ -11,7 +11,10 @@ rust_test( name = "unittest_proto_test", srcs = ["unittest_proto_test.rs"], # TODO(b/270274576): Enable testing on arm once we have a Rust Arm toolchain. - tags = ["not_build:arm"], + tags = [ + "not_build:arm", + "notsan", + ], deps = [":unittest_rs_proto"], ) @@ -41,7 +44,10 @@ rust_test( name = "child_parent_test", srcs = ["child_parent_test.rs"], # TODO(b/270274576): Enable testing on arm once we have a Rust Arm toolchain. - tags = ["not_build:arm"], + tags = [ + "not_build:arm", + "notsan", + ], deps = [ ":child_rs_proto", ":parent_rs_proto", diff --git a/rust/test/child_parent_test.rs b/rust/test/child_parent_test.rs index 0bfb332f87..04e11e9c8e 100644 --- a/rust/test/child_parent_test.rs +++ b/rust/test/child_parent_test.rs @@ -28,10 +28,21 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -fn main() { - let _child = child_proto::Child {}; - let _parent = parent_proto::Parent {}; +#[test] +fn test_canonical_types() { + let _child = child_proto::Child::new(); + let _parent = parent_proto::Parent::new(); // Parent from child_proto crate should be the same type as Parent from // parent_proto crate. - let _parent_from_child: child_proto::Parent = parent_proto::Parent {}; + let _parent_from_child: child_proto::Parent = parent_proto::Parent::new(); +} + +#[test] +fn test_parent_serialization() { + assert_eq!(*parent_proto::Parent::new().serialize(), []); +} + +#[test] +fn test_child_serialization() { + assert_eq!(*child_proto::Child::new().serialize(), []); } diff --git a/rust/test/rust_proto_library_unit_test/empty.cc b/rust/test/rust_proto_library_unit_test/empty.cc new file mode 100644 index 0000000000..25d162fd67 --- /dev/null +++ b/rust/test/rust_proto_library_unit_test/empty.cc @@ -0,0 +1,33 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 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. + +// Test input file. + +int main() { return 0; } \ No newline at end of file diff --git a/rust/test/rust_proto_library_unit_test/empty.rs b/rust/test/rust_proto_library_unit_test/empty.rs new file mode 100644 index 0000000000..ed21cb5ba5 --- /dev/null +++ b/rust/test/rust_proto_library_unit_test/empty.rs @@ -0,0 +1,33 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 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. + +// Test input file. + +fn main() {} diff --git a/rust/test/rust_proto_library_unit_test/rust_proto_library_unit_test.bzl b/rust/test/rust_proto_library_unit_test/rust_proto_library_unit_test.bzl index 6749cf9005..5379207e1c 100644 --- a/rust/test/rust_proto_library_unit_test/rust_proto_library_unit_test.bzl +++ b/rust/test/rust_proto_library_unit_test/rust_proto_library_unit_test.bzl @@ -2,6 +2,7 @@ load("@bazel_skylib//lib:unittest.bzl", "analysistest", "asserts") load(":defs.bzl", "ActionsInfo", "attach_aspect") +load("//rust:defs.bzl", "RustProtoInfo") def _find_action_with_mnemonic(actions, mnemonic): action = [a for a in actions if a.mnemonic == mnemonic] @@ -21,56 +22,58 @@ def _find_rust_lib_input(inputs, target_name): )) return input[0] +def _relevant_linker_inputs(ltl): + return ltl.objects + ltl.pic_objects + ( + [ltl.static_library] if ltl.static_library else [] + ) + ( + [ltl.pic_static_library] if ltl.pic_static_library else [] + ) + +def _find_linker_input(rust_proto_info, basename_substring): + cc_info = rust_proto_info.dep_variant_info.cc_info + for linker_input in cc_info.linking_context.linker_inputs.to_list(): + for ltl in linker_input.libraries: + for file in _relevant_linker_inputs(ltl): + if basename_substring in file.basename: + return file + + fail("Couldn't find file with suffix {} in {}".format( + basename_substring, + [ + f.basename + for input in cc_info.linking_context.linker_inputs.to_list() + for ltl in input.libraries + for f in _relevant_linker_inputs(ltl) + ], + )) + #################################################################################################### -def _rust_compilation_action_has_runtime_as_input_test_impl(ctx): +def _rust_aspect_test_impl(ctx): env = analysistest.begin(ctx) target_under_test = analysistest.target_under_test(env) actions = target_under_test[ActionsInfo].actions rustc_action = _find_action_with_mnemonic(actions, "Rustc") - _find_rust_lib_input(rustc_action.inputs, "protobuf") - asserts.true(env, rustc_action.outputs.to_list()[0].path.endswith(".rlib")) - return analysistest.end(env) - -rust_compilation_action_has_runtime_as_input_test = analysistest.make( - _rust_compilation_action_has_runtime_as_input_test_impl, -) - -def _test_rust_compilation_action_has_runtime_as_input(): - native.proto_library(name = "some_proto", srcs = ["some_proto.proto"]) - attach_aspect(name = "some_proto_with_aspect", dep = ":some_proto") - - rust_compilation_action_has_runtime_as_input_test( - name = "rust_compilation_action_has_runtime_as_input_test", - target_under_test = ":some_proto_with_aspect", - # TODO(b/270274576): Enable testing on arm once we have a Rust Arm toolchain. - tags = ["not_build:arm"], - ) + # The action needs to have the Rust runtime as an input + _find_rust_lib_input(rustc_action.inputs, "protobuf") -#################################################################################################### + # The action needs to produce a .rlib artifact (sometimes .rmeta as well, not tested here). + asserts.true(env, rustc_action.outputs.to_list()[0].path.endswith(".rlib")) -def _rust_compilation_action_has_deps_as_inputs_test_impl(ctx): - env = analysistest.begin(ctx) - target_under_test = analysistest.target_under_test(env) - actions = target_under_test[ActionsInfo].actions - rustc_action = _find_action_with_mnemonic(actions, "Rustc") - _find_rust_lib_input(rustc_action.inputs, "parent") + # The aspect needs to provide CcInfo that passes UPB gencode to the eventual linking. + _find_linker_input(target_under_test[RustProtoInfo], "child.upb.thunks") + _find_linker_input(target_under_test[RustProtoInfo], "parent.upb.thunks") return analysistest.end(env) -rust_compilation_action_has_deps_as_input_test = analysistest.make( - _rust_compilation_action_has_deps_as_inputs_test_impl, -) - -def _test_rust_compilation_action_has_deps_as_input(): - native.proto_library(name = "parent_proto", srcs = ["parent.proto"]) - native.proto_library(name = "child_proto", srcs = ["child.proto"], deps = [":parent_proto"]) +rust_aspect_test = analysistest.make(_rust_aspect_test_impl) +def _test_aspect(): attach_aspect(name = "child_proto_with_aspect", dep = ":child_proto") - rust_compilation_action_has_deps_as_input_test( - name = "rust_compilation_action_has_deps_as_input_test", + rust_aspect_test( + name = "rust_aspect_test", target_under_test = ":child_proto_with_aspect", # TODO(b/270274576): Enable testing on arm once we have a Rust Arm toolchain. tags = ["not_build:arm"], @@ -83,13 +86,14 @@ def rust_proto_library_unit_test(name): Args: name: name of the test suite""" - _test_rust_compilation_action_has_runtime_as_input() - _test_rust_compilation_action_has_deps_as_input() + native.proto_library(name = "parent_proto", srcs = ["parent.proto"]) + native.proto_library(name = "child_proto", srcs = ["child.proto"], deps = [":parent_proto"]) + + _test_aspect() native.test_suite( name = name, tests = [ - ":rust_compilation_action_has_runtime_as_input_test", - ":rust_compilation_action_has_deps_as_input_test", + ":rust_aspect_test", ], ) diff --git a/rust/test/unittest_proto_test.rs b/rust/test/unittest_proto_test.rs index 24cad65c06..73807c85c6 100644 --- a/rust/test/unittest_proto_test.rs +++ b/rust/test/unittest_proto_test.rs @@ -28,8 +28,8 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -fn main() { - // This is currently just a smoke test checking that we can generate gencode, compile it, and - // link the test binary. - let _test_all_types: unittest_proto::TestAllTypes; +#[test] +fn test_serialization() { + let test_all_types: unittest_proto::TestAllTypes = unittest_proto::TestAllTypes::new(); + assert_eq!(*test_all_types.serialize(), []); } diff --git a/rust/upb_backend/BUILD b/rust/upb_backend/BUILD new file mode 100644 index 0000000000..d51328b13a --- /dev/null +++ b/rust/upb_backend/BUILD @@ -0,0 +1,27 @@ +# This package contains Rust protobuf runtime implementation built on top of UPB. + +load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test") + +rust_library( + name = "upb", + srcs = ["upb.rs"], + visibility = ["//src/google/protobuf:__subpackages__"], + deps = [":upb_c_api"], +) + +rust_test( + name = "upb_test", + crate = ":upb", + tags = [ + "not_build:arm", + "notsan", + ], +) + +cc_library( + name = "upb_c_api", + srcs = ["upb_api.c"], + deps = [ + "//third_party/upb", + ], +) diff --git a/rust/upb_backend/upb.rs b/rust/upb_backend/upb.rs new file mode 100644 index 0000000000..35a09fa86f --- /dev/null +++ b/rust/upb_backend/upb.rs @@ -0,0 +1,53 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 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. + +// Rust bindings for UPB + +#[repr(C)] +pub struct upb_Arena { + _data: [u8; 0], + _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>, +} + +extern "C" { + pub fn upb_Arena_New() -> *mut upb_Arena; + pub fn upb_Arena_Free(arena: *mut upb_Arena); +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_arena_new_and_free() { + let arena = unsafe { upb_Arena_New() }; + unsafe { upb_Arena_Free(arena) }; + } +} diff --git a/rust/upb_backend/upb_api.c b/rust/upb_backend/upb_api.c new file mode 100644 index 0000000000..5b51fbfe3e --- /dev/null +++ b/rust/upb_backend/upb_api.c @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2009-2021, Google LLC + * All rights reserved. + * + * 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 Google LLC 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. + */ + +#define UPB_BUILD_API + +#include "third_party/upb/upb/mem/arena.h" // IWYU pragma: keep diff --git a/src/google/protobuf/compiler/rust/generator.cc b/src/google/protobuf/compiler/rust/generator.cc index 08a14eb6cc..171a2fb4dd 100644 --- a/src/google/protobuf/compiler/rust/generator.cc +++ b/src/google/protobuf/compiler/rust/generator.cc @@ -89,15 +89,11 @@ bool RustGenerator::Generate(const FileDescriptor* file, // TODO(b/270138878): Remove `do_nothing` import once we have real logic. This // is there only to smoke test rustc actions in rust_proto_library. p.Emit(R"rs( -#[allow(unused_imports)] - use protobuf::do_nothing; + extern crate protobuf as __pb; + extern crate std as __std; + )rs"); - for (int i = 0; i < file->message_type_count(); ++i) { - // TODO(b/270138878): Implement real logic - p.Emit({{"Msg", file->message_type(i)->name()}}, R"rs( - pub struct $Msg$ {} - )rs"); - } + // TODO(b/270124215): Delete the following "placeholder impl" of `import // public`. Also make sure to figure out how to map FileDescriptor#name to // Rust crate names (currently Bazel labels). @@ -114,6 +110,41 @@ bool RustGenerator::Generate(const FileDescriptor* file, } } + for (int i = 0; i < file->message_type_count(); ++i) { + // TODO(b/270138878): Implement real logic + std::string full_name = file->message_type(i)->full_name(); + absl::StrReplaceAll({{".", "_"}}, &full_name); + p.Emit({{"Msg", file->message_type(i)->name()}, {"pkg_Msg", full_name}}, + R"rs( + pub struct $Msg$ { + msg: ::__std::ptr::NonNull, + arena: *mut ::__pb::upb_Arena, + } + + impl $Msg$ { + pub fn new() -> $Msg$ { + let arena = unsafe { ::__pb::upb_Arena_New() }; + let msg = unsafe { $pkg_Msg$_new(arena) }; + $Msg$ { msg, arena } + } + pub fn serialize(&self) -> ::__pb::SerializedData { + let arena = unsafe { ::__pb::upb_Arena_New() }; + let mut len = 0; + let chars = unsafe { $pkg_Msg$_serialize(self.msg, arena, &mut len) }; + unsafe {::__pb::SerializedData::from_raw_parts(arena, chars, len)} + } + } + + extern "C" { + fn $pkg_Msg$_new(arena: *mut ::__pb::upb_Arena) -> ::__std::ptr::NonNull; + fn $pkg_Msg$_serialize( + msg: ::__std::ptr::NonNull, + arena: *mut ::__pb::upb_Arena, + len: &mut usize) -> ::__std::ptr::NonNull; + } + )rs"); + } + return true; }