1 /*
2 * Copyright (C) 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <stdlib.h>
18
19 #include <limits>
20 #include <map>
21 #include <memory>
22 #include <set>
23 #include <string>
24
25 #include <google/protobuf/compiler/code_generator.h>
26 #include <google/protobuf/compiler/plugin.h>
27 #include <google/protobuf/descriptor.h>
28 #include <google/protobuf/descriptor.pb.h>
29 #include <google/protobuf/io/printer.h>
30 #include <google/protobuf/io/zero_copy_stream.h>
31
32 #include "perfetto/ext/base/string_utils.h"
33
34 namespace protozero {
35 namespace {
36
37 using google::protobuf::Descriptor;
38 using google::protobuf::EnumDescriptor;
39 using google::protobuf::EnumValueDescriptor;
40 using google::protobuf::FieldDescriptor;
41 using google::protobuf::FileDescriptor;
42 using google::protobuf::compiler::GeneratorContext;
43 using google::protobuf::io::Printer;
44 using google::protobuf::io::ZeroCopyOutputStream;
45 using perfetto::base::SplitString;
46 using perfetto::base::StripChars;
47 using perfetto::base::StripPrefix;
48 using perfetto::base::StripSuffix;
49 using perfetto::base::ToUpper;
50 using perfetto::base::Uppercase;
51
Assert(bool condition)52 void Assert(bool condition) {
53 if (!condition)
54 abort();
55 }
56
57 struct FileDescriptorComp {
operator ()protozero::__anone56ddc450111::FileDescriptorComp58 bool operator()(const FileDescriptor* lhs, const FileDescriptor* rhs) const {
59 int comp = lhs->name().compare(rhs->name());
60 Assert(comp != 0 || lhs == rhs);
61 return comp < 0;
62 }
63 };
64
65 struct DescriptorComp {
operator ()protozero::__anone56ddc450111::DescriptorComp66 bool operator()(const Descriptor* lhs, const Descriptor* rhs) const {
67 int comp = lhs->full_name().compare(rhs->full_name());
68 Assert(comp != 0 || lhs == rhs);
69 return comp < 0;
70 }
71 };
72
73 struct EnumDescriptorComp {
operator ()protozero::__anone56ddc450111::EnumDescriptorComp74 bool operator()(const EnumDescriptor* lhs, const EnumDescriptor* rhs) const {
75 int comp = lhs->full_name().compare(rhs->full_name());
76 Assert(comp != 0 || lhs == rhs);
77 return comp < 0;
78 }
79 };
80
ProtoStubName(const FileDescriptor * proto)81 inline std::string ProtoStubName(const FileDescriptor* proto) {
82 return StripSuffix(proto->name(), ".proto") + ".pzc";
83 }
84
85 class GeneratorJob {
86 public:
GeneratorJob(const FileDescriptor * file,Printer * stub_h_printer)87 GeneratorJob(const FileDescriptor* file, Printer* stub_h_printer)
88 : source_(file), stub_h_(stub_h_printer) {}
89
GenerateStubs()90 bool GenerateStubs() {
91 Preprocess();
92 GeneratePrologue();
93 for (const EnumDescriptor* enumeration : enums_)
94 GenerateEnumDescriptor(enumeration);
95 for (const Descriptor* message : messages_)
96 GenerateMessageDescriptor(message);
97 for (const auto& [name, descriptors] : extensions_)
98 GenerateExtension(name, descriptors);
99 GenerateEpilogue();
100 return error_.empty();
101 }
102
SetOption(const std::string & name,const std::string & value)103 void SetOption(const std::string& name, const std::string& value) {
104 if (name == "wrapper_namespace") {
105 wrapper_namespace_ = value;
106 } else if (name == "guard_strip_prefix") {
107 guard_strip_prefix_ = value;
108 } else if (name == "guard_add_prefix") {
109 guard_add_prefix_ = value;
110 } else if (name == "path_strip_prefix") {
111 path_strip_prefix_ = value;
112 } else if (name == "path_add_prefix") {
113 path_add_prefix_ = value;
114 } else if (name == "invoker") {
115 invoker_ = value;
116 } else {
117 Abort(std::string() + "Unknown plugin option '" + name + "'.");
118 }
119 }
120
121 // If generator fails to produce stubs for a particular proto definitions
122 // it finishes with undefined output and writes the first error occurred.
GetFirstError() const123 const std::string& GetFirstError() const { return error_; }
124
125 private:
126 // Only the first error will be recorded.
Abort(const std::string & reason)127 void Abort(const std::string& reason) {
128 if (error_.empty())
129 error_ = reason;
130 }
131
132 // Get C++ class name corresponding to proto descriptor.
133 // Nested names are splitted by underscores. Underscores in type names aren't
134 // prohibited but not recommended in order to avoid name collisions.
135 template <class T>
GetCppClassName(const T * descriptor)136 inline std::string GetCppClassName(const T* descriptor) {
137 return StripChars(descriptor->full_name(), ".", '_');
138 }
139
FieldTypeToPackedBufferType(FieldDescriptor::Type type)140 const char* FieldTypeToPackedBufferType(FieldDescriptor::Type type) {
141 switch (type) {
142 case FieldDescriptor::TYPE_ENUM:
143 case FieldDescriptor::TYPE_INT32:
144 return "Int32";
145 case FieldDescriptor::TYPE_INT64:
146 return "Int64";
147 case FieldDescriptor::TYPE_UINT32:
148 return "Uint32";
149 case FieldDescriptor::TYPE_UINT64:
150 return "Uint64";
151 case FieldDescriptor::TYPE_SINT32:
152 return "Sint32";
153 case FieldDescriptor::TYPE_SINT64:
154 return "Sint64";
155 case FieldDescriptor::TYPE_FIXED32:
156 return "Fixed32";
157 case FieldDescriptor::TYPE_FIXED64:
158 return "Fixed64";
159 case FieldDescriptor::TYPE_SFIXED32:
160 return "Sfixed32";
161 case FieldDescriptor::TYPE_SFIXED64:
162 return "Sfixed64";
163 case FieldDescriptor::TYPE_FLOAT:
164 return "Float";
165 case FieldDescriptor::TYPE_DOUBLE:
166 return "Double";
167 case FieldDescriptor::TYPE_BOOL:
168 case FieldDescriptor::TYPE_STRING:
169 case FieldDescriptor::TYPE_BYTES:
170 case FieldDescriptor::TYPE_MESSAGE:
171 case FieldDescriptor::TYPE_GROUP:
172 break;
173 }
174 Abort("Unsupported packed type");
175 return "";
176 }
FieldToCppTypeName(const FieldDescriptor * field)177 std::string FieldToCppTypeName(const FieldDescriptor* field) {
178 switch (field->type()) {
179 case FieldDescriptor::TYPE_BOOL:
180 return "bool";
181 case FieldDescriptor::TYPE_INT32:
182 return "int32_t";
183 case FieldDescriptor::TYPE_INT64:
184 return "int64_t";
185 case FieldDescriptor::TYPE_UINT32:
186 return "uint32_t";
187 case FieldDescriptor::TYPE_UINT64:
188 return "uint64_t";
189 case FieldDescriptor::TYPE_SINT32:
190 return "int32_t";
191 case FieldDescriptor::TYPE_SINT64:
192 return "int64_t";
193 case FieldDescriptor::TYPE_FIXED32:
194 return "uint32_t";
195 case FieldDescriptor::TYPE_FIXED64:
196 return "uint64_t";
197 case FieldDescriptor::TYPE_SFIXED32:
198 return "int32_t";
199 case FieldDescriptor::TYPE_SFIXED64:
200 return "int64_t";
201 case FieldDescriptor::TYPE_FLOAT:
202 return "float";
203 case FieldDescriptor::TYPE_DOUBLE:
204 return "double";
205 case FieldDescriptor::TYPE_ENUM:
206 return "enum " + GetCppClassName(field->enum_type());
207 case FieldDescriptor::TYPE_STRING:
208 case FieldDescriptor::TYPE_BYTES:
209 return "const char*";
210 case FieldDescriptor::TYPE_MESSAGE:
211 return GetCppClassName(field->message_type());
212 case FieldDescriptor::TYPE_GROUP:
213 Abort("Groups not supported.");
214 return "";
215 }
216 Abort("Unrecognized FieldDescriptor::Type.");
217 return "";
218 }
219
CollectDescriptors()220 void CollectDescriptors() {
221 // Collect message descriptors in DFS order.
222 std::vector<const Descriptor*> stack;
223 stack.reserve(static_cast<size_t>(source_->message_type_count()));
224 for (int i = 0; i < source_->message_type_count(); ++i)
225 stack.push_back(source_->message_type(i));
226
227 while (!stack.empty()) {
228 const Descriptor* message = stack.back();
229 stack.pop_back();
230
231 if (message->extension_count() > 0) {
232 if (message->field_count() > 0 || message->nested_type_count() > 0 ||
233 message->enum_type_count() > 0) {
234 Abort("message with extend blocks shouldn't contain anything else");
235 }
236
237 // Iterate over all fields in "extend" blocks.
238 for (int i = 0; i < message->extension_count(); ++i) {
239 const FieldDescriptor* extension = message->extension(i);
240
241 // Protoc plugin API does not group fields in "extend" blocks.
242 // As the support for extensions in protozero is limited, the code
243 // assumes that extend blocks are located inside a wrapper message and
244 // name of this message is used to group them.
245 std::string extension_name =
246 GetCppClassName(extension->extension_scope());
247 extensions_[extension_name].push_back(extension);
248 }
249 } else {
250 messages_.push_back(message);
251 for (int i = 0; i < message->nested_type_count(); ++i) {
252 stack.push_back(message->nested_type(i));
253 // Emit a forward declaration of nested message types, as the outer
254 // class will refer to them when creating type aliases.
255 referenced_messages_.insert(message->nested_type(i));
256 }
257 }
258 }
259
260 // Collect enums.
261 for (int i = 0; i < source_->enum_type_count(); ++i)
262 enums_.push_back(source_->enum_type(i));
263
264 if (source_->extension_count() > 0) {
265 // TODO(b/336524288): emit field numbers
266 }
267
268 for (const Descriptor* message : messages_) {
269 for (int i = 0; i < message->enum_type_count(); ++i) {
270 enums_.push_back(message->enum_type(i));
271 }
272 }
273 }
274
CollectDependencies()275 void CollectDependencies() {
276 // Public import basically means that callers only need to import this
277 // proto in order to use the stuff publicly imported by this proto.
278 for (int i = 0; i < source_->public_dependency_count(); ++i)
279 public_imports_.insert(source_->public_dependency(i));
280
281 if (source_->weak_dependency_count() > 0)
282 Abort("Weak imports are not supported.");
283
284 // Validations. Collect public imports (of collected imports) in DFS order.
285 // Visibilty for current proto:
286 // - all imports listed in current proto,
287 // - public imports of everything imported (recursive).
288 std::vector<const FileDescriptor*> stack;
289 for (int i = 0; i < source_->dependency_count(); ++i) {
290 const FileDescriptor* imp = source_->dependency(i);
291 stack.push_back(imp);
292 if (public_imports_.count(imp) == 0) {
293 private_imports_.insert(imp);
294 }
295 }
296
297 while (!stack.empty()) {
298 const FileDescriptor* imp = stack.back();
299 stack.pop_back();
300 for (int i = 0; i < imp->public_dependency_count(); ++i) {
301 stack.push_back(imp->public_dependency(i));
302 }
303 }
304
305 // Collect descriptors of messages and enums used in current proto.
306 // It will be used to generate necessary forward declarations and
307 // check that everything lays in the same namespace.
308 for (const Descriptor* message : messages_) {
309 for (int i = 0; i < message->field_count(); ++i) {
310 const FieldDescriptor* field = message->field(i);
311
312 if (field->type() == FieldDescriptor::TYPE_MESSAGE) {
313 if (public_imports_.count(field->message_type()->file()) == 0) {
314 // Avoid multiple forward declarations since
315 // public imports have been already included.
316 referenced_messages_.insert(field->message_type());
317 }
318 } else if (field->type() == FieldDescriptor::TYPE_ENUM) {
319 if (public_imports_.count(field->enum_type()->file()) == 0) {
320 referenced_enums_.insert(field->enum_type());
321 }
322 }
323 }
324 }
325 }
326
Preprocess()327 void Preprocess() {
328 // Package name maps to a series of namespaces.
329 package_ = source_->package();
330 namespaces_ = SplitString(package_, ".");
331 if (!wrapper_namespace_.empty())
332 namespaces_.push_back(wrapper_namespace_);
333
334 full_namespace_prefix_ = "";
335 for (size_t i = 0; i < namespaces_.size(); i++) {
336 full_namespace_prefix_ += namespaces_[i];
337 if (i + 1 != namespaces_.size()) {
338 full_namespace_prefix_ += "_";
339 }
340 }
341
342 CollectDescriptors();
343 CollectDependencies();
344 }
345
GenerateGuard()346 std::string GenerateGuard() {
347 std::string guard = StripSuffix(source_->name(), ".proto");
348 guard = ToUpper(guard);
349 guard = StripChars(guard, ".-/\\", '_');
350 guard = StripPrefix(guard, guard_strip_prefix_);
351 guard = guard_add_prefix_ + guard + "_PZC_H_";
352 return guard;
353 }
354
355 // Print top header, namespaces and forward declarations.
GeneratePrologue()356 void GeneratePrologue() {
357 stub_h_->Print(
358 R"(/*
359 * Copyright (C) 2023 The Android Open Source Project
360 *
361 * Licensed under the Apache License, Version 2.0 (the "License");
362 * you may not use this file except in compliance with the License.
363 * You may obtain a copy of the License at
364 *
365 * http://www.apache.org/licenses/LICENSE-2.0
366 *
367 * Unless required by applicable law or agreed to in writing, software
368 * distributed under the License is distributed on an "AS IS" BASIS,
369 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
370 * See the License for the specific language governing permissions and
371 * limitations under the License.
372 */
373
374 )");
375 stub_h_->Print("// Autogenerated by the ProtoZero C compiler plugin.\n");
376 if (!invoker_.empty()) {
377 stub_h_->Print("// Invoked by $invoker$\n", "invoker", invoker_);
378 }
379 stub_h_->Print("// DO NOT EDIT.\n");
380
381 stub_h_->Print(
382 "#ifndef $guard$\n"
383 "#define $guard$\n\n"
384 "#include <stdbool.h>\n"
385 "#include <stdint.h>\n\n"
386 "#include \"perfetto/public/pb_macros.h\"\n",
387 "guard", GenerateGuard());
388
389 // Print includes for public imports and enums which cannot be forward
390 // declared.
391 std::vector<std::string> imports;
392 for (const FileDescriptor* dependency : public_imports_) {
393 imports.push_back(ProtoStubName(dependency));
394 }
395 for (const EnumDescriptor* e : referenced_enums_) {
396 if (e->file() != source_) {
397 imports.push_back(ProtoStubName(e->file()));
398 }
399 }
400
401 std::sort(imports.begin(), imports.end());
402
403 for (const std::string& imp : imports) {
404 std::string include_path = imp;
405 if (!path_strip_prefix_.empty()) {
406 include_path = StripPrefix(imp, path_strip_prefix_);
407 }
408 include_path = path_add_prefix_ + include_path;
409
410 stub_h_->Print("#include \"$name$.h\"\n", "name", include_path);
411 }
412 stub_h_->Print("\n");
413
414 // Print forward declarations.
415 for (const Descriptor* message : referenced_messages_) {
416 stub_h_->Print("PERFETTO_PB_MSG_DECL($class$);\n", "class",
417 GetCppClassName(message));
418 }
419
420 stub_h_->Print("\n");
421 }
422
GenerateEnumDescriptor(const EnumDescriptor * enumeration)423 void GenerateEnumDescriptor(const EnumDescriptor* enumeration) {
424 if (enumeration->containing_type()) {
425 stub_h_->Print("PERFETTO_PB_ENUM_IN_MSG($msg$, $class$){\n", "msg",
426 GetCppClassName(enumeration->containing_type()), "class",
427 enumeration->name());
428 } else {
429 stub_h_->Print("PERFETTO_PB_ENUM($class$){\n", "class",
430 GetCppClassName(enumeration));
431 }
432 stub_h_->Indent();
433
434 for (int i = 0; i < enumeration->value_count(); ++i) {
435 const EnumValueDescriptor* value = enumeration->value(i);
436 const std::string value_name = value->name();
437
438 if (enumeration->containing_type()) {
439 stub_h_->Print(
440 "PERFETTO_PB_ENUM_IN_MSG_ENTRY($msg$, $val$) = $number$,\n", "msg",
441 GetCppClassName(enumeration->containing_type()), "val", value_name,
442 "number", std::to_string(value->number()));
443 } else {
444 stub_h_->Print("PERFETTO_PB_ENUM_ENTRY($val$) = $number$, \n", "val",
445 full_namespace_prefix_ + "_" + value_name, "number",
446 std::to_string(value->number()));
447 }
448 }
449 stub_h_->Outdent();
450 stub_h_->Print("};\n\n");
451 }
452
453 // Packed repeated fields are encoded as a length-delimited field on the wire,
454 // where the payload is the concatenation of invidually encoded elements.
GeneratePackedRepeatedFieldDescriptorArgs(const std::string & message_cpp_type,const FieldDescriptor * field)455 void GeneratePackedRepeatedFieldDescriptorArgs(
456 const std::string& message_cpp_type,
457 const FieldDescriptor* field) {
458 std::map<std::string, std::string> setter;
459 setter["id"] = std::to_string(field->number());
460 setter["name"] = field->lowercase_name();
461 setter["class"] = message_cpp_type;
462 setter["buffer_type"] = FieldTypeToPackedBufferType(field->type());
463 stub_h_->Print(setter, "$class$, PACKED, $buffer_type$, $name$, $id$\n");
464 }
465
GeneratePackedRepeatedFieldDescriptor(const std::string & message_cpp_type,const FieldDescriptor * field)466 void GeneratePackedRepeatedFieldDescriptor(
467 const std::string& message_cpp_type,
468 const FieldDescriptor* field) {
469 stub_h_->Print("PERFETTO_PB_FIELD(");
470 GeneratePackedRepeatedFieldDescriptorArgs(message_cpp_type, field);
471 stub_h_->Print(");\n");
472 }
473
GeneratePackedRepeatedFieldDescriptorForExtension(const std::string & field_cpp_prefix,const std::string & message_cpp_type,const FieldDescriptor * field)474 void GeneratePackedRepeatedFieldDescriptorForExtension(
475 const std::string& field_cpp_prefix,
476 const std::string& message_cpp_type,
477 const FieldDescriptor* field) {
478 stub_h_->Print("PERFETTO_PB_EXTENSION_FIELD($prefix$, ", "prefix",
479 field_cpp_prefix);
480 GeneratePackedRepeatedFieldDescriptorArgs(message_cpp_type, field);
481 stub_h_->Print(");\n");
482 }
483
GenerateSimpleFieldDescriptorArgs(const std::string & message_cpp_type,const FieldDescriptor * field)484 void GenerateSimpleFieldDescriptorArgs(const std::string& message_cpp_type,
485 const FieldDescriptor* field) {
486 std::map<std::string, std::string> setter;
487 setter["id"] = std::to_string(field->number());
488 setter["name"] = field->lowercase_name();
489 setter["ctype"] = FieldToCppTypeName(field);
490 setter["class"] = message_cpp_type;
491
492 switch (field->type()) {
493 case FieldDescriptor::TYPE_BYTES:
494 case FieldDescriptor::TYPE_STRING:
495 stub_h_->Print(setter, "$class$, STRING, const char*, $name$, $id$");
496 break;
497 case FieldDescriptor::TYPE_UINT64:
498 case FieldDescriptor::TYPE_UINT32:
499 case FieldDescriptor::TYPE_INT64:
500 case FieldDescriptor::TYPE_INT32:
501 case FieldDescriptor::TYPE_BOOL:
502 case FieldDescriptor::TYPE_ENUM:
503 stub_h_->Print(setter, "$class$, VARINT, $ctype$, $name$, $id$");
504 break;
505 case FieldDescriptor::TYPE_SINT64:
506 case FieldDescriptor::TYPE_SINT32:
507 stub_h_->Print(setter, "$class$, ZIGZAG, $ctype$, $name$, $id$");
508 break;
509 case FieldDescriptor::TYPE_SFIXED32:
510 case FieldDescriptor::TYPE_FIXED32:
511 case FieldDescriptor::TYPE_FLOAT:
512 stub_h_->Print(setter, "$class$, FIXED32, $ctype$, $name$, $id$");
513 break;
514 case FieldDescriptor::TYPE_SFIXED64:
515 case FieldDescriptor::TYPE_FIXED64:
516 case FieldDescriptor::TYPE_DOUBLE:
517 stub_h_->Print(setter, "$class$, FIXED64, $ctype$, $name$, $id$");
518 break;
519 case FieldDescriptor::TYPE_MESSAGE:
520 case FieldDescriptor::TYPE_GROUP:
521 Abort("Groups not supported.");
522 break;
523 }
524 }
525
GenerateSimpleFieldDescriptor(const std::string & message_cpp_type,const FieldDescriptor * field)526 void GenerateSimpleFieldDescriptor(const std::string& message_cpp_type,
527 const FieldDescriptor* field) {
528 stub_h_->Print("PERFETTO_PB_FIELD(");
529 GenerateSimpleFieldDescriptorArgs(message_cpp_type, field);
530 stub_h_->Print(");\n");
531 }
532
GenerateSimpleFieldDescriptorForExtension(const std::string & field_cpp_prefix,const std::string & message_cpp_type,const FieldDescriptor * field)533 void GenerateSimpleFieldDescriptorForExtension(
534 const std::string& field_cpp_prefix,
535 const std::string& message_cpp_type,
536 const FieldDescriptor* field) {
537 stub_h_->Print("PERFETTO_PB_EXTENSION_FIELD($prefix$, ", "prefix",
538 field_cpp_prefix);
539 GenerateSimpleFieldDescriptorArgs(message_cpp_type, field);
540 stub_h_->Print(");\n");
541 }
542
GenerateNestedMessageFieldDescriptor(const std::string & message_cpp_type,const FieldDescriptor * field)543 void GenerateNestedMessageFieldDescriptor(const std::string& message_cpp_type,
544 const FieldDescriptor* field) {
545 std::string inner_class = GetCppClassName(field->message_type());
546 stub_h_->Print(
547 "PERFETTO_PB_FIELD($class$, MSG, $inner_class$, $name$, $id$);\n",
548 "class", message_cpp_type, "id", std::to_string(field->number()),
549 "name", field->lowercase_name(), "inner_class", inner_class);
550 }
551
GenerateNestedMessageFieldDescriptorForExtension(const std::string & field_cpp_prefix,const std::string & message_cpp_type,const FieldDescriptor * field)552 void GenerateNestedMessageFieldDescriptorForExtension(
553 const std::string& field_cpp_prefix,
554 const std::string& message_cpp_type,
555 const FieldDescriptor* field) {
556 std::string inner_class = GetCppClassName(field->message_type());
557 stub_h_->Print(
558 "PERFETTO_PB_EXTENSION_FIELD($prefix$, $class$, MSG, $inner_class$, "
559 "$name$, $id$);\n",
560 "prefix", field_cpp_prefix, "class", message_cpp_type, "id",
561 std::to_string(field->number()), "name", field->lowercase_name(),
562 "inner_class", inner_class);
563 }
564
GenerateMessageDescriptor(const Descriptor * message)565 void GenerateMessageDescriptor(const Descriptor* message) {
566 stub_h_->Print("PERFETTO_PB_MSG($name$);\n", "name",
567 GetCppClassName(message));
568
569 // Field descriptors.
570 for (int i = 0; i < message->field_count(); ++i) {
571 GenerateFieldDescriptor(GetCppClassName(message), message->field(i));
572 }
573 stub_h_->Print("\n");
574 }
575
GenerateFieldDescriptor(const std::string & message_cpp_type,const FieldDescriptor * field)576 void GenerateFieldDescriptor(const std::string& message_cpp_type,
577 const FieldDescriptor* field) {
578 // GenerateFieldMetadata(message_cpp_type, field);
579 if (field->is_packed()) {
580 GeneratePackedRepeatedFieldDescriptor(message_cpp_type, field);
581 } else if (field->type() != FieldDescriptor::TYPE_MESSAGE) {
582 GenerateSimpleFieldDescriptor(message_cpp_type, field);
583 } else {
584 GenerateNestedMessageFieldDescriptor(message_cpp_type, field);
585 }
586 }
587
GenerateExtensionFieldDescriptor(const std::string & field_cpp_prefix,const std::string & message_cpp_type,const FieldDescriptor * field)588 void GenerateExtensionFieldDescriptor(const std::string& field_cpp_prefix,
589 const std::string& message_cpp_type,
590 const FieldDescriptor* field) {
591 // GenerateFieldMetadata(message_cpp_type, field);
592 if (field->is_packed()) {
593 GeneratePackedRepeatedFieldDescriptorForExtension(
594 field_cpp_prefix, message_cpp_type, field);
595 } else if (field->type() != FieldDescriptor::TYPE_MESSAGE) {
596 GenerateSimpleFieldDescriptorForExtension(field_cpp_prefix,
597 message_cpp_type, field);
598 } else {
599 GenerateNestedMessageFieldDescriptorForExtension(field_cpp_prefix,
600 message_cpp_type, field);
601 }
602 }
603
604 // Generate extension class for a group of FieldDescriptor instances
605 // representing one "extend" block in proto definition. For example:
606 //
607 // message SpecificExtension {
608 // extend GeneralThing {
609 // optional Fizz fizz = 101;
610 // optional Buzz buzz = 102;
611 // }
612 // }
613 //
614 // This is going to be passed as a vector of two elements, "fizz" and
615 // "buzz". Wrapping message is used to provide a name for generated
616 // extension class.
617 //
618 // In the example above, generated code is going to look like:
619 //
620 // class SpecificExtension : public GeneralThing {
621 // Fizz* set_fizz();
622 // Buzz* set_buzz();
623 // }
GenerateExtension(const std::string & extension_name,const std::vector<const FieldDescriptor * > & descriptors)624 void GenerateExtension(
625 const std::string& extension_name,
626 const std::vector<const FieldDescriptor*>& descriptors) {
627 // Use an arbitrary descriptor in order to get generic information not
628 // specific to any of them.
629 const FieldDescriptor* descriptor = descriptors[0];
630 const Descriptor* base_message = descriptor->containing_type();
631
632 for (const FieldDescriptor* field : descriptors) {
633 if (field->containing_type() != base_message) {
634 Abort("one wrapper should extend only one message");
635 return;
636 }
637 GenerateExtensionFieldDescriptor(extension_name,
638 GetCppClassName(base_message), field);
639 }
640 }
641
GenerateEpilogue()642 void GenerateEpilogue() {
643 stub_h_->Print("#endif // $guard$\n", "guard", GenerateGuard());
644 }
645
646 const FileDescriptor* const source_;
647 Printer* const stub_h_;
648 std::string error_;
649
650 std::string package_;
651 std::string wrapper_namespace_;
652 std::string guard_strip_prefix_;
653 std::string guard_add_prefix_;
654 std::string path_strip_prefix_;
655 std::string path_add_prefix_;
656 std::string invoker_;
657 std::vector<std::string> namespaces_;
658 std::string full_namespace_prefix_;
659 std::vector<const Descriptor*> messages_;
660 std::vector<const EnumDescriptor*> enums_;
661 std::map<std::string, std::vector<const FieldDescriptor*>> extensions_;
662
663 // The custom *Comp comparators are to ensure determinism of the generator.
664 std::set<const FileDescriptor*, FileDescriptorComp> public_imports_;
665 std::set<const FileDescriptor*, FileDescriptorComp> private_imports_;
666 std::set<const Descriptor*, DescriptorComp> referenced_messages_;
667 std::set<const EnumDescriptor*, EnumDescriptorComp> referenced_enums_;
668 };
669
670 class ProtoZeroCGenerator : public google::protobuf::compiler::CodeGenerator {
671 public:
672 explicit ProtoZeroCGenerator();
673 ~ProtoZeroCGenerator() override;
674
675 // CodeGenerator implementation
676 bool Generate(const google::protobuf::FileDescriptor* file,
677 const std::string& options,
678 GeneratorContext* context,
679 std::string* error) const override;
680 };
681
ProtoZeroCGenerator()682 ProtoZeroCGenerator::ProtoZeroCGenerator() {}
683
~ProtoZeroCGenerator()684 ProtoZeroCGenerator::~ProtoZeroCGenerator() {}
685
Generate(const FileDescriptor * file,const std::string & options,GeneratorContext * context,std::string * error) const686 bool ProtoZeroCGenerator::Generate(const FileDescriptor* file,
687 const std::string& options,
688 GeneratorContext* context,
689 std::string* error) const {
690 const std::unique_ptr<ZeroCopyOutputStream> stub_h_file_stream(
691 context->Open(ProtoStubName(file) + ".h"));
692
693 // Variables are delimited by $.
694 Printer stub_h_printer(stub_h_file_stream.get(), '$');
695 GeneratorJob job(file, &stub_h_printer);
696
697 // Parse additional options.
698 for (const std::string& option : SplitString(options, ",")) {
699 std::vector<std::string> option_pair = SplitString(option, "=");
700 job.SetOption(option_pair[0], option_pair[1]);
701 }
702
703 if (!job.GenerateStubs()) {
704 *error = job.GetFirstError();
705 return false;
706 }
707 return true;
708 }
709
710 } // namespace
711 } // namespace protozero
712
main(int argc,char * argv[])713 int main(int argc, char* argv[]) {
714 protozero::ProtoZeroCGenerator generator;
715 return google::protobuf::compiler::PluginMain(argc, argv, &generator);
716 }
717