json_values_converter.py 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272
  1. #!/usr/bin/env python3
  2. # Copyright 2016 The Chromium Authors. All rights reserved.
  3. # Use of this source code is governed by a BSD-style license that can be
  4. # found in the LICENSE file.
  5. """protoc plugin to create C++ reader/writer for JSON-encoded protobufs
  6. The reader/writer use Chrome's base::Values.
  7. """
  8. import os
  9. import sys
  10. from util import plugin_protos, types, writer
  11. class CppConverterWriter(writer.CodeWriter):
  12. def WriteProtoFile(self, proto_file, output_dir):
  13. err = proto_file.CheckSupported()
  14. if err:
  15. self.AddError(err)
  16. return
  17. self.WriteCStyleHeader()
  18. self.Output('#include "{output_dir}{generated_pb_h}"',
  19. output_dir=output_dir + '/' if output_dir else '',
  20. generated_pb_h=proto_file.CppBaseHeader())
  21. self.Output('')
  22. # import is not supported
  23. assert [] == proto_file.GetDependencies()
  24. self.Output('// base dependencies')
  25. self.Output('#include "base/values.h"')
  26. self.Output('')
  27. self.Output('#include <memory>')
  28. self.Output('#include <string>')
  29. self.Output('#include <utility>')
  30. self.Output('')
  31. namespaces = proto_file.ProtoNamespaces() + ['json']
  32. for name in namespaces:
  33. self.Output('namespace {name} {{', name=name)
  34. self.IncreaseIndent()
  35. for message in proto_file.GetMessages():
  36. self.WriteMessage(message)
  37. # Nothing to do for enums
  38. for name in namespaces:
  39. self.DecreaseIndent()
  40. self.Output('}}')
  41. def WriteMessage(self, message):
  42. self.Output('class {class_name} {{',
  43. class_name=message.CppConverterClassName())
  44. self.Output(' public:')
  45. with self.AddIndent():
  46. for nested_class in message.GetMessages():
  47. self.WriteMessage(nested_class)
  48. generated_class_name = message.QualifiedTypes().cpp_base
  49. # Nothing to write for enums.
  50. self.Output(
  51. 'static bool ReadFromValue(const base::Value& dict, {generated_class_name}* message) {{\n'
  52. ' if (!dict.is_dict()) goto error;\n'
  53. '',
  54. generated_class_name=generated_class_name)
  55. with self.AddIndent():
  56. for field_proto in message.GetFields():
  57. self.WriteFieldRead(field_proto)
  58. self.Output(
  59. ' return true;\n'
  60. '\n'
  61. 'error:\n'
  62. ' return false;\n'
  63. '}}\n'
  64. '\n'
  65. 'static base::Value WriteToValue(const {generated_class_name}& message) {{\n'
  66. ' base::Value dict(base::Value::Type::DICTIONARY);\n'
  67. '',
  68. generated_class_name=generated_class_name)
  69. with self.AddIndent():
  70. for field_proto in message.GetFields():
  71. self.FieldWriteToValue(field_proto)
  72. self.Output(
  73. ' return dict;\n'
  74. '',
  75. generated_class_name=generated_class_name)
  76. self.Output('}}')
  77. self.Output('}};')
  78. self.Output('')
  79. def FieldWriteToValue(self, field):
  80. if field.IsRepeated():
  81. self.Output('{{')
  82. else:
  83. self.Output('if (message.has_{field_name}()) {{\n', field_name=field.name)
  84. with self.AddIndent():
  85. if field.IsRepeated():
  86. self.RepeatedMemberFieldWriteToValue(field)
  87. else:
  88. self.OptionalMemberFieldWriteToValue(field)
  89. self.Output('}}')
  90. def RepeatedMemberFieldWriteToValue(self, field):
  91. if field.IsClassType():
  92. self.Output(
  93. 'const auto& repeated_field = message.{field_name}();\n'
  94. 'base::Value::List field_list;\n'
  95. 'field_list.reserve(repeated_field.size());\n'
  96. 'for (const auto& element : repeated_field) {{\n'
  97. ' field_list.Append(\n'
  98. ' {inner_class_converter}::WriteToValue(element));\n'
  99. '}}\n'
  100. 'dict.SetKey("{field_number}",\n'
  101. ' base::Value(std::move(field_list)));\n',
  102. field_number=field.JavascriptIndex(),
  103. field_name=field.name,
  104. inner_class_converter=field.CppConverterType()
  105. )
  106. else:
  107. self.Output(
  108. 'const auto& repeated_field = message.{field_name}();\n'
  109. 'base::Value::List field_list;\n'
  110. 'field_list.reserve(repeated_field.size());\n'
  111. 'for (const auto& element : repeated_field) {{\n'
  112. ' field_list.Append(element);\n'
  113. '}}\n'
  114. 'dict.SetKey("{field_number}",\n'
  115. ' base::Value(std::move(field_list)));\n',
  116. field_number=field.JavascriptIndex(),
  117. field_name=field.name
  118. )
  119. def OptionalMemberFieldWriteToValue(self, field):
  120. if field.IsClassType():
  121. self.Output(
  122. 'dict.SetKey("{field_number}",\n'
  123. ' {inner_class_converter}::WriteToValue(\n'
  124. ' message.{field_name}()));\n',
  125. field_number=field.JavascriptIndex(),
  126. field_name=field.name,
  127. inner_class_converter=field.CppConverterType()
  128. )
  129. else:
  130. self.Output(
  131. 'dict.Set{value_type}Key("{field_number}", message.{field_name}());\n',
  132. field_number=field.JavascriptIndex(),
  133. field_name=field.name,
  134. value_type=field.CppValueType()
  135. )
  136. def WriteFieldRead(self, field):
  137. self.Output('if (const auto* value = dict.FindKey("{field_number}")) {{',
  138. field_number=field.JavascriptIndex())
  139. with self.AddIndent():
  140. if field.IsRepeated():
  141. self.RepeatedMemberFieldRead(field)
  142. else:
  143. self.OptionalMemberFieldRead(field)
  144. self.Output('}}')
  145. def RepeatedMemberFieldRead(self, field):
  146. self.Output(
  147. 'if (!value->is_list()) {{\n'
  148. ' goto error;\n'
  149. '}}\n'
  150. 'for (const auto& element : value->GetListDeprecated()) {{\n'
  151. )
  152. with self.AddIndent():
  153. if field.IsClassType():
  154. self.Output(
  155. 'if (!{inner_class_parser}::ReadFromValue(element, message->add_{field_name}())) {{\n'
  156. ' goto error;\n'
  157. '}}\n',
  158. field_name=field.name,
  159. inner_class_parser=field.CppConverterType()
  160. )
  161. else:
  162. self.Output(
  163. 'if (!{predicate}) {{\n'
  164. ' goto error;\n'
  165. '}}\n'
  166. 'message->add_{field_name}(element.Get{value_type}());\n',
  167. field_name=field.name,
  168. value_type=field.CppValueType(),
  169. predicate=field.CppValuePredicate('element')
  170. )
  171. self.Output('}}\n')
  172. def OptionalMemberFieldRead(self, field):
  173. if field.IsClassType():
  174. self.Output(
  175. 'if (!{inner_class_parser}::ReadFromValue(*value, message->mutable_{field_name}())) {{\n'
  176. ' goto error;\n'
  177. '}}\n',
  178. field_number=field.JavascriptIndex(),
  179. field_name=field.name,
  180. inner_class_parser=field.CppConverterType()
  181. )
  182. else:
  183. self.Output(
  184. 'if (!{predicate}) {{\n'
  185. ' goto error;\n'
  186. '}}\n'
  187. 'message->set_{field_name}(value->Get{value_type}());\n',
  188. field_name=field.name,
  189. value_type=field.CppValueType(),
  190. predicate=field.CppValuePredicate('(*value)')
  191. )
  192. def Indented(s, indent=2):
  193. return '\n'.join((' ' * indent) + p for p in s.rstrip('\n').split('\n'))
  194. def SetBinaryStdio():
  195. import platform
  196. if platform.system() == 'Windows':
  197. import msvcrt
  198. msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
  199. msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
  200. def ReadRequestFromStdin():
  201. stream = sys.stdin if sys.version_info[0] < 3 else sys.stdin.buffer
  202. data = stream.read()
  203. return plugin_protos.PluginRequestFromString(data)
  204. def main():
  205. SetBinaryStdio()
  206. request = ReadRequestFromStdin()
  207. response = plugin_protos.PluginResponse()
  208. output_dir = request.GetArgs().get('output_dir', '')
  209. for proto_file in request.GetAllFiles():
  210. types.RegisterProtoFile(proto_file)
  211. cppwriter = CppConverterWriter()
  212. cppwriter.WriteProtoFile(proto_file, output_dir)
  213. converter_filename = proto_file.CppConverterFilename()
  214. if output_dir:
  215. converter_filename = os.path.join(output_dir,
  216. os.path.split(converter_filename)[1])
  217. response.AddFileWithContent(converter_filename, cppwriter.GetValue())
  218. if cppwriter.GetErrors():
  219. response.AddError('\n'.join(cppwriter.GetErrors()))
  220. response.WriteToStdout()
  221. if __name__ == '__main__':
  222. main()