aboutsummaryrefslogtreecommitdiff
path: root/src/protozero/protoc_plugin/cppgen_plugin.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/protozero/protoc_plugin/cppgen_plugin.cc')
-rw-r--r--src/protozero/protoc_plugin/cppgen_plugin.cc581
1 files changed, 0 insertions, 581 deletions
diff --git a/src/protozero/protoc_plugin/cppgen_plugin.cc b/src/protozero/protoc_plugin/cppgen_plugin.cc
deleted file mode 100644
index e50c01e00..000000000
--- a/src/protozero/protoc_plugin/cppgen_plugin.cc
+++ /dev/null
@@ -1,581 +0,0 @@
-/*
- * Copyright (C) 2017 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 <stdio.h>
-#include <stdlib.h>
-
-#include <fstream>
-#include <iostream>
-#include <set>
-#include <stack>
-#include <vector>
-
-#include <google/protobuf/compiler/code_generator.h>
-#include <google/protobuf/compiler/importer.h>
-#include <google/protobuf/compiler/plugin.h>
-#include <google/protobuf/dynamic_message.h>
-#include <google/protobuf/io/printer.h>
-#include <google/protobuf/io/zero_copy_stream_impl.h>
-#include <google/protobuf/util/field_comparator.h>
-#include <google/protobuf/util/message_differencer.h>
-
-#include "perfetto/ext/base/string_utils.h"
-
-namespace protozero {
-namespace {
-
-using namespace google::protobuf;
-using namespace google::protobuf::compiler;
-using namespace google::protobuf::io;
-using perfetto::base::SplitString;
-using perfetto::base::StripChars;
-using perfetto::base::StripSuffix;
-using perfetto::base::ToUpper;
-
-static constexpr auto TYPE_MESSAGE = FieldDescriptor::TYPE_MESSAGE;
-
-static const char kHeader[] =
- "// DO NOT EDIT. Autogenerated by Perfetto cppgen_plugin\n";
-
-std::string GetProtoHeader(const FileDescriptor* file) {
- return StripSuffix(file->name(), ".proto") + ".pb.h";
-}
-
-template <typename T = Descriptor>
-std::string GetFullName(const T* msg, bool with_namespace = false) {
- std::string full_type;
- full_type.append(msg->name());
- for (const Descriptor* par = msg->containing_type(); par;
- par = par->containing_type()) {
- full_type.insert(0, par->name() + "_");
- }
- if (with_namespace) {
- std::vector<std::string> namespaces =
- SplitString(msg->file()->package(), ".");
- for (auto it = namespaces.rbegin(); it != namespaces.rend(); it++) {
- full_type.insert(0, *it + "::");
- }
- }
- return full_type;
-}
-
-class CppObjGenerator : public ::google::protobuf::compiler::CodeGenerator {
- public:
- CppObjGenerator();
- ~CppObjGenerator() override;
-
- // CodeGenerator implementation
- bool Generate(const google::protobuf::FileDescriptor* file,
- const std::string& options,
- GeneratorContext* context,
- std::string* error) const override;
-
- private:
- std::string GetCppType(const FieldDescriptor* field, bool constref) const;
- void GenEnum(const EnumDescriptor*, Printer*) const;
- void GenEnumAliases(const EnumDescriptor*, Printer*) const;
- void GenClassDecl(const Descriptor*, Printer*) const;
- void GenClassDef(const Descriptor*, Printer*) const;
-};
-
-CppObjGenerator::CppObjGenerator() = default;
-CppObjGenerator::~CppObjGenerator() = default;
-
-bool CppObjGenerator::Generate(const google::protobuf::FileDescriptor* file,
- const std::string& /*options*/,
- GeneratorContext* context,
- std::string* error) const {
- auto get_file_name = [](const FileDescriptor* proto) {
- return StripSuffix(proto->name(), ".proto") + ".gen";
- };
-
- const std::unique_ptr<ZeroCopyOutputStream> h_fstream(
- context->Open(get_file_name(file) + ".h"));
- const std::unique_ptr<ZeroCopyOutputStream> cc_fstream(
- context->Open(get_file_name(file) + ".cc"));
-
- // Variables are delimited by $.
- Printer h_printer(h_fstream.get(), '$');
- Printer cc_printer(cc_fstream.get(), '$');
-
- std::string include_guard = file->package() + "_" + file->name() + "_CPP_H_";
- include_guard = ToUpper(include_guard);
- include_guard = StripChars(include_guard, ".-/\\", '_');
-
- h_printer.Print(kHeader);
- h_printer.Print("#ifndef $g$\n#define $g$\n\n", "g", include_guard);
- h_printer.Print("#include <stdint.h>\n");
- h_printer.Print("#include <vector>\n");
- h_printer.Print("#include <string>\n");
- h_printer.Print("#include <type_traits>\n\n");
- h_printer.Print("#include \"perfetto/protozero/copyable_ptr.h\"\n");
- h_printer.Print("#include \"perfetto/base/export.h\"\n\n");
-
- cc_printer.Print(kHeader);
- cc_printer.Print("#pragma GCC diagnostic push\n");
- cc_printer.Print("#pragma GCC diagnostic ignored \"-Wfloat-equal\"\n");
-
- // Generate includes for translated types of dependencies.
-
- // Figure out the subset of imports that are used only for lazy fields. We
- // won't emit a C++ #include for them. This code is overly aggressive at
- // removing imports: it rules them out as soon as it sees one lazy field
- // whose type is defined in that import. A 100% correct solution would require
- // to check that *all* dependent types for a given import are lazy before
- // excluding that. In practice we don't need that because we don't use imports
- // for both lazy and non-lazy fields.
- std::set<std::string> lazy_imports;
- for (int m = 0; m < file->message_type_count(); m++) {
- const Descriptor* msg = file->message_type(m);
- for (int i = 0; i < msg->field_count(); i++) {
- const FieldDescriptor* field = msg->field(i);
- if (field->options().lazy()) {
- lazy_imports.insert(field->message_type()->file()->name());
- }
- }
- }
-
- // Include the .pb.h for the current file.
- cc_printer.Print("\n#include \"$f$\"\n", "f", GetProtoHeader(file));
-
- // Recursively traverse all imports and turn them into #include(s).
- std::vector<const FileDescriptor*> imports_to_visit;
- std::set<const FileDescriptor*> imports_visited;
- imports_to_visit.push_back(file);
-
- while (!imports_to_visit.empty()) {
- const FileDescriptor* cur = imports_to_visit.back();
- imports_to_visit.pop_back();
- imports_visited.insert(cur);
- cc_printer.Print("#include \"$f$.h\"\n", "f", get_file_name(cur));
- for (int i = 0; i < cur->dependency_count(); i++) {
- const FileDescriptor* dep = cur->dependency(i);
- if (imports_visited.count(dep) || lazy_imports.count(dep->name()))
- continue;
- imports_to_visit.push_back(dep);
- }
- }
-
- // Compute all nested types to generate forward declarations later.
-
- std::set<const Descriptor*> all_types_seen; // All deps
- std::set<const EnumDescriptor*> all_enums_seen;
-
- // We track the types additionally in vectors to guarantee a stable order in
- // the generated output.
- std::vector<const Descriptor*> local_types; // Cur .proto file only.
- std::vector<const Descriptor*> all_types; // All deps
- std::vector<const EnumDescriptor*> local_enums;
- std::vector<const EnumDescriptor*> all_enums;
-
- auto add_enum = [&local_enums, &all_enums, &all_enums_seen,
- &file](const EnumDescriptor* enum_desc) {
- if (all_enums_seen.count(enum_desc))
- return;
- all_enums_seen.insert(enum_desc);
- all_enums.push_back(enum_desc);
- if (enum_desc->file() == file)
- local_enums.push_back(enum_desc);
- };
-
- std::stack<const Descriptor*> recursion_stack;
- for (int i = 0; i < file->message_type_count(); i++)
- recursion_stack.push(file->message_type(i));
-
- while (!recursion_stack.empty()) {
- const Descriptor* msg = recursion_stack.top();
- recursion_stack.pop();
- if (all_types_seen.count(msg))
- continue;
- all_types_seen.insert(msg);
- all_types.push_back(msg);
- if (msg->file() == file)
- local_types.push_back(msg);
-
- for (int i = 0; i < msg->nested_type_count(); i++)
- recursion_stack.push(msg->nested_type(i));
-
- for (int i = 0; i < msg->enum_type_count(); i++)
- add_enum(msg->enum_type(i));
-
- for (int i = 0; i < msg->field_count(); i++) {
- const FieldDescriptor* field = msg->field(i);
- if (field->has_default_value()) {
- *error = "field " + field->name() +
- ": Explicitly declared default values are not supported";
- return false;
- }
- if (field->options().lazy() &&
- (field->is_repeated() || field->type() != TYPE_MESSAGE)) {
- *error = "[lazy=true] is supported only on non-repeated fields\n";
- return false;
- }
-
- if (field->type() == TYPE_MESSAGE && !field->options().lazy())
- recursion_stack.push(field->message_type());
-
- if (field->type() == FieldDescriptor::TYPE_ENUM)
- add_enum(field->enum_type());
- }
- } // while (!recursion_stack.empty())
-
- // Generate forward declarations in the header for proto types.
- h_printer.Print("// Forward declarations for protobuf types.\n");
- std::vector<std::string> namespaces = SplitString(file->package(), ".");
- for (size_t i = 0; i < namespaces.size(); i++)
- h_printer.Print("namespace $n$ {\n", "n", namespaces[i]);
-
- for (const Descriptor* msg : all_types)
- h_printer.Print("class $n$;\n", "n", GetFullName(msg));
-
- for (size_t i = 0; i < namespaces.size(); i++)
- h_printer.Print("}\n");
-
- h_printer.Print("\nnamespace perfetto {\n");
- cc_printer.Print("\nnamespace perfetto {\n");
-
- // Generate fwd declarations for C++ types.
- for (const EnumDescriptor* enm : all_enums) {
- h_printer.Print("enum $n$ : int;\n", "n", GetFullName(enm));
- }
-
- for (const Descriptor* msg : all_types)
- h_printer.Print("class $n$;\n", "n", GetFullName(msg));
-
- // Generate declarations and definitions.
- for (const EnumDescriptor* enm : local_enums)
- GenEnum(enm, &h_printer);
-
- for (const Descriptor* msg : local_types) {
- GenClassDecl(msg, &h_printer);
- GenClassDef(msg, &cc_printer);
- }
-
- cc_printer.Print("} // namespace perfetto\n");
- cc_printer.Print("#pragma GCC diagnostic pop\n");
-
- h_printer.Print("} // namespace perfetto\n");
- h_printer.Print("\n#endif // $g$\n", "g", include_guard);
-
- return true;
-}
-
-std::string CppObjGenerator::GetCppType(const FieldDescriptor* field,
- bool constref) const {
- switch (field->type()) {
- case FieldDescriptor::TYPE_DOUBLE:
- return "double";
- case FieldDescriptor::TYPE_FLOAT:
- return "float";
- case FieldDescriptor::TYPE_FIXED32:
- case FieldDescriptor::TYPE_UINT32:
- return "uint32_t";
- case FieldDescriptor::TYPE_SFIXED32:
- case FieldDescriptor::TYPE_INT32:
- case FieldDescriptor::TYPE_SINT32:
- return "int32_t";
- case FieldDescriptor::TYPE_FIXED64:
- case FieldDescriptor::TYPE_UINT64:
- return "uint64_t";
- case FieldDescriptor::TYPE_SFIXED64:
- case FieldDescriptor::TYPE_SINT64:
- case FieldDescriptor::TYPE_INT64:
- return "int64_t";
- case FieldDescriptor::TYPE_BOOL:
- return "bool";
- case FieldDescriptor::TYPE_STRING:
- case FieldDescriptor::TYPE_BYTES:
- return constref ? "const std::string&" : "std::string";
- case FieldDescriptor::TYPE_MESSAGE:
- assert(!field->options().lazy());
- return constref ? "const " + GetFullName(field->message_type()) + "&"
- : GetFullName(field->message_type());
- case FieldDescriptor::TYPE_ENUM:
- return GetFullName(field->enum_type());
- case FieldDescriptor::TYPE_GROUP:
- abort();
- }
- abort(); // for gcc
-}
-
-void CppObjGenerator::GenEnum(const EnumDescriptor* enum_desc,
- Printer* p) const {
- std::string full_name = GetFullName(enum_desc);
- p->Print("enum $f$ : int {\n", "f", full_name);
- for (int e = 0; e < enum_desc->value_count(); e++) {
- const EnumValueDescriptor* value = enum_desc->value(e);
- p->Print(" $f$_$n$ = $v$,\n", "f", full_name, "n", value->name(), "v",
- std::to_string(value->number()));
- }
- p->Print("};\n");
-}
-
-void CppObjGenerator::GenEnumAliases(const EnumDescriptor* enum_desc,
- Printer* p) const {
- std::string full_name = GetFullName(enum_desc);
- for (int e = 0; e < enum_desc->value_count(); e++) {
- const EnumValueDescriptor* value = enum_desc->value(e);
- p->Print("static constexpr auto $n$ = $f$_$n$;\n", "f", full_name, "n",
- value->name());
- }
-}
-
-void CppObjGenerator::GenClassDecl(const Descriptor* msg, Printer* p) const {
- std::string full_name = GetFullName(msg);
- p->Print("\nclass PERFETTO_EXPORT $n$ {\n", "n", full_name);
- p->Print(" public:\n");
- p->Indent();
-
- // Do a first pass to generate aliases for nested types.
- // e.g., using Foo = Parent_Foo;
- for (int i = 0; i < msg->nested_type_count(); i++) {
- const Descriptor* nested_msg = msg->nested_type(i);
- p->Print("using $n$ = $f$;\n", "n", nested_msg->name(), "f",
- GetFullName(nested_msg));
- }
- for (int i = 0; i < msg->enum_type_count(); i++) {
- const EnumDescriptor* nested_enum = msg->enum_type(i);
- p->Print("using $n$ = $f$;\n", "n", nested_enum->name(), "f",
- GetFullName(nested_enum));
- GenEnumAliases(nested_enum, p);
- }
-
- p->Print("$n$();\n", "n", full_name);
- p->Print("~$n$();\n", "n", full_name);
- p->Print("$n$($n$&&) noexcept;\n", "n", full_name);
- p->Print("$n$& operator=($n$&&);\n", "n", full_name);
- p->Print("$n$(const $n$&);\n", "n", full_name);
- p->Print("$n$& operator=(const $n$&);\n", "n", full_name);
- p->Print("bool operator==(const $n$&) const;\n", "n", full_name);
- p->Print(
- "bool operator!=(const $n$& other) const { return !(*this == other); }\n",
- "n", full_name);
- p->Print("\n");
-
- std::string proto_type = GetFullName(msg, true);
- p->Print("// Raw proto decoding.\n");
- p->Print("void ParseRawProto(const std::string&);\n");
- p->Print("// Conversion methods from/to the corresponding protobuf types.\n");
- p->Print("void FromProto(const $p$&);\n", "p", proto_type);
- p->Print("void ToProto($p$*) const;\n", "p", proto_type);
-
- // Generate accessors.
- for (int i = 0; i < msg->field_count(); i++) {
- const FieldDescriptor* field = msg->field(i);
- p->Print("\n");
- if (field->options().lazy()) {
- p->Print("const std::string& $n$_raw() const { return $n$_; }\n", "n",
- field->lowercase_name());
- p->Print("void set_$n$_raw(const std::string& raw) { $n$_ = raw; }\n",
- "n", field->lowercase_name());
- } else if (!field->is_repeated()) {
- if (field->type() == TYPE_MESSAGE) {
- p->Print("$t$ $n$() const { return *$n$_; }\n", "t",
- GetCppType(field, true), "n", field->lowercase_name());
- p->Print("$t$* mutable_$n$() { return $n$_.get(); }\n", "t",
- GetCppType(field, false), "n", field->lowercase_name());
- } else {
- p->Print("$t$ $n$() const { return $n$_; }\n", "t",
- GetCppType(field, true), "n", field->lowercase_name());
- p->Print("void set_$n$($t$ value) { $n$_ = value; }\n", "t",
- GetCppType(field, true), "n", field->lowercase_name());
- if (field->type() == FieldDescriptor::TYPE_BYTES) {
- p->Print(
- "void set_$n$(const void* p, size_t s) { "
- "$n$_.assign(reinterpret_cast<const char*>(p), s); }\n",
- "n", field->lowercase_name());
- }
- }
- } else { // is_repeated()
- p->Print(
- "int $n$_size() const { return static_cast<int>($n$_.size()); }\n",
- "t", GetCppType(field, false), "n", field->lowercase_name());
- p->Print("const std::vector<$t$>& $n$() const { return $n$_; }\n", "t",
- GetCppType(field, false), "n", field->lowercase_name());
- p->Print("std::vector<$t$>* mutable_$n$() { return &$n$_; }\n", "t",
- GetCppType(field, false), "n", field->lowercase_name());
- p->Print("void clear_$n$() { $n$_.clear(); }\n", "n",
- field->lowercase_name());
- p->Print("$t$* add_$n$() { $n$_.emplace_back(); return &$n$_.back(); }\n",
- "t", GetCppType(field, false), "n", field->lowercase_name());
- }
- }
- p->Outdent();
- p->Print("\n private:\n");
- p->Indent();
-
- // Generate fields.
- for (int i = 0; i < msg->field_count(); i++) {
- const FieldDescriptor* field = msg->field(i);
- if (field->options().lazy()) {
- p->Print("std::string $n$_; // [lazy=true]\n", "n",
- field->lowercase_name());
- } else if (!field->is_repeated()) {
- std::string type = GetCppType(field, false);
- if (field->type() == TYPE_MESSAGE) {
- type = "::protozero::CopyablePtr<" + type + ">";
- p->Print("$t$ $n$_;\n", "t", type, "n", field->lowercase_name());
- } else {
- p->Print("$t$ $n$_{};\n", "t", type, "n", field->lowercase_name());
- }
- } else { // is_repeated()
- p->Print("std::vector<$t$> $n$_;\n", "t", GetCppType(field, false), "n",
- field->lowercase_name());
- }
- }
- p->Print("\n");
- p->Print("// Allows to preserve unknown protobuf fields for compatibility\n");
- p->Print("// with future versions of .proto files.\n");
- p->Print("std::string unknown_fields_;\n");
- p->Outdent();
- p->Print("};\n\n");
-}
-
-void CppObjGenerator::GenClassDef(const Descriptor* msg, Printer* p) const {
- p->Print("\n");
- std::string full_name = GetFullName(msg);
-
- p->Print("$n$::$n$() = default;\n", "n", full_name);
- p->Print("$n$::~$n$() = default;\n", "n", full_name);
- p->Print("$n$::$n$(const $n$&) = default;\n", "n", full_name);
- p->Print("$n$& $n$::operator=(const $n$&) = default;\n", "n", full_name);
- p->Print("$n$::$n$($n$&&) noexcept = default;\n", "n", full_name);
- p->Print("$n$& $n$::operator=($n$&&) = default;\n", "n", full_name);
-
- p->Print("\n");
-
- // Comparison operator
- p->Print("bool $n$::operator==(const $n$& other) const {\n", "n", full_name);
- p->Indent();
-
- p->Print("return unknown_fields_ == other.unknown_fields_");
- for (int i = 0; i < msg->field_count(); i++)
- p->Print("\n && $n$_ == other.$n$_", "n", msg->field(i)->lowercase_name());
- p->Print(";");
- p->Outdent();
- p->Print("\n}\n\n");
-
- std::string proto_type = GetFullName(msg, true);
-
- // Genrate the ParseRawProto() method definition.
- p->Print("void $f$::ParseRawProto(const std::string& raw) {\n", "f",
- full_name);
- p->Indent();
- p->Print("$p$ proto;\n", "p", proto_type);
- p->Print("proto.ParseFromString(raw);\n");
- p->Print("FromProto(proto);\n");
- p->Outdent();
- p->Print("}\n\n");
-
- // Genrate the FromProto() method definition.
- p->Print("void $f$::FromProto(const $p$& proto) {\n", "f", full_name, "p",
- proto_type);
- p->Indent();
- for (int i = 0; i < msg->field_count(); i++) {
- p->Print("\n");
- const FieldDescriptor* field = msg->field(i);
- if (field->options().lazy()) {
- p->Print("$n$_ = proto.$n$().SerializeAsString();\n", "n",
- field->lowercase_name());
- } else if (!field->is_repeated()) {
- if (field->type() == TYPE_MESSAGE) {
- p->Print("$n$_->FromProto(proto.$n$());\n", "n",
- field->lowercase_name());
- } else {
- p->Print(
- "static_assert(sizeof($n$_) == sizeof(proto.$n$()), \"size "
- "mismatch\");\n",
- "n", field->lowercase_name());
- p->Print("$n$_ = static_cast<decltype($n$_)>(proto.$n$());\n", "n",
- field->lowercase_name());
- }
- } else { // is_repeated()
- p->Print("$n$_.clear();\n", "n", field->lowercase_name());
- p->Print("for (const auto& field : proto.$n$()) {\n", "n",
- field->lowercase_name());
- p->Print(" $n$_.emplace_back();\n", "n", field->lowercase_name());
- if (field->type() == TYPE_MESSAGE) {
- p->Print(" $n$_.back().FromProto(field);\n", "n",
- field->lowercase_name());
- } else {
- p->Print(
- "static_assert(sizeof($n$_.back()) == sizeof(proto.$n$(0)), \"size "
- "mismatch\");\n",
- "n", field->lowercase_name());
- p->Print(
- " $n$_.back() = static_cast<decltype($n$_)::value_type>(field);\n",
- "n", field->lowercase_name());
- }
- p->Print("}\n");
- }
- }
- p->Print("unknown_fields_ = proto.unknown_fields();\n");
- p->Outdent();
- p->Print("}\n\n");
-
- // Genrate the ToProto() method definition.
- p->Print("void $f$::ToProto($p$* proto) const {\n", "f", full_name, "p",
- proto_type);
- p->Indent();
- p->Print("proto->Clear();\n");
- for (int i = 0; i < msg->field_count(); i++) {
- p->Print("\n");
- const FieldDescriptor* field = msg->field(i);
- if (field->options().lazy()) {
- p->Print("proto->mutable_$n$()->ParseFromString($n$_);\n", "n",
- field->lowercase_name());
- } else if (!field->is_repeated()) {
- if (field->type() == TYPE_MESSAGE) {
- p->Print("$n$_->ToProto(proto->mutable_$n$());\n", "n",
- field->lowercase_name());
- } else {
- p->Print(
- "static_assert(sizeof($n$_) == sizeof(proto->$n$()), \"size "
- "mismatch\");\n",
- "n", field->lowercase_name());
- p->Print("proto->set_$n$(static_cast<decltype(proto->$n$())>($n$_));\n",
- "n", field->lowercase_name());
- }
- } else { // is_repeated()
- p->Print("for (const auto& it : $n$_) {\n", "n", field->lowercase_name());
- if (field->type() == TYPE_MESSAGE) {
- p->Print(" auto* entry = proto->add_$n$();\n", "n",
- field->lowercase_name());
- p->Print(" it.ToProto(entry);\n");
- } else {
- p->Print(
- " proto->add_$n$(static_cast<decltype(proto->$n$(0))>(it));\n",
- "n", field->lowercase_name());
- p->Print(
- "static_assert(sizeof(it) == sizeof(proto->$n$(0)), \"size "
- "mismatch\");\n",
- "n", field->lowercase_name());
- }
- p->Print("}\n");
- }
- }
- p->Print("*(proto->mutable_unknown_fields()) = unknown_fields_;\n");
- p->Outdent();
- p->Print("}\n\n");
-}
-
-} // namespace
-} // namespace protozero
-
-int main(int argc, char** argv) {
- ::protozero::CppObjGenerator generator;
- return google::protobuf::compiler::PluginMain(argc, argv, &generator);
-}