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