|  | // 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 |