• 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 "src/protozero/protoc_plugin/protozero_generator.h"
18 
19 #include <map>
20 #include <memory>
21 #include <set>
22 #include <string>
23 
24 #include "google/protobuf/descriptor.h"
25 #include "google/protobuf/io/printer.h"
26 #include "google/protobuf/io/zero_copy_stream.h"
27 #include "google/protobuf/stubs/strutil.h"
28 
29 namespace protozero {
30 
31 using google::protobuf::Descriptor;  // Message descriptor.
32 using google::protobuf::EnumDescriptor;
33 using google::protobuf::EnumValueDescriptor;
34 using google::protobuf::FieldDescriptor;
35 using google::protobuf::FileDescriptor;
36 using google::protobuf::compiler::GeneratorContext;
37 using google::protobuf::io::Printer;
38 using google::protobuf::io::ZeroCopyOutputStream;
39 
40 using google::protobuf::Split;
41 using google::protobuf::StripPrefixString;
42 using google::protobuf::StripString;
43 using google::protobuf::StripSuffixString;
44 using google::protobuf::UpperString;
45 
46 namespace {
47 
48 // Keep this value in sync with ProtoDecoder::kMaxDecoderFieldId. If they go out
49 // of sync pbzero.h files will stop compiling, hitting the at() static_assert.
50 // Not worth an extra dependency.
51 constexpr int kMaxDecoderFieldId = 999;
52 
Assert(bool condition)53 void Assert(bool condition) {
54   if (!condition)
55     __builtin_trap();
56 }
57 
58 struct FileDescriptorComp {
operator ()protozero::__anone8b65b2e0111::FileDescriptorComp59   bool operator()(const FileDescriptor* lhs, const FileDescriptor* rhs) const {
60     int comp = lhs->name().compare(rhs->name());
61     Assert(comp != 0 || lhs == rhs);
62     return comp < 0;
63   }
64 };
65 
66 struct DescriptorComp {
operator ()protozero::__anone8b65b2e0111::DescriptorComp67   bool operator()(const Descriptor* lhs, const Descriptor* rhs) const {
68     int comp = lhs->full_name().compare(rhs->full_name());
69     Assert(comp != 0 || lhs == rhs);
70     return comp < 0;
71   }
72 };
73 
74 struct EnumDescriptorComp {
operator ()protozero::__anone8b65b2e0111::EnumDescriptorComp75   bool operator()(const EnumDescriptor* lhs, const EnumDescriptor* rhs) const {
76     int comp = lhs->full_name().compare(rhs->full_name());
77     Assert(comp != 0 || lhs == rhs);
78     return comp < 0;
79   }
80 };
81 
ProtoStubName(const FileDescriptor * proto)82 inline std::string ProtoStubName(const FileDescriptor* proto) {
83   return StripSuffixString(proto->name(), ".proto") + ".pbzero";
84 }
85 
86 class GeneratorJob {
87  public:
GeneratorJob(const FileDescriptor * file,Printer * stub_h_printer)88   GeneratorJob(const FileDescriptor* file, Printer* stub_h_printer)
89       : source_(file), stub_h_(stub_h_printer) {}
90 
GenerateStubs()91   bool GenerateStubs() {
92     Preprocess();
93     GeneratePrologue();
94     for (const EnumDescriptor* enumeration : enums_)
95       GenerateEnumDescriptor(enumeration);
96     for (const Descriptor* message : messages_)
97       GenerateMessageDescriptor(message);
98     GenerateEpilogue();
99     return error_.empty();
100   }
101 
SetOption(const std::string & name,const std::string & value)102   void SetOption(const std::string& name, const std::string& value) {
103     if (name == "wrapper_namespace") {
104       wrapper_namespace_ = value;
105     } else {
106       Abort(std::string() + "Unknown plugin option '" + name + "'.");
107     }
108   }
109 
110   // If generator fails to produce stubs for a particular proto definitions
111   // it finishes with undefined output and writes the first error occured.
GetFirstError() const112   const std::string& GetFirstError() const { return error_; }
113 
114  private:
115   // Only the first error will be recorded.
Abort(const std::string & reason)116   void Abort(const std::string& reason) {
117     if (error_.empty())
118       error_ = reason;
119   }
120 
121   // Get full name (including outer descriptors) of proto descriptor.
122   template <class T>
GetDescriptorName(const T * descriptor)123   inline std::string GetDescriptorName(const T* descriptor) {
124     if (!package_.empty()) {
125       return StripPrefixString(descriptor->full_name(), package_ + ".");
126     } else {
127       return descriptor->full_name();
128     }
129   }
130 
131   // Get C++ class name corresponding to proto descriptor.
132   // Nested names are splitted by underscores. Underscores in type names aren't
133   // prohibited but not recommended in order to avoid name collisions.
134   template <class T>
GetCppClassName(const T * descriptor,bool full=false)135   inline std::string GetCppClassName(const T* descriptor, bool full = false) {
136     std::string name = GetDescriptorName(descriptor);
137     StripString(&name, ".", '_');
138     if (full)
139       name = full_namespace_prefix_ + name;
140     return name;
141   }
142 
GetFieldNumberConstant(const FieldDescriptor * field)143   inline std::string GetFieldNumberConstant(const FieldDescriptor* field) {
144     std::string name = field->camelcase_name();
145     if (!name.empty()) {
146       name.at(0) = static_cast<char>(toupper(name.at(0)));
147       name = "k" + name + "FieldNumber";
148     } else {
149       // Protoc allows fields like 'bool _ = 1'.
150       Abort("Empty field name in camel case notation.");
151     }
152     return name;
153   }
154 
155   // Small enums can be written faster without involving VarInt encoder.
IsTinyEnumField(const FieldDescriptor * field)156   inline bool IsTinyEnumField(const FieldDescriptor* field) {
157     if (field->type() != FieldDescriptor::TYPE_ENUM)
158       return false;
159     const EnumDescriptor* enumeration = field->enum_type();
160 
161     for (int i = 0; i < enumeration->value_count(); ++i) {
162       int32_t value = enumeration->value(i)->number();
163       if (value < 0 || value > 0x7F)
164         return false;
165     }
166     return true;
167   }
168 
CollectDescriptors()169   void CollectDescriptors() {
170     // Collect message descriptors in DFS order.
171     std::vector<const Descriptor*> stack;
172     for (int i = 0; i < source_->message_type_count(); ++i)
173       stack.push_back(source_->message_type(i));
174 
175     while (!stack.empty()) {
176       const Descriptor* message = stack.back();
177       stack.pop_back();
178       messages_.push_back(message);
179       for (int i = 0; i < message->nested_type_count(); ++i) {
180         stack.push_back(message->nested_type(i));
181       }
182     }
183 
184     // Collect enums.
185     for (int i = 0; i < source_->enum_type_count(); ++i)
186       enums_.push_back(source_->enum_type(i));
187 
188     for (const Descriptor* message : messages_) {
189       for (int i = 0; i < message->enum_type_count(); ++i) {
190         enums_.push_back(message->enum_type(i));
191       }
192     }
193   }
194 
CollectDependencies()195   void CollectDependencies() {
196     // Public import basically means that callers only need to import this
197     // proto in order to use the stuff publicly imported by this proto.
198     for (int i = 0; i < source_->public_dependency_count(); ++i)
199       public_imports_.insert(source_->public_dependency(i));
200 
201     if (source_->weak_dependency_count() > 0)
202       Abort("Weak imports are not supported.");
203 
204     // Sanity check. Collect public imports (of collected imports) in DFS order.
205     // Visibilty for current proto:
206     // - all imports listed in current proto,
207     // - public imports of everything imported (recursive).
208     std::vector<const FileDescriptor*> stack;
209     for (int i = 0; i < source_->dependency_count(); ++i) {
210       const FileDescriptor* import = source_->dependency(i);
211       stack.push_back(import);
212       if (public_imports_.count(import) == 0) {
213         private_imports_.insert(import);
214       }
215     }
216 
217     while (!stack.empty()) {
218       const FileDescriptor* import = stack.back();
219       stack.pop_back();
220       // Having imports under different packages leads to unnecessary
221       // complexity with namespaces.
222       if (import->package() != package_)
223         Abort("Imported proto must be in the same package.");
224 
225       for (int i = 0; i < import->public_dependency_count(); ++i) {
226         stack.push_back(import->public_dependency(i));
227       }
228     }
229 
230     // Collect descriptors of messages and enums used in current proto.
231     // It will be used to generate necessary forward declarations and performed
232     // sanity check guarantees that everything lays in the same namespace.
233     for (const Descriptor* message : messages_) {
234       for (int i = 0; i < message->field_count(); ++i) {
235         const FieldDescriptor* field = message->field(i);
236 
237         if (field->type() == FieldDescriptor::TYPE_MESSAGE) {
238           if (public_imports_.count(field->message_type()->file()) == 0) {
239             // Avoid multiple forward declarations since
240             // public imports have been already included.
241             referenced_messages_.insert(field->message_type());
242           }
243         } else if (field->type() == FieldDescriptor::TYPE_ENUM) {
244           if (public_imports_.count(field->enum_type()->file()) == 0) {
245             referenced_enums_.insert(field->enum_type());
246           }
247         }
248       }
249     }
250   }
251 
Preprocess()252   void Preprocess() {
253     // Package name maps to a series of namespaces.
254     package_ = source_->package();
255     namespaces_ = Split(package_, ".");
256     if (!wrapper_namespace_.empty())
257       namespaces_.push_back(wrapper_namespace_);
258 
259     full_namespace_prefix_ = "::";
260     for (const std::string& ns : namespaces_)
261       full_namespace_prefix_ += ns + "::";
262 
263     CollectDescriptors();
264     CollectDependencies();
265   }
266 
267   // Print top header, namespaces and forward declarations.
GeneratePrologue()268   void GeneratePrologue() {
269     std::string greeting =
270         "// Autogenerated by the ProtoZero compiler plugin. DO NOT EDIT.\n";
271     std::string guard = package_ + "_" + source_->name() + "_H_";
272     UpperString(&guard);
273     StripString(&guard, ".-/\\", '_');
274 
275     stub_h_->Print(
276         "$greeting$\n"
277         "#ifndef $guard$\n"
278         "#define $guard$\n\n"
279         "#include <stddef.h>\n"
280         "#include <stdint.h>\n\n"
281         "#include \"perfetto/base/export.h\"\n"
282         "#include \"perfetto/protozero/proto_decoder.h\"\n"
283         "#include \"perfetto/protozero/message.h\"\n",
284         "greeting", greeting, "guard", guard);
285 
286     // Print includes for public imports.
287     for (const FileDescriptor* dependency : public_imports_) {
288       // Dependency name could contain slashes but importing from upper-level
289       // directories is not possible anyway since build system processes each
290       // proto file individually. Hence proto lookup path is always equal to the
291       // directory where particular proto file is located and protoc does not
292       // allow reference to upper directory (aka ..) in import path.
293       //
294       // Laconically said:
295       // - source_->name() may never have slashes,
296       // - dependency->name() may have slashes but always refers to inner path.
297       stub_h_->Print("#include \"$name$.h\"\n", "name",
298                      ProtoStubName(dependency));
299     }
300     stub_h_->Print("\n");
301 
302     // Print namespaces.
303     for (const std::string& ns : namespaces_) {
304       stub_h_->Print("namespace $ns$ {\n", "ns", ns);
305     }
306     stub_h_->Print("\n");
307 
308     // Print forward declarations.
309     for (const Descriptor* message : referenced_messages_) {
310       stub_h_->Print("class $class$;\n", "class", GetCppClassName(message));
311     }
312     for (const EnumDescriptor* enumeration : referenced_enums_) {
313       stub_h_->Print("enum $class$ : int32_t;\n", "class",
314                      GetCppClassName(enumeration));
315     }
316     stub_h_->Print("\n");
317   }
318 
GenerateEnumDescriptor(const EnumDescriptor * enumeration)319   void GenerateEnumDescriptor(const EnumDescriptor* enumeration) {
320     stub_h_->Print("enum $class$ : int32_t {\n", "class",
321                    GetCppClassName(enumeration));
322     stub_h_->Indent();
323 
324     std::string value_name_prefix;
325     if (enumeration->containing_type() != nullptr)
326       value_name_prefix = GetCppClassName(enumeration) + "_";
327 
328     for (int i = 0; i < enumeration->value_count(); ++i) {
329       const EnumValueDescriptor* value = enumeration->value(i);
330       stub_h_->Print("$name$ = $number$,\n", "name",
331                      value_name_prefix + value->name(), "number",
332                      std::to_string(value->number()));
333     }
334 
335     stub_h_->Outdent();
336     stub_h_->Print("};\n\n");
337   }
338 
GenerateSimpleFieldDescriptor(const FieldDescriptor * field)339   void GenerateSimpleFieldDescriptor(const FieldDescriptor* field) {
340     std::map<std::string, std::string> setter;
341     setter["id"] = std::to_string(field->number());
342     setter["name"] = field->name();
343     setter["action"] = field->is_repeated() ? "add" : "set";
344 
345     std::string appender;
346     std::string cpp_type;
347 
348     switch (field->type()) {
349       case FieldDescriptor::TYPE_BOOL: {
350         appender = "AppendTinyVarInt";
351         cpp_type = "bool";
352         break;
353       }
354       case FieldDescriptor::TYPE_INT32: {
355         appender = "AppendVarInt";
356         cpp_type = "int32_t";
357         break;
358       }
359       case FieldDescriptor::TYPE_INT64: {
360         appender = "AppendVarInt";
361         cpp_type = "int64_t";
362         break;
363       }
364       case FieldDescriptor::TYPE_UINT32: {
365         appender = "AppendVarInt";
366         cpp_type = "uint32_t";
367         break;
368       }
369       case FieldDescriptor::TYPE_UINT64: {
370         appender = "AppendVarInt";
371         cpp_type = "uint64_t";
372         break;
373       }
374       case FieldDescriptor::TYPE_SINT32: {
375         appender = "AppendSignedVarInt";
376         cpp_type = "int32_t";
377         break;
378       }
379       case FieldDescriptor::TYPE_SINT64: {
380         appender = "AppendSignedVarInt";
381         cpp_type = "int64_t";
382         break;
383       }
384       case FieldDescriptor::TYPE_FIXED32: {
385         appender = "AppendFixed";
386         cpp_type = "uint32_t";
387         break;
388       }
389       case FieldDescriptor::TYPE_FIXED64: {
390         appender = "AppendFixed";
391         cpp_type = "uint64_t";
392         break;
393       }
394       case FieldDescriptor::TYPE_SFIXED32: {
395         appender = "AppendFixed";
396         cpp_type = "int32_t";
397         break;
398       }
399       case FieldDescriptor::TYPE_SFIXED64: {
400         appender = "AppendFixed";
401         cpp_type = "int64_t";
402         break;
403       }
404       case FieldDescriptor::TYPE_FLOAT: {
405         appender = "AppendFixed";
406         cpp_type = "float";
407         break;
408       }
409       case FieldDescriptor::TYPE_DOUBLE: {
410         appender = "AppendFixed";
411         cpp_type = "double";
412         break;
413       }
414       case FieldDescriptor::TYPE_ENUM: {
415         appender = IsTinyEnumField(field) ? "AppendTinyVarInt" : "AppendVarInt";
416         cpp_type = GetCppClassName(field->enum_type(), true);
417         break;
418       }
419       case FieldDescriptor::TYPE_STRING: {
420         appender = "AppendString";
421         cpp_type = "const char*";
422         break;
423       }
424       case FieldDescriptor::TYPE_BYTES: {
425         stub_h_->Print(
426             setter,
427             "void $action$_$name$(const uint8_t* data, size_t size) {\n"
428             "  AppendBytes($id$, data, size);\n"
429             "}\n");
430         return;
431       }
432       case FieldDescriptor::TYPE_GROUP:
433       case FieldDescriptor::TYPE_MESSAGE: {
434         Abort("Unsupported field type.");
435         return;
436       }
437     }
438     setter["appender"] = appender;
439     setter["cpp_type"] = cpp_type;
440     stub_h_->Print(setter,
441                    "void $action$_$name$($cpp_type$ value) {\n"
442                    "  $appender$($id$, value);\n"
443                    "}\n");
444 
445     // For strings also generate a variant for non-null terminated strings.
446     if (field->type() == FieldDescriptor::TYPE_STRING) {
447       stub_h_->Print(setter,
448                      "// Doesn't check for null terminator.\n"
449                      "// Expects |value| to be at least |size| long.\n"
450                      "void $action$_$name$($cpp_type$ value, size_t size) {\n"
451                      "  AppendBytes($id$, value, size);\n"
452                      "}\n");
453     }
454   }
455 
GenerateNestedMessageFieldDescriptor(const FieldDescriptor * field)456   void GenerateNestedMessageFieldDescriptor(const FieldDescriptor* field) {
457     std::string action = field->is_repeated() ? "add" : "set";
458     std::string inner_class = GetCppClassName(field->message_type());
459     stub_h_->Print(
460         "template <typename T = $inner_class$> T* $action$_$name$() {\n"
461         "  return BeginNestedMessage<T>($id$);\n"
462         "}\n\n",
463         "id", std::to_string(field->number()), "name", field->name(), "action",
464         action, "inner_class", inner_class);
465   }
466 
GenerateDecoder(const Descriptor * message)467   void GenerateDecoder(const Descriptor* message) {
468     int max_field_id = 0;
469     bool has_repeated_fields = false;
470     for (int i = 0; i < message->field_count(); ++i) {
471       const FieldDescriptor* field = message->field(i);
472       if (field->number() > kMaxDecoderFieldId)
473         continue;
474       max_field_id = std::max(max_field_id, field->number());
475       if (field->is_repeated())
476         has_repeated_fields = true;
477     }
478 
479     stub_h_->Print(
480         "class Decoder : public "
481         "::protozero::TypedProtoDecoder</*MAX_FIELD_ID=*/$max$, "
482         "/*HAS_REPEATED_FIELDS=*/$rep$> {\n",
483         "max", std::to_string(max_field_id), "rep",
484         has_repeated_fields ? "true" : "false");
485     stub_h_->Print(" public:\n");
486     stub_h_->Indent();
487     stub_h_->Print(
488         "Decoder(const uint8_t* data, size_t len) "
489         ": TypedProtoDecoder(data, len) {}\n");
490 
491     for (int i = 0; i < message->field_count(); ++i) {
492       const FieldDescriptor* field = message->field(i);
493       if (field->is_packed()) {
494         Abort("Packed repeated fields are not supported.");
495         return;
496       }
497 
498       if (field->number() > max_field_id) {
499         stub_h_->Print("// field $name$ omitted because its id is too high\n",
500                        "name", field->name());
501         continue;
502       }
503       std::string getter;
504       std::string cpp_type;
505       switch (field->type()) {
506         case FieldDescriptor::TYPE_BOOL:
507           getter = "as_bool";
508           cpp_type = "bool";
509           break;
510         case FieldDescriptor::TYPE_SFIXED32:
511         case FieldDescriptor::TYPE_SINT32:
512         case FieldDescriptor::TYPE_INT32:
513           getter = "as_int32";
514           cpp_type = "int32_t";
515           break;
516         case FieldDescriptor::TYPE_SFIXED64:
517         case FieldDescriptor::TYPE_SINT64:
518         case FieldDescriptor::TYPE_INT64:
519           getter = "as_int64";
520           cpp_type = "int64_t";
521           break;
522         case FieldDescriptor::TYPE_FIXED32:
523         case FieldDescriptor::TYPE_UINT32:
524           getter = "as_uint32";
525           cpp_type = "uint32_t";
526           break;
527         case FieldDescriptor::TYPE_FIXED64:
528         case FieldDescriptor::TYPE_UINT64:
529           getter = "as_uint64";
530           cpp_type = "uint64_t";
531           break;
532         case FieldDescriptor::TYPE_FLOAT:
533           getter = "as_float";
534           cpp_type = "float";
535           break;
536         case FieldDescriptor::TYPE_DOUBLE:
537           getter = "as_double";
538           cpp_type = "double";
539           break;
540         case FieldDescriptor::TYPE_ENUM:
541           getter = "as_int32";
542           cpp_type = "int32_t";
543           break;
544         case FieldDescriptor::TYPE_STRING:
545           getter = "as_string";
546           cpp_type = "::protozero::ConstChars";
547           break;
548         case FieldDescriptor::TYPE_MESSAGE:
549         case FieldDescriptor::TYPE_BYTES:
550           getter = "as_bytes";
551           cpp_type = "::protozero::ConstBytes";
552           break;
553         case FieldDescriptor::TYPE_GROUP:
554           continue;
555       }
556 
557       stub_h_->Print("bool has_$name$() const { return at<$id$>().valid(); }\n",
558                      "name", field->name(), "id",
559                      std::to_string(field->number()));
560 
561       if (field->is_repeated()) {
562         stub_h_->Print(
563             "::protozero::RepeatedFieldIterator $name$() const { return "
564             "GetRepeated($id$); }\n",
565             "name", field->name(), "id", std::to_string(field->number()));
566       } else {
567         stub_h_->Print(
568             "$cpp_type$ $name$() const { return at<$id$>().$getter$(); }\n",
569             "name", field->name(), "id", std::to_string(field->number()),
570             "cpp_type", cpp_type, "getter", getter);
571       }
572     }
573     stub_h_->Outdent();
574     stub_h_->Print("};\n");
575   }
576 
GenerateConstantsForMessageFields(const Descriptor * message)577   void GenerateConstantsForMessageFields(const Descriptor* message) {
578     const bool has_fields = (message->field_count() > 0);
579 
580     // Field number constants.
581     if (has_fields) {
582       stub_h_->Print("enum : int32_t {\n");
583       stub_h_->Indent();
584 
585       for (int i = 0; i < message->field_count(); ++i) {
586         const FieldDescriptor* field = message->field(i);
587         stub_h_->Print("$name$ = $id$,\n", "name",
588                        GetFieldNumberConstant(field), "id",
589                        std::to_string(field->number()));
590       }
591       stub_h_->Outdent();
592       stub_h_->Print("};\n");
593     }
594   }
595 
GenerateMessageDescriptor(const Descriptor * message)596   void GenerateMessageDescriptor(const Descriptor* message) {
597     stub_h_->Print(
598         "class PERFETTO_EXPORT $name$ : public ::protozero::Message {\n"
599         " public:\n",
600         "name", GetCppClassName(message));
601     stub_h_->Indent();
602 
603     GenerateConstantsForMessageFields(message);
604     GenerateDecoder(message);
605 
606     // Using statements for nested messages.
607     for (int i = 0; i < message->nested_type_count(); ++i) {
608       const Descriptor* nested_message = message->nested_type(i);
609       stub_h_->Print("using $local_name$ = $global_name$;\n", "local_name",
610                      nested_message->name(), "global_name",
611                      GetCppClassName(nested_message, true));
612     }
613 
614     // Using statements for nested enums.
615     for (int i = 0; i < message->enum_type_count(); ++i) {
616       const EnumDescriptor* nested_enum = message->enum_type(i);
617       stub_h_->Print("using $local_name$ = $global_name$;\n", "local_name",
618                      nested_enum->name(), "global_name",
619                      GetCppClassName(nested_enum, true));
620     }
621 
622     // Values of nested enums.
623     for (int i = 0; i < message->enum_type_count(); ++i) {
624       const EnumDescriptor* nested_enum = message->enum_type(i);
625       std::string value_name_prefix = GetCppClassName(nested_enum) + "_";
626 
627       for (int j = 0; j < nested_enum->value_count(); ++j) {
628         const EnumValueDescriptor* value = nested_enum->value(j);
629         stub_h_->Print("static const $class$ $name$ = $full_name$;\n", "class",
630                        nested_enum->name(), "name", value->name(), "full_name",
631                        value_name_prefix + value->name());
632       }
633     }
634 
635     // Field descriptors.
636     for (int i = 0; i < message->field_count(); ++i) {
637       const FieldDescriptor* field = message->field(i);
638       if (field->is_packed()) {
639         Abort("Packed repeated fields are not supported.");
640         return;
641       }
642       if (field->type() != FieldDescriptor::TYPE_MESSAGE) {
643         GenerateSimpleFieldDescriptor(field);
644       } else {
645         GenerateNestedMessageFieldDescriptor(field);
646       }
647     }
648 
649     stub_h_->Outdent();
650     stub_h_->Print("};\n\n");
651   }
652 
GenerateEpilogue()653   void GenerateEpilogue() {
654     for (unsigned i = 0; i < namespaces_.size(); ++i) {
655       stub_h_->Print("} // Namespace.\n");
656     }
657     stub_h_->Print("#endif  // Include guard.\n");
658   }
659 
660   const FileDescriptor* const source_;
661   Printer* const stub_h_;
662   std::string error_;
663 
664   std::string package_;
665   std::string wrapper_namespace_;
666   std::vector<std::string> namespaces_;
667   std::string full_namespace_prefix_;
668   std::vector<const Descriptor*> messages_;
669   std::vector<const EnumDescriptor*> enums_;
670 
671   // The custom *Comp comparators are to ensure determinism of the generator.
672   std::set<const FileDescriptor*, FileDescriptorComp> public_imports_;
673   std::set<const FileDescriptor*, FileDescriptorComp> private_imports_;
674   std::set<const Descriptor*, DescriptorComp> referenced_messages_;
675   std::set<const EnumDescriptor*, EnumDescriptorComp> referenced_enums_;
676 };
677 
678 }  // namespace
679 
ProtoZeroGenerator()680 ProtoZeroGenerator::ProtoZeroGenerator() {}
681 
~ProtoZeroGenerator()682 ProtoZeroGenerator::~ProtoZeroGenerator() {}
683 
Generate(const FileDescriptor * file,const std::string & options,GeneratorContext * context,std::string * error) const684 bool ProtoZeroGenerator::Generate(const FileDescriptor* file,
685                                   const std::string& options,
686                                   GeneratorContext* context,
687                                   std::string* error) const {
688   const std::unique_ptr<ZeroCopyOutputStream> stub_h_file_stream(
689       context->Open(ProtoStubName(file) + ".h"));
690   const std::unique_ptr<ZeroCopyOutputStream> stub_cc_file_stream(
691       context->Open(ProtoStubName(file) + ".cc"));
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   Printer stub_cc_printer(stub_cc_file_stream.get(), '$');
698   stub_cc_printer.Print("// Intentionally empty\n");
699 
700   // Parse additional options.
701   for (const std::string& option : Split(options, ",")) {
702     std::vector<std::string> option_pair = Split(option, "=");
703     job.SetOption(option_pair[0], option_pair[1]);
704   }
705 
706   if (!job.GenerateStubs()) {
707     *error = job.GetFirstError();
708     return false;
709   }
710   return true;
711 }
712 
713 }  // namespace protozero
714