• 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 
54 const char* const
55     kLabelToName[google::protobuf::FieldDescriptor::MAX_LABEL + 1] = {
56         "ERROR",  // 0 is reserved for errors
57 
58         "optional",  // LABEL_OPTIONAL
59         "required",  // LABEL_REQUIRED
60         "repeated",  // LABEL_REPEATED
61 };
62 
MinimizeType(const std::string & a,const std::string & b)63 std::optional<std::string> MinimizeType(const std::string& a,
64                                         const std::string& b) {
65   auto a_pieces = base::SplitString(a, ".");
66   auto b_pieces = base::SplitString(b, ".");
67 
68   size_t skip = 0;
69   for (size_t i = 0; i < std::min(a_pieces.size(), b_pieces.size()); ++i) {
70     if (a_pieces[i] != b_pieces[i])
71       return a.substr(skip);
72     skip += a_pieces[i].size() + 1;
73   }
74   return std::nullopt;
75 }
76 
SimpleFieldTypeFromDescriptor(const google::protobuf::Descriptor & parent,const google::protobuf::FieldDescriptor & desc,bool packageless_type)77 std::string SimpleFieldTypeFromDescriptor(
78     const google::protobuf::Descriptor& parent,
79     const google::protobuf::FieldDescriptor& desc,
80     bool packageless_type) {
81   switch (desc.type()) {
82     case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
83       if (packageless_type) {
84         return base::StripPrefix(desc.message_type()->full_name(),
85                                  desc.message_type()->file()->package() + ".");
86       } else {
87         return MinimizeType(desc.message_type()->full_name(),
88                             parent.full_name())
89             .value_or(desc.message_type()->name());
90       }
91     case google::protobuf::FieldDescriptor::TYPE_ENUM:
92       if (packageless_type) {
93         return base::StripPrefix(desc.enum_type()->full_name(),
94                                  desc.enum_type()->file()->package() + ".");
95       } else {
96         return MinimizeType(desc.enum_type()->full_name(), parent.full_name())
97             .value_or(desc.enum_type()->name());
98       }
99     default:
100       return kTypeToName[desc.type()];
101   }
102 }
103 
FieldTypeFromDescriptor(const google::protobuf::Descriptor & parent,const google::protobuf::FieldDescriptor & desc,bool packageless_type)104 std::string FieldTypeFromDescriptor(
105     const google::protobuf::Descriptor& parent,
106     const google::protobuf::FieldDescriptor& desc,
107     bool packageless_type) {
108   if (!desc.is_map())
109     return SimpleFieldTypeFromDescriptor(parent, desc, packageless_type);
110 
111   std::string field_type;
112   field_type += "map<";
113   field_type += FieldTypeFromDescriptor(parent, *desc.message_type()->field(0),
114                                         packageless_type);
115   field_type += ",";
116   field_type += FieldTypeFromDescriptor(parent, *desc.message_type()->field(1),
117                                         packageless_type);
118   field_type += ">";
119   return field_type;
120 }
121 
NormalizeOptionsMessage(const google::protobuf::DescriptorPool & pool,google::protobuf::DynamicMessageFactory * factory,const google::protobuf::Message & message)122 std::unique_ptr<google::protobuf::Message> NormalizeOptionsMessage(
123     const google::protobuf::DescriptorPool& pool,
124     google::protobuf::DynamicMessageFactory* factory,
125     const google::protobuf::Message& message) {
126   const auto* option_descriptor =
127       pool.FindMessageTypeByName(message.GetDescriptor()->full_name());
128   if (!option_descriptor)
129     return nullptr;
130 
131   std::unique_ptr<google::protobuf::Message> dynamic_options(
132       factory->GetPrototype(option_descriptor)->New());
133   PERFETTO_CHECK(dynamic_options->ParseFromString(message.SerializeAsString()));
134   return dynamic_options;
135 }
136 
OptionsFromMessage(const google::protobuf::DescriptorPool & pool,const google::protobuf::Message & raw_message)137 std::vector<ProtoFile::Option> OptionsFromMessage(
138     const google::protobuf::DescriptorPool& pool,
139     const google::protobuf::Message& raw_message) {
140   google::protobuf::DynamicMessageFactory factory;
141 
142   auto normalized = NormalizeOptionsMessage(pool, &factory, raw_message);
143   const auto* message = normalized ? normalized.get() : &raw_message;
144   const auto* reflection = message->GetReflection();
145 
146   std::vector<const google::protobuf::FieldDescriptor*> fields;
147   reflection->ListFields(*message, &fields);
148 
149   std::vector<ProtoFile::Option> options;
150   for (size_t i = 0; i < fields.size(); i++) {
151     int count = 1;
152     bool repeated = false;
153     if (fields[i]->is_repeated()) {
154       count = reflection->FieldSize(*message, fields[i]);
155       repeated = true;
156     }
157     for (int j = 0; j < count; j++) {
158       std::string name;
159       if (fields[i]->is_extension()) {
160         name = "(" + fields[i]->full_name() + ")";
161       } else {
162         name = fields[i]->name();
163       }
164 
165       std::string fieldval;
166       if (fields[i]->cpp_type() ==
167           google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
168         std::string tmp;
169         google::protobuf::TextFormat::Printer printer;
170         printer.PrintFieldValueToString(*message, fields[i], repeated ? j : -1,
171                                         &tmp);
172         fieldval.append("{\n");
173         fieldval.append(tmp);
174         fieldval.append("}");
175       } else {
176         google::protobuf::TextFormat::PrintFieldValueToString(
177             *message, fields[i], repeated ? j : -1, &fieldval);
178       }
179       options.push_back(ProtoFile::Option{name, fieldval});
180     }
181   }
182   return options;
183 }
184 
185 template <typename Output, typename Descriptor>
InitFromDescriptor(const Descriptor & desc)186 Output InitFromDescriptor(const Descriptor& desc) {
187   google::protobuf::SourceLocation source_loc;
188   if (!desc.GetSourceLocation(&source_loc))
189     return {};
190 
191   Output out;
192   out.leading_comments = base::SplitString(source_loc.leading_comments, "\n");
193   out.trailing_comments = base::SplitString(source_loc.trailing_comments, "\n");
194   return out;
195 }
196 
FieldFromDescriptor(const google::protobuf::Descriptor & parent,const google::protobuf::FieldDescriptor & desc)197 ProtoFile::Field FieldFromDescriptor(
198     const google::protobuf::Descriptor& parent,
199     const google::protobuf::FieldDescriptor& desc) {
200   auto field = InitFromDescriptor<ProtoFile::Field>(desc);
201   field.label = kLabelToName[desc.label()];
202   field.packageless_type = FieldTypeFromDescriptor(parent, desc, true);
203   field.type = FieldTypeFromDescriptor(parent, desc, false);
204   field.name = desc.name();
205   field.number = desc.number();
206   field.options = OptionsFromMessage(*desc.file()->pool(), desc.options());
207   return field;
208 }
209 
EnumValueFromDescriptor(const google::protobuf::EnumValueDescriptor & desc)210 ProtoFile::Enum::Value EnumValueFromDescriptor(
211     const google::protobuf::EnumValueDescriptor& desc) {
212   auto value = InitFromDescriptor<ProtoFile::Enum::Value>(desc);
213   value.name = desc.name();
214   value.number = desc.number();
215   value.options = OptionsFromMessage(*desc.file()->pool(), desc.options());
216   return value;
217 }
218 
EnumFromDescriptor(const google::protobuf::EnumDescriptor & desc)219 ProtoFile::Enum EnumFromDescriptor(
220     const google::protobuf::EnumDescriptor& desc) {
221   auto en = InitFromDescriptor<ProtoFile::Enum>(desc);
222   en.name = desc.name();
223   for (int i = 0; i < desc.value_count(); ++i) {
224     en.values.emplace_back(EnumValueFromDescriptor(*desc.value(i)));
225   }
226   return en;
227 }
228 
OneOfFromDescriptor(const google::protobuf::Descriptor & parent,const google::protobuf::OneofDescriptor & desc)229 ProtoFile::Oneof OneOfFromDescriptor(
230     const google::protobuf::Descriptor& parent,
231     const google::protobuf::OneofDescriptor& desc) {
232   auto oneof = InitFromDescriptor<ProtoFile::Oneof>(desc);
233   oneof.name = desc.name();
234   for (int i = 0; i < desc.field_count(); ++i) {
235     oneof.fields.emplace_back(FieldFromDescriptor(parent, *desc.field(i)));
236   }
237   return oneof;
238 }
239 
MessageFromDescriptor(const google::protobuf::Descriptor & desc)240 ProtoFile::Message MessageFromDescriptor(
241     const google::protobuf::Descriptor& desc) {
242   auto message = InitFromDescriptor<ProtoFile::Message>(desc);
243   message.name = desc.name();
244   for (int i = 0; i < desc.enum_type_count(); ++i) {
245     message.enums.emplace_back(EnumFromDescriptor(*desc.enum_type(i)));
246   }
247   for (int i = 0; i < desc.nested_type_count(); ++i) {
248     message.nested_messages.emplace_back(
249         MessageFromDescriptor(*desc.nested_type(i)));
250   }
251   for (int i = 0; i < desc.oneof_decl_count(); ++i) {
252     message.oneofs.emplace_back(OneOfFromDescriptor(desc, *desc.oneof_decl(i)));
253   }
254   for (int i = 0; i < desc.field_count(); ++i) {
255     auto* field = desc.field(i);
256     if (field->containing_oneof())
257       continue;
258     message.fields.emplace_back(FieldFromDescriptor(desc, *field));
259   }
260   return message;
261 }
262 
263 }  // namespace
264 
ProtoFileFromDescriptor(std::string preamble,const google::protobuf::FileDescriptor & desc)265 ProtoFile ProtoFileFromDescriptor(
266     std::string preamble,
267     const google::protobuf::FileDescriptor& desc) {
268   ProtoFile file;
269   file.preamble = std::move(preamble);
270   for (int i = 0; i < desc.enum_type_count(); ++i) {
271     file.enums.push_back(EnumFromDescriptor(*desc.enum_type(i)));
272   }
273   for (int i = 0; i < desc.message_type_count(); ++i) {
274     file.messages.push_back(MessageFromDescriptor(*desc.message_type(i)));
275   }
276   return file;
277 }
278 
279 }  // namespace proto_merger
280 }  // namespace perfetto
281