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