279 lines
9.4 KiB
C++
279 lines
9.4 KiB
C++
/*
|
|
* Copyright (C) 2021 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "tools/proto_merger/proto_file.h"
|
|
|
|
#include <google/protobuf/descriptor.h>
|
|
#include <google/protobuf/descriptor.pb.h>
|
|
#include <google/protobuf/dynamic_message.h>
|
|
#include <google/protobuf/text_format.h>
|
|
|
|
#include "perfetto/ext/base/string_utils.h"
|
|
|
|
namespace perfetto {
|
|
namespace proto_merger {
|
|
namespace {
|
|
|
|
const char* const
|
|
kTypeToName[google::protobuf::FieldDescriptor::Type::MAX_TYPE + 1] = {
|
|
"ERROR", // 0 is reserved for errors
|
|
|
|
"double", // TYPE_DOUBLE
|
|
"float", // TYPE_FLOAT
|
|
"int64", // TYPE_INT64
|
|
"uint64", // TYPE_UINT64
|
|
"int32", // TYPE_INT32
|
|
"fixed64", // TYPE_FIXED64
|
|
"fixed32", // TYPE_FIXED32
|
|
"bool", // TYPE_BOOL
|
|
"string", // TYPE_STRING
|
|
"group", // TYPE_GROUP
|
|
"message", // TYPE_MESSAGE
|
|
"bytes", // TYPE_BYTES
|
|
"uint32", // TYPE_UINT32
|
|
"enum", // TYPE_ENUM
|
|
"sfixed32", // TYPE_SFIXED32
|
|
"sfixed64", // TYPE_SFIXED64
|
|
"sint32", // TYPE_SINT32
|
|
"sint64", // TYPE_SINT64
|
|
};
|
|
|
|
const char* const
|
|
kLabelToName[google::protobuf::FieldDescriptor::MAX_LABEL + 1] = {
|
|
"ERROR", // 0 is reserved for errors
|
|
|
|
"optional", // LABEL_OPTIONAL
|
|
"required", // LABEL_REQUIRED
|
|
"repeated", // LABEL_REPEATED
|
|
};
|
|
|
|
base::Optional<std::string> MinimizeType(const std::string& a,
|
|
const std::string& b) {
|
|
auto a_pieces = base::SplitString(a, ".");
|
|
auto b_pieces = base::SplitString(b, ".");
|
|
|
|
size_t skip = 0;
|
|
for (size_t i = 0; i < std::min(a_pieces.size(), b_pieces.size()); ++i) {
|
|
if (a_pieces[i] != b_pieces[i])
|
|
return a.substr(skip);
|
|
skip += a_pieces[i].size() + 1;
|
|
}
|
|
return base::nullopt;
|
|
}
|
|
|
|
std::string SimpleFieldTypeFromDescriptor(
|
|
const google::protobuf::Descriptor& parent,
|
|
const google::protobuf::FieldDescriptor& desc,
|
|
bool packageless_type) {
|
|
switch (desc.type()) {
|
|
case google::protobuf::FieldDescriptor::TYPE_MESSAGE:
|
|
if (packageless_type) {
|
|
return base::StripPrefix(desc.message_type()->full_name(),
|
|
desc.message_type()->file()->package() + ".");
|
|
} else {
|
|
return MinimizeType(desc.message_type()->full_name(),
|
|
parent.full_name())
|
|
.value_or(desc.message_type()->name());
|
|
}
|
|
case google::protobuf::FieldDescriptor::TYPE_ENUM:
|
|
if (packageless_type) {
|
|
return base::StripPrefix(desc.enum_type()->full_name(),
|
|
desc.enum_type()->file()->package() + ".");
|
|
} else {
|
|
return MinimizeType(desc.enum_type()->full_name(), parent.full_name())
|
|
.value_or(desc.enum_type()->name());
|
|
}
|
|
default:
|
|
return kTypeToName[desc.type()];
|
|
}
|
|
}
|
|
|
|
std::string FieldTypeFromDescriptor(
|
|
const google::protobuf::Descriptor& parent,
|
|
const google::protobuf::FieldDescriptor& desc,
|
|
bool packageless_type) {
|
|
if (!desc.is_map())
|
|
return SimpleFieldTypeFromDescriptor(parent, desc, packageless_type);
|
|
|
|
std::string field_type;
|
|
field_type += "map<";
|
|
field_type += FieldTypeFromDescriptor(parent, *desc.message_type()->field(0),
|
|
packageless_type);
|
|
field_type += ",";
|
|
field_type += FieldTypeFromDescriptor(parent, *desc.message_type()->field(1),
|
|
packageless_type);
|
|
field_type += ">";
|
|
return field_type;
|
|
}
|
|
|
|
std::unique_ptr<google::protobuf::Message> NormalizeOptionsMessage(
|
|
const google::protobuf::DescriptorPool& pool,
|
|
google::protobuf::DynamicMessageFactory* factory,
|
|
const google::protobuf::Message& message) {
|
|
const auto* option_descriptor =
|
|
pool.FindMessageTypeByName(message.GetDescriptor()->full_name());
|
|
if (!option_descriptor)
|
|
return nullptr;
|
|
|
|
std::unique_ptr<google::protobuf::Message> dynamic_options(
|
|
factory->GetPrototype(option_descriptor)->New());
|
|
PERFETTO_CHECK(dynamic_options->ParseFromString(message.SerializeAsString()));
|
|
return dynamic_options;
|
|
}
|
|
|
|
std::vector<ProtoFile::Option> OptionsFromMessage(
|
|
const google::protobuf::DescriptorPool& pool,
|
|
const google::protobuf::Message& raw_message) {
|
|
google::protobuf::DynamicMessageFactory factory;
|
|
|
|
auto normalized = NormalizeOptionsMessage(pool, &factory, raw_message);
|
|
const auto* message = normalized ? normalized.get() : &raw_message;
|
|
const auto* reflection = message->GetReflection();
|
|
|
|
std::vector<const google::protobuf::FieldDescriptor*> fields;
|
|
reflection->ListFields(*message, &fields);
|
|
|
|
std::vector<ProtoFile::Option> options;
|
|
for (size_t i = 0; i < fields.size(); i++) {
|
|
int count = 1;
|
|
bool repeated = false;
|
|
if (fields[i]->is_repeated()) {
|
|
count = reflection->FieldSize(*message, fields[i]);
|
|
repeated = true;
|
|
}
|
|
for (int j = 0; j < count; j++) {
|
|
std::string name;
|
|
if (fields[i]->is_extension()) {
|
|
name = "(" + fields[i]->full_name() + ")";
|
|
} else {
|
|
name = fields[i]->name();
|
|
}
|
|
|
|
std::string fieldval;
|
|
if (fields[i]->cpp_type() ==
|
|
google::protobuf::FieldDescriptor::CPPTYPE_MESSAGE) {
|
|
std::string tmp;
|
|
google::protobuf::TextFormat::Printer printer;
|
|
printer.PrintFieldValueToString(*message, fields[i], repeated ? j : -1,
|
|
&tmp);
|
|
fieldval.append("{\n");
|
|
fieldval.append(tmp);
|
|
fieldval.append("}");
|
|
} else {
|
|
google::protobuf::TextFormat::PrintFieldValueToString(
|
|
*message, fields[i], repeated ? j : -1, &fieldval);
|
|
}
|
|
options.push_back(ProtoFile::Option{name, fieldval});
|
|
}
|
|
}
|
|
return options;
|
|
}
|
|
|
|
template <typename Output, typename Descriptor>
|
|
Output InitFromDescriptor(const Descriptor& desc) {
|
|
google::protobuf::SourceLocation source_loc;
|
|
if (!desc.GetSourceLocation(&source_loc))
|
|
return {};
|
|
|
|
Output out;
|
|
out.leading_comments = base::SplitString(source_loc.leading_comments, "\n");
|
|
out.trailing_comments = base::SplitString(source_loc.trailing_comments, "\n");
|
|
return out;
|
|
}
|
|
|
|
ProtoFile::Field FieldFromDescriptor(
|
|
const google::protobuf::Descriptor& parent,
|
|
const google::protobuf::FieldDescriptor& desc) {
|
|
auto field = InitFromDescriptor<ProtoFile::Field>(desc);
|
|
field.label = kLabelToName[desc.label()];
|
|
field.packageless_type = FieldTypeFromDescriptor(parent, desc, true);
|
|
field.type = FieldTypeFromDescriptor(parent, desc, false);
|
|
field.name = desc.name();
|
|
field.number = desc.number();
|
|
field.options = OptionsFromMessage(*desc.file()->pool(), desc.options());
|
|
return field;
|
|
}
|
|
|
|
ProtoFile::Enum::Value EnumValueFromDescriptor(
|
|
const google::protobuf::EnumValueDescriptor& desc) {
|
|
auto value = InitFromDescriptor<ProtoFile::Enum::Value>(desc);
|
|
value.name = desc.name();
|
|
value.number = desc.number();
|
|
value.options = OptionsFromMessage(*desc.file()->pool(), desc.options());
|
|
return value;
|
|
}
|
|
|
|
ProtoFile::Enum EnumFromDescriptor(
|
|
const google::protobuf::EnumDescriptor& desc) {
|
|
auto en = InitFromDescriptor<ProtoFile::Enum>(desc);
|
|
en.name = desc.name();
|
|
for (int i = 0; i < desc.value_count(); ++i) {
|
|
en.values.emplace_back(EnumValueFromDescriptor(*desc.value(i)));
|
|
}
|
|
return en;
|
|
}
|
|
|
|
ProtoFile::Oneof OneOfFromDescriptor(
|
|
const google::protobuf::Descriptor& parent,
|
|
const google::protobuf::OneofDescriptor& desc) {
|
|
auto oneof = InitFromDescriptor<ProtoFile::Oneof>(desc);
|
|
oneof.name = desc.name();
|
|
for (int i = 0; i < desc.field_count(); ++i) {
|
|
oneof.fields.emplace_back(FieldFromDescriptor(parent, *desc.field(i)));
|
|
}
|
|
return oneof;
|
|
}
|
|
|
|
ProtoFile::Message MessageFromDescriptor(
|
|
const google::protobuf::Descriptor& desc) {
|
|
auto message = InitFromDescriptor<ProtoFile::Message>(desc);
|
|
message.name = desc.name();
|
|
for (int i = 0; i < desc.enum_type_count(); ++i) {
|
|
message.enums.emplace_back(EnumFromDescriptor(*desc.enum_type(i)));
|
|
}
|
|
for (int i = 0; i < desc.nested_type_count(); ++i) {
|
|
message.nested_messages.emplace_back(
|
|
MessageFromDescriptor(*desc.nested_type(i)));
|
|
}
|
|
for (int i = 0; i < desc.oneof_decl_count(); ++i) {
|
|
message.oneofs.emplace_back(OneOfFromDescriptor(desc, *desc.oneof_decl(i)));
|
|
}
|
|
for (int i = 0; i < desc.field_count(); ++i) {
|
|
auto* field = desc.field(i);
|
|
if (field->containing_oneof())
|
|
continue;
|
|
message.fields.emplace_back(FieldFromDescriptor(desc, *field));
|
|
}
|
|
return message;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
ProtoFile ProtoFileFromDescriptor(
|
|
const google::protobuf::FileDescriptor& desc) {
|
|
ProtoFile file;
|
|
for (int i = 0; i < desc.enum_type_count(); ++i) {
|
|
file.enums.push_back(EnumFromDescriptor(*desc.enum_type(i)));
|
|
}
|
|
for (int i = 0; i < desc.message_type_count(); ++i) {
|
|
file.messages.push_back(MessageFromDescriptor(*desc.message_type(i)));
|
|
}
|
|
return file;
|
|
}
|
|
|
|
} // namespace proto_merger
|
|
} // namespace perfetto
|