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

Side by Side Diff: components/tracing/proto_zero_plugin/proto_zero_generator.cc

Issue 2083373002: proto_zero_plugin [NOT FOR REVIEW]. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: full_featured_will_split_into_smaller_CLs_for_review 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
OLDNEW
(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
OLDNEW
« no previous file with comments | « components/tracing/proto_zero_plugin/proto_zero_generator.h ('k') | components/tracing/proto_zero_plugin/proto_zero_plugin.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698