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