PageRenderTime 26ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/protobuf-2.5.0/src/google/protobuf/compiler/ruby/ruby_generator.cc

https://gitlab.com/f1ssi0n/sniffles
C++ | 401 lines | 311 code | 48 blank | 42 comment | 45 complexity | 6ab3aa96034c9cd20e1a3f3b69eb42b4 MD5 | raw file
  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. using google::protobuf::internal::scoped_ptr;
  39. namespace google {
  40. namespace protobuf {
  41. namespace compiler {
  42. namespace ruby {
  43. // Forward decls.
  44. std::string IntToString(int32 value);
  45. std::string StripDotProto(const std::string& proto_file);
  46. std::string LabelForField(google::protobuf::FieldDescriptor* field);
  47. std::string TypeName(google::protobuf::FieldDescriptor* field);
  48. void GenerateMessage(const google::protobuf::Descriptor* message,
  49. google::protobuf::io::Printer* printer);
  50. void GenerateEnum(const google::protobuf::EnumDescriptor* en,
  51. google::protobuf::io::Printer* printer);
  52. void GenerateMessageAssignment(
  53. const std::string& prefix,
  54. const google::protobuf::Descriptor* message,
  55. google::protobuf::io::Printer* printer);
  56. void GenerateEnumAssignment(
  57. const std::string& prefix,
  58. const google::protobuf::EnumDescriptor* en,
  59. google::protobuf::io::Printer* printer);
  60. std::string IntToString(int32 value) {
  61. std::ostringstream os;
  62. os << value;
  63. return os.str();
  64. }
  65. std::string StripDotProto(const std::string& proto_file) {
  66. int lastindex = proto_file.find_last_of(".");
  67. return proto_file.substr(0, lastindex);
  68. }
  69. std::string LabelForField(const google::protobuf::FieldDescriptor* field) {
  70. switch (field->label()) {
  71. case FieldDescriptor::LABEL_OPTIONAL: return "optional";
  72. case FieldDescriptor::LABEL_REQUIRED: return "required";
  73. case FieldDescriptor::LABEL_REPEATED: return "repeated";
  74. default: assert(false); return "";
  75. }
  76. }
  77. std::string TypeName(const google::protobuf::FieldDescriptor* field) {
  78. switch (field->type()) {
  79. case FieldDescriptor::TYPE_INT32: return "int32";
  80. case FieldDescriptor::TYPE_INT64: return "int64";
  81. case FieldDescriptor::TYPE_UINT32: return "uint32";
  82. case FieldDescriptor::TYPE_UINT64: return "uint64";
  83. case FieldDescriptor::TYPE_SINT32: return "sint32";
  84. case FieldDescriptor::TYPE_SINT64: return "sint64";
  85. case FieldDescriptor::TYPE_FIXED32: return "fixed32";
  86. case FieldDescriptor::TYPE_FIXED64: return "fixed64";
  87. case FieldDescriptor::TYPE_SFIXED32: return "sfixed32";
  88. case FieldDescriptor::TYPE_SFIXED64: return "sfixed64";
  89. case FieldDescriptor::TYPE_DOUBLE: return "double";
  90. case FieldDescriptor::TYPE_FLOAT: return "float";
  91. case FieldDescriptor::TYPE_BOOL: return "bool";
  92. case FieldDescriptor::TYPE_ENUM: return "enum";
  93. case FieldDescriptor::TYPE_STRING: return "string";
  94. case FieldDescriptor::TYPE_BYTES: return "bytes";
  95. case FieldDescriptor::TYPE_MESSAGE: return "message";
  96. case FieldDescriptor::TYPE_GROUP: return "group";
  97. default: assert(false); return "";
  98. }
  99. }
  100. void GenerateField(const google::protobuf::FieldDescriptor* field,
  101. google::protobuf::io::Printer* printer) {
  102. if (field->is_map()) {
  103. const FieldDescriptor* key_field =
  104. field->message_type()->FindFieldByNumber(1);
  105. const FieldDescriptor* value_field =
  106. field->message_type()->FindFieldByNumber(2);
  107. printer->Print(
  108. "map :$name$, :$key_type$, :$value_type$, $number$",
  109. "name", field->name(),
  110. "key_type", TypeName(key_field),
  111. "value_type", TypeName(value_field),
  112. "number", IntToString(field->number()));
  113. if (value_field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
  114. printer->Print(
  115. ", \"$subtype$\"\n",
  116. "subtype", value_field->message_type()->full_name());
  117. } else if (value_field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
  118. printer->Print(
  119. ", \"$subtype$\"\n",
  120. "subtype", value_field->enum_type()->full_name());
  121. } else {
  122. printer->Print("\n");
  123. }
  124. } else {
  125. printer->Print(
  126. "$label$ :$name$, ",
  127. "label", LabelForField(field),
  128. "name", field->name());
  129. printer->Print(
  130. ":$type$, $number$",
  131. "type", TypeName(field),
  132. "number", IntToString(field->number()));
  133. if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
  134. printer->Print(
  135. ", \"$subtype$\"\n",
  136. "subtype", field->message_type()->full_name());
  137. } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) {
  138. printer->Print(
  139. ", \"$subtype$\"\n",
  140. "subtype", field->enum_type()->full_name());
  141. } else {
  142. printer->Print("\n");
  143. }
  144. }
  145. }
  146. void GenerateOneof(const google::protobuf::OneofDescriptor* oneof,
  147. google::protobuf::io::Printer* printer) {
  148. printer->Print(
  149. "oneof :$name$ do\n",
  150. "name", oneof->name());
  151. printer->Indent();
  152. for (int i = 0; i < oneof->field_count(); i++) {
  153. const FieldDescriptor* field = oneof->field(i);
  154. GenerateField(field, printer);
  155. }
  156. printer->Outdent();
  157. printer->Print("end\n");
  158. }
  159. void GenerateMessage(const google::protobuf::Descriptor* message,
  160. google::protobuf::io::Printer* printer) {
  161. // Don't generate MapEntry messages -- we use the Ruby extension's native
  162. // support for map fields instead.
  163. if (message->options().map_entry()) {
  164. return;
  165. }
  166. printer->Print(
  167. "add_message \"$name$\" do\n",
  168. "name", message->full_name());
  169. printer->Indent();
  170. for (int i = 0; i < message->field_count(); i++) {
  171. const FieldDescriptor* field = message->field(i);
  172. if (!field->containing_oneof()) {
  173. GenerateField(field, printer);
  174. }
  175. }
  176. for (int i = 0; i < message->oneof_decl_count(); i++) {
  177. const OneofDescriptor* oneof = message->oneof_decl(i);
  178. GenerateOneof(oneof, printer);
  179. }
  180. printer->Outdent();
  181. printer->Print("end\n");
  182. for (int i = 0; i < message->nested_type_count(); i++) {
  183. GenerateMessage(message->nested_type(i), printer);
  184. }
  185. for (int i = 0; i < message->enum_type_count(); i++) {
  186. GenerateEnum(message->enum_type(i), printer);
  187. }
  188. }
  189. void GenerateEnum(const google::protobuf::EnumDescriptor* en,
  190. google::protobuf::io::Printer* printer) {
  191. printer->Print(
  192. "add_enum \"$name$\" do\n",
  193. "name", en->full_name());
  194. printer->Indent();
  195. for (int i = 0; i < en->value_count(); i++) {
  196. const EnumValueDescriptor* value = en->value(i);
  197. printer->Print(
  198. "value :$name$, $number$\n",
  199. "name", value->name(),
  200. "number", IntToString(value->number()));
  201. }
  202. printer->Outdent();
  203. printer->Print(
  204. "end\n");
  205. }
  206. // Module names, class names, and enum value names need to be Ruby constants,
  207. // which must start with a capital letter.
  208. std::string RubifyConstant(const std::string& name) {
  209. std::string ret = name;
  210. if (!ret.empty()) {
  211. if (ret[0] >= 'a' && ret[0] <= 'z') {
  212. // If it starts with a lowercase letter, capitalize it.
  213. ret[0] = ret[0] - 'a' + 'A';
  214. } else if (ret[0] < 'A' || ret[0] > 'Z') {
  215. // Otherwise (e.g. if it begins with an underscore), we need to come up
  216. // with some prefix that starts with a capital letter. We could be smarter
  217. // here, e.g. try to strip leading underscores, but this may cause other
  218. // problems if the user really intended the name. So let's just prepend a
  219. // well-known suffix.
  220. ret = "PB_" + ret;
  221. }
  222. }
  223. return ret;
  224. }
  225. void GenerateMessageAssignment(
  226. const std::string& prefix,
  227. const google::protobuf::Descriptor* message,
  228. google::protobuf::io::Printer* printer) {
  229. // Don't generate MapEntry messages -- we use the Ruby extension's native
  230. // support for map fields instead.
  231. if (message->options().map_entry()) {
  232. return;
  233. }
  234. printer->Print(
  235. "$prefix$$name$ = ",
  236. "prefix", prefix,
  237. "name", RubifyConstant(message->name()));
  238. printer->Print(
  239. "Google::Protobuf::DescriptorPool.generated_pool."
  240. "lookup(\"$full_name$\").msgclass\n",
  241. "full_name", message->full_name());
  242. std::string nested_prefix = prefix + message->name() + "::";
  243. for (int i = 0; i < message->nested_type_count(); i++) {
  244. GenerateMessageAssignment(nested_prefix, message->nested_type(i), printer);
  245. }
  246. for (int i = 0; i < message->enum_type_count(); i++) {
  247. GenerateEnumAssignment(nested_prefix, message->enum_type(i), printer);
  248. }
  249. }
  250. void GenerateEnumAssignment(
  251. const std::string& prefix,
  252. const google::protobuf::EnumDescriptor* en,
  253. google::protobuf::io::Printer* printer) {
  254. printer->Print(
  255. "$prefix$$name$ = ",
  256. "prefix", prefix,
  257. "name", RubifyConstant(en->name()));
  258. printer->Print(
  259. "Google::Protobuf::DescriptorPool.generated_pool."
  260. "lookup(\"$full_name$\").enummodule\n",
  261. "full_name", en->full_name());
  262. }
  263. int GeneratePackageModules(
  264. std::string package_name,
  265. google::protobuf::io::Printer* printer) {
  266. int levels = 0;
  267. while (!package_name.empty()) {
  268. size_t dot_index = package_name.find(".");
  269. string component;
  270. if (dot_index == string::npos) {
  271. component = package_name;
  272. package_name = "";
  273. } else {
  274. component = package_name.substr(0, dot_index);
  275. package_name = package_name.substr(dot_index + 1);
  276. }
  277. component = RubifyConstant(component);
  278. printer->Print(
  279. "module $name$\n",
  280. "name", component);
  281. printer->Indent();
  282. levels++;
  283. }
  284. return levels;
  285. }
  286. void EndPackageModules(
  287. int levels,
  288. google::protobuf::io::Printer* printer) {
  289. while (levels > 0) {
  290. levels--;
  291. printer->Outdent();
  292. printer->Print(
  293. "end\n");
  294. }
  295. }
  296. void GenerateFile(const google::protobuf::FileDescriptor* file,
  297. google::protobuf::io::Printer* printer) {
  298. printer->Print(
  299. "# Generated by the protocol buffer compiler. DO NOT EDIT!\n"
  300. "# source: $filename$\n"
  301. "\n",
  302. "filename", file->name());
  303. printer->Print(
  304. "require 'google/protobuf'\n\n");
  305. for (int i = 0; i < file->dependency_count(); i++) {
  306. const std::string& name = file->dependency(i)->name();
  307. printer->Print(
  308. "require '$name$'\n", "name", StripDotProto(name));
  309. }
  310. printer->Print(
  311. "Google::Protobuf::DescriptorPool.generated_pool.build do\n");
  312. printer->Indent();
  313. for (int i = 0; i < file->message_type_count(); i++) {
  314. GenerateMessage(file->message_type(i), printer);
  315. }
  316. for (int i = 0; i < file->enum_type_count(); i++) {
  317. GenerateEnum(file->enum_type(i), printer);
  318. }
  319. printer->Outdent();
  320. printer->Print(
  321. "end\n\n");
  322. int levels = GeneratePackageModules(file->package(), printer);
  323. for (int i = 0; i < file->message_type_count(); i++) {
  324. GenerateMessageAssignment("", file->message_type(i), printer);
  325. }
  326. for (int i = 0; i < file->enum_type_count(); i++) {
  327. GenerateEnumAssignment("", file->enum_type(i), printer);
  328. }
  329. EndPackageModules(levels, printer);
  330. }
  331. bool Generator::Generate(
  332. const FileDescriptor* file,
  333. const string& parameter,
  334. GeneratorContext* generator_context,
  335. string* error) const {
  336. if (file->syntax() != FileDescriptor::SYNTAX_PROTO3) {
  337. *error =
  338. "Can only generate Ruby code for proto3 .proto files.\n"
  339. "Please add 'syntax = \"proto3\";' to the top of your .proto file.\n";
  340. return false;
  341. }
  342. std::string filename =
  343. StripDotProto(file->name()) + ".rb";
  344. scoped_ptr<io::ZeroCopyOutputStream> output(
  345. generator_context->Open(filename));
  346. io::Printer printer(output.get(), '$');
  347. GenerateFile(file, &printer);
  348. return true;
  349. }
  350. } // namespace ruby
  351. } // namespace compiler
  352. } // namespace protobuf
  353. } // namespace google