Chromium Code Reviews| Index: components/tracing/proto_zero_plugin/proto_zero_generator.cc |
| diff --git a/components/tracing/proto_zero_plugin/proto_zero_generator.cc b/components/tracing/proto_zero_plugin/proto_zero_generator.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..c4a504519951571a72358a8426747ec624f5539a |
| --- /dev/null |
| +++ b/components/tracing/proto_zero_plugin/proto_zero_generator.cc |
| @@ -0,0 +1,400 @@ |
| +// Copyright (c) 2016 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "proto_zero_generator.h" |
| + |
| +#include <algorithm> |
| +#include <memory> |
| +#include <string> |
| + |
| +#include "third_party/protobuf/src/google/protobuf/descriptor.h" |
| +#include "third_party/protobuf/src/google/protobuf/io/printer.h" |
| +#include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream.h" |
| +#include "third_party/protobuf/src/google/protobuf/stubs/strutil.h" |
| + |
| +namespace tracing { |
| +namespace proto { |
| + |
| +// MessageDescriptor is a longer name but has more obvious meaning. |
|
Primiano Tucci (use gerrit)
2016/07/11 16:52:07
It also makes the class harder to codesearch. I'd
|
| +using MessageDescriptor = google::protobuf::Descriptor; |
| +using google::protobuf::EnumDescriptor; |
| +using google::protobuf::EnumValueDescriptor; |
| +using google::protobuf::FieldDescriptor; |
| +using google::protobuf::FileDescriptor; |
| +using google::protobuf::io::Printer; |
| +using google::protobuf::io::ZeroCopyOutputStream; |
| + |
| +using google::protobuf::Split; |
| +using google::protobuf::StripPrefixString; |
| +using google::protobuf::StripString; |
| +using google::protobuf::StripSuffixString; |
| +using google::protobuf::UpperString; |
| + |
| +namespace { |
| + |
| +class GeneratorJob { |
| + public: |
| + GeneratorJob(const FileDescriptor* file, Printer* printer) |
| + : file_(file), printer_(printer) {} |
| + |
| + bool GenerateStubs() { |
| + Prepare(); |
| + PrintPrologue(); |
| + for (const EnumDescriptor* enumeration: enums_) { |
|
Primiano Tucci (use gerrit)
2016/07/11 16:52:07
you can drop the braces for single line for loops
|
| + PrintEnum(enumeration); |
| + } |
| + for (const MessageDescriptor* message : messages_) { |
| + PrintMessage(message); |
| + } |
| + PrintEpilogue(); |
| + return error_.empty(); |
| + } |
| + |
| + std::string GetFirstError() { |
|
Primiano Tucci (use gerrit)
2016/07/11 16:52:07
not sure you want to return this by copy. I think
|
| + return error_; |
| + } |
| + |
| + private: |
| + // Get C++ class name corresponding to message/enum descriptor. |
| + // Nested names are splitted by underscores. |
| + template <class T> |
| + std::string GetCppClassName(const T* descriptor, bool absolute = false) { |
| + std::string local_name = descriptor->name(); |
| + if (local_name.find('_') != std::string::npos) { |
| + Abort("Underscores are not permitted in proto's type name."); |
| + return " /* Invalid name */ "; |
| + } |
| + std::string name = |
| + StripPrefixString(descriptor->full_name(), package_ + "."); |
| + StripString(&name, ".", '_'); |
| + if (absolute) { |
| + name = full_namespace_prefix_ + name; |
| + } |
| + return name; |
| + } |
| + |
| + // Small enums can be written faster without involving VarInt encoder. |
| + inline bool IsEnumOptimal(const EnumDescriptor* enumeration) { |
| + for (int i = 0; i < enumeration->value_count(); ++i) { |
| + int32_t number = enumeration->value(i)->number(); |
| + if (number < 0 || number > 0x7F) |
| + return false; |
| + } |
| + return true; |
| + } |
| + |
| + // If something went wrong, plugin returns the first error occured. |
| + void Abort(const std::string& reason) { |
| + if (error_.empty()) { |
| + error_ = reason; |
| + } |
| + } |
| + |
| + // Scanning pass. |
|
Primiano Tucci (use gerrit)
2016/07/11 16:52:07
This comment doesn't say anything more than "Prepa
|
| + void Prepare() { |
| + // Package name evaluates to a series of namespaces. |
| + package_ = file_->package(); |
| + namespaces_ = Split(package_, "."); |
| + full_namespace_prefix_ = "::"; |
| + for (const std::string& ns : namespaces_) { |
| + full_namespace_prefix_ += ns + "::"; |
| + } |
| + // Collect message descriptors in DFS order. |
| + std::vector<const MessageDescriptor*> stack; |
| + for (int i = 0; i < file_->message_type_count(); ++i) { |
| + stack.push_back(file_->message_type(i)); |
| + } |
| + while (!stack.empty()) { |
| + const MessageDescriptor* 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 < file_->enum_type_count(); ++i) { |
| + enums_.push_back(file_->enum_type(i)); |
| + } |
| + for (const MessageDescriptor* message : messages_) { |
| + 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 guard = package_ + "_" + file_->name() + "_H_"; |
| + UpperString(&guard); |
| + StripString(&guard, ".-", '_'); |
| + printer_->Print( |
| + "// Autogenerated. DO NOT EDIT.\n" |
| + "// Protoc has generated these stubs " |
| + "with //components/tracing/proto_zero_plugin.\n\n" |
| + "#ifndef $guard$\n" |
| + "#define $guard$\n\n" |
| + "#include <inttypes.h>\n\n" |
|
Primiano Tucci (use gerrit)
2016/07/11 16:52:07
s/inttypes/stdint/
|
| + "// Runtime is optional for testing purposes.\n" |
| + "#ifndef PROTO_ZERO_NO_RUNTIME\n" |
| + "#include \"components/tracing/core/proto_zero_message.h\"\n" |
| + "#endif\n\n", |
| + "guard", guard); |
| + // Print namespaces. |
|
Primiano Tucci (use gerrit)
2016/07/11 16:52:07
add a blank newline before these comments. It make
|
| + for (const std::string& ns : namespaces_) { |
| + printer_->Print("namespace $ns$ {\n", "ns", ns); |
| + } |
| + printer_->Print("\n"); |
| + // Print forward declarations. |
| + for (const MessageDescriptor* message : messages_) { |
| + printer_->Print( |
| + "class $class$;\n", |
| + "class", GetCppClassName(message)); |
| + } |
| + printer_->Print("\n"); |
| + } |
| + |
| + void PrintSimpleField(const FieldDescriptor* field) { |
| + std::string appender; |
| + std::string cpp_type; |
| + std::string action = field->is_repeated() ? "add" : "set"; |
| + |
| + 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())) { |
| + 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: { |
| + printer_->Print( |
| + "void $action$_$name$(const uint8_t* data, size_t size) {\n" |
| + " AppendBytes($key$, data, size);\n" |
| + "}\n", |
| + "action", action, |
| + "name", field->name(), |
| + "key", std::to_string(field->number())); |
| + return; |
| + } |
| + default: { |
| + Abort("Unsupported field type."); |
| + return; |
| + } |
| + } |
| + printer_->Print( |
| + "void $action$_$name$($cpp_type$ value) {\n" |
| + " $appender$($key$, value);\n" |
| + "}\n", |
| + "appender", appender, |
| + "cpp_type", cpp_type, |
| + "action", action, |
| + "name", field->name(), |
| + "key", std::to_string(field->number())); |
| + } |
| + |
| + // Embedded message field. |
| + void PrintNestedMessageField(const FieldDescriptor* field) { |
| + printer_->Print( |
| + "$class$* $action$_$name$() {\n" |
| + " $class$* nested;\n" |
| + " OpenNestedMessage($key$, &nested);\n" |
| + " return nested;\n" |
| + "}\n", |
| + "action", field->is_repeated() ? "add" : "set", |
| + "class", GetCppClassName(field->message_type()), |
| + "name", field->name(), |
| + "key", std::to_string(field->number())); |
| + } |
| + |
| + // Print fields of the message. |
| + void PrintFields(const MessageDescriptor* message) { |
| + 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."); |
| + return; |
| + } |
| + if (field->is_packed()) { |
| + Abort("Packed repeated fields are not supported."); |
| + return; |
| + } |
| + if (field->type() != FieldDescriptor::TYPE_MESSAGE) { |
| + PrintSimpleField(field); |
| + } else { |
| + PrintNestedMessageField(field); |
| + } |
| + } |
| + } |
| + |
| + // Print enum type definition. |
| + void PrintEnum(const EnumDescriptor* enumeration) { |
| + printer_->Print( |
| + "enum $class$ : int32_t {\n", |
| + "class", GetCppClassName(enumeration)); |
| + printer_->Indent(); |
| + for (int i = 0; i < enumeration->value_count(); ++i) { |
| + const EnumValueDescriptor* value = enumeration->value(i); |
| + printer_->Print( |
| + "$name$ = $number$,\n", |
| + "name", value->name(), |
| + "number", std::to_string(value->number())); |
| + } |
| + printer_->Outdent(); |
| + printer_->Print("};\n\n"); |
| + } |
| + |
| + // Print message class definition. |
| + void PrintMessage(const MessageDescriptor* message) { |
| + printer_->Print( |
| + "class $class$ : public $base$ {\n" |
| + " public:\n", |
| + |
| + "class", GetCppClassName(message), |
| + "base", "::tracing::proto::AppendOnlyProtoMessage"); |
| + printer_->Indent(); |
| + // Using nested messages. |
| + for (int i = 0; i < message->nested_type_count(); ++i) { |
| + const MessageDescriptor* nested_message = message->nested_type(i); |
| + printer_->Print( |
| + "using $local_name$ = $global_name$;\n", |
| + "local_name", nested_message->name(), |
| + "global_name", GetCppClassName(nested_message, true)); |
| + } |
| + // Using nested enums. |
| + for (int i = 0; i < message->enum_type_count(); ++i) { |
| + const EnumDescriptor* nested_enum = message->enum_type(i); |
| + printer_->Print( |
| + "using $local_name$ = $global_name$;\n", |
| + "local_name", nested_enum->name(), |
| + "global_name", GetCppClassName(nested_enum, true)); |
| + } |
| + PrintFields(message); |
| + printer_->Outdent(); |
| + printer_->Print("};\n\n"); |
| + } |
| + |
| + // Closing braces. |
| + void PrintEpilogue() { |
| + for (auto it = namespaces_.rbegin(); it != namespaces_.rend(); ++it) { |
| + printer_->Print("} // namespace $ns$\n", "ns", *it); |
| + } |
| + printer_->Print("#endif // Include guard.\n"); |
| + } |
| + |
| + const FileDescriptor* const file_; |
| + Printer* const printer_; |
| + std::string error_; |
| + |
| + std::string package_; |
| + std::vector<std::string> namespaces_; |
| + std::string full_namespace_prefix_; |
| + std::vector<const MessageDescriptor*> messages_; |
| + std::vector<const EnumDescriptor*> enums_; |
| +}; |
| + |
| +} // namespace |
| + |
| +Generator::Generator() { |
| +} |
| + |
| +Generator::~Generator() { |
| +} |
| + |
| +bool Generator::Generate(const FileDescriptor* file, |
| + const std::string& options, |
| + google::protobuf::compiler::GeneratorContext* context, |
| + std::string* error) const { |
| + |
| + std::string stub_name = StripSuffixString(file->name(), ".proto") + ".zeropb"; |
| + std::unique_ptr<ZeroCopyOutputStream> pb_h(context->Open(stub_name + ".h")); |
| + std::unique_ptr<ZeroCopyOutputStream> pb_cc(context->Open(stub_name + ".cc")); |
| + // $ used as variable delimiter. |
| + Printer pb_h_printer(pb_h.get(), '$'); |
| + Printer pb_cc_printer(pb_cc.get(), '$'); |
| + |
| + GeneratorJob job(file, &pb_h_printer); |
| + if (!job.GenerateStubs()) { |
| + *error = job.GetFirstError(); |
| + return false; |
| + } |
| + pb_cc_printer.Print("// This file intentionally left blank.\n"); |
| + |
| + return true; |
| +} |
| + |
| +} // namespace proto |
| +} // namespace tracing |