ruby_generator.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512
  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. #include <sstream>
  31. #include <google/protobuf/compiler/code_generator.h>
  32. #include <google/protobuf/compiler/plugin.h>
  33. #include <google/protobuf/descriptor.h>
  34. #include <google/protobuf/descriptor.pb.h>
  35. #include <google/protobuf/io/printer.h>
  36. #include <google/protobuf/io/zero_copy_stream.h>
  37. #include <google/protobuf/compiler/ruby/ruby_generator.h>
  38. namespace google {
  39. namespace protobuf {
  40. namespace compiler {
  41. namespace ruby {
  42. // Forward decls.
  43. std::string IntToString(int32 value);
  44. std::string GetRequireName(const std::string& proto_file);
  45. std::string LabelForField(google::protobuf::FieldDescriptor* field);
  46. std::string TypeName(google::protobuf::FieldDescriptor* field);
  47. void GenerateMessage(const google::protobuf::Descriptor* message,
  48. google::protobuf::io::Printer* printer);
  49. void GenerateEnum(const google::protobuf::EnumDescriptor* en,
  50. google::protobuf::io::Printer* printer);
  51. void GenerateMessageAssignment(
  52. const std::string& prefix,
  53. const google::protobuf::Descriptor* message,
  54. google::protobuf::io::Printer* printer);
  55. void GenerateEnumAssignment(
  56. const std::string& prefix,
  57. const google::protobuf::EnumDescriptor* en,
  58. google::protobuf::io::Printer* printer);
  59. std::string IntToString(int32 value) {
  60. std::ostringstream os;
  61. os << value;
  62. return os.str();
  63. }
  64. std::string GetRequireName(const std::string& proto_file) {
  65. int lastindex = proto_file.find_last_of(".");
  66. return proto_file.substr(0, lastindex) + "_pb";
  67. }
  68. std::string GetOutputFilename(const std::string& proto_file) {
  69. return GetRequireName(proto_file) + ".rb";
  70. }
  71. std::string LabelForField(const google::protobuf::FieldDescriptor* field) {
  72. switch (field->label()) {
  73. case FieldDescriptor::LABEL_OPTIONAL: return "optional";
  74. case FieldDescriptor::LABEL_REQUIRED: return "required";
  75. case FieldDescriptor::LABEL_REPEATED: return "repeated";
  76. default: assert(false); return "";
  77. }
  78. }
  79. std::string TypeName(const google::protobuf::FieldDescriptor* field) {
  80. switch (field->type()) {
  81. case FieldDescriptor::TYPE_INT32: return "int32";
  82. case FieldDescriptor::TYPE_INT64: return "int64";
  83. case FieldDescriptor::TYPE_UINT32: return "uint32";
  84. case FieldDescriptor::TYPE_UINT64: return "uint64";
  85. case FieldDescriptor::TYPE_SINT32: return "sint32";
  86. case FieldDescriptor::TYPE_SINT64: return "sint64";
  87. case FieldDescriptor::TYPE_FIXED32: return "fixed32";
  88. case FieldDescriptor::TYPE_FIXED64: return "fixed64";
  89. case FieldDescriptor::TYPE_SFIXED32: return "sfixed32";
  90. case FieldDescriptor::TYPE_SFIXED64: return "sfixed64";
  91. case FieldDescriptor::TYPE_DOUBLE: return "double";
  92. case FieldDescriptor::TYPE_FLOAT: return "float";
  93. case FieldDescriptor::TYPE_BOOL: return "bool";
  94. case FieldDescriptor::TYPE_ENUM: return "enum";
  95. case FieldDescriptor::TYPE_STRING: return "string";
  96. case FieldDescriptor::TYPE_BYTES: return "bytes";
  97. case FieldDescriptor::TYPE_MESSAGE: return "message";
  98. case FieldDescriptor::TYPE_GROUP: return "group";
  99. default: assert(false); return "";
  100. }
  101. }
  102. void GenerateField(const google::protobuf::FieldDescriptor* field,
  103. google::protobuf::io::Printer* printer) {
  104. if (field->is_map()) {
  105. const FieldDescriptor* key_field =
  106. field->message_type()->FindFieldByNumber(1);
  107. const FieldDescriptor* value_field =
  108. field->message_type()->FindFieldByNumber(2);
  109. printer->Print(
  110. "map :$name$, :$key_type$, :$value_type$, $number$",
  111. "name", field->name(),
  112. "key_type", TypeName(key_field),
  113. "value_type", TypeName(value_field),
  114. "number", IntToString(field->number()));
  115. if (value_field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
  116. printer->Print(
  117. ", \"$subtype$\"\n",
  118. "subtype", value_field->message_type()->full_name());
  119. } else if (value_field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
  120. printer->Print(
  121. ", \"$subtype$\"\n",
  122. "subtype", value_field->enum_type()->full_name());
  123. } else {
  124. printer->Print("\n");
  125. }
  126. } else {
  127. printer->Print(
  128. "$label$ :$name$, ",
  129. "label", LabelForField(field),
  130. "name", field->name());
  131. printer->Print(
  132. ":$type$, $number$",
  133. "type", TypeName(field),
  134. "number", IntToString(field->number()));
  135. if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
  136. printer->Print(
  137. ", \"$subtype$\"\n",
  138. "subtype", field->message_type()->full_name());
  139. } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
  140. printer->Print(
  141. ", \"$subtype$\"\n",
  142. "subtype", field->enum_type()->full_name());
  143. } else {
  144. printer->Print("\n");
  145. }
  146. }
  147. }
  148. void GenerateOneof(const google::protobuf::OneofDescriptor* oneof,
  149. google::protobuf::io::Printer* printer) {
  150. printer->Print(
  151. "oneof :$name$ do\n",
  152. "name", oneof->name());
  153. printer->Indent();
  154. for (int i = 0; i < oneof->field_count(); i++) {
  155. const FieldDescriptor* field = oneof->field(i);
  156. GenerateField(field, printer);
  157. }
  158. printer->Outdent();
  159. printer->Print("end\n");
  160. }
  161. void GenerateMessage(const google::protobuf::Descriptor* message,
  162. google::protobuf::io::Printer* printer) {
  163. // Don't generate MapEntry messages -- we use the Ruby extension's native
  164. // support for map fields instead.
  165. if (message->options().map_entry()) {
  166. return;
  167. }
  168. printer->Print(
  169. "add_message \"$name$\" do\n",
  170. "name", message->full_name());
  171. printer->Indent();
  172. for (int i = 0; i < message->field_count(); i++) {
  173. const FieldDescriptor* field = message->field(i);
  174. if (!field->containing_oneof()) {
  175. GenerateField(field, printer);
  176. }
  177. }
  178. for (int i = 0; i < message->oneof_decl_count(); i++) {
  179. const OneofDescriptor* oneof = message->oneof_decl(i);
  180. GenerateOneof(oneof, printer);
  181. }
  182. printer->Outdent();
  183. printer->Print("end\n");
  184. for (int i = 0; i < message->nested_type_count(); i++) {
  185. GenerateMessage(message->nested_type(i), printer);
  186. }
  187. for (int i = 0; i < message->enum_type_count(); i++) {
  188. GenerateEnum(message->enum_type(i), printer);
  189. }
  190. }
  191. void GenerateEnum(const google::protobuf::EnumDescriptor* en,
  192. google::protobuf::io::Printer* printer) {
  193. printer->Print(
  194. "add_enum \"$name$\" do\n",
  195. "name", en->full_name());
  196. printer->Indent();
  197. for (int i = 0; i < en->value_count(); i++) {
  198. const EnumValueDescriptor* value = en->value(i);
  199. printer->Print(
  200. "value :$name$, $number$\n",
  201. "name", value->name(),
  202. "number", IntToString(value->number()));
  203. }
  204. printer->Outdent();
  205. printer->Print(
  206. "end\n");
  207. }
  208. // Locale-agnostic utility functions.
  209. bool IsLower(char ch) { return ch >= 'a' && ch <= 'z'; }
  210. bool IsUpper(char ch) { return ch >= 'A' && ch <= 'Z'; }
  211. bool IsAlpha(char ch) { return IsLower(ch) || IsUpper(ch); }
  212. char ToUpper(char ch) { return IsLower(ch) ? (ch - 'a' + 'A') : ch; }
  213. // Package names in protobuf are snake_case by convention, but Ruby module
  214. // names must be PascalCased.
  215. //
  216. // foo_bar_baz -> FooBarBaz
  217. std::string PackageToModule(const std::string& name) {
  218. bool next_upper = true;
  219. std::string result;
  220. result.reserve(name.size());
  221. for (int i = 0; i < name.size(); i++) {
  222. if (name[i] == '_') {
  223. next_upper = true;
  224. } else {
  225. if (next_upper) {
  226. result.push_back(ToUpper(name[i]));
  227. } else {
  228. result.push_back(name[i]);
  229. }
  230. next_upper = false;
  231. }
  232. }
  233. return result;
  234. }
  235. // Class and enum names in protobuf should be PascalCased by convention, but
  236. // since there is nothing enforcing this we need to ensure that they are valid
  237. // Ruby constants. That mainly means making sure that the first character is
  238. // an upper-case letter.
  239. std::string RubifyConstant(const std::string& name) {
  240. std::string ret = name;
  241. if (!ret.empty()) {
  242. if (IsLower(ret[0])) {
  243. // If it starts with a lowercase letter, capitalize it.
  244. ret[0] = ToUpper(ret[0]);
  245. } else if (!IsAlpha(ret[0])) {
  246. // Otherwise (e.g. if it begins with an underscore), we need to come up
  247. // with some prefix that starts with a capital letter. We could be smarter
  248. // here, e.g. try to strip leading underscores, but this may cause other
  249. // problems if the user really intended the name. So let's just prepend a
  250. // well-known suffix.
  251. ret = "PB_" + ret;
  252. }
  253. }
  254. return ret;
  255. }
  256. void GenerateMessageAssignment(
  257. const std::string& prefix,
  258. const google::protobuf::Descriptor* message,
  259. google::protobuf::io::Printer* printer) {
  260. // Don't generate MapEntry messages -- we use the Ruby extension's native
  261. // support for map fields instead.
  262. if (message->options().map_entry()) {
  263. return;
  264. }
  265. printer->Print(
  266. "$prefix$$name$ = ",
  267. "prefix", prefix,
  268. "name", RubifyConstant(message->name()));
  269. printer->Print(
  270. "Google::Protobuf::DescriptorPool.generated_pool."
  271. "lookup(\"$full_name$\").msgclass\n",
  272. "full_name", message->full_name());
  273. std::string nested_prefix = prefix + message->name() + "::";
  274. for (int i = 0; i < message->nested_type_count(); i++) {
  275. GenerateMessageAssignment(nested_prefix, message->nested_type(i), printer);
  276. }
  277. for (int i = 0; i < message->enum_type_count(); i++) {
  278. GenerateEnumAssignment(nested_prefix, message->enum_type(i), printer);
  279. }
  280. }
  281. void GenerateEnumAssignment(
  282. const std::string& prefix,
  283. const google::protobuf::EnumDescriptor* en,
  284. google::protobuf::io::Printer* printer) {
  285. printer->Print(
  286. "$prefix$$name$ = ",
  287. "prefix", prefix,
  288. "name", RubifyConstant(en->name()));
  289. printer->Print(
  290. "Google::Protobuf::DescriptorPool.generated_pool."
  291. "lookup(\"$full_name$\").enummodule\n",
  292. "full_name", en->full_name());
  293. }
  294. int GeneratePackageModules(
  295. const FileDescriptor* file,
  296. google::protobuf::io::Printer* printer) {
  297. int levels = 0;
  298. bool need_change_to_module;
  299. std::string package_name;
  300. if (file->options().has_ruby_package()) {
  301. package_name = file->options().ruby_package();
  302. need_change_to_module = false;
  303. } else {
  304. package_name = file->package();
  305. need_change_to_module = true;
  306. }
  307. while (!package_name.empty()) {
  308. size_t dot_index = package_name.find(".");
  309. string component;
  310. if (dot_index == string::npos) {
  311. component = package_name;
  312. package_name = "";
  313. } else {
  314. component = package_name.substr(0, dot_index);
  315. package_name = package_name.substr(dot_index + 1);
  316. }
  317. if (need_change_to_module) {
  318. component = PackageToModule(component);
  319. }
  320. printer->Print(
  321. "module $name$\n",
  322. "name", component);
  323. printer->Indent();
  324. levels++;
  325. }
  326. return levels;
  327. }
  328. void EndPackageModules(
  329. int levels,
  330. google::protobuf::io::Printer* printer) {
  331. while (levels > 0) {
  332. levels--;
  333. printer->Outdent();
  334. printer->Print(
  335. "end\n");
  336. }
  337. }
  338. bool UsesTypeFromFile(const Descriptor* message, const FileDescriptor* file,
  339. string* error) {
  340. for (int i = 0; i < message->field_count(); i++) {
  341. const FieldDescriptor* field = message->field(i);
  342. if ((field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
  343. field->message_type()->file() == file) ||
  344. (field->type() == FieldDescriptor::TYPE_ENUM &&
  345. field->enum_type()->file() == file)) {
  346. *error = "proto3 message field " + field->full_name() + " in file " +
  347. file->name() + " has a dependency on a type from proto2 file " +
  348. file->name() +
  349. ". Ruby doesn't support proto2 yet, so we must fail.";
  350. return true;
  351. }
  352. }
  353. for (int i = 0; i < message->nested_type_count(); i++) {
  354. if (UsesTypeFromFile(message->nested_type(i), file, error)) {
  355. return true;
  356. }
  357. }
  358. return false;
  359. }
  360. // Ruby doesn't currently support proto2. This causes a failure even for proto3
  361. // files that import proto2. But in some cases, the proto2 file is only being
  362. // imported to extend another proto2 message. The prime example is declaring
  363. // custom options by extending FileOptions/FieldOptions/etc.
  364. //
  365. // If the proto3 messages don't have any proto2 submessages, it is safe to omit
  366. // the dependency completely. Users won't be able to use any proto2 extensions,
  367. // but they already couldn't because proto2 messages aren't supported.
  368. //
  369. // If/when we add proto2 support, we should remove this.
  370. bool MaybeEmitDependency(const FileDescriptor* import,
  371. const FileDescriptor* from,
  372. io::Printer* printer,
  373. string* error) {
  374. if (import->syntax() == FileDescriptor::SYNTAX_PROTO2) {
  375. for (int i = 0; i < from->message_type_count(); i++) {
  376. if (UsesTypeFromFile(from->message_type(i), import, error)) {
  377. // Error text was already set by UsesTypeFromFile().
  378. return false;
  379. }
  380. }
  381. // Ok to omit this proto2 dependency -- so we won't print anything.
  382. GOOGLE_LOG(WARNING) << "Omitting proto2 dependency '" << import->name()
  383. << "' from proto3 output file '"
  384. << GetOutputFilename(from->name())
  385. << "' because we don't support proto2 and no proto2 "
  386. "types from that file are being used.";
  387. return true;
  388. } else {
  389. printer->Print(
  390. "require '$name$'\n", "name", GetRequireName(import->name()));
  391. return true;
  392. }
  393. }
  394. bool GenerateFile(const FileDescriptor* file, io::Printer* printer,
  395. string* error) {
  396. printer->Print(
  397. "# Generated by the protocol buffer compiler. DO NOT EDIT!\n"
  398. "# source: $filename$\n"
  399. "\n",
  400. "filename", file->name());
  401. printer->Print(
  402. "require 'google/protobuf'\n\n");
  403. for (int i = 0; i < file->dependency_count(); i++) {
  404. if (!MaybeEmitDependency(file->dependency(i), file, printer, error)) {
  405. return false;
  406. }
  407. }
  408. printer->Print(
  409. "Google::Protobuf::DescriptorPool.generated_pool.build do\n");
  410. printer->Indent();
  411. for (int i = 0; i < file->message_type_count(); i++) {
  412. GenerateMessage(file->message_type(i), printer);
  413. }
  414. for (int i = 0; i < file->enum_type_count(); i++) {
  415. GenerateEnum(file->enum_type(i), printer);
  416. }
  417. printer->Outdent();
  418. printer->Print(
  419. "end\n\n");
  420. int levels = GeneratePackageModules(file, printer);
  421. for (int i = 0; i < file->message_type_count(); i++) {
  422. GenerateMessageAssignment("", file->message_type(i), printer);
  423. }
  424. for (int i = 0; i < file->enum_type_count(); i++) {
  425. GenerateEnumAssignment("", file->enum_type(i), printer);
  426. }
  427. EndPackageModules(levels, printer);
  428. return true;
  429. }
  430. bool Generator::Generate(
  431. const FileDescriptor* file,
  432. const string& parameter,
  433. GeneratorContext* generator_context,
  434. string* error) const {
  435. if (file->syntax() != FileDescriptor::SYNTAX_PROTO3) {
  436. *error =
  437. "Can only generate Ruby code for proto3 .proto files.\n"
  438. "Please add 'syntax = \"proto3\";' to the top of your .proto file.\n";
  439. return false;
  440. }
  441. std::unique_ptr<io::ZeroCopyOutputStream> output(
  442. generator_context->Open(GetOutputFilename(file->name())));
  443. io::Printer printer(output.get(), '$');
  444. return GenerateFile(file, &printer, error);
  445. }
  446. } // namespace ruby
  447. } // namespace compiler
  448. } // namespace protobuf
  449. } // namespace google