123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272 |
- #!/usr/bin/env python3
- # Copyright 2016 The Chromium Authors. All rights reserved.
- # Use of this source code is governed by a BSD-style license that can be
- # found in the LICENSE file.
- """protoc plugin to create C++ reader/writer for JSON-encoded protobufs
- The reader/writer use Chrome's base::Values.
- """
- import os
- import sys
- from util import plugin_protos, types, writer
- class CppConverterWriter(writer.CodeWriter):
- def WriteProtoFile(self, proto_file, output_dir):
- err = proto_file.CheckSupported()
- if err:
- self.AddError(err)
- return
- self.WriteCStyleHeader()
- self.Output('#include "{output_dir}{generated_pb_h}"',
- output_dir=output_dir + '/' if output_dir else '',
- generated_pb_h=proto_file.CppBaseHeader())
- self.Output('')
- # import is not supported
- assert [] == proto_file.GetDependencies()
- self.Output('// base dependencies')
- self.Output('#include "base/values.h"')
- self.Output('')
- self.Output('#include <memory>')
- self.Output('#include <string>')
- self.Output('#include <utility>')
- self.Output('')
- namespaces = proto_file.ProtoNamespaces() + ['json']
- for name in namespaces:
- self.Output('namespace {name} {{', name=name)
- self.IncreaseIndent()
- for message in proto_file.GetMessages():
- self.WriteMessage(message)
- # Nothing to do for enums
- for name in namespaces:
- self.DecreaseIndent()
- self.Output('}}')
- def WriteMessage(self, message):
- self.Output('class {class_name} {{',
- class_name=message.CppConverterClassName())
- self.Output(' public:')
- with self.AddIndent():
- for nested_class in message.GetMessages():
- self.WriteMessage(nested_class)
- generated_class_name = message.QualifiedTypes().cpp_base
- # Nothing to write for enums.
- self.Output(
- 'static bool ReadFromValue(const base::Value& dict, {generated_class_name}* message) {{\n'
- ' if (!dict.is_dict()) goto error;\n'
- '',
- generated_class_name=generated_class_name)
- with self.AddIndent():
- for field_proto in message.GetFields():
- self.WriteFieldRead(field_proto)
- self.Output(
- ' return true;\n'
- '\n'
- 'error:\n'
- ' return false;\n'
- '}}\n'
- '\n'
- 'static base::Value WriteToValue(const {generated_class_name}& message) {{\n'
- ' base::Value dict(base::Value::Type::DICTIONARY);\n'
- '',
- generated_class_name=generated_class_name)
- with self.AddIndent():
- for field_proto in message.GetFields():
- self.FieldWriteToValue(field_proto)
- self.Output(
- ' return dict;\n'
- '',
- generated_class_name=generated_class_name)
- self.Output('}}')
- self.Output('}};')
- self.Output('')
- def FieldWriteToValue(self, field):
- if field.IsRepeated():
- self.Output('{{')
- else:
- self.Output('if (message.has_{field_name}()) {{\n', field_name=field.name)
- with self.AddIndent():
- if field.IsRepeated():
- self.RepeatedMemberFieldWriteToValue(field)
- else:
- self.OptionalMemberFieldWriteToValue(field)
- self.Output('}}')
- def RepeatedMemberFieldWriteToValue(self, field):
- if field.IsClassType():
- self.Output(
- 'const auto& repeated_field = message.{field_name}();\n'
- 'base::Value::List field_list;\n'
- 'field_list.reserve(repeated_field.size());\n'
- 'for (const auto& element : repeated_field) {{\n'
- ' field_list.Append(\n'
- ' {inner_class_converter}::WriteToValue(element));\n'
- '}}\n'
- 'dict.SetKey("{field_number}",\n'
- ' base::Value(std::move(field_list)));\n',
- field_number=field.JavascriptIndex(),
- field_name=field.name,
- inner_class_converter=field.CppConverterType()
- )
- else:
- self.Output(
- 'const auto& repeated_field = message.{field_name}();\n'
- 'base::Value::List field_list;\n'
- 'field_list.reserve(repeated_field.size());\n'
- 'for (const auto& element : repeated_field) {{\n'
- ' field_list.Append(element);\n'
- '}}\n'
- 'dict.SetKey("{field_number}",\n'
- ' base::Value(std::move(field_list)));\n',
- field_number=field.JavascriptIndex(),
- field_name=field.name
- )
- def OptionalMemberFieldWriteToValue(self, field):
- if field.IsClassType():
- self.Output(
- 'dict.SetKey("{field_number}",\n'
- ' {inner_class_converter}::WriteToValue(\n'
- ' message.{field_name}()));\n',
- field_number=field.JavascriptIndex(),
- field_name=field.name,
- inner_class_converter=field.CppConverterType()
- )
- else:
- self.Output(
- 'dict.Set{value_type}Key("{field_number}", message.{field_name}());\n',
- field_number=field.JavascriptIndex(),
- field_name=field.name,
- value_type=field.CppValueType()
- )
- def WriteFieldRead(self, field):
- self.Output('if (const auto* value = dict.FindKey("{field_number}")) {{',
- field_number=field.JavascriptIndex())
- with self.AddIndent():
- if field.IsRepeated():
- self.RepeatedMemberFieldRead(field)
- else:
- self.OptionalMemberFieldRead(field)
- self.Output('}}')
- def RepeatedMemberFieldRead(self, field):
- self.Output(
- 'if (!value->is_list()) {{\n'
- ' goto error;\n'
- '}}\n'
- 'for (const auto& element : value->GetListDeprecated()) {{\n'
- )
- with self.AddIndent():
- if field.IsClassType():
- self.Output(
- 'if (!{inner_class_parser}::ReadFromValue(element, message->add_{field_name}())) {{\n'
- ' goto error;\n'
- '}}\n',
- field_name=field.name,
- inner_class_parser=field.CppConverterType()
- )
- else:
- self.Output(
- 'if (!{predicate}) {{\n'
- ' goto error;\n'
- '}}\n'
- 'message->add_{field_name}(element.Get{value_type}());\n',
- field_name=field.name,
- value_type=field.CppValueType(),
- predicate=field.CppValuePredicate('element')
- )
- self.Output('}}\n')
- def OptionalMemberFieldRead(self, field):
- if field.IsClassType():
- self.Output(
- 'if (!{inner_class_parser}::ReadFromValue(*value, message->mutable_{field_name}())) {{\n'
- ' goto error;\n'
- '}}\n',
- field_number=field.JavascriptIndex(),
- field_name=field.name,
- inner_class_parser=field.CppConverterType()
- )
- else:
- self.Output(
- 'if (!{predicate}) {{\n'
- ' goto error;\n'
- '}}\n'
- 'message->set_{field_name}(value->Get{value_type}());\n',
- field_name=field.name,
- value_type=field.CppValueType(),
- predicate=field.CppValuePredicate('(*value)')
- )
- def Indented(s, indent=2):
- return '\n'.join((' ' * indent) + p for p in s.rstrip('\n').split('\n'))
- def SetBinaryStdio():
- import platform
- if platform.system() == 'Windows':
- import msvcrt
- msvcrt.setmode(sys.stdin.fileno(), os.O_BINARY)
- msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
- def ReadRequestFromStdin():
- stream = sys.stdin if sys.version_info[0] < 3 else sys.stdin.buffer
- data = stream.read()
- return plugin_protos.PluginRequestFromString(data)
- def main():
- SetBinaryStdio()
- request = ReadRequestFromStdin()
- response = plugin_protos.PluginResponse()
- output_dir = request.GetArgs().get('output_dir', '')
- for proto_file in request.GetAllFiles():
- types.RegisterProtoFile(proto_file)
- cppwriter = CppConverterWriter()
- cppwriter.WriteProtoFile(proto_file, output_dir)
- converter_filename = proto_file.CppConverterFilename()
- if output_dir:
- converter_filename = os.path.join(output_dir,
- os.path.split(converter_filename)[1])
- response.AddFileWithContent(converter_filename, cppwriter.GetValue())
- if cppwriter.GetErrors():
- response.AddError('\n'.join(cppwriter.GetErrors()))
- response.WriteToStdout()
- if __name__ == '__main__':
- main()
|