Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2016 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "proto_zero_generator.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <memory> | |
| 9 #include <string> | |
| 10 | |
| 11 #include "third_party/protobuf/src/google/protobuf/descriptor.h" | |
| 12 #include "third_party/protobuf/src/google/protobuf/io/printer.h" | |
| 13 #include "third_party/protobuf/src/google/protobuf/io/zero_copy_stream.h" | |
| 14 #include "third_party/protobuf/src/google/protobuf/stubs/strutil.h" | |
| 15 | |
| 16 namespace tracing { | |
| 17 namespace proto { | |
| 18 | |
| 19 // 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
| |
| 20 using MessageDescriptor = google::protobuf::Descriptor; | |
| 21 using google::protobuf::EnumDescriptor; | |
| 22 using google::protobuf::EnumValueDescriptor; | |
| 23 using google::protobuf::FieldDescriptor; | |
| 24 using google::protobuf::FileDescriptor; | |
| 25 using google::protobuf::io::Printer; | |
| 26 using google::protobuf::io::ZeroCopyOutputStream; | |
| 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 | |
| 34 namespace { | |
| 35 | |
| 36 class GeneratorJob { | |
| 37 public: | |
| 38 GeneratorJob(const FileDescriptor* file, Printer* printer) | |
| 39 : file_(file), printer_(printer) {} | |
| 40 | |
| 41 bool GenerateStubs() { | |
| 42 Prepare(); | |
| 43 PrintPrologue(); | |
| 44 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
| |
| 45 PrintEnum(enumeration); | |
| 46 } | |
| 47 for (const MessageDescriptor* message : messages_) { | |
| 48 PrintMessage(message); | |
| 49 } | |
| 50 PrintEpilogue(); | |
| 51 return error_.empty(); | |
| 52 } | |
| 53 | |
| 54 std::string GetFirstError() { | |
|
Primiano Tucci (use gerrit)
2016/07/11 16:52:07
not sure you want to return this by copy. I think
| |
| 55 return error_; | |
| 56 } | |
| 57 | |
| 58 private: | |
| 59 // Get C++ class name corresponding to message/enum descriptor. | |
| 60 // Nested names are splitted by underscores. | |
| 61 template <class T> | |
| 62 std::string GetCppClassName(const T* descriptor, bool absolute = false) { | |
| 63 std::string local_name = descriptor->name(); | |
| 64 if (local_name.find('_') != std::string::npos) { | |
| 65 Abort("Underscores are not permitted in proto's type name."); | |
| 66 return " /* Invalid name */ "; | |
| 67 } | |
| 68 std::string name = | |
| 69 StripPrefixString(descriptor->full_name(), package_ + "."); | |
| 70 StripString(&name, ".", '_'); | |
| 71 if (absolute) { | |
| 72 name = full_namespace_prefix_ + name; | |
| 73 } | |
| 74 return name; | |
| 75 } | |
| 76 | |
| 77 // Small enums can be written faster without involving VarInt encoder. | |
| 78 inline bool IsEnumOptimal(const EnumDescriptor* enumeration) { | |
| 79 for (int i = 0; i < enumeration->value_count(); ++i) { | |
| 80 int32_t number = enumeration->value(i)->number(); | |
| 81 if (number < 0 || number > 0x7F) | |
| 82 return false; | |
| 83 } | |
| 84 return true; | |
| 85 } | |
| 86 | |
| 87 // If something went wrong, plugin returns the first error occured. | |
| 88 void Abort(const std::string& reason) { | |
| 89 if (error_.empty()) { | |
| 90 error_ = reason; | |
| 91 } | |
| 92 } | |
| 93 | |
| 94 // Scanning pass. | |
|
Primiano Tucci (use gerrit)
2016/07/11 16:52:07
This comment doesn't say anything more than "Prepa
| |
| 95 void Prepare() { | |
| 96 // Package name evaluates to a series of namespaces. | |
| 97 package_ = file_->package(); | |
| 98 namespaces_ = Split(package_, "."); | |
| 99 full_namespace_prefix_ = "::"; | |
| 100 for (const std::string& ns : namespaces_) { | |
| 101 full_namespace_prefix_ += ns + "::"; | |
| 102 } | |
| 103 // Collect message descriptors in DFS order. | |
| 104 std::vector<const MessageDescriptor*> stack; | |
| 105 for (int i = 0; i < file_->message_type_count(); ++i) { | |
| 106 stack.push_back(file_->message_type(i)); | |
| 107 } | |
| 108 while (!stack.empty()) { | |
| 109 const MessageDescriptor* message = stack.back(); | |
| 110 stack.pop_back(); | |
| 111 messages_.push_back(message); | |
| 112 for (int i = 0; i < message->nested_type_count(); ++i) { | |
| 113 stack.push_back(message->nested_type(i)); | |
| 114 } | |
| 115 } | |
| 116 // Collect enums. | |
| 117 for (int i = 0; i < file_->enum_type_count(); ++i) { | |
| 118 enums_.push_back(file_->enum_type(i)); | |
| 119 } | |
| 120 for (const MessageDescriptor* message : messages_) { | |
| 121 for (int i = 0; i < message->enum_type_count(); ++i) { | |
| 122 enums_.push_back(message->enum_type(i)); | |
| 123 } | |
| 124 } | |
| 125 } | |
| 126 | |
| 127 // Print top header, namespaces and forward declarations. | |
| 128 void PrintPrologue() { | |
| 129 std::string guard = package_ + "_" + file_->name() + "_H_"; | |
| 130 UpperString(&guard); | |
| 131 StripString(&guard, ".-", '_'); | |
| 132 printer_->Print( | |
| 133 "// Autogenerated. DO NOT EDIT.\n" | |
| 134 "// Protoc has generated these stubs " | |
| 135 "with //components/tracing/proto_zero_plugin.\n\n" | |
| 136 "#ifndef $guard$\n" | |
| 137 "#define $guard$\n\n" | |
| 138 "#include <inttypes.h>\n\n" | |
|
Primiano Tucci (use gerrit)
2016/07/11 16:52:07
s/inttypes/stdint/
| |
| 139 "// Runtime is optional for testing purposes.\n" | |
| 140 "#ifndef PROTO_ZERO_NO_RUNTIME\n" | |
| 141 "#include \"components/tracing/core/proto_zero_message.h\"\n" | |
| 142 "#endif\n\n", | |
| 143 "guard", guard); | |
| 144 // Print namespaces. | |
|
Primiano Tucci (use gerrit)
2016/07/11 16:52:07
add a blank newline before these comments. It make
| |
| 145 for (const std::string& ns : namespaces_) { | |
| 146 printer_->Print("namespace $ns$ {\n", "ns", ns); | |
| 147 } | |
| 148 printer_->Print("\n"); | |
| 149 // Print forward declarations. | |
| 150 for (const MessageDescriptor* message : messages_) { | |
| 151 printer_->Print( | |
| 152 "class $class$;\n", | |
| 153 "class", GetCppClassName(message)); | |
| 154 } | |
| 155 printer_->Print("\n"); | |
| 156 } | |
| 157 | |
| 158 void PrintSimpleField(const FieldDescriptor* field) { | |
| 159 std::string appender; | |
| 160 std::string cpp_type; | |
| 161 std::string action = field->is_repeated() ? "add" : "set"; | |
| 162 | |
| 163 switch (field->type()) { | |
| 164 case FieldDescriptor::TYPE_BOOL: { | |
| 165 appender = "AppendBool"; | |
| 166 cpp_type = "bool"; | |
| 167 break; | |
| 168 } | |
| 169 case FieldDescriptor::TYPE_INT32: { | |
| 170 appender = "AppendInt32"; | |
| 171 cpp_type = "int32_t"; | |
| 172 break; | |
| 173 } | |
| 174 case FieldDescriptor::TYPE_INT64: { | |
| 175 appender = "AppendInt64"; | |
| 176 cpp_type = "int64_t"; | |
| 177 break; | |
| 178 } | |
| 179 case FieldDescriptor::TYPE_UINT32: { | |
| 180 appender = "AppendUint32"; | |
| 181 cpp_type = "uint32_t"; | |
| 182 break; | |
| 183 } | |
| 184 case FieldDescriptor::TYPE_UINT64: { | |
| 185 appender = "AppendUint64"; | |
| 186 cpp_type = "uint64_t"; | |
| 187 break; | |
| 188 } | |
| 189 case FieldDescriptor::TYPE_SINT32: { | |
| 190 appender = "AppendSint32"; | |
| 191 cpp_type = "int32_t"; | |
| 192 break; | |
| 193 } | |
| 194 case FieldDescriptor::TYPE_SINT64: { | |
| 195 appender = "AppendSint64"; | |
| 196 cpp_type = "int64_t"; | |
| 197 break; | |
| 198 } | |
| 199 case FieldDescriptor::TYPE_FIXED32: { | |
| 200 appender = "AppendFixed32"; | |
| 201 cpp_type = "uint32_t"; | |
| 202 break; | |
| 203 } | |
| 204 case FieldDescriptor::TYPE_FIXED64: { | |
| 205 appender = "AppendFixed64"; | |
| 206 cpp_type = "uint64_t"; | |
| 207 break; | |
| 208 } | |
| 209 case FieldDescriptor::TYPE_SFIXED32: { | |
| 210 appender = "AppendSfixed32"; | |
| 211 cpp_type = "int32_t"; | |
| 212 break; | |
| 213 } | |
| 214 case FieldDescriptor::TYPE_SFIXED64: { | |
| 215 appender = "AppendSfixed64"; | |
| 216 cpp_type = "int64_t"; | |
| 217 break; | |
| 218 } | |
| 219 case FieldDescriptor::TYPE_FLOAT: { | |
| 220 appender = "AppendFloat"; | |
| 221 cpp_type = "float"; | |
| 222 break; | |
| 223 } | |
| 224 case FieldDescriptor::TYPE_DOUBLE: { | |
| 225 appender = "AppendDouble"; | |
| 226 cpp_type = "double"; | |
| 227 break; | |
| 228 } | |
| 229 case FieldDescriptor::TYPE_ENUM: { | |
| 230 if (IsEnumOptimal(field->enum_type())) { | |
| 231 appender = "AppendTinyNumber"; | |
| 232 } else { | |
| 233 appender = "AppendInt32"; | |
| 234 } | |
| 235 cpp_type = GetCppClassName(field->enum_type(), true); | |
| 236 break; | |
| 237 } | |
| 238 case FieldDescriptor::TYPE_STRING: { | |
| 239 appender = "AppendString"; | |
| 240 cpp_type = "const char*"; | |
| 241 break; | |
| 242 } | |
| 243 case FieldDescriptor::TYPE_BYTES: { | |
| 244 printer_->Print( | |
| 245 "void $action$_$name$(const uint8_t* data, size_t size) {\n" | |
| 246 " AppendBytes($key$, data, size);\n" | |
| 247 "}\n", | |
| 248 "action", action, | |
| 249 "name", field->name(), | |
| 250 "key", std::to_string(field->number())); | |
| 251 return; | |
| 252 } | |
| 253 default: { | |
| 254 Abort("Unsupported field type."); | |
| 255 return; | |
| 256 } | |
| 257 } | |
| 258 printer_->Print( | |
| 259 "void $action$_$name$($cpp_type$ value) {\n" | |
| 260 " $appender$($key$, value);\n" | |
| 261 "}\n", | |
| 262 "appender", appender, | |
| 263 "cpp_type", cpp_type, | |
| 264 "action", action, | |
| 265 "name", field->name(), | |
| 266 "key", std::to_string(field->number())); | |
| 267 } | |
| 268 | |
| 269 // Embedded message field. | |
| 270 void PrintNestedMessageField(const FieldDescriptor* field) { | |
| 271 printer_->Print( | |
| 272 "$class$* $action$_$name$() {\n" | |
| 273 " $class$* nested;\n" | |
| 274 " OpenNestedMessage($key$, &nested);\n" | |
| 275 " return nested;\n" | |
| 276 "}\n", | |
| 277 "action", field->is_repeated() ? "add" : "set", | |
| 278 "class", GetCppClassName(field->message_type()), | |
| 279 "name", field->name(), | |
| 280 "key", std::to_string(field->number())); | |
| 281 } | |
| 282 | |
| 283 // Print fields of the message. | |
| 284 void PrintFields(const MessageDescriptor* message) { | |
| 285 for (int i = 0; i < message->field_count(); ++i) { | |
| 286 const FieldDescriptor* field = message->field(i); | |
| 287 if (field->is_required()) { | |
| 288 Abort("Required fields are not supported."); | |
| 289 return; | |
| 290 } | |
| 291 if (field->is_packed()) { | |
| 292 Abort("Packed repeated fields are not supported."); | |
| 293 return; | |
| 294 } | |
| 295 if (field->type() != FieldDescriptor::TYPE_MESSAGE) { | |
| 296 PrintSimpleField(field); | |
| 297 } else { | |
| 298 PrintNestedMessageField(field); | |
| 299 } | |
| 300 } | |
| 301 } | |
| 302 | |
| 303 // Print enum type definition. | |
| 304 void PrintEnum(const EnumDescriptor* enumeration) { | |
| 305 printer_->Print( | |
| 306 "enum $class$ : int32_t {\n", | |
| 307 "class", GetCppClassName(enumeration)); | |
| 308 printer_->Indent(); | |
| 309 for (int i = 0; i < enumeration->value_count(); ++i) { | |
| 310 const EnumValueDescriptor* value = enumeration->value(i); | |
| 311 printer_->Print( | |
| 312 "$name$ = $number$,\n", | |
| 313 "name", value->name(), | |
| 314 "number", std::to_string(value->number())); | |
| 315 } | |
| 316 printer_->Outdent(); | |
| 317 printer_->Print("};\n\n"); | |
| 318 } | |
| 319 | |
| 320 // Print message class definition. | |
| 321 void PrintMessage(const MessageDescriptor* message) { | |
| 322 printer_->Print( | |
| 323 "class $class$ : public $base$ {\n" | |
| 324 " public:\n", | |
| 325 | |
| 326 "class", GetCppClassName(message), | |
| 327 "base", "::tracing::proto::AppendOnlyProtoMessage"); | |
| 328 printer_->Indent(); | |
| 329 // Using nested messages. | |
| 330 for (int i = 0; i < message->nested_type_count(); ++i) { | |
| 331 const MessageDescriptor* nested_message = message->nested_type(i); | |
| 332 printer_->Print( | |
| 333 "using $local_name$ = $global_name$;\n", | |
| 334 "local_name", nested_message->name(), | |
| 335 "global_name", GetCppClassName(nested_message, true)); | |
| 336 } | |
| 337 // Using nested enums. | |
| 338 for (int i = 0; i < message->enum_type_count(); ++i) { | |
| 339 const EnumDescriptor* nested_enum = message->enum_type(i); | |
| 340 printer_->Print( | |
| 341 "using $local_name$ = $global_name$;\n", | |
| 342 "local_name", nested_enum->name(), | |
| 343 "global_name", GetCppClassName(nested_enum, true)); | |
| 344 } | |
| 345 PrintFields(message); | |
| 346 printer_->Outdent(); | |
| 347 printer_->Print("};\n\n"); | |
| 348 } | |
| 349 | |
| 350 // Closing braces. | |
| 351 void PrintEpilogue() { | |
| 352 for (auto it = namespaces_.rbegin(); it != namespaces_.rend(); ++it) { | |
| 353 printer_->Print("} // namespace $ns$\n", "ns", *it); | |
| 354 } | |
| 355 printer_->Print("#endif // Include guard.\n"); | |
| 356 } | |
| 357 | |
| 358 const FileDescriptor* const file_; | |
| 359 Printer* const printer_; | |
| 360 std::string error_; | |
| 361 | |
| 362 std::string package_; | |
| 363 std::vector<std::string> namespaces_; | |
| 364 std::string full_namespace_prefix_; | |
| 365 std::vector<const MessageDescriptor*> messages_; | |
| 366 std::vector<const EnumDescriptor*> enums_; | |
| 367 }; | |
| 368 | |
| 369 } // namespace | |
| 370 | |
| 371 Generator::Generator() { | |
| 372 } | |
| 373 | |
| 374 Generator::~Generator() { | |
| 375 } | |
| 376 | |
| 377 bool Generator::Generate(const FileDescriptor* file, | |
| 378 const std::string& options, | |
| 379 google::protobuf::compiler::GeneratorContext* context, | |
| 380 std::string* error) const { | |
| 381 | |
| 382 std::string stub_name = StripSuffixString(file->name(), ".proto") + ".zeropb"; | |
| 383 std::unique_ptr<ZeroCopyOutputStream> pb_h(context->Open(stub_name + ".h")); | |
| 384 std::unique_ptr<ZeroCopyOutputStream> pb_cc(context->Open(stub_name + ".cc")); | |
| 385 // $ used as variable delimiter. | |
| 386 Printer pb_h_printer(pb_h.get(), '$'); | |
| 387 Printer pb_cc_printer(pb_cc.get(), '$'); | |
| 388 | |
| 389 GeneratorJob job(file, &pb_h_printer); | |
| 390 if (!job.GenerateStubs()) { | |
| 391 *error = job.GetFirstError(); | |
| 392 return false; | |
| 393 } | |
| 394 pb_cc_printer.Print("// This file intentionally left blank.\n"); | |
| 395 | |
| 396 return true; | |
| 397 } | |
| 398 | |
| 399 } // namespace proto | |
| 400 } // namespace tracing | |
| OLD | NEW |