• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "src/tools/proto_merger/proto_file.h"
18 
19 #include <google/protobuf/descriptor.h>
20 #include <google/protobuf/descriptor.pb.h>
21 #include <google/protobuf/dynamic_message.h>
22 #include <google/protobuf/text_format.h>
23 
24 #include "perfetto/ext/base/string_utils.h"
25 
26 namespace perfetto {
27 namespace proto_merger {
28 namespace {
29 
30 const char* const
31     kTypeToName[google::protobuf::FieldDescriptor::Type::MAX_TYPE + 1] = {
32         "ERROR",  // 0 is reserved for errors
33 
34         "double",    // TYPE_DOUBLE
35         "float",     // TYPE_FLOAT
36         "int64",     // TYPE_INT64
37         "uint64",    // TYPE_UINT64
38         "int32",     // TYPE_INT32
39         "fixed64",   // TYPE_FIXED64
40         "fixed32",   // TYPE_FIXED32
41         "bool",      // TYPE_BOOL
42         "string",    // TYPE_STRING
43         "group",     // TYPE_GROUP
44         "message",   // TYPE_MESSAGE
45         "bytes",     // TYPE_BYTES
46         "uint32",    // TYPE_UINT32
47         "enum",      // TYPE_ENUM
48         "sfixed32",  // TYPE_SFIXED32
49         "sfixed64",  // TYPE_SFIXED64
50         "sint32",    // TYPE_SINT32
51         "sint64",    // TYPE_SINT64
52 };
53 
MinimizeType(const std::string & a,const std::string & b)54 std::optional<std::string> MinimizeType(const std::string& a,
55                                         const std::string& b) {
56   auto a_pieces = base::SplitString(a, ".");
57   auto b_pieces = base::SplitString(b, ".");
58 
59   size_t skip = 0;
60   for (size_t i = 0; i < std::min(a_pieces.size(), b_pieces.size()); ++i) {
61     if (a_pieces[i] != b_pieces[i])
62       return a.substr(skip);
63     skip += a_pieces[i].size() + 1;
64   }
65   return std::nullopt;
66 }
67 
SimpleFieldTypeFromDescriptor(const google::protobuf::Descriptor & parent,const google::protobuf::FieldDescriptor & desc,bool packageless_type)68 std::string SimpleFieldTypeFromDescriptor(
69     const google::protobuf::Descriptor& parent,
70     const google::protobuf::FieldDescriptor& desc,
71     bool packageless_type) {
72   switch (desc.type()) {
73     case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
74       if (packageless_type) {
75         return base::StripPrefix(
76             std::string(desc.message_type()->full_name()),
77             std::string(desc.message_type()->file()->package()) + ".");
78       } else {
79         return MinimizeType(std::string(desc.message_type()->full_name()),
80                             std::string(parent.full_name()))
81             .value_or(std::string(desc.message_type()->name()));
82       }
83     case google::protobuf::FieldDescriptor::TYPE_ENUM:
84       if (packageless_type) {
85         return base::StripPrefix(
86             std::string(desc.enum_type()->full_name()),
87             std::string(desc.enum_type()->file()->package()) + ".");
88       } else {
89         return MinimizeType(std::string(desc.enum_type()->full_name()),
90                             std::string(parent.full_name()))
91             .value_or(std::string(desc.enum_type()->name()));
92       }
93     default:
94       return kTypeToName[desc.type()];
95   }
96 }
97 
FieldTypeFromDescriptor(const google::protobuf::Descriptor & parent,const google::protobuf::FieldDescriptor & desc,bool packageless_type)98 std::string FieldTypeFromDescriptor(
99     const google::protobuf::Descriptor& parent,
100     const google::protobuf::FieldDescriptor& desc,
101     bool packageless_type) {
102   if (!desc.is_map())
103     return SimpleFieldTypeFromDescriptor(parent, desc, packageless_type);
104 
105   std::string field_type;
106   field_type += "map<";
107   field_type += FieldTypeFromDescriptor(parent, *desc.message_type()->field(0),
108                                         packageless_type);
109   field_type += ",";
110   field_type += FieldTypeFromDescriptor(parent, *desc.message_type()->field(1),
111                                         packageless_type);
112   field_type += ">";
113   return field_type;
114 }
115 
NormalizeOptionsMessage(const google::protobuf::DescriptorPool & pool,google::protobuf::DynamicMessageFactory * factory,const google::protobuf::Message & message)116 std::unique_ptr<google::protobuf::Message> NormalizeOptionsMessage(
117     const google::protobuf::DescriptorPool& pool,
118     google::protobuf::DynamicMessageFactory* factory,
119     const google::protobuf::Message& message) {
120   const auto* option_descriptor =
121       pool.FindMessageTypeByName(message.GetDescriptor()->full_name());
122   if (!option_descriptor)
123     return nullptr;
124 
125   std::unique_ptr<google::protobuf::Message> dynamic_options(
126       factory->GetPrototype(option_descriptor)->New());
127   PERFETTO_CHECK(dynamic_options->ParseFromString(message.SerializeAsString()));
128   return dynamic_options;
129 }
130 
OptionsFromMessage(const google::protobuf::DescriptorPool & pool,const google::protobuf::Message & raw_message)131 std::vector<ProtoFile::Option> OptionsFromMessage(
132     const google::protobuf::DescriptorPool& pool,
133     const google::protobuf::Message& raw_message) {
134   google::protobuf::DynamicMessageFactory factory;
135 
136   auto normalized = NormalizeOptionsMessage(pool, &factory, raw_message);
137   const auto* message = normalized ? normalized.get() : &raw_message;
138   const auto* reflection = message->GetReflection();
139 
140   std::vector<const google::protobuf::FieldDescriptor*> fields;
141   reflection->ListFields(*message, &fields);
142 
143   std::vector<ProtoFile::Option> options;
144   for (size_t i = 0; i < fields.size(); i++) {
145     int count = 1;
146     bool repeated = false;
147     if (fields[i]->is_repeated()) {
148       count = reflection->FieldSize(*message, fields[i]);
149       repeated = true;
150     }
151     for (int j = 0; j < count; j++) {
152       std::string name;
153       if (fields[i]->is_extension()) {
154         name = "(" + std::string(fields[i]->full_name()) + ")";
155       } else {
156         name = fields[i]->name();
157       }
158 
159       std::string fieldval;
160       if (fields[i]->cpp_type() ==
161           google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
162         std::string tmp;
163         google::protobuf::TextFormat::Printer printer;
164         printer.PrintFieldValueToString(*message, fields[i], repeated ? j : -1,
165                                         &tmp);
166         fieldval.append("{\n");
167         fieldval.append(tmp);
168         fieldval.append("}");
169       } else {
170         google::protobuf::TextFormat::PrintFieldValueToString(
171             *message, fields[i], repeated ? j : -1, &fieldval);
172       }
173       options.push_back(ProtoFile::Option{name, fieldval});
174     }
175   }
176   return options;
177 }
178 
179 template <typename Output, typename Descriptor>
InitFromDescriptor(const Descriptor & desc)180 Output InitFromDescriptor(const Descriptor& desc) {
181   google::protobuf::SourceLocation source_loc;
182   if (!desc.GetSourceLocation(&source_loc))
183     return {};
184 
185   Output out;
186   out.leading_comments = base::SplitString(source_loc.leading_comments, "\n");
187   out.trailing_comments = base::SplitString(source_loc.trailing_comments, "\n");
188   return out;
189 }
190 
FieldFromDescriptor(const google::protobuf::Descriptor & parent,const google::protobuf::FieldDescriptor & desc)191 ProtoFile::Field FieldFromDescriptor(
192     const google::protobuf::Descriptor& parent,
193     const google::protobuf::FieldDescriptor& desc) {
194   auto field = InitFromDescriptor<ProtoFile::Field>(desc);
195   field.is_repeated = desc.is_repeated();
196   field.packageless_type = FieldTypeFromDescriptor(parent, desc, true);
197   field.type = FieldTypeFromDescriptor(parent, desc, false);
198   field.name = desc.name();
199   field.number = desc.number();
200   field.options = OptionsFromMessage(*desc.file()->pool(), desc.options());
201 
202   // Protobuf editions: packed fields are no longer an option, but have the same
203   // syntax as far as writing the merged .proto file is concerned.
204   if (desc.is_packed()) {
205     field.options.push_back(
206         ProtoFile::Option{"features.repeated_field_encoding", "PACKED"});
207   }
208 
209   return field;
210 }
211 
EnumValueFromDescriptor(const google::protobuf::EnumValueDescriptor & desc)212 ProtoFile::Enum::Value EnumValueFromDescriptor(
213     const google::protobuf::EnumValueDescriptor& desc) {
214   auto value = InitFromDescriptor<ProtoFile::Enum::Value>(desc);
215   value.name = desc.name();
216   value.number = desc.number();
217   value.options = OptionsFromMessage(*desc.file()->pool(), desc.options());
218   return value;
219 }
220 
EnumFromDescriptor(const google::protobuf::EnumDescriptor & desc)221 ProtoFile::Enum EnumFromDescriptor(
222     const google::protobuf::EnumDescriptor& desc) {
223   auto en = InitFromDescriptor<ProtoFile::Enum>(desc);
224   en.name = desc.name();
225   for (int i = 0; i < desc.value_count(); ++i) {
226     en.values.emplace_back(EnumValueFromDescriptor(*desc.value(i)));
227   }
228   return en;
229 }
230 
OneOfFromDescriptor(const google::protobuf::Descriptor & parent,const google::protobuf::OneofDescriptor & desc)231 ProtoFile::Oneof OneOfFromDescriptor(
232     const google::protobuf::Descriptor& parent,
233     const google::protobuf::OneofDescriptor& desc) {
234   auto oneof = InitFromDescriptor<ProtoFile::Oneof>(desc);
235   oneof.name = desc.name();
236   for (int i = 0; i < desc.field_count(); ++i) {
237     oneof.fields.emplace_back(FieldFromDescriptor(parent, *desc.field(i)));
238   }
239   return oneof;
240 }
241 
MessageFromDescriptor(const google::protobuf::Descriptor & desc)242 ProtoFile::Message MessageFromDescriptor(
243     const google::protobuf::Descriptor& desc) {
244   auto message = InitFromDescriptor<ProtoFile::Message>(desc);
245   message.name = desc.name();
246   for (int i = 0; i < desc.enum_type_count(); ++i) {
247     message.enums.emplace_back(EnumFromDescriptor(*desc.enum_type(i)));
248   }
249   for (int i = 0; i < desc.nested_type_count(); ++i) {
250     message.nested_messages.emplace_back(
251         MessageFromDescriptor(*desc.nested_type(i)));
252   }
253   for (int i = 0; i < desc.oneof_decl_count(); ++i) {
254     message.oneofs.emplace_back(OneOfFromDescriptor(desc, *desc.oneof_decl(i)));
255   }
256   for (int i = 0; i < desc.field_count(); ++i) {
257     auto* field = desc.field(i);
258     if (field->containing_oneof())
259       continue;
260     message.fields.emplace_back(FieldFromDescriptor(desc, *field));
261   }
262   return message;
263 }
264 
265 }  // namespace
266 
ProtoFileFromDescriptor(std::string preamble,const google::protobuf::FileDescriptor & desc)267 ProtoFile ProtoFileFromDescriptor(
268     std::string preamble,
269     const google::protobuf::FileDescriptor& desc) {
270   ProtoFile file;
271   file.preamble = std::move(preamble);
272   for (int i = 0; i < desc.enum_type_count(); ++i) {
273     file.enums.push_back(EnumFromDescriptor(*desc.enum_type(i)));
274   }
275   for (int i = 0; i < desc.message_type_count(); ++i) {
276     file.messages.push_back(MessageFromDescriptor(*desc.message_type(i)));
277   }
278   return file;
279 }
280 
281 }  // namespace proto_merger
282 }  // namespace perfetto
283