Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(551)

Unified Diff: components/tracing/tools/proto_zero_plugin/proto_zero_generator.cc

Issue 2145423002: ProtoZero plugin implementation. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: implementation Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « components/tracing/test/example_messages.proto ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: components/tracing/tools/proto_zero_plugin/proto_zero_generator.cc
diff --git a/components/tracing/tools/proto_zero_plugin/proto_zero_generator.cc b/components/tracing/tools/proto_zero_plugin/proto_zero_generator.cc
index 28fc99771588f5ff1b8ef90af9da552db8f86662..2d57ce02b6cb83396df7ec1bcd7455e20834cbab 100644
--- a/components/tracing/tools/proto_zero_plugin/proto_zero_generator.cc
+++ b/components/tracing/tools/proto_zero_plugin/proto_zero_generator.cc
@@ -4,6 +4,7 @@
#include "proto_zero_generator.h"
+#include <map>
#include <memory>
#include <string>
@@ -15,12 +16,24 @@
namespace tracing {
namespace proto {
+using google::protobuf::Descriptor; // Message descriptor.
+using google::protobuf::EnumDescriptor;
+using google::protobuf::EnumValueDescriptor;
+using google::protobuf::FieldDescriptor;
using google::protobuf::FileDescriptor;
-using google::protobuf::StripSuffixString;
using google::protobuf::compiler::GeneratorContext;
using google::protobuf::io::Printer;
using google::protobuf::io::ZeroCopyOutputStream;
+// These protobuf-specific string utils are used since not all required ones
+// are presented in //base and it's better than reinventing a wheel or mixing up
Primiano Tucci (use gerrit) 2016/07/15 10:33:56 NO need for this comment. In general it would be i
kraynov 2016/07/15 11:01:52 Discussed offline. Not going to depend on //base a
+// different string util libraries.
+using google::protobuf::Split;
Primiano Tucci (use gerrit) 2016/07/15 10:33:56 don't you have this in base?
+using google::protobuf::StripPrefixString;
+using google::protobuf::StripString;
+using google::protobuf::StripSuffixString;
+using google::protobuf::UpperString;
Primiano Tucci (use gerrit) 2016/07/15 10:33:56 You have base::ToUpperASCII
kraynov 2016/07/15 11:01:52 Discussed offline. Not going to change.
+
namespace {
class GeneratorJob {
@@ -28,22 +41,21 @@ class GeneratorJob {
GeneratorJob(const FileDescriptor *file,
Printer* stub_h_printer,
Printer* stub_cc_printer)
- : file_(file),
+ : source_(file),
stub_h_(stub_h_printer),
stub_cc_(stub_cc_printer) {}
bool GenerateStubs() {
- stub_h_->Print(
- "// Autogenerated. DO NOT EDIT.\n"
- "// Generated by: //components/tracing/proto_zero_plugin.\n\n"
- "// Package: $package$\n",
- "package", file_->package());
- stub_cc_->Print(
- "// Autogenerated. DO NOT EDIT.\n"
- "// Generated by: //components/tracing/proto_zero_plugin.\n\n"
- "// This file intentionally left blank.\n");
- // TODO(kraynov) Implement in the next CL (crbug.com/608721).
- return true;
+ Preprocess();
+ PrintPrologue();
Primiano Tucci (use gerrit) 2016/07/15 10:33:56 S/Print/Generate/ here and below
kraynov 2016/07/15 11:01:52 Acknowledged.
+ for (const EnumDescriptor* enumeration: enums_) {
+ PrintEnumDescriptor(enumeration);
+ }
+ for (const Descriptor* message : messages_) {
+ PrintMessageDescriptor(message);
+ }
+ PrintEpilogue();
+ return error_.empty();
}
// If generator fails to produce stubs for a particular proto definitions
@@ -60,10 +72,312 @@ class GeneratorJob {
}
}
- const FileDescriptor* const file_;
+ // Get full name (including outer descriptors) of proto descriptor.
+ template <class T>
+ inline std::string GetDescriptorName(const T* descriptor) {
+ if (!package_.empty()) {
+ return StripPrefixString(descriptor->full_name(), package_ + ".");
+ } else {
+ return descriptor->full_name();
+ }
+ }
+
+ // Get C++ class name corresponding to proto descriptor.
+ // Nested names are splitted by underscores. Underscores in type names aren't
+ // prohibited but not recommended in order to avoid name collisions.
+ template <class T>
+ inline std::string GetCppClassName(const T* descriptor, bool full = false) {
+ std::string name = GetDescriptorName(descriptor);
+ StripString(&name, ".", '_');
Primiano Tucci (use gerrit) 2016/07/15 10:33:56 use base
kraynov 2016/07/15 11:01:52 Discussed offline. Not going to change.
+ if (full)
+ name = full_namespace_prefix_ + name;
+ return name;
+ }
+
+ // Small enums can be written faster without involving VarInt encoder.
+ inline bool IsEnumOptimal(const EnumDescriptor* enumeration) {
Primiano Tucci (use gerrit) 2016/07/15 10:33:55 s/IsEnumOptimal/IsTinyEnum/
kraynov 2016/07/15 11:01:52 Acknowledged.
+ for (int i = 0; i < enumeration->value_count(); ++i) {
+ int32_t value = enumeration->value(i)->number();
+ if (value < 0 || value > 0x7F)
+ return false;
+ }
+ return true;
+ }
+
+ void Preprocess() {
+ // Package name maps to a series of namespaces.
+ package_ = source_->package();
+ namespaces_ = Split(package_, ".");
+ full_namespace_prefix_ = "::";
+ for (const std::string& ns : namespaces_) {
Primiano Tucci (use gerrit) 2016/07/15 10:33:56 don't need the braces for single line loops
kraynov 2016/07/15 11:01:52 Acknowledged.
+ full_namespace_prefix_ += ns + "::";
+ }
+ // Collect message descriptors in DFS order.
+ std::vector<const Descriptor*> stack;
+ for (int i = 0; i < source_->message_type_count(); ++i) {
+ stack.push_back(source_->message_type(i));
+ }
+ while (!stack.empty()) {
+ const Descriptor* message = stack.back();
+ stack.pop_back();
+ messages_.push_back(message);
+ for (int i = 0; i < message->nested_type_count(); ++i) {
+ stack.push_back(message->nested_type(i));
+ }
+ }
+ // Collect enums.
+ for (int i = 0; i < source_->enum_type_count(); ++i) {
+ enums_.push_back(source_->enum_type(i));
+ }
+ for (const Descriptor* message : messages_) {
Primiano Tucci (use gerrit) 2016/07/15 10:33:56 are youy sure this loop should not go out? Feels l
kraynov 2016/07/15 11:01:53 I iterate over messages_ and populate enums_.
Primiano Tucci (use gerrit) 2016/07/15 11:53:12 Oh ignore my comment, I didn't realize that the wh
+ for (int i = 0; i < message->enum_type_count(); ++i) {
+ enums_.push_back(message->enum_type(i));
+ }
+ }
+ }
+
+ // Print top header, namespaces and forward declarations.
+ void PrintPrologue() {
+ std::string greeting =
+ "// Autogenerated. DO NOT EDIT.\n"
+ "// Protobuf compiler (protoc) has generated these stubs with\n"
+ "// //components/tracing/tools/proto_zero_plugin.\n";
+ std::string guard = package_ + "_" + source_->name() + "_H_";
+ UpperString(&guard);
Primiano Tucci (use gerrit) 2016/07/15 10:33:56 use base for these
kraynov 2016/07/15 11:01:52 Discussed offline. Not going to change.
+ StripString(&guard, ".-", '_');
+
+ stub_h_->Print(
+ "$greeting$\n"
+ "#ifndef $guard$\n"
+ "#define $guard$\n\n"
+ "#include <stddef.h>\n"
+ "#include <stdint.h>\n\n"
+ "// Runtime is optional for testing purposes.\n"
+ "#ifndef PROTO_ZERO_NO_RUNTIME\n"
Primiano Tucci (use gerrit) 2016/07/15 10:33:56 what is this PROTO_ZERO_NO_RUNTIME? I think you c
kraynov 2016/07/15 11:01:52 #ifndef here. PROTO_ZERO_NO_RUNTIME is going to be
Primiano Tucci (use gerrit) 2016/07/15 11:53:12 Hmm feels very weird that all emitted messages hav
kraynov 2016/07/15 13:06:58 Ok, will remove for this time.
+ "// #include \"components/tracing/core/proto_zero_message.h\"\n"
+ "#endif\n\n",
+ "greeting", greeting,
+ "guard", guard);
+ stub_cc_->Print(
+ "$greeting$\n"
+ "// This file intentionally left blank.\n",
+ "greeting", greeting);
+
+ // Print namespaces.
+ for (const std::string& ns : namespaces_)
+ stub_h_->Print("namespace $ns$ {\n", "ns", ns);
+ stub_h_->Print("\n");
+ // Print forward declarations.
+ for (const Descriptor* message : messages_) {
+ stub_h_->Print(
+ "class $class$;\n",
+ "class", GetCppClassName(message));
+ }
+ stub_h_->Print("\n");
+ }
+
+ void PrintEnumDescriptor(const EnumDescriptor* enumeration) {
+ stub_h_->Print(
+ "enum $class$ : int32_t {\n",
Primiano Tucci (use gerrit) 2016/07/15 10:33:56 It seems that the standard proto just generates an
+ "class", GetCppClassName(enumeration));
+ stub_h_->Indent();
+ for (int i = 0; i < enumeration->value_count(); ++i) {
+ const EnumValueDescriptor* value = enumeration->value(i);
+ stub_h_->Print(
+ "$name$ = $number$,\n",
+ "name", value->name(),
+ "number", std::to_string(value->number()));
Primiano Tucci (use gerrit) 2016/07/15 10:33:56 is this going to generate enums according to the s
kraynov 2016/07/15 11:01:53 Ok, thanks for pointing on that!
+ }
+ stub_h_->Outdent();
+ stub_h_->Print("};\n\n");
+ }
+
+ void PrintSimpleFieldDescriptor(const FieldDescriptor* field) {
+ std::map<std::string, std::string> setter;
+ setter["id"] = std::to_string(field->number());
+ setter["name"] = field->name();
+ setter["action"] = field->is_repeated() ? "add" : "set";
+
+ std::string appender;
+ std::string cpp_type;
+
+ switch (field->type()) {
+ case FieldDescriptor::TYPE_BOOL: {
+ appender = "AppendBool";
+ cpp_type = "bool";
+ break;
+ }
+ case FieldDescriptor::TYPE_INT32: {
+ appender = "AppendInt32";
+ cpp_type = "int32_t";
+ break;
+ }
+ case FieldDescriptor::TYPE_INT64: {
+ appender = "AppendInt64";
+ cpp_type = "int64_t";
+ break;
+ }
+ case FieldDescriptor::TYPE_UINT32: {
+ appender = "AppendUint32";
+ cpp_type = "uint32_t";
+ break;
+ }
+ case FieldDescriptor::TYPE_UINT64: {
+ appender = "AppendUint64";
+ cpp_type = "uint64_t";
+ break;
+ }
+ case FieldDescriptor::TYPE_SINT32: {
+ appender = "AppendSint32";
+ cpp_type = "int32_t";
+ break;
+ }
+ case FieldDescriptor::TYPE_SINT64: {
+ appender = "AppendSint64";
+ cpp_type = "int64_t";
+ break;
+ }
+ case FieldDescriptor::TYPE_FIXED32: {
+ appender = "AppendFixed32";
+ cpp_type = "uint32_t";
+ break;
+ }
+ case FieldDescriptor::TYPE_FIXED64: {
+ appender = "AppendFixed64";
+ cpp_type = "uint64_t";
+ break;
+ }
+ case FieldDescriptor::TYPE_SFIXED32: {
+ appender = "AppendSfixed32";
+ cpp_type = "int32_t";
+ break;
+ }
+ case FieldDescriptor::TYPE_SFIXED64: {
+ appender = "AppendSfixed64";
+ cpp_type = "int64_t";
+ break;
+ }
+ case FieldDescriptor::TYPE_FLOAT: {
+ appender = "AppendFloat";
+ cpp_type = "float";
+ break;
+ }
+ case FieldDescriptor::TYPE_DOUBLE: {
+ appender = "AppendDouble";
+ cpp_type = "double";
+ break;
+ }
+ case FieldDescriptor::TYPE_ENUM: {
+ if (IsEnumOptimal(field->enum_type())) {
Primiano Tucci (use gerrit) 2016/07/15 10:33:56 appender = IsTinyEnum(field.enum_type()) ? "Append
kraynov 2016/07/15 11:01:53 Acknowledged.
+ appender = "AppendTinyNumber";
+ } else {
+ appender = "AppendInt32";
+ }
+ cpp_type = GetCppClassName(field->enum_type(), true);
+ break;
+ }
+ case FieldDescriptor::TYPE_STRING: {
+ appender = "AppendString";
+ cpp_type = "const char*";
+ break;
+ }
+ case FieldDescriptor::TYPE_BYTES: {
+ stub_h_->Print(
+ setter,
+ "void $action$_$name$(const uint8_t* data, size_t size) {\n"
+ " // AppendBytes($id$, data, size);\n"
Primiano Tucci (use gerrit) 2016/07/15 10:33:56 Why AppendBytes is commented out?
kraynov 2016/07/15 11:01:53 It's to be uncommentend in the next CL when API wi
+ "}\n");
+ return;
+ }
+ default: {
+ Abort("Unsupported field type.");
+ return;
+ }
+ }
+ setter["appender"] = appender;
+ setter["cpp_type"] = cpp_type;
+ stub_h_->Print(
+ setter,
+ "void $action$_$name$($cpp_type$ value) {\n"
+ " // $appender$($id$, value);\n"
Primiano Tucci (use gerrit) 2016/07/15 10:33:56 why the appender is commented?
kraynov 2016/07/15 11:01:53 It's to be uncommentend in the next CL when API wi
+ "}\n");
+ }
+
+ void PrintNestedMessageFieldDescriptor(const FieldDescriptor* field) {
+ stub_h_->Print(
+ "$class$* $action$_$name$() {\n"
+ " // $class$* nested;\n"
+ " // OpenNestedMessage($id$, &nested);\n"
Primiano Tucci (use gerrit) 2016/07/15 11:53:12 This is BeginNestedMessage. See https://cs.chromiu
+ " return nullptr;\n"
+ "}\n",
+ "id", std::to_string(field->number()),
+ "name", field->name(),
+ "action", field->is_repeated() ? "add" : "set",
+ "class", GetCppClassName(field->message_type()));
+ }
+
+ void PrintMessageDescriptor(const Descriptor* message) {
+ stub_h_->Print(
+ "class $name$ /* : public ::tracing::proto::ProtoZeroMessage */ {\n"
Primiano Tucci (use gerrit) 2016/07/15 10:33:56 why this is commented?
+ " public:\n",
+ "name", GetCppClassName(message));
+ stub_h_->Indent();
+
+ // Using statements for nested messages.
+ for (int i = 0; i < message->nested_type_count(); ++i) {
+ const Descriptor* nested_message = message->nested_type(i);
+ stub_h_->Print(
+ "using $local_name$ = $global_name$;\n",
+ "local_name", nested_message->name(),
+ "global_name", GetCppClassName(nested_message, true));
+ }
+ // Using statements for nested enums.
+ for (int i = 0; i < message->enum_type_count(); ++i) {
+ const EnumDescriptor* nested_enum = message->enum_type(i);
+ stub_h_->Print(
+ "using $local_name$ = $global_name$;\n",
+ "local_name", nested_enum->name(),
+ "global_name", GetCppClassName(nested_enum, true));
+ }
+ // Fields descriptors.
+ for (int i = 0; i < message->field_count(); ++i) {
+ const FieldDescriptor* field = message->field(i);
+ if (field->is_required()) {
+ Abort("Required fields are not supported.");
Primiano Tucci (use gerrit) 2016/07/15 10:33:56 why this?
kraynov 2016/07/15 11:01:53 required considered harmful and we aren't going to
Primiano Tucci (use gerrit) 2016/07/15 11:53:12 I know but doesn't seem an argument for not emitti
kraynov 2016/07/15 13:06:58 Acknowledged.
+ return;
+ }
+ if (field->is_packed()) {
+ Abort("Packed repeated fields are not supported.");
+ return;
+ }
+ if (field->type() != FieldDescriptor::TYPE_MESSAGE) {
Primiano Tucci (use gerrit) 2016/07/15 10:33:56 Is the case of nested messages not handled yet?
kraynov 2016/07/15 11:01:52 It's handled in PrintNestedMessageFieldDescriptor.
Primiano Tucci (use gerrit) 2016/07/15 11:53:12 Acknowledged.
+ PrintSimpleFieldDescriptor(field);
+ } else {
+ PrintNestedMessageFieldDescriptor(field);
+ }
+ }
+
+ stub_h_->Outdent();
+ stub_h_->Print("};\n\n");
+ }
+
+ void PrintEpilogue() {
+ for (unsigned i = 0; i < namespaces_.size(); ++i) {
+ stub_h_->Print("} // Namespace.\n");
+ }
+ stub_h_->Print("#endif // Include guard.\n");
+ }
+
+ const FileDescriptor* const source_;
Printer* const stub_h_;
Printer* const stub_cc_;
std::string error_;
+
+ std::string package_;
+ std::vector<std::string> namespaces_;
+ std::string full_namespace_prefix_;
+ std::vector<const Descriptor*> messages_;
+ std::vector<const EnumDescriptor*> enums_;
};
} // namespace
« no previous file with comments | « components/tracing/test/example_messages.proto ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698