• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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::ReplaceAll;
46 using perfetto::base::SplitString;
47 using perfetto::base::StripChars;
48 using perfetto::base::StripPrefix;
49 using perfetto::base::StripSuffix;
50 using perfetto::base::ToUpper;
51 using perfetto::base::Uppercase;
52 
53 // Keep this value in sync with ProtoDecoder::kMaxDecoderFieldId. If they go out
54 // of sync pbzero.h files will stop compiling, hitting the at() static_assert.
55 // Not worth an extra dependency.
56 constexpr int kMaxDecoderFieldId = 999;
57 
Assert(bool condition)58 void Assert(bool condition) {
59   if (!condition)
60     abort();
61 }
62 
63 struct FileDescriptorComp {
operator ()protozero::__anon3a308b630111::FileDescriptorComp64   bool operator()(const FileDescriptor* lhs, const FileDescriptor* rhs) const {
65     int comp = lhs->name().compare(rhs->name());
66     Assert(comp != 0 || lhs == rhs);
67     return comp < 0;
68   }
69 };
70 
71 struct DescriptorComp {
operator ()protozero::__anon3a308b630111::DescriptorComp72   bool operator()(const Descriptor* lhs, const Descriptor* rhs) const {
73     int comp = lhs->full_name().compare(rhs->full_name());
74     Assert(comp != 0 || lhs == rhs);
75     return comp < 0;
76   }
77 };
78 
79 struct EnumDescriptorComp {
operator ()protozero::__anon3a308b630111::EnumDescriptorComp80   bool operator()(const EnumDescriptor* lhs, const EnumDescriptor* rhs) const {
81     int comp = lhs->full_name().compare(rhs->full_name());
82     Assert(comp != 0 || lhs == rhs);
83     return comp < 0;
84   }
85 };
86 
ProtoStubName(const FileDescriptor * proto)87 inline std::string ProtoStubName(const FileDescriptor* proto) {
88   return StripSuffix(std::string(proto->name()), ".proto") + ".pbzero";
89 }
90 
Int32LiteralString(int32_t number)91 std::string Int32LiteralString(int32_t number) {
92   // Special case for -2147483648. If int is 32-bit, the compiler will
93   // misinterpret it.
94   if (number == std::numeric_limits<int32_t>::min()) {
95     return "-2147483647 - 1";
96   }
97   return std::to_string(number);
98 }
99 
100 class GeneratorJob {
101  public:
GeneratorJob(const FileDescriptor * file,Printer * stub_h_printer)102   GeneratorJob(const FileDescriptor* file, Printer* stub_h_printer)
103       : source_(file), stub_h_(stub_h_printer) {}
104 
GenerateStubs()105   bool GenerateStubs() {
106     Preprocess();
107     GeneratePrologue();
108     for (const EnumDescriptor* enumeration : enums_)
109       GenerateEnumDescriptor(enumeration);
110     for (const Descriptor* message : messages_)
111       GenerateMessageDescriptor(message);
112     for (const auto& key_value : extensions_)
113       GenerateExtension(key_value.first, key_value.second);
114     GenerateEpilogue();
115     return error_.empty();
116   }
117 
SetOption(const std::string & name,const std::string & value)118   void SetOption(const std::string& name, const std::string& value) {
119     if (name == "wrapper_namespace") {
120       wrapper_namespace_ = value;
121     } else if (name == "sdk") {
122       sdk_mode_ = (value == "true" || value == "1");
123     } else {
124       Abort(std::string() + "Unknown plugin option '" + name + "'.");
125     }
126   }
127 
128   // If generator fails to produce stubs for a particular proto definitions
129   // it finishes with undefined output and writes the first error occurred.
GetFirstError() const130   const std::string& GetFirstError() const { return error_; }
131 
132  private:
133   // Only the first error will be recorded.
Abort(const std::string & reason)134   void Abort(const std::string& reason) {
135     if (error_.empty())
136       error_ = reason;
137   }
138 
139   template <class T>
HasSamePackage(const T * descriptor) const140   bool HasSamePackage(const T* descriptor) const {
141     return descriptor->file()->package() == package_;
142   }
143 
144   // Get C++ class name corresponding to proto descriptor.
145   // Nested names are splitted by underscores. Underscores in type names aren't
146   // prohibited but not recommended in order to avoid name collisions.
147   template <class T>
GetCppClassName(const T * descriptor,bool full=false)148   inline std::string GetCppClassName(const T* descriptor, bool full = false) {
149     std::string package(descriptor->file()->package());
150     std::string name =
151         StripPrefix(std::string(descriptor->full_name()), package + ".");
152     name = StripChars(name, ".", '_');
153 
154     if (full && !package.empty()) {
155       auto get_full_namespace = [&]() {
156         std::vector<std::string> namespaces = SplitString(package, ".");
157         if (!wrapper_namespace_.empty())
158           namespaces.push_back(wrapper_namespace_);
159 
160         std::string result = "";
161         for (const std::string& ns : namespaces) {
162           result += "::";
163           result += ns;
164         }
165         return result;
166       };
167 
168       std::string namespaces = ReplaceAll(package, ".", "::");
169       name = get_full_namespace() + "::" + name;
170     }
171 
172     return name;
173   }
174 
GetFieldNumberConstant(const FieldDescriptor * field)175   inline std::string GetFieldNumberConstant(const FieldDescriptor* field) {
176     std::string name(field->camelcase_name());
177     if (!name.empty()) {
178       name.at(0) = Uppercase(name.at(0));
179       name = "k" + name + "FieldNumber";
180     } else {
181       // Protoc allows fields like 'bool _ = 1'.
182       Abort("Empty field name in camel case notation.");
183     }
184     return name;
185   }
186 
187   // Note: intentionally avoiding depending on protozero sources, as well as
188   // protobuf-internal WireFormat/WireFormatLite classes.
FieldTypeToProtozeroWireType(FieldDescriptor::Type proto_type)189   const char* FieldTypeToProtozeroWireType(FieldDescriptor::Type proto_type) {
190     switch (proto_type) {
191       case FieldDescriptor::TYPE_INT64:
192       case FieldDescriptor::TYPE_UINT64:
193       case FieldDescriptor::TYPE_INT32:
194       case FieldDescriptor::TYPE_BOOL:
195       case FieldDescriptor::TYPE_UINT32:
196       case FieldDescriptor::TYPE_ENUM:
197       case FieldDescriptor::TYPE_SINT32:
198       case FieldDescriptor::TYPE_SINT64:
199         return "::protozero::proto_utils::ProtoWireType::kVarInt";
200 
201       case FieldDescriptor::TYPE_FIXED32:
202       case FieldDescriptor::TYPE_SFIXED32:
203       case FieldDescriptor::TYPE_FLOAT:
204         return "::protozero::proto_utils::ProtoWireType::kFixed32";
205 
206       case FieldDescriptor::TYPE_FIXED64:
207       case FieldDescriptor::TYPE_SFIXED64:
208       case FieldDescriptor::TYPE_DOUBLE:
209         return "::protozero::proto_utils::ProtoWireType::kFixed64";
210 
211       case FieldDescriptor::TYPE_STRING:
212       case FieldDescriptor::TYPE_MESSAGE:
213       case FieldDescriptor::TYPE_BYTES:
214         return "::protozero::proto_utils::ProtoWireType::kLengthDelimited";
215 
216       case FieldDescriptor::TYPE_GROUP:
217         Abort("Groups not supported.");
218     }
219     Abort("Unrecognized FieldDescriptor::Type.");
220     return "";
221   }
222 
FieldTypeToPackedBufferType(FieldDescriptor::Type proto_type)223   const char* FieldTypeToPackedBufferType(FieldDescriptor::Type proto_type) {
224     switch (proto_type) {
225       case FieldDescriptor::TYPE_INT64:
226       case FieldDescriptor::TYPE_UINT64:
227       case FieldDescriptor::TYPE_INT32:
228       case FieldDescriptor::TYPE_BOOL:
229       case FieldDescriptor::TYPE_UINT32:
230       case FieldDescriptor::TYPE_ENUM:
231       case FieldDescriptor::TYPE_SINT32:
232       case FieldDescriptor::TYPE_SINT64:
233         return "::protozero::PackedVarInt";
234 
235       case FieldDescriptor::TYPE_FIXED32:
236         return "::protozero::PackedFixedSizeInt<uint32_t>";
237       case FieldDescriptor::TYPE_SFIXED32:
238         return "::protozero::PackedFixedSizeInt<int32_t>";
239       case FieldDescriptor::TYPE_FLOAT:
240         return "::protozero::PackedFixedSizeInt<float>";
241 
242       case FieldDescriptor::TYPE_FIXED64:
243         return "::protozero::PackedFixedSizeInt<uint64_t>";
244       case FieldDescriptor::TYPE_SFIXED64:
245         return "::protozero::PackedFixedSizeInt<int64_t>";
246       case FieldDescriptor::TYPE_DOUBLE:
247         return "::protozero::PackedFixedSizeInt<double>";
248 
249       case FieldDescriptor::TYPE_STRING:
250       case FieldDescriptor::TYPE_MESSAGE:
251       case FieldDescriptor::TYPE_BYTES:
252       case FieldDescriptor::TYPE_GROUP:
253         Abort("Unexpected FieldDescritor::Type.");
254     }
255     Abort("Unrecognized FieldDescriptor::Type.");
256     return "";
257   }
258 
FieldToProtoSchemaType(const FieldDescriptor * field)259   const char* FieldToProtoSchemaType(const FieldDescriptor* field) {
260     switch (field->type()) {
261       case FieldDescriptor::TYPE_BOOL:
262         return "kBool";
263       case FieldDescriptor::TYPE_INT32:
264         return "kInt32";
265       case FieldDescriptor::TYPE_INT64:
266         return "kInt64";
267       case FieldDescriptor::TYPE_UINT32:
268         return "kUint32";
269       case FieldDescriptor::TYPE_UINT64:
270         return "kUint64";
271       case FieldDescriptor::TYPE_SINT32:
272         return "kSint32";
273       case FieldDescriptor::TYPE_SINT64:
274         return "kSint64";
275       case FieldDescriptor::TYPE_FIXED32:
276         return "kFixed32";
277       case FieldDescriptor::TYPE_FIXED64:
278         return "kFixed64";
279       case FieldDescriptor::TYPE_SFIXED32:
280         return "kSfixed32";
281       case FieldDescriptor::TYPE_SFIXED64:
282         return "kSfixed64";
283       case FieldDescriptor::TYPE_FLOAT:
284         return "kFloat";
285       case FieldDescriptor::TYPE_DOUBLE:
286         return "kDouble";
287       case FieldDescriptor::TYPE_ENUM:
288         return "kEnum";
289       case FieldDescriptor::TYPE_STRING:
290         return "kString";
291       case FieldDescriptor::TYPE_MESSAGE:
292         return "kMessage";
293       case FieldDescriptor::TYPE_BYTES:
294         return "kBytes";
295 
296       case FieldDescriptor::TYPE_GROUP:
297         Abort("Groups not supported.");
298         return "";
299     }
300     Abort("Unrecognized FieldDescriptor::Type.");
301     return "";
302   }
303 
FieldToCppTypeName(const FieldDescriptor * field)304   std::string FieldToCppTypeName(const FieldDescriptor* field) {
305     switch (field->type()) {
306       case FieldDescriptor::TYPE_BOOL:
307         return "bool";
308       case FieldDescriptor::TYPE_INT32:
309         return "int32_t";
310       case FieldDescriptor::TYPE_INT64:
311         return "int64_t";
312       case FieldDescriptor::TYPE_UINT32:
313         return "uint32_t";
314       case FieldDescriptor::TYPE_UINT64:
315         return "uint64_t";
316       case FieldDescriptor::TYPE_SINT32:
317         return "int32_t";
318       case FieldDescriptor::TYPE_SINT64:
319         return "int64_t";
320       case FieldDescriptor::TYPE_FIXED32:
321         return "uint32_t";
322       case FieldDescriptor::TYPE_FIXED64:
323         return "uint64_t";
324       case FieldDescriptor::TYPE_SFIXED32:
325         return "int32_t";
326       case FieldDescriptor::TYPE_SFIXED64:
327         return "int64_t";
328       case FieldDescriptor::TYPE_FLOAT:
329         return "float";
330       case FieldDescriptor::TYPE_DOUBLE:
331         return "double";
332       case FieldDescriptor::TYPE_ENUM:
333         return GetCppClassName(field->enum_type(),
334                                !HasSamePackage(field->enum_type()));
335       case FieldDescriptor::TYPE_STRING:
336       case FieldDescriptor::TYPE_BYTES:
337         return "std::string";
338       case FieldDescriptor::TYPE_MESSAGE:
339         return GetCppClassName(field->message_type(),
340                                !HasSamePackage(field->message_type()));
341       case FieldDescriptor::TYPE_GROUP:
342         Abort("Groups not supported.");
343         return "";
344     }
345     Abort("Unrecognized FieldDescriptor::Type.");
346     return "";
347   }
348 
FieldToRepetitionType(const FieldDescriptor * field)349   const char* FieldToRepetitionType(const FieldDescriptor* field) {
350     if (!field->is_repeated())
351       return "kNotRepeated";
352     if (field->is_packed())
353       return "kRepeatedPacked";
354     return "kRepeatedNotPacked";
355   }
356 
CollectDescriptors()357   void CollectDescriptors() {
358     // Collect message descriptors in DFS order.
359     std::vector<const Descriptor*> stack;
360     stack.reserve(static_cast<size_t>(source_->message_type_count()));
361     for (int i = 0; i < source_->message_type_count(); ++i)
362       stack.push_back(source_->message_type(i));
363 
364     while (!stack.empty()) {
365       const Descriptor* message = stack.back();
366       stack.pop_back();
367 
368       if (message->extension_count() > 0) {
369         if (message->field_count() > 0 || message->nested_type_count() > 0 ||
370             message->enum_type_count() > 0) {
371           Abort("message with extend blocks shouldn't contain anything else");
372         }
373 
374         // Iterate over all fields in "extend" blocks.
375         for (int i = 0; i < message->extension_count(); ++i) {
376           const FieldDescriptor* extension = message->extension(i);
377 
378           // Protoc plugin API does not group fields in "extend" blocks.
379           // As the support for extensions in protozero is limited, the code
380           // assumes that extend blocks are located inside a wrapper message and
381           // name of this message is used to group them.
382           std::string extension_name(extension->extension_scope()->name());
383           extensions_[extension_name].push_back(extension);
384 
385           if (extension->message_type()) {
386             // Emit a forward declaration of nested message types, as the outer
387             // class will refer to them when creating type aliases.
388             referenced_messages_.insert(extension->message_type());
389           }
390         }
391       } else {
392         messages_.push_back(message);
393         for (int i = 0; i < message->nested_type_count(); ++i) {
394           stack.push_back(message->nested_type(i));
395           // Emit a forward declaration of nested message types, as the outer
396           // class will refer to them when creating type aliases.
397           referenced_messages_.insert(message->nested_type(i));
398         }
399       }
400     }
401 
402     // Collect enums.
403     for (int i = 0; i < source_->enum_type_count(); ++i)
404       enums_.push_back(source_->enum_type(i));
405 
406     if (source_->extension_count() > 0) {
407       // TODO(b/336524288): emit field numbers
408     }
409 
410     for (const Descriptor* message : messages_) {
411       for (int i = 0; i < message->enum_type_count(); ++i) {
412         enums_.push_back(message->enum_type(i));
413       }
414     }
415   }
416 
CollectDependencies()417   void CollectDependencies() {
418     // Public import basically means that callers only need to import this
419     // proto in order to use the stuff publicly imported by this proto.
420     for (int i = 0; i < source_->public_dependency_count(); ++i)
421       public_imports_.insert(source_->public_dependency(i));
422 
423     if (source_->weak_dependency_count() > 0)
424       Abort("Weak imports are not supported.");
425 
426     // Validations. Collect public imports (of collected imports) in DFS order.
427     // Visibilty for current proto:
428     // - all imports listed in current proto,
429     // - public imports of everything imported (recursive).
430     std::vector<const FileDescriptor*> stack;
431     for (int i = 0; i < source_->dependency_count(); ++i) {
432       const FileDescriptor* import = source_->dependency(i);
433       stack.push_back(import);
434       if (public_imports_.count(import) == 0) {
435         private_imports_.insert(import);
436       }
437     }
438 
439     while (!stack.empty()) {
440       const FileDescriptor* import = stack.back();
441       stack.pop_back();
442       for (int i = 0; i < import->public_dependency_count(); ++i) {
443         stack.push_back(import->public_dependency(i));
444       }
445     }
446 
447     // Collect descriptors of messages and enums used in current proto.
448     // It will be used to generate necessary forward declarations and
449     // check that everything lays in the same namespace.
450     for (const Descriptor* message : messages_) {
451       for (int i = 0; i < message->field_count(); ++i) {
452         const FieldDescriptor* field = message->field(i);
453 
454         if (field->type() == FieldDescriptor::TYPE_MESSAGE) {
455           if (public_imports_.count(field->message_type()->file()) == 0) {
456             // Avoid multiple forward declarations since
457             // public imports have been already included.
458             referenced_messages_.insert(field->message_type());
459           }
460         } else if (field->type() == FieldDescriptor::TYPE_ENUM) {
461           if (public_imports_.count(field->enum_type()->file()) == 0) {
462             referenced_enums_.insert(field->enum_type());
463           }
464         }
465       }
466     }
467   }
468 
Preprocess()469   void Preprocess() {
470     // Package name maps to a series of namespaces.
471     package_ = source_->package();
472     namespaces_ = SplitString(package_, ".");
473     if (!wrapper_namespace_.empty())
474       namespaces_.push_back(wrapper_namespace_);
475 
476     full_namespace_prefix_ = "::";
477     for (const std::string& ns : namespaces_)
478       full_namespace_prefix_ += ns + "::";
479 
480     CollectDescriptors();
481     CollectDependencies();
482   }
483 
GetNamespaceNameForInnerEnum(const EnumDescriptor * enumeration)484   std::string GetNamespaceNameForInnerEnum(const EnumDescriptor* enumeration) {
485     return "perfetto_pbzero_enum_" +
486            GetCppClassName(enumeration->containing_type());
487   }
488 
489   // Print top header, namespaces and forward declarations.
GeneratePrologue()490   void GeneratePrologue() {
491     std::string greeting =
492         "// Autogenerated by the ProtoZero compiler plugin. DO NOT EDIT.\n";
493     std::string guard = package_ + "_" + std::string(source_->name()) + "_H_";
494     guard = ToUpper(guard);
495     guard = StripChars(guard, ".-/\\", '_');
496 
497     stub_h_->Print(
498         "$greeting$\n"
499         "#ifndef $guard$\n"
500         "#define $guard$\n\n"
501         "#include <stddef.h>\n"
502         "#include <stdint.h>\n\n",
503         "greeting", greeting, "guard", guard);
504 
505     if (sdk_mode_) {
506       stub_h_->Print("#include \"perfetto.h\"\n");
507     } else {
508       stub_h_->Print(
509           "#include \"perfetto/protozero/field_writer.h\"\n"
510           "#include \"perfetto/protozero/message.h\"\n"
511           "#include \"perfetto/protozero/packed_repeated_fields.h\"\n"
512           "#include \"perfetto/protozero/proto_decoder.h\"\n"
513           "#include \"perfetto/protozero/proto_utils.h\"\n");
514     }
515 
516     // Print includes for public imports. In sdk mode, all imports are assumed
517     // to be part of the sdk.
518     if (!sdk_mode_) {
519       for (const FileDescriptor* dependency : public_imports_) {
520         // Dependency name could contain slashes but importing from upper-level
521         // directories is not possible anyway since build system processes each
522         // proto file individually. Hence proto lookup path is always equal to
523         // the directory where particular proto file is located and protoc does
524         // not allow reference to upper directory (aka ..) in import path.
525         //
526         // Laconically said:
527         // - source_->name() may never have slashes,
528         // - dependency->name() may have slashes but always refers to inner
529         // path.
530         stub_h_->Print("#include \"$name$.h\"\n", "name",
531                        ProtoStubName(dependency));
532       }
533     }
534     stub_h_->Print("\n");
535 
536     PrintForwardDeclarations();
537 
538     // Print namespaces.
539     for (const std::string& ns : namespaces_) {
540       stub_h_->Print("namespace $ns$ {\n", "ns", ns);
541     }
542     stub_h_->Print("\n");
543   }
544 
PrintForwardDeclarations()545   void PrintForwardDeclarations() {
546     struct Descriptors {
547       std::vector<const Descriptor*> messages_;
548       std::vector<const EnumDescriptor*> enums_;
549     };
550     std::map<std::string, Descriptors> package_to_descriptors;
551 
552     for (const Descriptor* message : referenced_messages_) {
553       std::string package(message->file()->package());
554       package_to_descriptors[package].messages_.push_back(message);
555     }
556 
557     for (const EnumDescriptor* enumeration : referenced_enums_) {
558       std::string package(enumeration->file()->package());
559       package_to_descriptors[package].enums_.push_back(enumeration);
560     }
561 
562     for (const auto& [package, descriptors] : package_to_descriptors) {
563       std::vector<std::string> namespaces = SplitString(package, ".");
564       namespaces.push_back(wrapper_namespace_);
565 
566       // open namespaces
567       for (const auto& ns : namespaces) {
568         stub_h_->Print("namespace $ns$ {\n", "ns", ns);
569       }
570 
571       for (const Descriptor* message : descriptors.messages_) {
572         stub_h_->Print("class $class$;\n", "class", GetCppClassName(message));
573       }
574 
575       for (const EnumDescriptor* enumeration : descriptors.enums_) {
576         if (enumeration->containing_type()) {
577           stub_h_->Print("namespace $namespace_name$ {\n", "namespace_name",
578                          GetNamespaceNameForInnerEnum(enumeration));
579         }
580         stub_h_->Print("enum $class$ : int32_t;\n", "class",
581                        enumeration->name());
582 
583         if (enumeration->containing_type()) {
584           stub_h_->Print("}  // namespace $namespace_name$\n", "namespace_name",
585                          GetNamespaceNameForInnerEnum(enumeration));
586           stub_h_->Print("using $alias$ = $namespace_name$::$short_name$;\n",
587                          "alias", GetCppClassName(enumeration),
588                          "namespace_name",
589                          GetNamespaceNameForInnerEnum(enumeration),
590                          "short_name", enumeration->name());
591         }
592       }
593 
594       // close namespaces
595       for (auto it = namespaces.crbegin(); it != namespaces.crend(); ++it) {
596         stub_h_->Print("} // Namespace $ns$.\n", "ns", *it);
597       }
598     }
599 
600     stub_h_->Print("\n");
601   }
602 
GenerateEnumDescriptor(const EnumDescriptor * enumeration)603   void GenerateEnumDescriptor(const EnumDescriptor* enumeration) {
604     bool is_inner_enum = !!enumeration->containing_type();
605     if (is_inner_enum) {
606       stub_h_->Print("namespace $namespace_name$ {\n", "namespace_name",
607                      GetNamespaceNameForInnerEnum(enumeration));
608     }
609 
610     stub_h_->Print("enum $class$ : int32_t {\n", "class", enumeration->name());
611     stub_h_->Indent();
612 
613     std::string min_name, max_name;
614     int min_val = std::numeric_limits<int>::max();
615     int max_val = -1;
616     for (int i = 0; i < enumeration->value_count(); ++i) {
617       const EnumValueDescriptor* value = enumeration->value(i);
618       const std::string value_name(value->name());
619       stub_h_->Print("$name$ = $number$,\n", "name", value_name, "number",
620                      Int32LiteralString(value->number()));
621       if (value->number() < min_val) {
622         min_val = value->number();
623         min_name = value_name;
624       }
625       if (value->number() > max_val) {
626         max_val = value->number();
627         max_name = value_name;
628       }
629     }
630     stub_h_->Outdent();
631     stub_h_->Print("};\n");
632     if (is_inner_enum) {
633       const std::string namespace_name =
634           GetNamespaceNameForInnerEnum(enumeration);
635       stub_h_->Print("} // namespace $namespace_name$\n", "namespace_name",
636                      namespace_name);
637       stub_h_->Print(
638           "using $full_enum_name$ = $namespace_name$::$enum_name$;\n\n",
639           "full_enum_name", GetCppClassName(enumeration), "enum_name",
640           enumeration->name(), "namespace_name", namespace_name);
641     }
642     stub_h_->Print("\n");
643     stub_h_->Print("constexpr $class$ $class$_MIN = $class$::$min$;\n", "class",
644                    GetCppClassName(enumeration), "min", min_name);
645     stub_h_->Print("constexpr $class$ $class$_MAX = $class$::$max$;\n", "class",
646                    GetCppClassName(enumeration), "max", max_name);
647     stub_h_->Print("\n");
648 
649     GenerateEnumToStringConversion(enumeration);
650   }
651 
GenerateEnumToStringConversion(const EnumDescriptor * enumeration)652   void GenerateEnumToStringConversion(const EnumDescriptor* enumeration) {
653     std::string fullClassName =
654         full_namespace_prefix_ + GetCppClassName(enumeration);
655     const char* function_header_stub = R"(
656 PERFETTO_PROTOZERO_CONSTEXPR14_OR_INLINE
657 const char* $class_name$_Name($full_class$ value) {
658 )";
659     stub_h_->Print(function_header_stub, "full_class", fullClassName,
660                    "class_name", GetCppClassName(enumeration));
661     stub_h_->Indent();
662     stub_h_->Print("switch (value) {");
663     for (int index = 0; index < enumeration->value_count(); ++index) {
664       const EnumValueDescriptor* value = enumeration->value(index);
665       const char* switch_stub = R"(
666 case $full_class$::$value_name$:
667   return "$value_name$";
668 )";
669       stub_h_->Print(switch_stub, "full_class", fullClassName, "value_name",
670                      value->name());
671     }
672     stub_h_->Print("}\n");
673     stub_h_->Print(R"(return "PBZERO_UNKNOWN_ENUM_VALUE";)");
674     stub_h_->Print("\n");
675     stub_h_->Outdent();
676     stub_h_->Print("}\n\n");
677   }
678 
679   // Packed repeated fields are encoded as a length-delimited field on the wire,
680   // where the payload is the concatenation of invidually encoded elements.
GeneratePackedRepeatedFieldDescriptor(const FieldDescriptor * field)681   void GeneratePackedRepeatedFieldDescriptor(const FieldDescriptor* field) {
682     std::map<std::string, std::string> setter;
683     setter["name"] = field->lowercase_name();
684     setter["field_metadata"] = GetFieldMetadataTypeName(field);
685     setter["action"] = "set";
686     setter["buffer_type"] = FieldTypeToPackedBufferType(field->type());
687     stub_h_->Print(
688         setter,
689         "void $action$_$name$(const $buffer_type$& packed_buffer) {\n"
690         "  AppendBytes($field_metadata$::kFieldId, packed_buffer.data(),\n"
691         "              packed_buffer.size());\n"
692         "}\n");
693   }
694 
GenerateSimpleFieldDescriptor(const FieldDescriptor * field)695   void GenerateSimpleFieldDescriptor(const FieldDescriptor* field) {
696     std::map<std::string, std::string> setter;
697     setter["id"] = std::to_string(field->number());
698     setter["name"] = field->lowercase_name();
699     setter["field_metadata"] = GetFieldMetadataTypeName(field);
700     setter["action"] = field->is_repeated() ? "add" : "set";
701     setter["cpp_type"] = FieldToCppTypeName(field);
702     setter["proto_field_type"] = FieldToProtoSchemaType(field);
703 
704     const char* code_stub =
705         "void $action$_$name$($cpp_type$ value) {\n"
706         "  static constexpr uint32_t field_id = $field_metadata$::kFieldId;\n"
707         "  // Call the appropriate protozero::Message::Append(field_id, ...)\n"
708         "  // method based on the type of the field.\n"
709         "  ::protozero::internal::FieldWriter<\n"
710         "    ::protozero::proto_utils::ProtoSchemaType::$proto_field_type$>\n"
711         "      ::Append(*this, field_id, value);\n"
712         "}\n";
713 
714     if (field->type() == FieldDescriptor::TYPE_STRING) {
715       // Strings and bytes should have an additional accessor which specifies
716       // the length explicitly.
717       const char* additional_method =
718           "void $action$_$name$(const char* data, size_t size) {\n"
719           "  AppendBytes($field_metadata$::kFieldId, data, size);\n"
720           "}\n"
721           "void $action$_$name$(::protozero::ConstChars chars) {\n"
722           "  AppendBytes($field_metadata$::kFieldId, chars.data, chars.size);\n"
723           "}\n";
724       stub_h_->Print(setter, additional_method);
725     } else if (field->type() == FieldDescriptor::TYPE_BYTES) {
726       const char* additional_method =
727           "void $action$_$name$(const uint8_t* data, size_t size) {\n"
728           "  AppendBytes($field_metadata$::kFieldId, data, size);\n"
729           "}\n"
730           "void $action$_$name$(::protozero::ConstBytes bytes) {\n"
731           "  AppendBytes($field_metadata$::kFieldId, bytes.data, bytes.size);\n"
732           "}\n";
733       stub_h_->Print(setter, additional_method);
734     } else if (field->type() == FieldDescriptor::TYPE_GROUP ||
735                field->type() == FieldDescriptor::TYPE_MESSAGE) {
736       Abort("Unsupported field type.");
737       return;
738     }
739 
740     stub_h_->Print(setter, code_stub);
741   }
742 
GenerateNestedMessageFieldDescriptor(const FieldDescriptor * field)743   void GenerateNestedMessageFieldDescriptor(const FieldDescriptor* field) {
744     std::string action = field->is_repeated() ? "add" : "set";
745     std::string inner_class = GetCppClassName(
746         field->message_type(), !HasSamePackage(field->message_type()));
747     stub_h_->Print(
748         "template <typename T = $inner_class$> T* $action$_$name$() {\n"
749         "  return BeginNestedMessage<T>($id$);\n"
750         "}\n\n",
751         "id", std::to_string(field->number()), "name", field->lowercase_name(),
752         "action", action, "inner_class", inner_class);
753     if (field->options().lazy()) {
754       stub_h_->Print(
755           "void $action$_$name$_raw(const std::string& raw) {\n"
756           "  return AppendBytes($id$, raw.data(), raw.size());\n"
757           "}\n\n",
758           "id", std::to_string(field->number()), "name",
759           field->lowercase_name(), "action", action, "inner_class",
760           inner_class);
761     }
762   }
763 
GenerateDecoder(const Descriptor * message)764   void GenerateDecoder(const Descriptor* message) {
765     int max_field_id = 0;
766     bool has_nonpacked_repeated_fields = false;
767     for (int i = 0; i < message->field_count(); ++i) {
768       const FieldDescriptor* field = message->field(i);
769       if (field->number() > kMaxDecoderFieldId)
770         continue;
771       max_field_id = std::max(max_field_id, field->number());
772       if (field->is_repeated() && !field->is_packed())
773         has_nonpacked_repeated_fields = true;
774     }
775     // Iterate over all fields in "extend" blocks.
776     for (int i = 0; i < message->extension_range_count(); ++i) {
777       Descriptor::ExtensionRange::Proto range;
778       message->extension_range(i)->CopyTo(&range);
779       int candidate = range.end() - 1;
780       if (candidate > kMaxDecoderFieldId)
781         continue;
782       max_field_id = std::max(max_field_id, candidate);
783     }
784 
785     std::string class_name = GetCppClassName(message) + "_Decoder";
786     stub_h_->Print(
787         "class $name$ : public "
788         "::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/$max$, "
789         "/*HAS_NONPACKED_REPEATED_FIELDS=*/$rep$> {\n",
790         "name", class_name, "max", std::to_string(max_field_id), "rep",
791         has_nonpacked_repeated_fields ? "true" : "false");
792     stub_h_->Print(" public:\n");
793     stub_h_->Indent();
794     stub_h_->Print(
795         "$name$(const uint8_t* data, size_t len) "
796         ": TypedProtoDecoder(data, len) {}\n",
797         "name", class_name);
798     stub_h_->Print(
799         "explicit $name$(const std::string& raw) : "
800         "TypedProtoDecoder(reinterpret_cast<const uint8_t*>(raw.data()), "
801         "raw.size()) {}\n",
802         "name", class_name);
803     stub_h_->Print(
804         "explicit $name$(const ::protozero::ConstBytes& raw) : "
805         "TypedProtoDecoder(raw.data, raw.size) {}\n",
806         "name", class_name);
807 
808     for (int i = 0; i < message->field_count(); ++i) {
809       const FieldDescriptor* field = message->field(i);
810       if (field->number() > max_field_id) {
811         stub_h_->Print("// field $name$ omitted because its id is too high\n",
812                        "name", field->name());
813         continue;
814       }
815       std::string getter;
816       std::string cpp_type;
817       switch (field->type()) {
818         case FieldDescriptor::TYPE_BOOL:
819           getter = "as_bool";
820           cpp_type = "bool";
821           break;
822         case FieldDescriptor::TYPE_SFIXED32:
823         case FieldDescriptor::TYPE_INT32:
824           getter = "as_int32";
825           cpp_type = "int32_t";
826           break;
827         case FieldDescriptor::TYPE_SINT32:
828           getter = "as_sint32";
829           cpp_type = "int32_t";
830           break;
831         case FieldDescriptor::TYPE_SFIXED64:
832         case FieldDescriptor::TYPE_INT64:
833           getter = "as_int64";
834           cpp_type = "int64_t";
835           break;
836         case FieldDescriptor::TYPE_SINT64:
837           getter = "as_sint64";
838           cpp_type = "int64_t";
839           break;
840         case FieldDescriptor::TYPE_FIXED32:
841         case FieldDescriptor::TYPE_UINT32:
842           getter = "as_uint32";
843           cpp_type = "uint32_t";
844           break;
845         case FieldDescriptor::TYPE_FIXED64:
846         case FieldDescriptor::TYPE_UINT64:
847           getter = "as_uint64";
848           cpp_type = "uint64_t";
849           break;
850         case FieldDescriptor::TYPE_FLOAT:
851           getter = "as_float";
852           cpp_type = "float";
853           break;
854         case FieldDescriptor::TYPE_DOUBLE:
855           getter = "as_double";
856           cpp_type = "double";
857           break;
858         case FieldDescriptor::TYPE_ENUM:
859           getter = "as_int32";
860           cpp_type = "int32_t";
861           break;
862         case FieldDescriptor::TYPE_STRING:
863           getter = "as_string";
864           cpp_type = "::protozero::ConstChars";
865           break;
866         case FieldDescriptor::TYPE_MESSAGE:
867         case FieldDescriptor::TYPE_BYTES:
868           getter = "as_bytes";
869           cpp_type = "::protozero::ConstBytes";
870           break;
871         case FieldDescriptor::TYPE_GROUP:
872           continue;
873       }
874 
875       stub_h_->Print("bool has_$name$() const { return at<$id$>().valid(); }\n",
876                      "name", field->lowercase_name(), "id",
877                      std::to_string(field->number()));
878 
879       if (field->is_packed()) {
880         const char* protozero_wire_type =
881             FieldTypeToProtozeroWireType(field->type());
882         stub_h_->Print(
883             "::protozero::PackedRepeatedFieldIterator<$wire_type$, $cpp_type$> "
884             "$name$(bool* parse_error_ptr) const { return "
885             "GetPackedRepeated<$wire_type$, $cpp_type$>($id$, "
886             "parse_error_ptr); }\n",
887             "wire_type", protozero_wire_type, "cpp_type", cpp_type, "name",
888             field->lowercase_name(), "id", std::to_string(field->number()));
889       } else if (field->is_repeated()) {
890         stub_h_->Print(
891             "::protozero::RepeatedFieldIterator<$cpp_type$> $name$() const { "
892             "return "
893             "GetRepeated<$cpp_type$>($id$); }\n",
894             "name", field->lowercase_name(), "cpp_type", cpp_type, "id",
895             std::to_string(field->number()));
896       } else {
897         stub_h_->Print(
898             "$cpp_type$ $name$() const { return at<$id$>().$getter$(); }\n",
899             "name", field->lowercase_name(), "id",
900             std::to_string(field->number()), "cpp_type", cpp_type, "getter",
901             getter);
902       }
903     }
904     stub_h_->Outdent();
905     stub_h_->Print("};\n\n");
906   }
907 
GenerateConstantsForMessageFields(const Descriptor * message)908   void GenerateConstantsForMessageFields(const Descriptor* message) {
909     const bool has_fields =
910         message->field_count() > 0 || message->extension_count() > 0;
911 
912     // Field number constants.
913     if (has_fields) {
914       stub_h_->Print("enum : int32_t {\n");
915       stub_h_->Indent();
916 
917       for (int i = 0; i < message->field_count(); ++i) {
918         const FieldDescriptor* field = message->field(i);
919         stub_h_->Print("$name$ = $id$,\n", "name",
920                        GetFieldNumberConstant(field), "id",
921                        std::to_string(field->number()));
922       }
923 
924       for (int i = 0; i < message->extension_count(); ++i) {
925         const FieldDescriptor* field = message->extension(i);
926 
927         stub_h_->Print("$name$ = $id$,\n", "name",
928                        GetFieldNumberConstant(field), "id",
929                        std::to_string(field->number()));
930       }
931 
932       stub_h_->Outdent();
933       stub_h_->Print("};\n");
934     }
935   }
936 
GenerateMessageDescriptor(const Descriptor * message)937   void GenerateMessageDescriptor(const Descriptor* message) {
938     GenerateDecoder(message);
939 
940     stub_h_->Print(
941         "class $name$ : public ::protozero::Message {\n"
942         " public:\n",
943         "name", GetCppClassName(message));
944     stub_h_->Indent();
945 
946     stub_h_->Print("using Decoder = $name$_Decoder;\n", "name",
947                    GetCppClassName(message));
948 
949     GenerateConstantsForMessageFields(message);
950 
951     stub_h_->Print(
952         "static constexpr const char* GetName() { return \".$name$\"; }\n\n",
953         "name", message->full_name());
954 
955     // Using statements for nested messages.
956     for (int i = 0; i < message->nested_type_count(); ++i) {
957       const Descriptor* nested_message = message->nested_type(i);
958       stub_h_->Print("using $local_name$ = $global_name$;\n", "local_name",
959                      nested_message->name(), "global_name",
960                      GetCppClassName(nested_message, true));
961     }
962 
963     // Using statements for nested enums.
964     for (int i = 0; i < message->enum_type_count(); ++i) {
965       const EnumDescriptor* nested_enum = message->enum_type(i);
966       const char* stub = R"(
967 using $local_name$ = $global_name$;
968 static inline const char* $local_name$_Name($local_name$ value) {
969   return $global_name$_Name(value);
970 }
971 )";
972       stub_h_->Print(stub, "local_name", nested_enum->name(), "global_name",
973                      GetCppClassName(nested_enum, true));
974     }
975 
976     // Values of nested enums.
977     for (int i = 0; i < message->enum_type_count(); ++i) {
978       const EnumDescriptor* nested_enum = message->enum_type(i);
979 
980       for (int j = 0; j < nested_enum->value_count(); ++j) {
981         const EnumValueDescriptor* value = nested_enum->value(j);
982         stub_h_->Print(
983             "static inline const $class$ $name$ = $class$::$name$;\n", "class",
984             nested_enum->name(), "name", value->name());
985       }
986     }
987 
988     // Field descriptors.
989     for (int i = 0; i < message->field_count(); ++i) {
990       GenerateFieldDescriptor(GetCppClassName(message), message->field(i));
991     }
992 
993     stub_h_->Outdent();
994     stub_h_->Print("};\n\n");
995   }
996 
GetFieldMetadataTypeName(const FieldDescriptor * field)997   std::string GetFieldMetadataTypeName(const FieldDescriptor* field) {
998     std::string name(field->camelcase_name());
999     if (isalpha(name[0]))
1000       name[0] = static_cast<char>(toupper(name[0]));
1001     return "FieldMetadata_" + name;
1002   }
1003 
GetFieldMetadataVariableName(const FieldDescriptor * field)1004   std::string GetFieldMetadataVariableName(const FieldDescriptor* field) {
1005     std::string name(field->camelcase_name());
1006     if (isalpha(name[0]))
1007       name[0] = static_cast<char>(toupper(name[0]));
1008     return "k" + name;
1009   }
1010 
GenerateFieldMetadata(const std::string & message_cpp_type,const FieldDescriptor * field)1011   void GenerateFieldMetadata(const std::string& message_cpp_type,
1012                              const FieldDescriptor* field) {
1013     const char* code_stub = R"(
1014 using $field_metadata_type$ =
1015   ::protozero::proto_utils::FieldMetadata<
1016     $field_id$,
1017     ::protozero::proto_utils::RepetitionType::$repetition_type$,
1018     ::protozero::proto_utils::ProtoSchemaType::$proto_field_type$,
1019     $cpp_type$,
1020     $message_cpp_type$>;
1021 
1022 static constexpr $field_metadata_type$ $field_metadata_var${};
1023 )";
1024 
1025     stub_h_->Print(code_stub, "field_id", std::to_string(field->number()),
1026                    "repetition_type", FieldToRepetitionType(field),
1027                    "proto_field_type", FieldToProtoSchemaType(field),
1028                    "cpp_type", FieldToCppTypeName(field), "message_cpp_type",
1029                    message_cpp_type, "field_metadata_type",
1030                    GetFieldMetadataTypeName(field), "field_metadata_var",
1031                    GetFieldMetadataVariableName(field));
1032   }
1033 
GenerateFieldDescriptor(const std::string & message_cpp_type,const FieldDescriptor * field)1034   void GenerateFieldDescriptor(const std::string& message_cpp_type,
1035                                const FieldDescriptor* field) {
1036     GenerateFieldMetadata(message_cpp_type, field);
1037     if (field->is_packed()) {
1038       GeneratePackedRepeatedFieldDescriptor(field);
1039     } else if (field->type() != FieldDescriptor::TYPE_MESSAGE) {
1040       GenerateSimpleFieldDescriptor(field);
1041     } else {
1042       GenerateNestedMessageFieldDescriptor(field);
1043     }
1044   }
1045 
1046   // Generate extension class for a group of FieldDescriptor instances
1047   // representing one "extend" block in proto definition. For example:
1048   //
1049   //   message SpecificExtension {
1050   //     extend GeneralThing {
1051   //       optional Fizz fizz = 101;
1052   //       optional Buzz buzz = 102;
1053   //     }
1054   //   }
1055   //
1056   // This is going to be passed as a vector of two elements, "fizz" and
1057   // "buzz". Wrapping message is used to provide a name for generated
1058   // extension class.
1059   //
1060   // In the example above, generated code is going to look like:
1061   //
1062   //   class SpecificExtension : public GeneralThing {
1063   //     Fizz* set_fizz();
1064   //     Buzz* set_buzz();
1065   //   }
GenerateExtension(const std::string & extension_name,const std::vector<const FieldDescriptor * > & descriptors)1066   void GenerateExtension(
1067       const std::string& extension_name,
1068       const std::vector<const FieldDescriptor*>& descriptors) {
1069     // Use an arbitrary descriptor in order to get generic information not
1070     // specific to any of them.
1071     const FieldDescriptor* descriptor = descriptors[0];
1072     const Descriptor* base_message = descriptor->containing_type();
1073 
1074     // TODO(ddrone): ensure that this code works when containing_type located in
1075     // other file or namespace.
1076     stub_h_->Print("class $name$ : public $extendee$ {\n", "name",
1077                    extension_name, "extendee",
1078                    GetCppClassName(base_message, /*full=*/true));
1079     stub_h_->Print(" public:\n");
1080     stub_h_->Indent();
1081     for (const FieldDescriptor* field : descriptors) {
1082       if (field->containing_type() != base_message) {
1083         Abort("one wrapper should extend only one message");
1084         return;
1085       }
1086       GenerateFieldDescriptor(extension_name, field);
1087     }
1088 
1089     if (!descriptors.empty()) {
1090       stub_h_->Print("enum : int32_t {\n");
1091       stub_h_->Indent();
1092 
1093       for (const FieldDescriptor* field : descriptors) {
1094         stub_h_->Print("$name$ = $id$,\n", "name",
1095                        GetFieldNumberConstant(field), "id",
1096                        std::to_string(field->number()));
1097       }
1098       stub_h_->Outdent();
1099       stub_h_->Print("};\n");
1100     }
1101 
1102     stub_h_->Outdent();
1103     stub_h_->Print("};\n");
1104   }
1105 
GenerateEpilogue()1106   void GenerateEpilogue() {
1107     for (unsigned i = 0; i < namespaces_.size(); ++i) {
1108       stub_h_->Print("} // Namespace.\n");
1109     }
1110     stub_h_->Print("#endif  // Include guard.\n");
1111   }
1112 
1113   const FileDescriptor* const source_;
1114   Printer* const stub_h_;
1115   std::string error_;
1116 
1117   std::string package_;
1118   std::string wrapper_namespace_;
1119   std::vector<std::string> namespaces_;
1120   std::string full_namespace_prefix_;
1121   std::vector<const Descriptor*> messages_;
1122   std::vector<const EnumDescriptor*> enums_;
1123   std::map<std::string, std::vector<const FieldDescriptor*>> extensions_;
1124 
1125   // Generate headers that can be used with the Perfetto SDK.
1126   bool sdk_mode_ = false;
1127 
1128   // The custom *Comp comparators are to ensure determinism of the generator.
1129   std::set<const FileDescriptor*, FileDescriptorComp> public_imports_;
1130   std::set<const FileDescriptor*, FileDescriptorComp> private_imports_;
1131   std::set<const Descriptor*, DescriptorComp> referenced_messages_;
1132   std::set<const EnumDescriptor*, EnumDescriptorComp> referenced_enums_;
1133 };
1134 
1135 class ProtoZeroGenerator : public ::google::protobuf::compiler::CodeGenerator {
1136  public:
1137   explicit ProtoZeroGenerator();
1138   ~ProtoZeroGenerator() override;
1139 
1140   // CodeGenerator implementation
1141   bool Generate(const google::protobuf::FileDescriptor* file,
1142                 const std::string& options,
1143                 GeneratorContext* context,
1144                 std::string* error) const override;
1145 };
1146 
ProtoZeroGenerator()1147 ProtoZeroGenerator::ProtoZeroGenerator() {}
1148 
~ProtoZeroGenerator()1149 ProtoZeroGenerator::~ProtoZeroGenerator() {}
1150 
Generate(const FileDescriptor * file,const std::string & options,GeneratorContext * context,std::string * error) const1151 bool ProtoZeroGenerator::Generate(const FileDescriptor* file,
1152                                   const std::string& options,
1153                                   GeneratorContext* context,
1154                                   std::string* error) const {
1155   const std::unique_ptr<ZeroCopyOutputStream> stub_h_file_stream(
1156       context->Open(ProtoStubName(file) + ".h"));
1157   const std::unique_ptr<ZeroCopyOutputStream> stub_cc_file_stream(
1158       context->Open(ProtoStubName(file) + ".cc"));
1159 
1160   // Variables are delimited by $.
1161   Printer stub_h_printer(stub_h_file_stream.get(), '$');
1162   GeneratorJob job(file, &stub_h_printer);
1163 
1164   Printer stub_cc_printer(stub_cc_file_stream.get(), '$');
1165   stub_cc_printer.Print("// Intentionally empty (crbug.com/998165)\n");
1166 
1167   // Parse additional options.
1168   for (const std::string& option : SplitString(options, ",")) {
1169     std::vector<std::string> option_pair = SplitString(option, "=");
1170     job.SetOption(option_pair[0], option_pair[1]);
1171   }
1172 
1173   if (!job.GenerateStubs()) {
1174     *error = job.GetFirstError();
1175     return false;
1176   }
1177   return true;
1178 }
1179 
1180 }  // namespace
1181 }  // namespace protozero
1182 
main(int argc,char * argv[])1183 int main(int argc, char* argv[]) {
1184   ::protozero::ProtoZeroGenerator generator;
1185   return google::protobuf::compiler::PluginMain(argc, argv, &generator);
1186 }
1187