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