/* * 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 #include #include #include #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 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 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 dynamic_options( factory->GetPrototype(option_descriptor)->New()); PERFETTO_CHECK(dynamic_options->ParseFromString(message.SerializeAsString())); return dynamic_options; } std::vector 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 fields; reflection->ListFields(*message, &fields); std::vector 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 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(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(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(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(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(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