Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "proto_zero_generator.h" | 5 #include "proto_zero_generator.h" |
| 6 | 6 |
| 7 #include <map> | |
| 7 #include <memory> | 8 #include <memory> |
| 8 #include <string> | 9 #include <string> |
| 9 | 10 |
| 10 #include "third_party/protobuf/src/google/protobuf/descriptor.h" | 11 #include "third_party/protobuf/src/google/protobuf/descriptor.h" |
| 11 #include "third_party/protobuf/src/google/protobuf/io/printer.h" | 12 #include "third_party/protobuf/src/google/protobuf/io/printer.h" |
| 12 #include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream.h" | 13 #include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream.h" |
| 13 #include "third_party/protobuf/src/google/protobuf/stubs/strutil.h" | 14 #include "third_party/protobuf/src/google/protobuf/stubs/strutil.h" |
| 14 | 15 |
| 15 namespace tracing { | 16 namespace tracing { |
| 16 namespace proto { | 17 namespace proto { |
| 17 | 18 |
| 19 using google::protobuf::Descriptor; // Message descriptor. | |
| 20 using google::protobuf::EnumDescriptor; | |
| 21 using google::protobuf::EnumValueDescriptor; | |
| 22 using google::protobuf::FieldDescriptor; | |
| 18 using google::protobuf::FileDescriptor; | 23 using google::protobuf::FileDescriptor; |
| 19 using google::protobuf::StripSuffixString; | |
| 20 using google::protobuf::compiler::GeneratorContext; | 24 using google::protobuf::compiler::GeneratorContext; |
| 21 using google::protobuf::io::Printer; | 25 using google::protobuf::io::Printer; |
| 22 using google::protobuf::io::ZeroCopyOutputStream; | 26 using google::protobuf::io::ZeroCopyOutputStream; |
| 23 | 27 |
| 28 // These protobuf-specific string utils are used since not all required ones | |
| 29 // 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
| |
| 30 // different string util libraries. | |
| 31 using google::protobuf::Split; | |
|
Primiano Tucci (use gerrit)
2016/07/15 10:33:56
don't you have this in base?
| |
| 32 using google::protobuf::StripPrefixString; | |
| 33 using google::protobuf::StripString; | |
| 34 using google::protobuf::StripSuffixString; | |
| 35 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.
| |
| 36 | |
| 24 namespace { | 37 namespace { |
| 25 | 38 |
| 26 class GeneratorJob { | 39 class GeneratorJob { |
| 27 public: | 40 public: |
| 28 GeneratorJob(const FileDescriptor *file, | 41 GeneratorJob(const FileDescriptor *file, |
| 29 Printer* stub_h_printer, | 42 Printer* stub_h_printer, |
| 30 Printer* stub_cc_printer) | 43 Printer* stub_cc_printer) |
| 31 : file_(file), | 44 : source_(file), |
| 32 stub_h_(stub_h_printer), | 45 stub_h_(stub_h_printer), |
| 33 stub_cc_(stub_cc_printer) {} | 46 stub_cc_(stub_cc_printer) {} |
| 34 | 47 |
| 35 bool GenerateStubs() { | 48 bool GenerateStubs() { |
| 36 stub_h_->Print( | 49 Preprocess(); |
| 37 "// Autogenerated. DO NOT EDIT.\n" | 50 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.
| |
| 38 "// Generated by: //components/tracing/proto_zero_plugin.\n\n" | 51 for (const EnumDescriptor* enumeration: enums_) { |
| 39 "// Package: $package$\n", | 52 PrintEnumDescriptor(enumeration); |
| 40 "package", file_->package()); | 53 } |
| 41 stub_cc_->Print( | 54 for (const Descriptor* message : messages_) { |
| 42 "// Autogenerated. DO NOT EDIT.\n" | 55 PrintMessageDescriptor(message); |
| 43 "// Generated by: //components/tracing/proto_zero_plugin.\n\n" | 56 } |
| 44 "// This file intentionally left blank.\n"); | 57 PrintEpilogue(); |
| 45 // TODO(kraynov) Implement in the next CL (crbug.com/608721). | 58 return error_.empty(); |
| 46 return true; | |
| 47 } | 59 } |
| 48 | 60 |
| 49 // If generator fails to produce stubs for a particular proto definitions | 61 // If generator fails to produce stubs for a particular proto definitions |
| 50 // it finishes with undefined output and writes the first error occured. | 62 // it finishes with undefined output and writes the first error occured. |
| 51 const std::string& GetFirstError() const { | 63 const std::string& GetFirstError() const { |
| 52 return error_; | 64 return error_; |
| 53 } | 65 } |
| 54 | 66 |
| 55 private: | 67 private: |
| 56 // Only the first error will be recorded. | 68 // Only the first error will be recorded. |
| 57 void Abort(const std::string& reason) { | 69 void Abort(const std::string& reason) { |
| 58 if (error_.empty()) { | 70 if (error_.empty()) { |
| 59 error_ = reason; | 71 error_ = reason; |
| 60 } | 72 } |
| 61 } | 73 } |
| 62 | 74 |
| 63 const FileDescriptor* const file_; | 75 // Get full name (including outer descriptors) of proto descriptor. |
| 76 template <class T> | |
| 77 inline std::string GetDescriptorName(const T* descriptor) { | |
| 78 if (!package_.empty()) { | |
| 79 return StripPrefixString(descriptor->full_name(), package_ + "."); | |
| 80 } else { | |
| 81 return descriptor->full_name(); | |
| 82 } | |
| 83 } | |
| 84 | |
| 85 // Get C++ class name corresponding to proto descriptor. | |
| 86 // Nested names are splitted by underscores. Underscores in type names aren't | |
| 87 // prohibited but not recommended in order to avoid name collisions. | |
| 88 template <class T> | |
| 89 inline std::string GetCppClassName(const T* descriptor, bool full = false) { | |
| 90 std::string name = GetDescriptorName(descriptor); | |
| 91 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.
| |
| 92 if (full) | |
| 93 name = full_namespace_prefix_ + name; | |
| 94 return name; | |
| 95 } | |
| 96 | |
| 97 // Small enums can be written faster without involving VarInt encoder. | |
| 98 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.
| |
| 99 for (int i = 0; i < enumeration->value_count(); ++i) { | |
| 100 int32_t value = enumeration->value(i)->number(); | |
| 101 if (value < 0 || value > 0x7F) | |
| 102 return false; | |
| 103 } | |
| 104 return true; | |
| 105 } | |
| 106 | |
| 107 void Preprocess() { | |
| 108 // Package name maps to a series of namespaces. | |
| 109 package_ = source_->package(); | |
| 110 namespaces_ = Split(package_, "."); | |
| 111 full_namespace_prefix_ = "::"; | |
| 112 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.
| |
| 113 full_namespace_prefix_ += ns + "::"; | |
| 114 } | |
| 115 // Collect message descriptors in DFS order. | |
| 116 std::vector<const Descriptor*> stack; | |
| 117 for (int i = 0; i < source_->message_type_count(); ++i) { | |
| 118 stack.push_back(source_->message_type(i)); | |
| 119 } | |
| 120 while (!stack.empty()) { | |
| 121 const Descriptor* message = stack.back(); | |
| 122 stack.pop_back(); | |
| 123 messages_.push_back(message); | |
| 124 for (int i = 0; i < message->nested_type_count(); ++i) { | |
| 125 stack.push_back(message->nested_type(i)); | |
| 126 } | |
| 127 } | |
| 128 // Collect enums. | |
| 129 for (int i = 0; i < source_->enum_type_count(); ++i) { | |
| 130 enums_.push_back(source_->enum_type(i)); | |
| 131 } | |
| 132 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
| |
| 133 for (int i = 0; i < message->enum_type_count(); ++i) { | |
| 134 enums_.push_back(message->enum_type(i)); | |
| 135 } | |
| 136 } | |
| 137 } | |
| 138 | |
| 139 // Print top header, namespaces and forward declarations. | |
| 140 void PrintPrologue() { | |
| 141 std::string greeting = | |
| 142 "// Autogenerated. DO NOT EDIT.\n" | |
| 143 "// Protobuf compiler (protoc) has generated these stubs with\n" | |
| 144 "// //components/tracing/tools/proto_zero_plugin.\n"; | |
| 145 std::string guard = package_ + "_" + source_->name() + "_H_"; | |
| 146 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.
| |
| 147 StripString(&guard, ".-", '_'); | |
| 148 | |
| 149 stub_h_->Print( | |
| 150 "$greeting$\n" | |
| 151 "#ifndef $guard$\n" | |
| 152 "#define $guard$\n\n" | |
| 153 "#include <stddef.h>\n" | |
| 154 "#include <stdint.h>\n\n" | |
| 155 "// Runtime is optional for testing purposes.\n" | |
| 156 "#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.
| |
| 157 "// #include \"components/tracing/core/proto_zero_message.h\"\n" | |
| 158 "#endif\n\n", | |
| 159 "greeting", greeting, | |
| 160 "guard", guard); | |
| 161 stub_cc_->Print( | |
| 162 "$greeting$\n" | |
| 163 "// This file intentionally left blank.\n", | |
| 164 "greeting", greeting); | |
| 165 | |
| 166 // Print namespaces. | |
| 167 for (const std::string& ns : namespaces_) | |
| 168 stub_h_->Print("namespace $ns$ {\n", "ns", ns); | |
| 169 stub_h_->Print("\n"); | |
| 170 // Print forward declarations. | |
| 171 for (const Descriptor* message : messages_) { | |
| 172 stub_h_->Print( | |
| 173 "class $class$;\n", | |
| 174 "class", GetCppClassName(message)); | |
| 175 } | |
| 176 stub_h_->Print("\n"); | |
| 177 } | |
| 178 | |
| 179 void PrintEnumDescriptor(const EnumDescriptor* enumeration) { | |
| 180 stub_h_->Print( | |
| 181 "enum $class$ : int32_t {\n", | |
|
Primiano Tucci (use gerrit)
2016/07/15 10:33:56
It seems that the standard proto just generates an
| |
| 182 "class", GetCppClassName(enumeration)); | |
| 183 stub_h_->Indent(); | |
| 184 for (int i = 0; i < enumeration->value_count(); ++i) { | |
| 185 const EnumValueDescriptor* value = enumeration->value(i); | |
| 186 stub_h_->Print( | |
| 187 "$name$ = $number$,\n", | |
| 188 "name", value->name(), | |
| 189 "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!
| |
| 190 } | |
| 191 stub_h_->Outdent(); | |
| 192 stub_h_->Print("};\n\n"); | |
| 193 } | |
| 194 | |
| 195 void PrintSimpleFieldDescriptor(const FieldDescriptor* field) { | |
| 196 std::map<std::string, std::string> setter; | |
| 197 setter["id"] = std::to_string(field->number()); | |
| 198 setter["name"] = field->name(); | |
| 199 setter["action"] = field->is_repeated() ? "add" : "set"; | |
| 200 | |
| 201 std::string appender; | |
| 202 std::string cpp_type; | |
| 203 | |
| 204 switch (field->type()) { | |
| 205 case FieldDescriptor::TYPE_BOOL: { | |
| 206 appender = "AppendBool"; | |
| 207 cpp_type = "bool"; | |
| 208 break; | |
| 209 } | |
| 210 case FieldDescriptor::TYPE_INT32: { | |
| 211 appender = "AppendInt32"; | |
| 212 cpp_type = "int32_t"; | |
| 213 break; | |
| 214 } | |
| 215 case FieldDescriptor::TYPE_INT64: { | |
| 216 appender = "AppendInt64"; | |
| 217 cpp_type = "int64_t"; | |
| 218 break; | |
| 219 } | |
| 220 case FieldDescriptor::TYPE_UINT32: { | |
| 221 appender = "AppendUint32"; | |
| 222 cpp_type = "uint32_t"; | |
| 223 break; | |
| 224 } | |
| 225 case FieldDescriptor::TYPE_UINT64: { | |
| 226 appender = "AppendUint64"; | |
| 227 cpp_type = "uint64_t"; | |
| 228 break; | |
| 229 } | |
| 230 case FieldDescriptor::TYPE_SINT32: { | |
| 231 appender = "AppendSint32"; | |
| 232 cpp_type = "int32_t"; | |
| 233 break; | |
| 234 } | |
| 235 case FieldDescriptor::TYPE_SINT64: { | |
| 236 appender = "AppendSint64"; | |
| 237 cpp_type = "int64_t"; | |
| 238 break; | |
| 239 } | |
| 240 case FieldDescriptor::TYPE_FIXED32: { | |
| 241 appender = "AppendFixed32"; | |
| 242 cpp_type = "uint32_t"; | |
| 243 break; | |
| 244 } | |
| 245 case FieldDescriptor::TYPE_FIXED64: { | |
| 246 appender = "AppendFixed64"; | |
| 247 cpp_type = "uint64_t"; | |
| 248 break; | |
| 249 } | |
| 250 case FieldDescriptor::TYPE_SFIXED32: { | |
| 251 appender = "AppendSfixed32"; | |
| 252 cpp_type = "int32_t"; | |
| 253 break; | |
| 254 } | |
| 255 case FieldDescriptor::TYPE_SFIXED64: { | |
| 256 appender = "AppendSfixed64"; | |
| 257 cpp_type = "int64_t"; | |
| 258 break; | |
| 259 } | |
| 260 case FieldDescriptor::TYPE_FLOAT: { | |
| 261 appender = "AppendFloat"; | |
| 262 cpp_type = "float"; | |
| 263 break; | |
| 264 } | |
| 265 case FieldDescriptor::TYPE_DOUBLE: { | |
| 266 appender = "AppendDouble"; | |
| 267 cpp_type = "double"; | |
| 268 break; | |
| 269 } | |
| 270 case FieldDescriptor::TYPE_ENUM: { | |
| 271 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.
| |
| 272 appender = "AppendTinyNumber"; | |
| 273 } else { | |
| 274 appender = "AppendInt32"; | |
| 275 } | |
| 276 cpp_type = GetCppClassName(field->enum_type(), true); | |
| 277 break; | |
| 278 } | |
| 279 case FieldDescriptor::TYPE_STRING: { | |
| 280 appender = "AppendString"; | |
| 281 cpp_type = "const char*"; | |
| 282 break; | |
| 283 } | |
| 284 case FieldDescriptor::TYPE_BYTES: { | |
| 285 stub_h_->Print( | |
| 286 setter, | |
| 287 "void $action$_$name$(const uint8_t* data, size_t size) {\n" | |
| 288 " // 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
| |
| 289 "}\n"); | |
| 290 return; | |
| 291 } | |
| 292 default: { | |
| 293 Abort("Unsupported field type."); | |
| 294 return; | |
| 295 } | |
| 296 } | |
| 297 setter["appender"] = appender; | |
| 298 setter["cpp_type"] = cpp_type; | |
| 299 stub_h_->Print( | |
| 300 setter, | |
| 301 "void $action$_$name$($cpp_type$ value) {\n" | |
| 302 " // $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
| |
| 303 "}\n"); | |
| 304 } | |
| 305 | |
| 306 void PrintNestedMessageFieldDescriptor(const FieldDescriptor* field) { | |
| 307 stub_h_->Print( | |
| 308 "$class$* $action$_$name$() {\n" | |
| 309 " // $class$* nested;\n" | |
| 310 " // OpenNestedMessage($id$, &nested);\n" | |
|
Primiano Tucci (use gerrit)
2016/07/15 11:53:12
This is BeginNestedMessage. See
https://cs.chromiu
| |
| 311 " return nullptr;\n" | |
| 312 "}\n", | |
| 313 "id", std::to_string(field->number()), | |
| 314 "name", field->name(), | |
| 315 "action", field->is_repeated() ? "add" : "set", | |
| 316 "class", GetCppClassName(field->message_type())); | |
| 317 } | |
| 318 | |
| 319 void PrintMessageDescriptor(const Descriptor* message) { | |
| 320 stub_h_->Print( | |
| 321 "class $name$ /* : public ::tracing::proto::ProtoZeroMessage */ {\n" | |
|
Primiano Tucci (use gerrit)
2016/07/15 10:33:56
why this is commented?
| |
| 322 " public:\n", | |
| 323 "name", GetCppClassName(message)); | |
| 324 stub_h_->Indent(); | |
| 325 | |
| 326 // Using statements for nested messages. | |
| 327 for (int i = 0; i < message->nested_type_count(); ++i) { | |
| 328 const Descriptor* nested_message = message->nested_type(i); | |
| 329 stub_h_->Print( | |
| 330 "using $local_name$ = $global_name$;\n", | |
| 331 "local_name", nested_message->name(), | |
| 332 "global_name", GetCppClassName(nested_message, true)); | |
| 333 } | |
| 334 // Using statements for nested enums. | |
| 335 for (int i = 0; i < message->enum_type_count(); ++i) { | |
| 336 const EnumDescriptor* nested_enum = message->enum_type(i); | |
| 337 stub_h_->Print( | |
| 338 "using $local_name$ = $global_name$;\n", | |
| 339 "local_name", nested_enum->name(), | |
| 340 "global_name", GetCppClassName(nested_enum, true)); | |
| 341 } | |
| 342 // Fields descriptors. | |
| 343 for (int i = 0; i < message->field_count(); ++i) { | |
| 344 const FieldDescriptor* field = message->field(i); | |
| 345 if (field->is_required()) { | |
| 346 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.
| |
| 347 return; | |
| 348 } | |
| 349 if (field->is_packed()) { | |
| 350 Abort("Packed repeated fields are not supported."); | |
| 351 return; | |
| 352 } | |
| 353 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.
| |
| 354 PrintSimpleFieldDescriptor(field); | |
| 355 } else { | |
| 356 PrintNestedMessageFieldDescriptor(field); | |
| 357 } | |
| 358 } | |
| 359 | |
| 360 stub_h_->Outdent(); | |
| 361 stub_h_->Print("};\n\n"); | |
| 362 } | |
| 363 | |
| 364 void PrintEpilogue() { | |
| 365 for (unsigned i = 0; i < namespaces_.size(); ++i) { | |
| 366 stub_h_->Print("} // Namespace.\n"); | |
| 367 } | |
| 368 stub_h_->Print("#endif // Include guard.\n"); | |
| 369 } | |
| 370 | |
| 371 const FileDescriptor* const source_; | |
| 64 Printer* const stub_h_; | 372 Printer* const stub_h_; |
| 65 Printer* const stub_cc_; | 373 Printer* const stub_cc_; |
| 66 std::string error_; | 374 std::string error_; |
| 375 | |
| 376 std::string package_; | |
| 377 std::vector<std::string> namespaces_; | |
| 378 std::string full_namespace_prefix_; | |
| 379 std::vector<const Descriptor*> messages_; | |
| 380 std::vector<const EnumDescriptor*> enums_; | |
| 67 }; | 381 }; |
| 68 | 382 |
| 69 } // namespace | 383 } // namespace |
| 70 | 384 |
| 71 ProtoZeroGenerator::ProtoZeroGenerator() { | 385 ProtoZeroGenerator::ProtoZeroGenerator() { |
| 72 } | 386 } |
| 73 | 387 |
| 74 ProtoZeroGenerator::~ProtoZeroGenerator() { | 388 ProtoZeroGenerator::~ProtoZeroGenerator() { |
| 75 } | 389 } |
| 76 | 390 |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 94 GeneratorJob job(file, &stub_h_printer, &stub_cc_printer); | 408 GeneratorJob job(file, &stub_h_printer, &stub_cc_printer); |
| 95 if (!job.GenerateStubs()) { | 409 if (!job.GenerateStubs()) { |
| 96 *error = job.GetFirstError(); | 410 *error = job.GetFirstError(); |
| 97 return false; | 411 return false; |
| 98 } | 412 } |
| 99 return true; | 413 return true; |
| 100 } | 414 } |
| 101 | 415 |
| 102 } // namespace proto | 416 } // namespace proto |
| 103 } // namespace tracing | 417 } // namespace tracing |
| OLD | NEW |