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

Side by Side 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 unified diff | Download patch
« no previous file with comments | « components/tracing/test/example_messages.proto ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
OLDNEW
« 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