| // Protocol Buffers - Google's data interchange format |
| // Copyright 2023 Google LLC. All rights reserved. |
| // |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file or at |
| // https://developers.google.com/open-source/licenses/bsd |
| |
| #include "hpb_generator/generator.h" |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| #include <vector> |
| |
| #include "absl/strings/str_cat.h" |
| #include "absl/strings/str_replace.h" |
| #include "google/protobuf/compiler/code_generator.h" |
| #include "google/protobuf/compiler/code_generator_lite.h" |
| #include "hpb_generator/context.h" |
| #include "hpb_generator/gen_enums.h" |
| #include "hpb_generator/gen_extensions.h" |
| #include "hpb_generator/gen_messages.h" |
| #include "hpb_generator/gen_utils.h" |
| #include "hpb_generator/names.h" |
| #include "google/protobuf/descriptor.h" |
| |
| namespace google { |
| namespace protobuf { |
| namespace hpb_generator { |
| namespace { |
| |
| using FileDescriptor = ::google::protobuf::FileDescriptor; |
| |
| void WriteTypedefForwardingHeader( |
| const google::protobuf::FileDescriptor* file, |
| const std::vector<const google::protobuf::Descriptor*>& file_messages, Context& ctx); |
| |
| void WriteHeaderMessageForwardDecls(const google::protobuf::FileDescriptor* file, |
| Context& ctx); |
| |
| void WriteMessageImplementations(const google::protobuf::FileDescriptor* file, |
| Context& ctx); |
| |
| void WriteForwardDecls(const google::protobuf::FileDescriptor* file, Context& ctx) { |
| for (int i = 0; i < file->public_dependency_count(); ++i) { |
| const auto target_file_messages = |
| SortedMessages(file->public_dependency(i)); |
| WriteTypedefForwardingHeader(file->public_dependency(i), |
| target_file_messages, ctx); |
| } |
| const auto this_file_messages = SortedMessages(file); |
| WriteTypedefForwardingHeader(file, this_file_messages, ctx); |
| } |
| |
| void WriteHeader(const google::protobuf::FileDescriptor* file, Context& ctx) { |
| if (ctx.options().backend == Backend::CPP) { |
| EmitFileWarning(file, ctx); |
| const auto msgs = SortedMessages(file); |
| for (auto message : msgs) { |
| ctx.Emit({{"type", QualifiedClassName(message)}, |
| {"class_name", ClassName(message)}, |
| {"namespace", absl::StrCat(absl::StrReplaceAll(file->package(), |
| {{".", "::"}}), |
| "::protos")}}, |
| R"cc( |
| // message stubs |
| namespace $namespace$ { |
| |
| class $class_name$ { |
| public: |
| using CProxy = bool; |
| using Proxy = bool; |
| using Access = bool; |
| |
| $class_name$() = default; |
| |
| private: |
| $class_name$($type$* msg) : msg_(msg) {} |
| |
| $type$* msg_; |
| |
| $type$* msg() const { return msg_; } |
| |
| friend struct ::hpb::internal::PrivateAccess; |
| }; |
| } // namespace $namespace$ |
| )cc"); |
| } |
| return; |
| } |
| EmitFileWarning(file, ctx); |
| ctx.Emit({{"filename", ToPreproc(file->name())}}, |
| R"cc( |
| #ifndef $filename$_HPB_PROTO_H_ |
| #define $filename$_HPB_PROTO_H_ |
| |
| #include "hpb/repeated_field.h" |
| |
| #include "absl/status/statusor.h" |
| #include "absl/strings/string_view.h" |
| )cc"); |
| |
| // Import headers for proto public dependencies. |
| for (int i = 0; i < file->public_dependency_count(); i++) { |
| if (i == 0) { |
| ctx.Emit("// Public Imports.\n"); |
| } |
| ctx.Emit({{"header", CppHeaderFilename(file->public_dependency(i))}}, |
| "#include \"$header$\"\n"); |
| if (i == file->public_dependency_count() - 1) { |
| ctx.Emit("\n"); |
| } |
| } |
| |
| ctx.Emit("#include \"upb/port/def.inc\"\n"); |
| ctx.Emit( |
| "#include \"hpb/internal/os_macros_undef.inc\"\n"); |
| |
| const std::vector<const google::protobuf::Descriptor*> this_file_messages = |
| SortedMessages(file); |
| const std::vector<const google::protobuf::FieldDescriptor*> this_file_exts = |
| SortedExtensions(file); |
| |
| if (!this_file_messages.empty()) { |
| ctx.Emit("\n"); |
| } |
| |
| WriteHeaderMessageForwardDecls(file, ctx); |
| WriteForwardDecls(file, ctx); |
| |
| std::vector<const google::protobuf::EnumDescriptor*> this_file_enums = |
| SortedEnums(file); |
| |
| WrapNamespace(file, ctx, [&]() { |
| // Write Class and Enums. |
| WriteEnumDeclarations(this_file_enums, ctx); |
| ctx.Emit("\n"); |
| |
| for (auto message : this_file_messages) { |
| WriteMessageClassDeclarations(message, this_file_exts, this_file_enums, |
| ctx); |
| } |
| ctx.Emit("\n"); |
| |
| WriteExtensionIdentifiersHeader(this_file_exts, ctx); |
| ctx.Emit("\n"); |
| }); |
| |
| ctx.Emit( |
| "#include \"hpb/internal/os_macros_restore.inc\"\n"); |
| ctx.Emit("\n#include \"upb/port/undef.inc\"\n\n"); |
| // End of "C" section. |
| |
| ctx.Emit({{"filename", ToPreproc(file->name())}}, |
| "#endif /* $filename$_HPB_PROTO_H_ */\n"); |
| } |
| |
| // Writes a .hpb.cc source file. |
| void WriteSource(const google::protobuf::FileDescriptor* file, Context& ctx) { |
| if (ctx.options().backend == Backend::CPP) { |
| ctx.Emit("// Placeholder hpb C++ source stub"); |
| return; |
| } |
| EmitFileWarning(file, ctx); |
| |
| ctx.Emit({{"header", CppHeaderFilename(file)}}, |
| R"cc( |
| #include <stddef.h> |
| #include "absl/log/absl_check.h" |
| #include "absl/strings/string_view.h" |
| #include "$header$" |
| )cc"); |
| |
| for (int i = 0; i < file->dependency_count(); i++) { |
| if (ctx.options().strip_feature_includes && |
| compiler::IsKnownFeatureProto(file->dependency(i)->name())) { |
| // Strip feature imports for editions codegen tests. |
| continue; |
| } |
| ctx.Emit({{"header", CppHeaderFilename(file->dependency(i))}}, |
| "#include \"$header$\"\n"); |
| } |
| ctx.Emit("#include \"upb/port/def.inc\"\n"); |
| |
| WrapNamespace(file, ctx, [&]() { WriteMessageImplementations(file, ctx); }); |
| |
| ctx.Emit("#include \"upb/port/undef.inc\"\n\n"); |
| } |
| |
| void WriteMessageImplementations(const google::protobuf::FileDescriptor* file, |
| Context& ctx) { |
| const std::vector<const google::protobuf::FieldDescriptor*> file_exts = |
| SortedExtensions(file); |
| const std::vector<const google::protobuf::Descriptor*> this_file_messages = |
| SortedMessages(file); |
| for (auto message : this_file_messages) { |
| WriteMessageImplementation(message, file_exts, ctx); |
| } |
| } |
| |
| void WriteTypedefForwardingHeader( |
| const google::protobuf::FileDescriptor* file, |
| const std::vector<const google::protobuf::Descriptor*>& file_messages, Context& ctx) { |
| WrapNamespace(file, ctx, [&]() { |
| // Forward-declare types defined in this file. |
| for (auto message : file_messages) { |
| ctx.Emit({{"class_name", ClassName(message)}}, |
| R"cc( |
| class $class_name$; |
| namespace internal { |
| class $class_name$Access; |
| class $class_name$Proxy; |
| class $class_name$CProxy; |
| } // namespace internal |
| )cc"); |
| } |
| }); |
| ctx.Emit("\n"); |
| } |
| |
| /// Writes includes for upb C minitables and fwd.h for transitive typedefs. |
| void WriteHeaderMessageForwardDecls(const google::protobuf::FileDescriptor* file, |
| Context& ctx) { |
| // Import forward-declaration of types defined in this file. |
| ctx.Emit({{"upb_filename", UpbCFilename(file)}}, |
| "#include \"$upb_filename$\"\n"); |
| WriteForwardDecls(file, ctx); |
| // Import forward-declaration of types in dependencies. |
| for (int i = 0; i < file->dependency_count(); ++i) { |
| if (ctx.options().strip_feature_includes && |
| compiler::IsKnownFeatureProto(file->dependency(i)->name())) { |
| // Strip feature imports for editions codegen tests. |
| continue; |
| } |
| WriteForwardDecls(file->dependency(i), ctx); |
| } |
| ctx.Emit("\n"); |
| } |
| |
| } // namespace |
| |
| bool Generator::Generate(const google::protobuf::FileDescriptor* file, |
| const std::string& parameter, |
| protoc::GeneratorContext* context, |
| std::string* error) const { |
| { |
| bool strip_nonfunctional_codegen = false; |
| Backend backend = Backend::UPB; |
| std::vector<std::pair<std::string, std::string>> params; |
| google::protobuf::compiler::ParseGeneratorParameter(parameter, ¶ms); |
| |
| for (const auto& pair : params) { |
| if (pair.first == "experimental_strip_nonfunctional_codegen") { |
| strip_nonfunctional_codegen = true; |
| } else if (pair.first == "backend" && pair.second == "cpp") { |
| backend = Backend::CPP; |
| } else { |
| *error = "Unknown parameter: " + pair.first; |
| return false; |
| } |
| } |
| // Write model.hpb.h |
| Options options = {.backend = backend, |
| .strip_feature_includes = strip_nonfunctional_codegen}; |
| std::unique_ptr<google::protobuf::io::ZeroCopyOutputStream> header_output_stream( |
| context->Open(CppHeaderFilename(file))); |
| Context hdr_ctx(file, header_output_stream.get(), options); |
| WriteHeader(file, hdr_ctx); |
| |
| // Write model.hpb.cc |
| std::unique_ptr<google::protobuf::io::ZeroCopyOutputStream> cc_output_stream( |
| context->Open(CppSourceFilename(file))); |
| auto cc_ctx = Context(file, cc_output_stream.get(), options); |
| WriteSource(file, cc_ctx); |
| return true; |
| } |
| } |
| } // namespace hpb_generator |
| } // namespace protobuf |
| } // namespace google |