| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <zircon/assert.h> |
| |
| #include <algorithm> |
| #include <optional> |
| #include <sstream> |
| #include <string_view> |
| #include <type_traits> |
| #include <utility> |
| |
| #include "tools/kazoo/alias_workaround.h" |
| #include "tools/kazoo/output_util.h" |
| #include "tools/kazoo/outputs.h" |
| #include "tools/kazoo/string_util.h" |
| |
| using namespace std::literals; |
| |
| namespace { |
| |
| std::string ToSingular(const std::string& s) { |
| if (s.size() > 1 && s[s.size() - 1] == 's') { |
| return s.substr(0, s.size() - 1); |
| } |
| return s; |
| } |
| |
| class Formatter { |
| public: |
| explicit Formatter(const SyscallLibrary* library) : library_(library) {} |
| |
| struct Names { |
| std::string base_name; // signals |
| std::string type_name; // zxio_signals_t |
| }; |
| |
| Names Format(const Alias& alias) { |
| return Names{ |
| .base_name = alias.base_name(), |
| .type_name = library_->name() + "_" + alias.base_name() + "_t", |
| }; |
| } |
| |
| Names Format(const Enum& e) { |
| return Names{ |
| .base_name = e.base_name(), |
| .type_name = library_->name() + "_" + e.base_name() + "_t", |
| }; |
| } |
| |
| struct StructNames { |
| std::string base_name; // signals |
| std::string type_name; // zxio_dirent_t |
| std::string c_struct_name; // zxio_dirent |
| }; |
| |
| StructNames Format(const Table& table) { |
| return StructNames{ |
| .base_name = table.base_name(), |
| .type_name = library_->name() + "_" + table.base_name() + "_t", |
| .c_struct_name = library_->name() + "_" + table.base_name(), |
| }; |
| } |
| |
| std::string FormatMember(const Enum& e, const std::string& member_name) { |
| return ToUpperAscii(library_->name()) + "_" + ToUpperAscii(ToSingular(e.base_name())) + "_" + |
| member_name; |
| } |
| |
| std::string TypeName(const Type& type) { |
| return std::visit( |
| [this](auto&& type) -> std::string { |
| using T = std::decay_t<decltype(type)>; |
| if constexpr (std::is_same_v<T, TypeBool>) { |
| return "bool"; |
| } |
| if constexpr (std::is_same_v<T, TypeChar>) { |
| return "char"; |
| } |
| if constexpr (std::is_same_v<T, TypeInt32>) { |
| return "int32_t"; |
| } |
| if constexpr (std::is_same_v<T, TypeInt64>) { |
| return "int64_t"; |
| } |
| if constexpr (std::is_same_v<T, TypeSizeT>) { |
| return "size_t"; |
| } |
| if constexpr (std::is_same_v<T, TypeUint16>) { |
| return "uint16_t"; |
| } |
| if constexpr (std::is_same_v<T, TypeUint32>) { |
| return "uint32_t"; |
| } |
| if constexpr (std::is_same_v<T, TypeUint64>) { |
| return "uint64_t"; |
| } |
| if constexpr (std::is_same_v<T, TypeUint8>) { |
| return "uint8_t"; |
| } |
| if constexpr (std::is_same_v<T, TypeUintptrT>) { |
| return "uintptr_t"; |
| } |
| if constexpr (std::is_same_v<T, TypeVoid>) { |
| return "void"; |
| } |
| if constexpr (std::is_same_v<T, TypeZxBasicAlias>) { |
| return type.name(); |
| } |
| if constexpr (std::is_same_v<T, TypeAlias>) { |
| return Format(type.alias_data()).type_name; |
| } |
| if constexpr (std::is_same_v<T, TypeEnum>) { |
| return Format(type.enum_data()).type_name; |
| } |
| if constexpr (std::is_same_v<T, TypeHandle>) { |
| return "zx_handle_t"; |
| } |
| if constexpr (std::is_same_v<T, TypePointer>) { |
| return TypeName(type.pointed_to_type()) + "*"; |
| } |
| ZX_ASSERT(false && "Unhandled type in TypeName"); |
| }, |
| type.type_data()); |
| } |
| |
| std::string FormatConstant(const Enum& e, uint64_t raw) { |
| return std::visit( |
| [raw](auto&& type) -> std::string { |
| std::stringstream stream; |
| stream << std::hex << raw; |
| std::string num_str = "0x" + stream.str(); |
| using T = std::decay_t<decltype(type)>; |
| if constexpr (std::is_same_v<T, TypeUint8>) { |
| return num_str; |
| } |
| if constexpr (std::is_same_v<T, TypeUint16>) { |
| return num_str; |
| } |
| if constexpr (std::is_same_v<T, TypeUint32>) { |
| return num_str + "u"; |
| } |
| if constexpr (std::is_same_v<T, TypeUint64>) { |
| return num_str + "ul"; |
| } |
| if constexpr (std::is_same_v<T, TypeChar>) { |
| return num_str; |
| } |
| if constexpr (std::is_same_v<T, TypeInt32>) { |
| return num_str; |
| } |
| if constexpr (std::is_same_v<T, TypeInt64>) { |
| return num_str + "l"; |
| } |
| ZX_PANIC("Unhandled primitive type"); |
| }, |
| e.underlying_type().type_data()); |
| } |
| |
| private: |
| const SyscallLibrary* library_; |
| }; |
| |
| std::string MakeTitleLine(const std::string& base_name) { |
| std::vector<std::string> words = SplitString(base_name, '_', kTrimWhitespace); |
| std::for_each(words.begin(), words.end(), [](std::string &s){ s[0] = ToUpperASCII(s[0]); }); |
| std::string title = JoinStrings(words, " "); |
| // Pad up to 80 columns. 4 is to account for space characters around. |
| int trailing_length = 80 - static_cast<int>(title.size()) - 4; |
| if (trailing_length > 0) { |
| return "// " + title + " " + std::string(trailing_length, '-'); |
| } |
| return "// " + title; |
| } |
| |
| void PrintDocComments(const std::vector<std::string>& lines, Writer* writer, |
| uint32_t indent_level = 0) { |
| if (!lines.empty()) { |
| writer->PrintSpacerLine(); |
| } |
| std::string indent(2 * indent_level, ' '); |
| for (const auto& line : lines) { |
| if (!line.empty()) { |
| writer->Printf("%s// %s\n", indent.c_str(), line.c_str()); |
| } else { |
| writer->Printf("%s//\n", indent.c_str()); |
| } |
| } |
| } |
| |
| } // namespace |
| |
| bool CUlibHeaderOutput(const SyscallLibrary& library, Writer* writer) { |
| CopyrightHeaderWithCppComments(writer); |
| |
| std::string prelude = R"( |
| #ifndef LIB_ZXIO_TYPES_H_ |
| #define LIB_ZXIO_TYPES_H_ |
| |
| #include <stdbool.h> |
| #include <stdint.h> |
| #include <zircon/compiler.h> |
| |
| // This header defines the public types used in the zxio and zxio_ops interface. |
| |
| __BEGIN_CDECLS |
| )"; |
| writer->Printf("%s\n", TrimString(prelude, "\n").c_str()); |
| writer->Puts("\n"); |
| |
| Formatter formatter(&library); |
| for (const auto& bits : library.bits()) { |
| auto names = formatter.Format(*bits); |
| std::string title_line = MakeTitleLine(names.base_name); |
| writer->Printf("%s\n", title_line.c_str()); |
| writer->Puts("\n"); |
| PrintDocComments(bits->description(), writer); |
| writer->Printf("typedef %s %s;\n", formatter.TypeName(bits->underlying_type()).c_str(), |
| names.type_name.c_str()); |
| writer->Puts("\n"); |
| writer->Printf("#define %s ((%s)%s)\n", formatter.FormatMember(*bits, "NONE").c_str(), |
| names.type_name.c_str(), formatter.FormatConstant(*bits, 0).c_str()); |
| writer->Puts("\n"); |
| uint64_t all = 0; |
| for (const auto& k : bits->members()) { |
| auto v = bits->ValueForMember(k); |
| PrintDocComments(v.description, writer); |
| writer->Printf("#define %s ((%s)%s)\n", formatter.FormatMember(*bits, k).c_str(), |
| names.type_name.c_str(), formatter.FormatConstant(*bits, v.value).c_str()); |
| all |= v.value; |
| } |
| writer->Puts("\n"); |
| writer->Printf("#define %s ((%s)%s)\n", formatter.FormatMember(*bits, "ALL").c_str(), |
| names.type_name.c_str(), formatter.FormatConstant(*bits, all).c_str()); |
| writer->Puts("\n"); |
| } |
| |
| for (const auto& e : library.enums()) { |
| auto names = formatter.Format(*e); |
| std::string title_line = MakeTitleLine(names.base_name); |
| writer->Printf("%s\n", title_line.c_str()); |
| writer->Puts("\n"); |
| PrintDocComments(e->description(), writer); |
| writer->Printf("typedef %s %s;\n", formatter.TypeName(e->underlying_type()).c_str(), |
| names.type_name.c_str()); |
| writer->Printf("\n"); |
| for (const auto& k : e->members()) { |
| auto v = e->ValueForMember(k); |
| PrintDocComments(v.description, writer); |
| writer->Printf("#define %s ((%s)%s)\n", formatter.FormatMember(*e, k).c_str(), |
| names.type_name.c_str(), formatter.FormatConstant(*e, v.value).c_str()); |
| } |
| writer->Puts("\n"); |
| } |
| |
| for (const auto& alias : library.type_aliases()) { |
| Type workaround_type; |
| if (AliasWorkaround(alias->original_name(), library, &workaround_type)) { |
| // Hide workaround types |
| continue; |
| } |
| |
| auto names = formatter.Format(*alias); |
| PrintDocComments(alias->description(), writer); |
| writer->Printf("typedef %s %s;\n", |
| formatter.TypeName(library.TypeFromName(alias->partial_type_ctor())).c_str(), |
| names.type_name.c_str()); |
| writer->Puts("\n"); |
| } |
| |
| for (const auto& table : library.tables()) { |
| bool all_required = true; |
| for (const auto& member : table->members()) { |
| if (member.required() == Required::kNo) { |
| all_required = false; |
| break; |
| } |
| } |
| |
| auto names = formatter.Format(*table); |
| std::string setter_macro_name = ToUpperAscii(names.c_struct_name) + "_SET"; |
| PrintDocComments(table->description(), writer); |
| if (!all_required) { |
| writer->Printf(R"(// |
| // Optional fields have corresponding presence indicators. When creating |
| // a new object, it is desirable to use the %s helper macro |
| // to set the fields, to avoid forgetting to change the presence indicator. |
| )", setter_macro_name.c_str()); |
| } |
| writer->Printf("typedef struct %s {", names.c_struct_name.c_str()); |
| writer->Puts("\n"); |
| // Pack optional fields together |
| for (const auto& member : table->members()) { |
| if (member.required() == Required::kYes) { |
| continue; |
| } |
| PrintDocComments(member.description(), writer, 1); |
| writer->Printf(" %s %s;\n", formatter.TypeName(member.type()).c_str(), |
| member.name().c_str()); |
| } |
| |
| if (!all_required) { |
| std::string presence_bits_name = names.c_struct_name + "_has_t"; |
| writer->Printf(R"( |
| // Presence indicator for these fields. |
| // |
| // If a particular field is absent, it should be set to zero/none, |
| // and the corresponding presence indicator will be false. |
| // Therefore, a completely empty |%s| may be conveniently |
| // obtained via value-initialization e.g. `%s a = {};`. |
| )", names.type_name.c_str(), names.type_name.c_str()); |
| writer->Printf(" struct %s {\n", presence_bits_name.c_str()); |
| for (const auto& member : table->members()) { |
| if (member.required() == Required::kYes) { |
| continue; |
| } |
| writer->Printf(" bool %s;\n", member.name().c_str()); |
| } |
| writer->Printf(" } has;\n"); |
| } |
| |
| // Followed by required fields |
| for (const auto& member : table->members()) { |
| if (member.required() == Required::kNo) { |
| continue; |
| } |
| PrintDocComments(member.description(), writer, 1); |
| writer->Printf(" %s %s;\n", formatter.TypeName(member.type()).c_str(), |
| member.name().c_str()); |
| } |
| |
| writer->Printf("} %s;\n", names.type_name.c_str()); |
| writer->Printf(R"( |
| #define %s(%s, field_name, value) \ |
| do { \ |
| %s* _tmp_%s= &(%s); \ |
| _tmp_%s->field_name = value; \ |
| _tmp_%s->has.field_name = true; \ |
| } while (0) |
| )", |
| setter_macro_name.c_str(), names.base_name.c_str(), names.type_name.c_str(), |
| names.base_name.c_str(), names.base_name.c_str(), names.base_name.c_str(), |
| names.base_name.c_str()); |
| writer->Puts("\n"); |
| } |
| |
| std::string epilogue = R"( |
| __END_CDECLS |
| |
| #endif // LIB_ZXIO_TYPES_H_ |
| )"; |
| writer->Printf("%s\n", TrimString(epilogue, "\n").c_str()); |
| |
| return true; |
| } |