diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml index af22782e82..8c509d2b2c 100644 --- a/.github/workflows/codespell.yml +++ b/.github/workflows/codespell.yml @@ -24,4 +24,4 @@ jobs: with: check_filenames: true skip: ./.git,./third_party,./conformance/third_party,*.snk,*.pb,*.pb.cc,*.pb.h,*.pb.cs,*-upb.h,*-upb.c,./src/google/protobuf/testdata,./objectivec/Tests,./python/compatibility_tests/v2.5.0/tests/google/protobuf/internal,./.github/workflows/codespell.yml - ignore_words_list: "alow,alse,atleast,ba,chec,cleare,copyable,couldn,cloneable,crate,dedup,dur,errorprone,falsy,files',fo,fundementals,hel,importd,inherting,inout,leapyear,nd,nin,ois,ons,parseable,process',ro,te,testof,ue,unparseable,wasn,wee,gae,keyserver,objext,od,optin,streem,sur,falsy,cleary" + ignore_words_list: "alow,alse,atleast,ba,chec,cleare,copyable,couldn,cloneable,crate,dedup,dur,errorprone,falsy,files',fo,fundementals,generaters,hel,importd,inherting,inout,leapyear,nd,nin,ois,ons,parseable,process',ro,te,testof,ue,unparseable,wasn,wee,gae,keyserver,objext,od,optin,streem,sur,falsy,cleary" diff --git a/src/google/protobuf/compiler/cpp/BUILD.bazel b/src/google/protobuf/compiler/cpp/BUILD.bazel index e2908b3dc5..5d23c660e5 100644 --- a/src/google/protobuf/compiler/cpp/BUILD.bazel +++ b/src/google/protobuf/compiler/cpp/BUILD.bazel @@ -33,7 +33,10 @@ cc_library( ], copts = COPTS, include_prefix = "google/protobuf/compiler/cpp", - visibility = ["//pkg:__pkg__"], + visibility = [ + "//pkg:__pkg__", + "//src/google/protobuf/compiler/rust:__subpackages__", + ], deps = [ "//src/google/protobuf:protobuf_nowkt", "//src/google/protobuf/compiler:code_generator", diff --git a/src/google/protobuf/compiler/rust/BUILD.bazel b/src/google/protobuf/compiler/rust/BUILD.bazel index a868d23a97..61d159bb34 100644 --- a/src/google/protobuf/compiler/rust/BUILD.bazel +++ b/src/google/protobuf/compiler/rust/BUILD.bazel @@ -17,30 +17,67 @@ cc_library( ], deps = [ ":context", + ":message", ":naming", ":upb_kernel", "//src/google/protobuf:protobuf_nowkt", "//src/google/protobuf/compiler:code_generator", "//src/google/protobuf/compiler/cpp:names", + "@com_google_absl//absl/algorithm:container", "@com_google_absl//absl/log:absl_check", "@com_google_absl//absl/types:optional", ], ) +cc_library( + name = "message", + srcs = ["message.cc"], + hdrs = ["message.h"], + copts = COPTS, + include_prefix = "google/protobuf/compiler/rust", + deps = [ + ":context", + ":naming", + "//src/google/protobuf:protobuf_nowkt", + "//src/google/protobuf/compiler/cpp:names", + ":accessors", + "@com_google_absl//absl/log:absl_check", + "@com_google_absl//absl/log:absl_log", + ], +) + +cc_library( + name = "accessors", + srcs = [ + "accessors/accessors.cc", + "accessors/singular_bytes.cc", + "accessors/singular_scalar.cc", + ], + hdrs = ["accessors/accessors.h"], + copts = COPTS, + include_prefix = "google/protobuf/compiler/rust", + deps = [ + "//src/google/protobuf:protobuf_nowkt", + "//src/google/protobuf/compiler/cpp:names_internal", + ":context", + ":naming", + "@com_google_absl//absl/log:absl_check", + "@com_google_absl//absl/strings", + ], +) + + cc_library( name = "context", srcs = ["context.cc"], hdrs = ["context.h"], copts = COPTS, include_prefix = "google/protobuf/compiler/rust", - visibility = [ - "//pkg:__pkg__", - "//src/google/protobuf/compiler:__pkg__", - ], deps = [ "//src/google/protobuf/compiler:code_generator", "//src/google/protobuf/io:printer", "@com_google_absl//absl/algorithm:container", + "@com_google_absl//absl/log:absl_log", "@com_google_absl//absl/status", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", @@ -54,10 +91,6 @@ cc_library( hdrs = ["naming.h"], copts = COPTS, include_prefix = "google/protobuf/compiler/rust", - visibility = [ - "//pkg:__pkg__", - "//src/google/protobuf/compiler:__pkg__", - ], deps = [ ":context", "//src/google/protobuf:protobuf_nowkt", diff --git a/src/google/protobuf/compiler/rust/accessors/accessors.cc b/src/google/protobuf/compiler/rust/accessors/accessors.cc new file mode 100644 index 0000000000..26ff03e2ac --- /dev/null +++ b/src/google/protobuf/compiler/rust/accessors/accessors.cc @@ -0,0 +1,67 @@ +// 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 "google/protobuf/compiler/rust/accessors/accessors.h" + +#include + +#include "google/protobuf/compiler/rust/context.h" +#include "google/protobuf/descriptor.h" +#include "google/protobuf/descriptor.pb.h" + +namespace google { +namespace protobuf { +namespace compiler { +namespace rust { +std::unique_ptr AccessorGenerator::For( + Context field) { + // We do not support [ctype=FOO] (used to set the field type in C++ to + // cord or string_piece) in V0 API. + if (field.desc().options().has_ctype()) { + return nullptr; + } + + switch (field.desc().type()) { + case FieldDescriptor::TYPE_INT64: + case FieldDescriptor::TYPE_BOOL: + if (field.desc().is_repeated()) return nullptr; + return ForSingularScalar(field); + case FieldDescriptor::TYPE_BYTES: + if (field.desc().is_repeated() || field.is_upb()) return nullptr; + return ForSingularBytes(field); + + default: + return nullptr; + } +} +} // namespace rust +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/rust/accessors/accessors.h b/src/google/protobuf/compiler/rust/accessors/accessors.h new file mode 100644 index 0000000000..d384ba76b3 --- /dev/null +++ b/src/google/protobuf/compiler/rust/accessors/accessors.h @@ -0,0 +1,103 @@ +// 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. + +#ifndef GOOGLE_PROTOBUF_COMPILER_RUST_ACCESSORS_ACCESSORS_H__ +#define GOOGLE_PROTOBUF_COMPILER_RUST_ACCESSORS_ACCESSORS_H__ + +#include + +#include "absl/log/absl_check.h" +#include "google/protobuf/compiler/rust/context.h" +#include "google/protobuf/descriptor.h" + +namespace google { +namespace protobuf { +namespace compiler { +namespace rust { + +class AccessorGenerator { + public: + AccessorGenerator() = default; + + AccessorGenerator(const AccessorGenerator &) = delete; + AccessorGenerator(AccessorGenerator &&) = delete; + AccessorGenerator &operator=(const AccessorGenerator &) = delete; + AccessorGenerator &operator=(AccessorGenerator &&) = delete; + + virtual ~AccessorGenerator(); + + // Constructs a generator for the given field. + // + // Returns `nullptr` if there is no known generator for this field. + static std::unique_ptr For(Context field); + + void GenerateMsgImpl(Context field) const { + InMsgImpl(field); + } + void GenerateExternC(Context field) const { + InExternC(field); + } + void GenerateThunkCc(Context field) const { + ABSL_CHECK(field.is_cpp()); + InThunkCc(field); + } + + private: + // Note: the virtual functions are duplicated as non-virtual public functions, + // so that we can customize prologue and epilogue behavior for these + // functions. For example, consider calling `field.printer.WithVars()` as a + // prologue to inject variables automatically. + + // Called inside the main inherent `impl Msg {}` block. + virtual void InMsgImpl(Context field) const {} + + // Called inside of a message's `extern "C" {}` block. + virtual void InExternC(Context field) const {} + + // Called inside of an `extern "C" {}` block in the `.thunk.cc` file, if such + // a file is being generated. + virtual void InThunkCc(Context field) const {} + + // These static factories are defined in the corresponding implementation + // files for each implementation of AccessorGenerator. + static std::unique_ptr ForSingularScalar( + Context field); + static std::unique_ptr ForSingularBytes( + Context field); +}; + +inline AccessorGenerator::~AccessorGenerator() = default; + +} // namespace rust +} // namespace compiler +} // namespace protobuf +} // namespace google + +#endif // GOOGLE_PROTOBUF_COMPILER_RUST_ACCESSORS_ACCESSORS_H__ diff --git a/src/google/protobuf/compiler/rust/accessors/singular_bytes.cc b/src/google/protobuf/compiler/rust/accessors/singular_bytes.cc new file mode 100644 index 0000000000..a5b0171322 --- /dev/null +++ b/src/google/protobuf/compiler/rust/accessors/singular_bytes.cc @@ -0,0 +1,128 @@ +// 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 + +#include "absl/strings/string_view.h" +#include "google/protobuf/compiler/cpp/helpers.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 { +class SingularBytes final : public AccessorGenerator { + public: + ~SingularBytes() override = default; + + void InMsgImpl(Context field) const override { + field.Emit( + { + {"field", field.desc().name()}, + {"hazzer_thunk", GetAccessorThunkName(field, "has")}, + {"getter_thunk", GetAccessorThunkName(field, "")}, + {"setter_thunk", GetAccessorThunkName(field, "set")}, + {"clearer_thunk", GetAccessorThunkName(field, "clear")}, + }, + R"rs( + pub fn $field$(&self) -> Option<&[u8]> { + if !unsafe { $hazzer_thunk$(self.msg) } { + return None; + } + unsafe { + let val = $getter_thunk$(self.msg); + Some($std$::slice::from_raw_parts(val.ptr, val.len)) + } + } + pub fn $field$_set(&mut self, val: Option<&[u8]>) { + match val { + Some(val) => unsafe { $setter_thunk$(self.msg, val.as_ptr(), val.len()) }, + None => unsafe { $clearer_thunk$(self.msg) }, + } + } + )rs"); + } + + void InExternC(Context field) const override { + field.Emit( + { + {"hazzer_thunk", GetAccessorThunkName(field, "has")}, + {"getter_thunk", GetAccessorThunkName(field, "")}, + {"setter_thunk", GetAccessorThunkName(field, "set")}, + {"clearer_thunk", GetAccessorThunkName(field, "clear")}, + }, + R"rs( + fn $hazzer_thunk$(raw_msg: $NonNull$) -> bool; + fn $getter_thunk$(raw_msg: $NonNull$) -> $pb$::PtrAndLen; + fn $setter_thunk$(raw_msg: $NonNull$, val: *const u8, len: usize); + fn $clearer_thunk$(raw_msg: $NonNull$); + )rs"); + } + + void InThunkCc(Context field) const override { + field.Emit( + { + {"field", field.desc().name()}, + {"namespace", cpp::Namespace(field.desc().containing_type())}, + {"hazzer_thunk", GetAccessorThunkName(field, "has")}, + {"getter_thunk", GetAccessorThunkName(field, "")}, + {"setter_thunk", GetAccessorThunkName(field, "set")}, + {"clearer_thunk", GetAccessorThunkName(field, "clear")}, + }, + R"cc( + bool $hazzer_thunk$($namespace$::$Msg$* msg) { + return msg->has_$field$(); + } + ::google::protobuf::rust_internal::PtrAndLen $getter_thunk$($namespace$::$Msg$* msg) { + absl::string_view val = msg->$field$(); + return google::protobuf::rust_internal::PtrAndLen(val.data(), val.size()); + } + void $setter_thunk$($namespace$::$Msg$* msg, const char* ptr, + ::std::size_t size) { + msg->set_$field$(absl::string_view(ptr, size)); + } + void $clearer_thunk$($namespace$::$Msg$* msg) { msg->clear_$field$(); } + )cc"); + } +}; +} // namespace + +std::unique_ptr AccessorGenerator::ForSingularBytes( + Context field) { + return std::make_unique(); +} +} // namespace rust +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/rust/accessors/singular_scalar.cc b/src/google/protobuf/compiler/rust/accessors/singular_scalar.cc new file mode 100644 index 0000000000..5797a19b38 --- /dev/null +++ b/src/google/protobuf/compiler/rust/accessors/singular_scalar.cc @@ -0,0 +1,124 @@ +// 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 + +#include "absl/strings/string_view.h" +#include "google/protobuf/compiler/cpp/helpers.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 { +class SingularScalar final : public AccessorGenerator { + public: + ~SingularScalar() override = default; + + void InMsgImpl(Context field) const override { + field.Emit( + { + {"field", field.desc().name()}, + {"Scalar", PrimitiveRsTypeName(field)}, + {"hazzer_thunk", GetAccessorThunkName(field, "has")}, + {"getter_thunk", GetAccessorThunkName(field, "")}, + {"setter_thunk", GetAccessorThunkName(field, "set")}, + {"clearer_thunk", GetAccessorThunkName(field, "clear")}, + }, + R"rs( + pub fn $field$(&self) -> Option<$Scalar$> { + if !unsafe { $hazzer_thunk$(self.msg) } { + return None; + } + Some(unsafe { $getter_thunk$(self.msg) }) + } + pub fn $field$_set(&mut self, val: Option<$Scalar$>) { + match val { + Some(val) => unsafe { $setter_thunk$(self.msg, val) }, + None => unsafe { $clearer_thunk$(self.msg) }, + } + } + )rs"); + } + + void InExternC(Context field) const override { + field.Emit( + { + {"Scalar", PrimitiveRsTypeName(field)}, + {"hazzer_thunk", GetAccessorThunkName(field, "has")}, + {"getter_thunk", GetAccessorThunkName(field, "")}, + {"setter_thunk", GetAccessorThunkName(field, "set")}, + {"clearer_thunk", GetAccessorThunkName(field, "clear")}, + }, + R"rs( + fn $hazzer_thunk$(raw_msg: $NonNull$) -> bool; + fn $getter_thunk$(raw_msg: $NonNull$) -> $Scalar$; + fn $setter_thunk$(raw_msg: $NonNull$, val: $Scalar$); + fn $clearer_thunk$(raw_msg: $NonNull$); + )rs"); + } + + void InThunkCc(Context field) const override { + field.Emit( + { + {"field", field.desc().name()}, + {"Scalar", cpp::PrimitiveTypeName(field.desc().cpp_type())}, + {"namespace", cpp::Namespace(field.desc().containing_type())}, + {"hazzer_thunk", GetAccessorThunkName(field, "has")}, + {"getter_thunk", GetAccessorThunkName(field, "")}, + {"setter_thunk", GetAccessorThunkName(field, "set")}, + {"clearer_thunk", GetAccessorThunkName(field, "clear")}, + }, + R"cc( + bool $hazzer_thunk$($namespace$::$Msg$* msg) { + return msg->has_$field$(); + } + $Scalar$ $getter_thunk$($namespace$::$Msg$* msg) { return msg->$field$(); } + void $setter_thunk$($namespace$::$Msg$* msg, $Scalar$ val) { + msg->set_$field$(val); + } + void $clearer_thunk$($namespace$::$Msg$* msg) { msg->clear_$field$(); } + )cc"); + } +}; +} // namespace + +std::unique_ptr AccessorGenerator::ForSingularScalar( + Context field) { + return std::make_unique(); +} +} // namespace rust +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/rust/context.h b/src/google/protobuf/compiler/rust/context.h index f3633816c4..376053c47e 100644 --- a/src/google/protobuf/compiler/rust/context.h +++ b/src/google/protobuf/compiler/rust/context.h @@ -31,6 +31,7 @@ #ifndef GOOGLE_PROTOBUF_COMPILER_RUST_CONTEXT_H__ #define GOOGLE_PROTOBUF_COMPILER_RUST_CONTEXT_H__ +#include "absl/log/absl_log.h" #include "absl/status/statusor.h" #include "absl/strings/string_view.h" #include "absl/types/span.h" @@ -46,6 +47,18 @@ enum class Kernel { kCpp, }; +inline absl::string_view KernelRsName(Kernel kernel) { + switch (kernel) { + case Kernel::kUpb: + return "upb"; + case Kernel::kCpp: + return "cpp"; + default: + ABSL_LOG(FATAL) << "Unknown kernel type: " << static_cast(kernel); + return ""; + } +} + // Global options for a codegen invocation. struct Options { Kernel kernel; diff --git a/src/google/protobuf/compiler/rust/generator.cc b/src/google/protobuf/compiler/rust/generator.cc index 7a432794b1..4a1d6dd072 100644 --- a/src/google/protobuf/compiler/rust/generator.cc +++ b/src/google/protobuf/compiler/rust/generator.cc @@ -30,30 +30,29 @@ #include "google/protobuf/compiler/rust/generator.h" +#include #include +#include -#include "absl/log/absl_log.h" +#include "absl/algorithm/container.h" #include "absl/memory/memory.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/str_join.h" -#include "absl/strings/str_replace.h" #include "absl/strings/str_split.h" #include "absl/strings/string_view.h" -#include "absl/strings/substitute.h" #include "google/protobuf/compiler/code_generator.h" -#include "google/protobuf/compiler/cpp/helpers.h" #include "google/protobuf/compiler/cpp/names.h" #include "google/protobuf/compiler/rust/context.h" +#include "google/protobuf/compiler/rust/message.h" #include "google/protobuf/compiler/rust/naming.h" -#include "google/protobuf/compiler/rust/upb_kernel.h" #include "google/protobuf/descriptor.h" #include "google/protobuf/descriptor.pb.h" #include "google/protobuf/io/printer.h" +#include "google/protobuf/io/zero_copy_stream.h" namespace google { namespace protobuf { namespace compiler { namespace rust { +namespace { void EmitOpeningOfPackageModules(Context file) { if (file.desc().package().empty()) return; for (absl::string_view segment : absl::StrSplit(file.desc().package(), '.')) { @@ -69,291 +68,14 @@ void EmitClosingOfPackageModules(Context file) { std::vector segments = absl::StrSplit(file.desc().package(), '.'); absl::c_reverse(segments); - for (absl::string_view segment : segments) { - file.Emit({{"segment", segment}}, - R"rs( - } // mod $segment$ - )rs"); - } -} - -void EmitGetterExpr(Context field) { - std::string thunk_name = GetAccessorThunkName(field, "get"); - switch (field.desc().type()) { - case FieldDescriptor::TYPE_BYTES: - field.Emit({{"getter_thunk_name", thunk_name}}, - R"rs( - let val = unsafe { $getter_thunk_name$(self.msg) }; - Some(unsafe { ::__std::slice::from_raw_parts(val.ptr, val.len) }) - )rs"); - return; - default: - field.Emit({{"getter_thunk_name", thunk_name}}, - R"rs( - Some(unsafe { $getter_thunk_name$(self.msg) }) - )rs"); - } -} - -void GenerateAccessorFns(Context msg) { - for (int i = 0; i < msg.desc().field_count(); ++i) { - auto field = msg.WithDesc(msg.desc().field(i)); - if (!IsSupportedFieldType(field)) { - continue; - } - field.Emit( - { - {"field_name", field.desc().name()}, - {"FieldType", PrimitiveRsTypeName(field)}, - {"hazzer_thunk_name", GetAccessorThunkName(field, "has")}, - {"getter_thunk_name", GetAccessorThunkName(field, "get")}, - {"getter_expr", [&] { EmitGetterExpr(field); }}, - {"setter_thunk_name", GetAccessorThunkName(field, "set")}, - {"setter_args", - [&] { - switch (field.desc().type()) { - case FieldDescriptor::TYPE_BYTES: - field.Emit("val.as_ptr(), val.len()"); - return; - default: - field.Emit("val"); - } - }}, - {"clearer_thunk_name", GetAccessorThunkName(field, "clear")}, - }, - R"rs( - pub fn $field_name$(&self) -> Option<$FieldType$> { - if !unsafe { $hazzer_thunk_name$(self.msg) } { - return None; - } - $getter_expr$ - } - pub fn $field_name$_set(&mut self, val: Option<$FieldType$>) { - match val { - Some(val) => unsafe { $setter_thunk_name$(self.msg, $setter_args$) }, - None => unsafe { $clearer_thunk_name$(self.msg) }, - } - } - )rs"); - } -} - -void GenerateAccessorThunkRsDeclarations(Context msg) { - for (int i = 0; i < msg.desc().field_count(); ++i) { - auto field = msg.WithDesc(msg.desc().field(i)); - if (!IsSupportedFieldType(field)) { - continue; - } - absl::string_view type_name = PrimitiveRsTypeName(field); - field.Emit( - { - {"FieldType", type_name}, - {"GetterReturnType", - [&] { - switch (field.desc().type()) { - case FieldDescriptor::TYPE_BYTES: - field.Emit("::__pb::PtrAndLen"); - return; - default: - field.Emit(type_name); - } - }}, - {"hazzer_thunk_name", GetAccessorThunkName(field, "has")}, - {"getter_thunk_name", GetAccessorThunkName(field, "get")}, - {"setter_thunk_name", GetAccessorThunkName(field, "set")}, - {"setter_params", - [&] { - switch (field.desc().type()) { - case FieldDescriptor::TYPE_BYTES: - field.Emit("val: *const u8, len: usize"); - return; - default: - field.Emit({{"type_name", type_name}}, "val: $type_name$"); - } - }}, - {"clearer_thunk_name", GetAccessorThunkName(field, "clear")}, - }, - R"rs( - fn $hazzer_thunk_name$(raw_msg: ::__std::ptr::NonNull) -> bool; - fn $getter_thunk_name$(raw_msg: ::__std::ptr::NonNull) -> $GetterReturnType$;; - fn $setter_thunk_name$(raw_msg: ::__std::ptr::NonNull, $setter_params$); - fn $clearer_thunk_name$(raw_msg: ::__std::ptr::NonNull); - )rs"); - } -} - -void GenerateAccessorThunksCcDefinitions(Context msg) { - for (int i = 0; i < msg.desc().field_count(); ++i) { - auto field = msg.WithDesc(msg.desc().field(i)); - if (!IsSupportedFieldType(field)) { - continue; - } - absl::string_view type_name = - cpp::PrimitiveTypeName(field.desc().cpp_type()); - field.Emit( - {{"field_name", field.desc().name()}, - {"FieldType", type_name}, - {"GetterReturnType", - [&] { - switch (field.desc().type()) { - case FieldDescriptor::TYPE_BYTES: - field.Emit("::google::protobuf::rust_internal::PtrAndLen"); - return; - default: - field.Emit(type_name); - } - }}, - {"namespace", cpp::Namespace(&field.desc())}, - {"hazzer_thunk_name", GetAccessorThunkName(field, "has")}, - {"getter_thunk_name", GetAccessorThunkName(field, "get")}, - {"getter_body", - [&] { - switch (field.desc().type()) { - case FieldDescriptor::TYPE_BYTES: - field.Emit({{"field_name", field.desc().name()}}, R"cc( - absl::string_view val = msg->$field_name$(); - return google::protobuf::rust_internal::PtrAndLen(val.data(), val.size()); - )cc"); - return; - default: - field.Emit(R"cc(return msg->$field_name$();)cc"); - } - }}, - {"setter_thunk_name", GetAccessorThunkName(field, "set")}, - {"setter_params", - [&] { - switch (field.desc().type()) { - case FieldDescriptor::TYPE_BYTES: - field.Emit("const char* ptr, size_t size"); - return; - default: - field.Emit({{"type_name", type_name}}, "$type_name$ val"); - } - }}, - {"setter_args", - [&] { - switch (field.desc().type()) { - case FieldDescriptor::TYPE_BYTES: - field.Emit("absl::string_view(ptr, size)"); - return; - default: - field.Emit("val"); - } - }}, - {"clearer_thunk_name", GetAccessorThunkName(field, "clear")}}, - R"cc( - extern "C" { - bool $hazzer_thunk_name$($namespace$::$Msg$* msg) { - return msg->has_$field_name$(); - } - $GetterReturnType$ $getter_thunk_name$($namespace$::$Msg$* msg) { - $getter_body$ - } - void $setter_thunk_name$($namespace$::$Msg$* msg, $setter_params$) { - msg->set_$field_name$($setter_args$); - } - void $clearer_thunk_name$($namespace$::$Msg$* msg) { - msg->clear_$field_name$(); - } - } - )cc"); - } -} - -void GenerateForCpp(Context file) { - for (int i = 0; i < file.desc().message_type_count(); ++i) { - auto msg = file.WithDesc(file.desc().message_type(i)); - msg.Emit( - { - {"Msg", msg.desc().name()}, - {"pkg_Msg", GetUnderscoreDelimitedFullName(msg)}, - {"accessor_fns", [&] { GenerateAccessorFns(msg); }}, - {"accessor_thunks", - [&] { GenerateAccessorThunkRsDeclarations(msg); }}, - }, - R"rs( - #[allow(non_camel_case_types)] - pub struct $Msg$ { - msg: ::__std::ptr::NonNull, - } - - 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 { - self.msg - } - pub fn deserialize(&mut self, data: &[u8]) -> Result<(), ::__pb::ParseError> { - let success = unsafe { __rust_proto_thunk__$pkg_Msg$__deserialize( - self.msg, - ::__pb::SerializedData::from_raw_parts( - ::__std::ptr::NonNull::new(data.as_ptr() as *mut _).unwrap(), - data.len())) - }; - success.then_some(()).ok_or(::__pb::ParseError) - } - $accessor_fns$ - } - extern "C" { - fn __rust_proto_thunk__$pkg_Msg$__new() -> ::__std::ptr::NonNull; - fn __rust_proto_thunk__$pkg_Msg$__serialize(raw_msg: ::__std::ptr::NonNull) -> ::__pb::SerializedData; - fn __rust_proto_thunk__$pkg_Msg$__deserialize(raw_msg: ::__std::ptr::NonNull, data: ::__pb::SerializedData) -> bool; - - $accessor_thunks$ - } - )rs"); - } -} - -void GenerateThunksForCpp(Context file) { - for (int i = 0; i < file.desc().message_type_count(); ++i) { - auto msg = file.WithDesc(file.desc().message_type(i)); - file.Emit( - { - {"Msg", msg.desc().name()}, - {"pkg_Msg", GetUnderscoreDelimitedFullName(msg)}, - {"namespace", cpp::Namespace(&msg.desc())}, - {"accessor_thunks", - [&] { GenerateAccessorThunksCcDefinitions(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); - } - - bool __rust_proto_thunk__$pkg_Msg$__deserialize( - $namespace$::$Msg$* msg, - google::protobuf::rust_internal::SerializedData data) { - return msg->ParseFromArray(data.data, data.len); - } - - $accessor_thunks$ - } - )cc"); - } -} - -std::string GetKernelRustName(Kernel kernel) { - switch (kernel) { - case Kernel::kUpb: - return "upb"; - case Kernel::kCpp: - return "cpp"; + for (absl::string_view segment : segments) { + file.Emit({{"segment", segment}}, R"rs( + } // mod $segment$ + )rs"); } - ABSL_LOG(FATAL) << "Unknown kernel type: " << static_cast(kernel); - return ""; } +} // namespace bool RustGenerator::Generate(const FileDescriptor* file_desc, const std::string& parameter, @@ -371,7 +93,14 @@ bool RustGenerator::Generate(const FileDescriptor* file_desc, io::Printer printer(outfile.get()); file = file.WithPrinter(&printer); - file.Emit({{"kernel", GetKernelRustName(opts->kernel)}}, R"rs( + // Convenience shorthands for common symbols. + auto v = file.printer().WithVars({ + {"std", "::__std"}, + {"pb", "::__pb"}, + {"NonNull", "::__std::ptr::NonNull"}, + }); + + file.Emit({{"kernel", KernelRsName(file.opts().kernel)}}, R"rs( extern crate protobuf_$kernel$ as __pb; extern crate std as __std; @@ -387,33 +116,46 @@ bool RustGenerator::Generate(const FileDescriptor* file_desc, for (int j = 0; j < dep.desc().message_type_count(); ++j) { auto msg = file.WithDesc(dep.desc().message_type(j)); // TODO(b/272728844): Implement real logic - file.Emit({{"crate", crate_name}, - {"pkg::Msg", GetCrateRelativeQualifiedPath(msg)}}, - R"rs( - pub use $crate$::$pkg::Msg$; - )rs"); + file.Emit( + { + {"crate", crate_name}, + {"pkg::Msg", GetCrateRelativeQualifiedPath(msg)}, + }, + R"rs( + pub use $crate$::$pkg::Msg$; + )rs"); } } - switch (opts->kernel) { - case Kernel::kUpb: - rust::UpbKernel().Generate(&file.desc(), printer); - break; - case Kernel::kCpp: - GenerateForCpp(file); + std::unique_ptr thunks_cc; + std::unique_ptr thunks_printer; + if (file.is_cpp()) { + thunks_cc.reset(generator_context->Open(GetThunkCcFile(file))); + thunks_printer = std::make_unique(thunks_cc.get()); - auto thunksfile = - absl::WrapUnique(generator_context->Open(GetThunkCcFile(file))); - google::protobuf::io::Printer thunks(thunksfile.get()); - auto thunk_file = file.WithPrinter(&thunks); - - thunk_file.Emit({{"proto_h", GetHeaderFile(file)}}, - R"cc( + thunks_printer->Emit({{"proto_h", GetHeaderFile(file)}}, + R"cc( #include "$proto_h$" #include "google/protobuf/rust/cpp_kernel/cpp_api.h" - )cc"); - GenerateThunksForCpp(thunk_file); - break; + )cc"); + } + + for (int i = 0; i < file.desc().message_type_count(); ++i) { + auto msg = file.WithDesc(file.desc().message_type(i)); + + MessageGenerator gen(msg); + gen.GenerateRs(msg); + msg.printer().PrintRaw("\n"); + + if (file.is_cpp()) { + auto thunks_msg = msg.WithPrinter(thunks_printer.get()); + + thunks_msg.Emit({{"Msg", msg.desc().full_name()}}, R"cc( + // $Msg$ + )cc"); + gen.GenerateThunkCc(thunks_msg); + thunks_msg.printer().PrintRaw("\n"); + } } EmitClosingOfPackageModules(file); return true; diff --git a/src/google/protobuf/compiler/rust/message.cc b/src/google/protobuf/compiler/rust/message.cc new file mode 100644 index 0000000000..ba22d696ec --- /dev/null +++ b/src/google/protobuf/compiler/rust/message.cc @@ -0,0 +1,302 @@ +// 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 "google/protobuf/compiler/rust/message.h" + +#include "absl/log/absl_check.h" +#include "absl/log/absl_log.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 msg) { + switch (msg.opts().kernel) { + case Kernel::kCpp: + msg.Emit(R"rs( + msg: $NonNull$, + )rs"); + return; + + case Kernel::kUpb: + msg.Emit(R"rs( + msg: $NonNull$, + arena: *mut $pb$::Arena, + )rs"); + return; + } + + ABSL_LOG(FATAL) << "unreachable"; +} + +void MessageNew(Context msg) { + switch (msg.opts().kernel) { + case Kernel::kCpp: + msg.Emit(R"rs( + Self { msg: unsafe { __rust_proto_thunk__$pkg_Msg$__new() } } + )rs"); + return; + + case Kernel::kUpb: + msg.Emit(R"rs( + let arena = unsafe { $pb$::Arena::new() }; + let msg = unsafe { $pkg_Msg$_new(arena) }; + $Msg$ { msg, arena } + )rs"); + return; + } + + ABSL_LOG(FATAL) << "unreachable"; +} + +void MessageSerialize(Context msg) { + switch (msg.opts().kernel) { + case Kernel::kCpp: + msg.Emit(R"rs( + unsafe { __rust_proto_thunk__$pkg_Msg$__serialize(self.msg) } + )rs"); + return; + + case Kernel::kUpb: + msg.Emit(R"rs( + let arena = unsafe { $pb$::__runtime::upb_Arena_New() }; + let mut len = 0; + unsafe { + let data = $pkg_Msg$_serialize(self.msg, arena, &mut len); + $pb$::SerializedData::from_raw_parts(arena, data, len) + } + )rs"); + return; + } + + ABSL_LOG(FATAL) << "unreachable"; +} + +void MessageDeserialize(Context msg) { + switch (msg.opts().kernel) { + case Kernel::kCpp: + msg.Emit(R"rs( + let success = unsafe { + let data = $pb$::SerializedData::from_raw_parts( + $NonNull$::new(data.as_ptr() as *mut _).unwrap(), + data.len(), + ); + + __rust_proto_thunk__$pkg_Msg$__deserialize(self.msg, data) + }; + success.then_some(()).ok_or($pb$::ParseError) + )rs"); + return; + + case Kernel::kUpb: + msg.Emit(R"rs( + let _ = data; + $std$::unimplemented!() + )rs"); + return; + } + + ABSL_LOG(FATAL) << "unreachable"; +} + +void MessageExterns(Context msg) { + switch (msg.opts().kernel) { + case Kernel::kCpp: + msg.Emit(R"rs( + fn __rust_proto_thunk__$pkg_Msg$__new() -> $NonNull$; + fn __rust_proto_thunk__$pkg_Msg$__serialize(raw_msg: $NonNull$) + -> $pb$::SerializedData; + fn __rust_proto_thunk__$pkg_Msg$__deserialize( + raw_msg: $NonNull$, + data: $pb$::SerializedData, + ) -> bool; + )rs"); + return; + + case Kernel::kUpb: + msg.Emit(R"rs( + fn $pkg_Msg$_new(arena: *mut $pb$::Arena) -> $NonNull$; + fn $pkg_Msg$_serialize( + msg: $NonNull$, + arena: *mut $pb$::Arena, + len: &mut usize) -> $NonNull$; + )rs"); + return; + } + + ABSL_LOG(FATAL) << "unreachable"; +} +} // namespace + +MessageGenerator::MessageGenerator(Context 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 msg) { + msg.Emit( + { + {"Msg", msg.desc().name()}, + {"pkg_Msg", GetUnderscoreDelimitedFullName(msg)}, + {"Msg.fields", [&] { MessageStructFields(msg); }}, + {"Msg::new", [&] { MessageNew(msg); }}, + {"Msg::serialize", [&] { MessageSerialize(msg); }}, + {"Msg::deserialize", [&] { MessageDeserialize(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"); + } + }}, + }, + R"rs( + #[allow(non_camel_case_types)] + pub struct $Msg$ { + $Msg.fields$ + } + + impl $Msg$ { + pub fn new() -> Self { + $Msg::new$ + } + + pub fn serialize(&self) -> $pb$::SerializedData { + $Msg::serialize$ + } + pub fn deserialize(&mut self, data: &[u8]) -> Result<(), $pb$::ParseError> { + $Msg::deserialize$ + } + + $accessor_fns$ + } // impl $Msg$ + + extern "C" { + $Msg_externs$ + + $accessor_externs$ + } // extern "C" for $Msg$ + )rs"); + + if (msg.is_cpp()) { + msg.printer().PrintRaw("\n"); + msg.Emit({{"Msg", msg.desc().name()}}, R"rs( + impl $Msg$ { + pub fn __unstable_cpp_repr_grant_permission_to_break(&mut self) -> $NonNull$ { + self.msg + } + } + )rs"); + } +} + +// Generates code for a particular message in `.pb.thunk.cc`. +void MessageGenerator::GenerateThunkCc(Context msg) { + ABSL_CHECK(msg.is_cpp()); + + msg.Emit( + { + {"abi", "\"C\""}, // Workaround for syntax highlight bug in VSCode. + {"Msg", msg.desc().name()}, + {"pkg_Msg", GetUnderscoreDelimitedFullName(msg)}, + {"namespace", cpp::Namespace(&msg.desc())}, + {"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( + extern $abi$ { + 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); + } + + bool __rust_proto_thunk__$pkg_Msg$__deserialize( + $namespace$::$Msg$ * msg, + google::protobuf::rust_internal::SerializedData data) { + return msg->ParseFromArray(data.data, data.len); + } + + $accessor_thunks$ + } // extern $abi$ + )cc"); +} + +} // namespace rust +} // namespace compiler +} // namespace protobuf +} // namespace google diff --git a/src/google/protobuf/compiler/rust/message.h b/src/google/protobuf/compiler/rust/message.h new file mode 100644 index 0000000000..e205ed6f8e --- /dev/null +++ b/src/google/protobuf/compiler/rust/message.h @@ -0,0 +1,65 @@ +// 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. + +#ifndef GOOGLE_PROTOBUF_COMPILER_RUST_MESSAGE_H__ +#define GOOGLE_PROTOBUF_COMPILER_RUST_MESSAGE_H__ + +#include +#include + +#include "google/protobuf/compiler/rust/accessors/accessors.h" +#include "google/protobuf/compiler/rust/context.h" +#include "google/protobuf/descriptor.h" + +namespace google { +namespace protobuf { +namespace compiler { +namespace rust { + +class MessageGenerator final { + public: + explicit MessageGenerator(Context msg); + + // Generates code for a particular message in `.pb.rs`. + void GenerateRs(Context msg); + + // Generates code for a particular message in `.pb.thunk.cc`. + void GenerateThunkCc(Context msg); + + private: + std::vector> accessors_; +}; + +} // namespace rust +} // namespace compiler +} // namespace protobuf +} // namespace google + +#endif // GOOGLE_PROTOBUF_COMPILER_RUST_MESSAGE_H__ diff --git a/src/google/protobuf/compiler/rust/naming.cc b/src/google/protobuf/compiler/rust/naming.cc index 7dc02ab8e6..3e21a6e0c0 100644 --- a/src/google/protobuf/compiler/rust/naming.cc +++ b/src/google/protobuf/compiler/rust/naming.cc @@ -34,7 +34,6 @@ #include "absl/log/absl_log.h" #include "absl/strings/str_cat.h" -#include "absl/strings/str_join.h" #include "absl/strings/str_replace.h" #include "absl/strings/string_view.h" #include "absl/strings/substitute.h" @@ -83,10 +82,17 @@ std::string GetUnderscoreDelimitedFullName(Context msg) { std::string GetAccessorThunkName(Context field, absl::string_view op) { - std::string thunk = "__rust_proto_thunk__"; - absl::StrAppend(&thunk, GetUnderscoreDelimitedFullName( - field.WithDesc(field.desc().containing_type()))); - absl::SubstituteAndAppend(&thunk, "_$0_$1", op, field.desc().name()); + absl::string_view prefix = field.is_cpp() ? "__rust_proto_thunk__" : ""; + std::string thunk = + absl::StrCat(prefix, GetUnderscoreDelimitedFullName( + field.WithDesc(field.desc().containing_type()))); + + if (field.is_upb() && op.empty()) { + absl::SubstituteAndAppend(&thunk, "_$0", field.desc().name()); + } else { + absl::SubstituteAndAppend(&thunk, "_$0_$1", op, field.desc().name()); + } + return thunk; } @@ -124,6 +130,23 @@ std::string RustModule(Context msg) { std::string GetCrateRelativeQualifiedPath(Context msg) { return absl::StrCat(RustModule(msg), "::", msg.desc().name()); } + +std::string FieldInfoComment(Context field) { + absl::string_view label = + field.desc().is_repeated() ? "repeated" : "optional"; + std::string comment = + absl::StrCat(field.desc().name(), ": ", label, " ", + FieldDescriptor::TypeName(field.desc().type())); + + if (auto* m = field.desc().message_type()) { + absl::StrAppend(&comment, " ", m->full_name()); + } + if (auto* m = field.desc().enum_type()) { + absl::StrAppend(&comment, " ", m->full_name()); + } + + return comment; +} } // namespace rust } // namespace compiler } // namespace protobuf diff --git a/src/google/protobuf/compiler/rust/naming.h b/src/google/protobuf/compiler/rust/naming.h index dabc0527ed..0b34c23a10 100644 --- a/src/google/protobuf/compiler/rust/naming.h +++ b/src/google/protobuf/compiler/rust/naming.h @@ -57,6 +57,8 @@ bool IsSupportedFieldType(Context field); absl::string_view PrimitiveRsTypeName(Context field); +std::string FieldInfoComment(Context field); + std::string RustModule(Context msg); std::string GetCrateRelativeQualifiedPath(Context msg);