• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2023 Google LLC.  All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7 
8 #include <cstdint>
9 #include <memory>
10 #include <string>
11 #include <utility>
12 #include <vector>
13 
14 #include "google/protobuf/descriptor.pb.h"
15 #include "google/protobuf/compiler/code_generator.h"
16 #include "google/protobuf/compiler/hpb/gen_enums.h"
17 #include "google/protobuf/compiler/hpb/gen_extensions.h"
18 #include "google/protobuf/compiler/hpb/gen_messages.h"
19 #include "google/protobuf/compiler/hpb/gen_utils.h"
20 #include "google/protobuf/compiler/hpb/names.h"
21 #include "google/protobuf/compiler/hpb/output.h"
22 #include "google/protobuf/compiler/plugin.h"
23 #include "google/protobuf/descriptor.h"
24 
25 namespace google::protobuf::hpb_generator {
26 namespace {
27 
28 namespace protoc = ::google::protobuf::compiler;
29 namespace protobuf = ::proto2;
30 using FileDescriptor = ::google::protobuf::FileDescriptor;
31 using google::protobuf::Edition;
32 
33 void WriteSource(const protobuf::FileDescriptor* file, Output& output,
34                  bool fasttable_enabled, bool strip_feature_includes);
35 void WriteHeader(const protobuf::FileDescriptor* file, Output& output,
36                  bool strip_feature_includes);
37 void WriteForwardingHeader(const protobuf::FileDescriptor* file,
38                            Output& output);
39 void WriteMessageImplementations(const protobuf::FileDescriptor* file,
40                                  Output& output);
41 void WriteTypedefForwardingHeader(
42     const protobuf::FileDescriptor* file,
43     const std::vector<const protobuf::Descriptor*>& file_messages,
44     Output& output);
45 void WriteHeaderMessageForwardDecls(const protobuf::FileDescriptor* file,
46                                     Output& output,
47                                     bool strip_feature_includes);
48 
49 class Generator : public protoc::CodeGenerator {
50  public:
51   ~Generator() override = default;
52   bool Generate(const protobuf::FileDescriptor* file,
53                 const std::string& parameter, protoc::GeneratorContext* context,
54                 std::string* error) const override;
GetSupportedFeatures() const55   uint64_t GetSupportedFeatures() const override {
56     return Feature::FEATURE_PROTO3_OPTIONAL |
57            Feature::FEATURE_SUPPORTS_EDITIONS;
58   }
GetMinimumEdition() const59   Edition GetMinimumEdition() const override { return Edition::EDITION_PROTO2; }
GetMaximumEdition() const60   Edition GetMaximumEdition() const override { return Edition::EDITION_2023; }
61 };
62 
Generate(const protobuf::FileDescriptor * file,const std::string & parameter,protoc::GeneratorContext * context,std::string * error) const63 bool Generator::Generate(const protobuf::FileDescriptor* file,
64                          const std::string& parameter,
65                          protoc::GeneratorContext* context,
66                          std::string* error) const {
67   bool fasttable_enabled = false;
68   bool strip_nonfunctional_codegen = false;
69   std::vector<std::pair<std::string, std::string>> params;
70   google::protobuf::compiler::ParseGeneratorParameter(parameter, &params);
71 
72   for (const auto& pair : params) {
73     if (pair.first == "fasttable") {
74       fasttable_enabled = true;
75     } else if (pair.first == "experimental_strip_nonfunctional_codegen") {
76       strip_nonfunctional_codegen = true;
77     } else {
78       *error = "Unknown parameter: " + pair.first;
79       return false;
80     }
81   }
82 
83   // Write model.upb.fwd.h
84   std::unique_ptr<google::protobuf::io::ZeroCopyOutputStream> output_stream(
85       context->Open(ForwardingHeaderFilename(file)));
86   Output forwarding_header_output(output_stream.get());
87   WriteForwardingHeader(file, forwarding_header_output);
88 
89   // Write model.upb.proto.h
90   std::unique_ptr<google::protobuf::io::ZeroCopyOutputStream> header_output_stream(
91       context->Open(CppHeaderFilename(file)));
92   Output header_output(header_output_stream.get());
93   WriteHeader(file, header_output, strip_nonfunctional_codegen);
94 
95   // Write model.upb.proto.cc
96   std::unique_ptr<google::protobuf::io::ZeroCopyOutputStream> cc_output_stream(
97       context->Open(CppSourceFilename(file)));
98   Output cc_output(cc_output_stream.get());
99   WriteSource(file, cc_output, fasttable_enabled, strip_nonfunctional_codegen);
100   return true;
101 }
102 
103 // The forwarding header defines Access/Proxy/CProxy for message classes
104 // used to include when referencing dependencies to prevent transitive
105 // dependency headers from being included.
WriteForwardingHeader(const protobuf::FileDescriptor * file,Output & output)106 void WriteForwardingHeader(const protobuf::FileDescriptor* file,
107                            Output& output) {
108   EmitFileWarning(file, output);
109   output(
110       R"cc(
111 #ifndef $0_UPB_FWD_H_
112 #define $0_UPB_FWD_H_
113       )cc",
114       ToPreproc(file->name()));
115   output("\n");
116   for (int i = 0; i < file->public_dependency_count(); ++i) {
117     output("#include \"$0\"\n",
118            ForwardingHeaderFilename(file->public_dependency(i)));
119   }
120   if (file->public_dependency_count() > 0) {
121     output("\n");
122   }
123   const std::vector<const protobuf::Descriptor*> this_file_messages =
124       SortedMessages(file);
125   WriteTypedefForwardingHeader(file, this_file_messages, output);
126   output("#endif  /* $0_UPB_FWD_H_ */\n", ToPreproc(file->name()));
127 }
128 
WriteHeader(const protobuf::FileDescriptor * file,Output & output,bool strip_feature_includes)129 void WriteHeader(const protobuf::FileDescriptor* file, Output& output,
130                  bool strip_feature_includes) {
131   EmitFileWarning(file, output);
132   output(
133       R"cc(
134 #ifndef $0_HPB_PROTO_H_
135 #define $0_HPB_PROTO_H_
136 
137 #include "absl/status/statusor.h"
138 #include "absl/strings/string_view.h"
139 
140 #include "google/protobuf/hpb/repeated_field.h"
141       )cc",
142       ToPreproc(file->name()));
143 
144   // Import headers for proto public dependencies.
145   for (int i = 0; i < file->public_dependency_count(); i++) {
146     if (i == 0) {
147       output("// Public Imports.\n");
148     }
149     output("#include \"$0\"\n", CppHeaderFilename(file->public_dependency(i)));
150     if (i == file->public_dependency_count() - 1) {
151       output("\n");
152     }
153   }
154 
155   output("#include \"upb/port/def.inc\"\n");
156 
157   const std::vector<const protobuf::Descriptor*> this_file_messages =
158       SortedMessages(file);
159   const std::vector<const protobuf::FieldDescriptor*> this_file_exts =
160       SortedExtensions(file);
161 
162   if (!this_file_messages.empty()) {
163     output("\n");
164   }
165 
166   WriteHeaderMessageForwardDecls(file, output, strip_feature_includes);
167   WriteStartNamespace(file, output);
168 
169   std::vector<const protobuf::EnumDescriptor*> this_file_enums =
170       SortedEnums(file);
171 
172   // Write Class and Enums.
173   WriteEnumDeclarations(this_file_enums, output);
174   output("\n");
175 
176   for (auto message : this_file_messages) {
177     WriteMessageClassDeclarations(message, this_file_exts, this_file_enums,
178                                   output);
179   }
180   output("\n");
181 
182   WriteExtensionIdentifiersHeader(this_file_exts, output);
183   output("\n");
184 
185   WriteEndNamespace(file, output);
186 
187   output("\n#include \"upb/port/undef.inc\"\n\n");
188   // End of "C" section.
189 
190   output("#endif  /* $0_HPB_PROTO_H_ */\n", ToPreproc(file->name()));
191 }
192 
193 // Writes a .upb.cc source file.
WriteSource(const protobuf::FileDescriptor * file,Output & output,bool fasttable_enabled,bool strip_feature_includes)194 void WriteSource(const protobuf::FileDescriptor* file, Output& output,
195                  bool fasttable_enabled, bool strip_feature_includes) {
196   EmitFileWarning(file, output);
197 
198   output(
199       R"cc(
200 #include <stddef.h>
201 #include "absl/strings/string_view.h"
202 #include "$0"
203       )cc",
204       CppHeaderFilename(file));
205 
206   for (int i = 0; i < file->dependency_count(); i++) {
207     if (strip_feature_includes &&
208         compiler::IsKnownFeatureProto(file->dependency(i)->name())) {
209       // Strip feature imports for editions codegen tests.
210       continue;
211     }
212     output("#include \"$0\"\n", CppHeaderFilename(file->dependency(i)));
213   }
214   output("#include \"upb/port/def.inc\"\n");
215 
216   WriteStartNamespace(file, output);
217   WriteMessageImplementations(file, output);
218   const std::vector<const protobuf::FieldDescriptor*> this_file_exts =
219       SortedExtensions(file);
220   WriteExtensionIdentifiers(this_file_exts, output);
221   WriteEndNamespace(file, output);
222 
223   output("#include \"upb/port/undef.inc\"\n\n");
224 }
225 
WriteMessageImplementations(const protobuf::FileDescriptor * file,Output & output)226 void WriteMessageImplementations(const protobuf::FileDescriptor* file,
227                                  Output& output) {
228   const std::vector<const protobuf::FieldDescriptor*> file_exts =
229       SortedExtensions(file);
230   const std::vector<const protobuf::Descriptor*> this_file_messages =
231       SortedMessages(file);
232   for (auto message : this_file_messages) {
233     WriteMessageImplementation(message, file_exts, output);
234   }
235 }
236 
WriteTypedefForwardingHeader(const protobuf::FileDescriptor * file,const std::vector<const protobuf::Descriptor * > & file_messages,Output & output)237 void WriteTypedefForwardingHeader(
238     const protobuf::FileDescriptor* file,
239     const std::vector<const protobuf::Descriptor*>& file_messages,
240     Output& output) {
241   WriteStartNamespace(file, output);
242 
243   // Forward-declare types defined in this file.
244   for (auto message : file_messages) {
245     output(
246         R"cc(
247           class $0;
248           namespace internal {
249           class $0Access;
250           class $0Proxy;
251           class $0CProxy;
252           }  // namespace internal
253         )cc",
254         ClassName(message));
255   }
256   output("\n");
257   WriteEndNamespace(file, output);
258 }
259 
260 /// Writes includes for upb C minitables and fwd.h for transitive typedefs.
WriteHeaderMessageForwardDecls(const protobuf::FileDescriptor * file,Output & output,bool strip_feature_includes)261 void WriteHeaderMessageForwardDecls(const protobuf::FileDescriptor* file,
262                                     Output& output,
263                                     bool strip_feature_includes) {
264   // Import forward-declaration of types defined in this file.
265   output("#include \"$0\"\n", UpbCFilename(file));
266   output("#include \"$0\"\n", ForwardingHeaderFilename(file));
267   // Import forward-declaration of types in dependencies.
268   for (int i = 0; i < file->dependency_count(); ++i) {
269     if (strip_feature_includes &&
270         compiler::IsKnownFeatureProto(file->dependency(i)->name())) {
271       // Strip feature imports for editions codegen tests.
272       continue;
273     }
274     output("#include \"$0\"\n", ForwardingHeaderFilename(file->dependency(i)));
275   }
276   output("\n");
277 }
278 
279 }  // namespace
280 }  // namespace protobuf
281 }  // namespace google::hpb_generator
282 
283 int main(int argc, char** argv) {
284   google::protobuf::hpb_generator::Generator generator_cc;
285   return google::protobuf::compiler::PluginMain(argc, argv, &generator_cc);
286 }
287