• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 //     * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 //     * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 #include <iomanip>
32 #include <sstream>
33 
34 #include <google/protobuf/compiler/code_generator.h>
35 #include <google/protobuf/compiler/plugin.h>
36 #include <google/protobuf/descriptor.h>
37 #include <google/protobuf/descriptor.pb.h>
38 #include <google/protobuf/io/printer.h>
39 #include <google/protobuf/io/zero_copy_stream.h>
40 
41 #include <google/protobuf/compiler/ruby/ruby_generator.h>
42 
43 namespace google {
44 namespace protobuf {
45 namespace compiler {
46 namespace ruby {
47 
48 // Forward decls.
49 template <class numeric_type>
50 std::string NumberToString(numeric_type value);
51 std::string GetRequireName(const std::string& proto_file);
52 std::string LabelForField(FieldDescriptor* field);
53 std::string TypeName(FieldDescriptor* field);
54 bool GenerateMessage(const Descriptor* message, io::Printer* printer,
55                      std::string* error);
56 void GenerateEnum(const EnumDescriptor* en, io::Printer* printer);
57 void GenerateMessageAssignment(const std::string& prefix,
58                                const Descriptor* message, io::Printer* printer);
59 void GenerateEnumAssignment(const std::string& prefix, const EnumDescriptor* en,
60                             io::Printer* printer);
61 std::string DefaultValueForField(const FieldDescriptor* field);
62 
63 template<class numeric_type>
NumberToString(numeric_type value)64 std::string NumberToString(numeric_type value) {
65   std::ostringstream os;
66   os << value;
67   return os.str();
68 }
69 
GetRequireName(const std::string & proto_file)70 std::string GetRequireName(const std::string& proto_file) {
71   int lastindex = proto_file.find_last_of(".");
72   return proto_file.substr(0, lastindex) + "_pb";
73 }
74 
GetOutputFilename(const std::string & proto_file)75 std::string GetOutputFilename(const std::string& proto_file) {
76   return GetRequireName(proto_file) + ".rb";
77 }
78 
LabelForField(const FieldDescriptor * field)79 std::string LabelForField(const FieldDescriptor* field) {
80   if (field->has_optional_keyword() &&
81       field->file()->syntax() == FileDescriptor::SYNTAX_PROTO3) {
82     return "proto3_optional";
83   }
84   switch (field->label()) {
85     case FieldDescriptor::LABEL_OPTIONAL: return "optional";
86     case FieldDescriptor::LABEL_REQUIRED: return "required";
87     case FieldDescriptor::LABEL_REPEATED: return "repeated";
88     default: assert(false); return "";
89   }
90 }
91 
TypeName(const FieldDescriptor * field)92 std::string TypeName(const FieldDescriptor* field) {
93   switch (field->type()) {
94     case FieldDescriptor::TYPE_INT32: return "int32";
95     case FieldDescriptor::TYPE_INT64: return "int64";
96     case FieldDescriptor::TYPE_UINT32: return "uint32";
97     case FieldDescriptor::TYPE_UINT64: return "uint64";
98     case FieldDescriptor::TYPE_SINT32: return "sint32";
99     case FieldDescriptor::TYPE_SINT64: return "sint64";
100     case FieldDescriptor::TYPE_FIXED32: return "fixed32";
101     case FieldDescriptor::TYPE_FIXED64: return "fixed64";
102     case FieldDescriptor::TYPE_SFIXED32: return "sfixed32";
103     case FieldDescriptor::TYPE_SFIXED64: return "sfixed64";
104     case FieldDescriptor::TYPE_DOUBLE: return "double";
105     case FieldDescriptor::TYPE_FLOAT: return "float";
106     case FieldDescriptor::TYPE_BOOL: return "bool";
107     case FieldDescriptor::TYPE_ENUM: return "enum";
108     case FieldDescriptor::TYPE_STRING: return "string";
109     case FieldDescriptor::TYPE_BYTES: return "bytes";
110     case FieldDescriptor::TYPE_MESSAGE: return "message";
111     case FieldDescriptor::TYPE_GROUP: return "group";
112     default: assert(false); return "";
113   }
114 }
115 
StringifySyntax(FileDescriptor::Syntax syntax)116 string StringifySyntax(FileDescriptor::Syntax syntax) {
117   switch (syntax) {
118     case FileDescriptor::SYNTAX_PROTO2:
119       return "proto2";
120     case FileDescriptor::SYNTAX_PROTO3:
121       return "proto3";
122     case FileDescriptor::SYNTAX_UNKNOWN:
123     default:
124       GOOGLE_LOG(FATAL) << "Unsupported syntax; this generator only supports "
125                            "proto2 and proto3 syntax.";
126       return "";
127   }
128 }
129 
DefaultValueForField(const FieldDescriptor * field)130 std::string DefaultValueForField(const FieldDescriptor* field) {
131   switch(field->cpp_type()) {
132     case FieldDescriptor::CPPTYPE_INT32:
133       return NumberToString(field->default_value_int32());
134     case FieldDescriptor::CPPTYPE_INT64:
135       return NumberToString(field->default_value_int64());
136     case FieldDescriptor::CPPTYPE_UINT32:
137       return NumberToString(field->default_value_uint32());
138     case FieldDescriptor::CPPTYPE_UINT64:
139       return NumberToString(field->default_value_uint64());
140     case FieldDescriptor::CPPTYPE_FLOAT:
141       return NumberToString(field->default_value_float());
142     case FieldDescriptor::CPPTYPE_DOUBLE:
143       return NumberToString(field->default_value_double());
144     case FieldDescriptor::CPPTYPE_BOOL:
145       return field->default_value_bool() ? "true" : "false";
146     case FieldDescriptor::CPPTYPE_ENUM:
147       return NumberToString(field->default_value_enum()->number());
148     case FieldDescriptor::CPPTYPE_STRING: {
149       std::ostringstream os;
150       string default_str = field->default_value_string();
151 
152       if (field->type() == FieldDescriptor::TYPE_STRING) {
153         os << "\"" << default_str << "\"";
154       } else if (field->type() == FieldDescriptor::TYPE_BYTES) {
155         os << "\"";
156 
157         os.fill('0');
158         for (int i = 0; i < default_str.length(); ++i) {
159           // Write the hex form of each byte.
160           os << "\\x" << std::hex << std::setw(2)
161              << ((uint16)((unsigned char)default_str.at(i)));
162         }
163         os << "\".force_encoding(\"ASCII-8BIT\")";
164       }
165 
166       return os.str();
167     }
168     default: assert(false); return "";
169   }
170 }
171 
GenerateField(const FieldDescriptor * field,io::Printer * printer)172 void GenerateField(const FieldDescriptor* field, io::Printer* printer) {
173   if (field->is_map()) {
174     const FieldDescriptor* key_field =
175         field->message_type()->FindFieldByNumber(1);
176     const FieldDescriptor* value_field =
177         field->message_type()->FindFieldByNumber(2);
178 
179     printer->Print(
180       "map :$name$, :$key_type$, :$value_type$, $number$",
181       "name", field->name(),
182       "key_type", TypeName(key_field),
183       "value_type", TypeName(value_field),
184       "number", NumberToString(field->number()));
185 
186     if (value_field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
187       printer->Print(
188         ", \"$subtype$\"\n",
189         "subtype", value_field->message_type()->full_name());
190     } else if (value_field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
191       printer->Print(
192         ", \"$subtype$\"\n",
193         "subtype", value_field->enum_type()->full_name());
194     } else {
195       printer->Print("\n");
196     }
197   } else {
198 
199     printer->Print(
200       "$label$ :$name$, ",
201       "label", LabelForField(field),
202       "name", field->name());
203     printer->Print(
204       ":$type$, $number$",
205       "type", TypeName(field),
206       "number", NumberToString(field->number()));
207 
208     if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
209       printer->Print(
210         ", \"$subtype$\"",
211        "subtype", field->message_type()->full_name());
212     } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
213       printer->Print(
214         ", \"$subtype$\"",
215         "subtype", field->enum_type()->full_name());
216     }
217 
218     if (field->has_default_value()) {
219       printer->Print(", default: $default$", "default",
220                      DefaultValueForField(field));
221     }
222 
223     printer->Print("\n");
224   }
225 }
226 
GenerateOneof(const OneofDescriptor * oneof,io::Printer * printer)227 void GenerateOneof(const OneofDescriptor* oneof, io::Printer* printer) {
228   printer->Print(
229       "oneof :$name$ do\n",
230       "name", oneof->name());
231   printer->Indent();
232 
233   for (int i = 0; i < oneof->field_count(); i++) {
234     const FieldDescriptor* field = oneof->field(i);
235     GenerateField(field, printer);
236   }
237 
238   printer->Outdent();
239   printer->Print("end\n");
240 }
241 
GenerateMessage(const Descriptor * message,io::Printer * printer,std::string * error)242 bool GenerateMessage(const Descriptor* message, io::Printer* printer,
243                      std::string* error) {
244   if (message->extension_range_count() > 0 || message->extension_count() > 0) {
245     *error = "Extensions are not yet supported for proto2 .proto files.";
246     return false;
247   }
248 
249   // Don't generate MapEntry messages -- we use the Ruby extension's native
250   // support for map fields instead.
251   if (message->options().map_entry()) {
252     return true;
253   }
254 
255   printer->Print(
256     "add_message \"$name$\" do\n",
257     "name", message->full_name());
258   printer->Indent();
259 
260   for (int i = 0; i < message->field_count(); i++) {
261     const FieldDescriptor* field = message->field(i);
262     if (!field->real_containing_oneof()) {
263       GenerateField(field, printer);
264     }
265   }
266 
267   for (int i = 0; i < message->real_oneof_decl_count(); i++) {
268     const OneofDescriptor* oneof = message->oneof_decl(i);
269     GenerateOneof(oneof, printer);
270   }
271 
272   printer->Outdent();
273   printer->Print("end\n");
274 
275   for (int i = 0; i < message->nested_type_count(); i++) {
276     if (!GenerateMessage(message->nested_type(i), printer, error)) {
277       return false;
278     }
279   }
280   for (int i = 0; i < message->enum_type_count(); i++) {
281     GenerateEnum(message->enum_type(i), printer);
282   }
283 
284   return true;
285 }
286 
GenerateEnum(const EnumDescriptor * en,io::Printer * printer)287 void GenerateEnum(const EnumDescriptor* en, io::Printer* printer) {
288   printer->Print(
289     "add_enum \"$name$\" do\n",
290     "name", en->full_name());
291   printer->Indent();
292 
293   for (int i = 0; i < en->value_count(); i++) {
294     const EnumValueDescriptor* value = en->value(i);
295     printer->Print(
296       "value :$name$, $number$\n",
297       "name", value->name(),
298       "number", NumberToString(value->number()));
299   }
300 
301   printer->Outdent();
302   printer->Print(
303     "end\n");
304 }
305 
306 // Locale-agnostic utility functions.
IsLower(char ch)307 bool IsLower(char ch) { return ch >= 'a' && ch <= 'z'; }
308 
IsUpper(char ch)309 bool IsUpper(char ch) { return ch >= 'A' && ch <= 'Z'; }
310 
IsAlpha(char ch)311 bool IsAlpha(char ch) { return IsLower(ch) || IsUpper(ch); }
312 
UpperChar(char ch)313 char UpperChar(char ch) { return IsLower(ch) ? (ch - 'a' + 'A') : ch; }
314 
315 
316 // Package names in protobuf are snake_case by convention, but Ruby module
317 // names must be PascalCased.
318 //
319 //   foo_bar_baz -> FooBarBaz
PackageToModule(const std::string & name)320 std::string PackageToModule(const std::string& name) {
321   bool next_upper = true;
322   std::string result;
323   result.reserve(name.size());
324 
325   for (int i = 0; i < name.size(); i++) {
326     if (name[i] == '_') {
327       next_upper = true;
328     } else {
329       if (next_upper) {
330         result.push_back(UpperChar(name[i]));
331       } else {
332         result.push_back(name[i]);
333       }
334       next_upper = false;
335     }
336   }
337 
338   return result;
339 }
340 
341 // Class and enum names in protobuf should be PascalCased by convention, but
342 // since there is nothing enforcing this we need to ensure that they are valid
343 // Ruby constants.  That mainly means making sure that the first character is
344 // an upper-case letter.
RubifyConstant(const std::string & name)345 std::string RubifyConstant(const std::string& name) {
346   std::string ret = name;
347   if (!ret.empty()) {
348     if (IsLower(ret[0])) {
349       // If it starts with a lowercase letter, capitalize it.
350       ret[0] = UpperChar(ret[0]);
351     } else if (!IsAlpha(ret[0])) {
352       // Otherwise (e.g. if it begins with an underscore), we need to come up
353       // with some prefix that starts with a capital letter. We could be smarter
354       // here, e.g. try to strip leading underscores, but this may cause other
355       // problems if the user really intended the name. So let's just prepend a
356       // well-known suffix.
357       ret = "PB_" + ret;
358     }
359   }
360 
361   return ret;
362 }
363 
GenerateMessageAssignment(const std::string & prefix,const Descriptor * message,io::Printer * printer)364 void GenerateMessageAssignment(const std::string& prefix,
365                                const Descriptor* message,
366                                io::Printer* printer) {
367   // Don't generate MapEntry messages -- we use the Ruby extension's native
368   // support for map fields instead.
369   if (message->options().map_entry()) {
370     return;
371   }
372 
373   printer->Print(
374     "$prefix$$name$ = ",
375     "prefix", prefix,
376     "name", RubifyConstant(message->name()));
377   printer->Print(
378     "::Google::Protobuf::DescriptorPool.generated_pool."
379     "lookup(\"$full_name$\").msgclass\n",
380     "full_name", message->full_name());
381 
382   std::string nested_prefix = prefix + RubifyConstant(message->name()) + "::";
383   for (int i = 0; i < message->nested_type_count(); i++) {
384     GenerateMessageAssignment(nested_prefix, message->nested_type(i), printer);
385   }
386   for (int i = 0; i < message->enum_type_count(); i++) {
387     GenerateEnumAssignment(nested_prefix, message->enum_type(i), printer);
388   }
389 }
390 
GenerateEnumAssignment(const std::string & prefix,const EnumDescriptor * en,io::Printer * printer)391 void GenerateEnumAssignment(const std::string& prefix, const EnumDescriptor* en,
392                             io::Printer* printer) {
393   printer->Print(
394     "$prefix$$name$ = ",
395     "prefix", prefix,
396     "name", RubifyConstant(en->name()));
397   printer->Print(
398     "::Google::Protobuf::DescriptorPool.generated_pool."
399     "lookup(\"$full_name$\").enummodule\n",
400     "full_name", en->full_name());
401 }
402 
GeneratePackageModules(const FileDescriptor * file,io::Printer * printer)403 int GeneratePackageModules(const FileDescriptor* file, io::Printer* printer) {
404   int levels = 0;
405   bool need_change_to_module = true;
406   std::string package_name;
407 
408   // Determine the name to use in either format:
409   //   proto package:         one.two.three
410   //   option ruby_package:   One::Two::Three
411   if (file->options().has_ruby_package()) {
412     package_name = file->options().ruby_package();
413 
414     // If :: is in the package use the Ruby formatted name as-is
415     //    -> A::B::C
416     // otherwise, use the dot seperator
417     //    -> A.B.C
418     if (package_name.find("::") != std::string::npos) {
419       need_change_to_module = false;
420     } else {
421       GOOGLE_LOG(WARNING) << "ruby_package option should be in the form of:"
422                           << " 'A::B::C' and not 'A.B.C'";
423     }
424   } else {
425     package_name = file->package();
426   }
427 
428   // Use the appropriate delimiter
429   string delimiter = need_change_to_module ? "." : "::";
430   int delimiter_size = need_change_to_module ? 1 : 2;
431 
432   // Extract each module name and indent
433   while (!package_name.empty()) {
434     size_t dot_index = package_name.find(delimiter);
435     string component;
436     if (dot_index == string::npos) {
437       component = package_name;
438       package_name = "";
439     } else {
440       component = package_name.substr(0, dot_index);
441       package_name = package_name.substr(dot_index + delimiter_size);
442     }
443     if (need_change_to_module) {
444       component = PackageToModule(component);
445     }
446     printer->Print(
447       "module $name$\n",
448       "name", component);
449     printer->Indent();
450     levels++;
451   }
452   return levels;
453 }
454 
EndPackageModules(int levels,io::Printer * printer)455 void EndPackageModules(int levels, io::Printer* printer) {
456   while (levels > 0) {
457     levels--;
458     printer->Outdent();
459     printer->Print(
460       "end\n");
461   }
462 }
463 
UsesTypeFromFile(const Descriptor * message,const FileDescriptor * file,string * error)464 bool UsesTypeFromFile(const Descriptor* message, const FileDescriptor* file,
465                       string* error) {
466   for (int i = 0; i < message->field_count(); i++) {
467     const FieldDescriptor* field = message->field(i);
468     if ((field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
469          field->message_type()->file() == file) ||
470         (field->type() == FieldDescriptor::TYPE_ENUM &&
471          field->enum_type()->file() == file)) {
472       *error = "proto3 message field " + field->full_name() + " in file " +
473                file->name() + " has a dependency on a type from proto2 file " +
474                file->name() +
475                ".  Ruby doesn't support proto2 yet, so we must fail.";
476       return true;
477     }
478   }
479 
480   for (int i = 0; i < message->nested_type_count(); i++) {
481     if (UsesTypeFromFile(message->nested_type(i), file, error)) {
482       return true;
483     }
484   }
485 
486   return false;
487 }
488 
489 // Ruby doesn't currently support proto2.  This causes a failure even for proto3
490 // files that import proto2.  But in some cases, the proto2 file is only being
491 // imported to extend another proto2 message.  The prime example is declaring
492 // custom options by extending FileOptions/FieldOptions/etc.
493 //
494 // If the proto3 messages don't have any proto2 submessages, it is safe to omit
495 // the dependency completely.  Users won't be able to use any proto2 extensions,
496 // but they already couldn't because proto2 messages aren't supported.
497 //
498 // If/when we add proto2 support, we should remove this.
MaybeEmitDependency(const FileDescriptor * import,const FileDescriptor * from,io::Printer * printer,string * error)499 bool MaybeEmitDependency(const FileDescriptor* import,
500                          const FileDescriptor* from,
501                          io::Printer* printer,
502                          string* error) {
503   if (from->syntax() == FileDescriptor::SYNTAX_PROTO3 &&
504       import->syntax() == FileDescriptor::SYNTAX_PROTO2) {
505     for (int i = 0; i < from->message_type_count(); i++) {
506       if (UsesTypeFromFile(from->message_type(i), import, error)) {
507         // Error text was already set by UsesTypeFromFile().
508         return false;
509       }
510     }
511 
512     // Ok to omit this proto2 dependency -- so we won't print anything.
513     GOOGLE_LOG(WARNING) << "Omitting proto2 dependency '" << import->name()
514                         << "' from proto3 output file '"
515                         << GetOutputFilename(from->name())
516                         << "' because we don't support proto2 and no proto2 "
517                            "types from that file are being used.";
518     return true;
519   } else {
520     printer->Print(
521       "require '$name$'\n", "name", GetRequireName(import->name()));
522     return true;
523   }
524 }
525 
GenerateFile(const FileDescriptor * file,io::Printer * printer,string * error)526 bool GenerateFile(const FileDescriptor* file, io::Printer* printer,
527                   string* error) {
528   printer->Print(
529     "# Generated by the protocol buffer compiler.  DO NOT EDIT!\n"
530     "# source: $filename$\n"
531     "\n",
532     "filename", file->name());
533 
534   printer->Print(
535     "require 'google/protobuf'\n\n");
536 
537   for (int i = 0; i < file->dependency_count(); i++) {
538     if (!MaybeEmitDependency(file->dependency(i), file, printer, error)) {
539       return false;
540     }
541   }
542 
543   // TODO: Remove this when ruby supports extensions for proto2 syntax.
544   if (file->syntax() == FileDescriptor::SYNTAX_PROTO2 &&
545       file->extension_count() > 0) {
546     *error = "Extensions are not yet supported for proto2 .proto files.";
547     return false;
548   }
549 
550   printer->Print("Google::Protobuf::DescriptorPool.generated_pool.build do\n");
551   printer->Indent();
552   printer->Print("add_file(\"$filename$\", :syntax => :$syntax$) do\n",
553 		 "filename", file->name(), "syntax",
554 		 StringifySyntax(file->syntax()));
555   printer->Indent();
556   for (int i = 0; i < file->message_type_count(); i++) {
557     if (!GenerateMessage(file->message_type(i), printer, error)) {
558       return false;
559     }
560   }
561   for (int i = 0; i < file->enum_type_count(); i++) {
562     GenerateEnum(file->enum_type(i), printer);
563   }
564   printer->Outdent();
565   printer->Print("end\n");
566   printer->Outdent();
567   printer->Print(
568     "end\n\n");
569 
570   int levels = GeneratePackageModules(file, printer);
571   for (int i = 0; i < file->message_type_count(); i++) {
572     GenerateMessageAssignment("", file->message_type(i), printer);
573   }
574   for (int i = 0; i < file->enum_type_count(); i++) {
575     GenerateEnumAssignment("", file->enum_type(i), printer);
576   }
577   EndPackageModules(levels, printer);
578   return true;
579 }
580 
Generate(const FileDescriptor * file,const string & parameter,GeneratorContext * generator_context,string * error) const581 bool Generator::Generate(
582     const FileDescriptor* file,
583     const string& parameter,
584     GeneratorContext* generator_context,
585     string* error) const {
586 
587   if (file->syntax() != FileDescriptor::SYNTAX_PROTO3 &&
588       file->syntax() != FileDescriptor::SYNTAX_PROTO2) {
589     *error = "Invalid or unsupported proto syntax";
590     return false;
591   }
592 
593   std::unique_ptr<io::ZeroCopyOutputStream> output(
594       generator_context->Open(GetOutputFilename(file->name())));
595   io::Printer printer(output.get(), '$');
596 
597   return GenerateFile(file, &printer, error);
598 }
599 
600 }  // namespace ruby
601 }  // namespace compiler
602 }  // namespace protobuf
603 }  // namespace google
604