xref: /aosp_15_r20/external/perfetto/src/protozero/protoc_plugin/protozero_c_plugin.cc (revision 6dbdd20afdafa5e3ca9b8809fa73465d530080dc)
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