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, ¶ms);
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