// Copyright 2018 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 #include "banjo/ddk_generator.h" #include "banjo/attributes.h" #include "banjo/names.h" namespace banjo { namespace { // Various string values are looked up or computed in these // functions. Nothing else should be dealing in string literals, or // computing strings from these or AST values. constexpr const char kIndent[] = " "; // Functions named "Emit..." are called to actually emit to an std::ostream // is here. No other functions should directly emit to the streams. std::ostream& operator<<(std::ostream& stream, StringView view) { stream.rdbuf()->sputn(view.data(), view.size()); return stream; } std::string ToSnakeCase(StringView name, bool upper = false) { const auto is_upper = [](char c) { return c >= 'A' && c <= 'Z'; }; const auto is_lower = [](char c) { return c >= 'a' && c <= 'z'; }; const auto to_upper = [&](char c) { return is_lower(c) ? c + ('A' - 'a') : c; }; const auto to_lower = [&](char c) { return is_upper(c) ? c - ('A' - 'a') : c; }; std::string snake; snake += name[0]; for (auto it = name.begin() + 1; it != name.end(); ++it) { if (is_upper(*it) && *(it - 1) != '_' && !is_upper(*(it - 1))) { snake += '_'; } snake += *it; } if (upper) { std::transform(snake.begin(), snake.end(), snake.begin(), to_upper); } else { std::transform(snake.begin(), snake.end(), snake.begin(), to_lower); } return snake; } std::string ToLispCase(StringView name) { std::string lisp = ToSnakeCase(name); const auto to_lisp = [&](char c) { return c == '_' ? '-' : c; }; std::transform(lisp.begin(), lisp.end(), lisp.begin(), to_lisp); return lisp; } std::string NameBuffer(const DdkGenerator::Member& member) { return member.name + (member.element_type == "void" ? "_buffer" : "_list"); } std::string NameCount(const DdkGenerator::Member& member) { return member.name + (member.element_type == "void" ? "_size" : "_count"); } bool ReturnFirst(const std::vector& output) { return output.size() > 0 && (output[0].kind == flat::Type::Kind::kPrimitive || (output[0].kind == flat::Type::Kind::kIdentifier && output[0].decl_kind == flat::Decl::Kind::kEnum)); } void EmitFileComment(std::ostream* file, banjo::StringView name) { *file << "// Copyright 2018 The Fuchsia Authors. All rights reserved.\n"; *file << "// Use of this source code is governed by a BSD-style license that can be\n"; *file << "// found in the LICENSE file.\n\n"; *file << "// WARNING: THIS FILE IS MACHINE GENERATED. DO NOT EDIT.\n"; *file << "// MODIFY system/banjo/ddk-protocol-" << name << "/" << name << ".banjo INSTEAD.\n\n"; } void EmitHeaderGuard(std::ostream* file) { // TODO(704) Generate an appropriate header guard name. *file << "#pragma once\n"; } void EmitIncludeHeader(std::ostream* file, StringView header) { *file << "#include " << header << "\n"; } void EmitNamespacePrologue(std::ostream* file, StringView name) { *file << "namespace " << name << " {\n"; } void EmitNamespaceEpilogue(std::ostream* file, StringView name) { *file << "} // namespace " << name << "\n"; } void EmitBlank(std::ostream* file) { *file << "\n"; } std::vector SplitString(const std::string& src, char delimiter) { std::vector result; if (src.empty()) return result; size_t start = 0; while (start != std::string::npos) { const size_t end = src.find(delimiter, start); if (end == std::string::npos) { StringView view(&src[start], src.size() - start); if (!view.empty()) result.push_back(view); start = std::string::npos; } else { StringView view(&src[start], end - start); if (!view.empty()) result.push_back(view); start = end + 1; } } return result; } template void EmitDocstring(std::ostream* file, const T& decl, bool indent) { if (!decl.doc.empty()) { const auto lines = SplitString(decl.doc, '\n'); for (auto line : lines) { if (indent) { *file << kIndent; } *file << "//" << line << "\n"; } } } void EmitMemberDecl(std::ostream* file, const DdkGenerator::Member& member, bool output = false) { const auto member_name = (output ? "* " : " ") + member.name; switch (member.kind) { case flat::Type::Kind::kArray: *file << member.type << member_name; for (uint32_t array_count : member.array_counts) { *file << "[" << array_count << "]"; } break; case flat::Type::Kind::kVector: if (output) { *file << member.element_type << (output ? "* " : " ") << NameBuffer(member) << ";\n" << kIndent << "size_t " << NameCount(member) << ";\n" << kIndent << "size_t" << member_name << "_actual"; } else { const auto prefix = member.nullability == types::Nullability::kNullable ? "" : "const "; *file << prefix << member.element_type << (output ? "** " : "* ") << NameBuffer(member) << ";\n" << kIndent << "size_t " << NameCount(member); } break; case flat::Type::Kind::kString: if (member.array_counts.size() > 0) { *file << "char " << member_name; for (uint32_t array_count : member.array_counts) { *file << "[" << array_count << "]"; } } else { *file << member.type << member_name; } break; case flat::Type::Kind::kHandle: case flat::Type::Kind::kRequestHandle: case flat::Type::Kind::kPrimitive: *file << member.type << member_name; break; case flat::Type::Kind::kIdentifier: switch (member.decl_kind) { case flat::Decl::Kind::kConst: assert(false && "bad decl kind for member"); break; case flat::Decl::Kind::kEnum: *file << member.type << member_name; break; case flat::Decl::Kind::kInterface: *file << member.type << (output ? "*" : "") << member_name; break; case flat::Decl::Kind::kStruct: case flat::Decl::Kind::kUnion: *file << member.type << member_name; break; } break; } } void EmitMethodInParamDecl(std::ostream* file, const DdkGenerator::Member& member, bool emit_name = true) { const auto member_name = emit_name ? " " + member.name : ""; switch (member.kind) { case flat::Type::Kind::kArray: *file << "const " << member.type << member_name; for (uint32_t array_count : member.array_counts) { *file << "[" << array_count << "]"; } break; case flat::Type::Kind::kVector: if (emit_name) { *file << "const " << member.element_type << "* " << NameBuffer(member) << ", " << "size_t " << NameCount(member); } else { *file << "const " << member.element_type << "*, size_t"; } break; case flat::Type::Kind::kString: case flat::Type::Kind::kHandle: case flat::Type::Kind::kRequestHandle: case flat::Type::Kind::kPrimitive: *file << member.type << member_name; break; case flat::Type::Kind::kIdentifier: switch (member.decl_kind) { case flat::Decl::Kind::kConst: assert(false && "bad decl kind for member"); break; case flat::Decl::Kind::kEnum: *file << member.type << member_name; break; case flat::Decl::Kind::kInterface: *file << member.type << "*" << member_name; break; case flat::Decl::Kind::kStruct: case flat::Decl::Kind::kUnion: switch (member.nullability) { case types::Nullability::kNullable: // TODO: We are using nullability as a proxy for const... *file << member.type << member_name; break; case types::Nullability::kNonnullable: *file << "const " << member.type << "*" << member_name; break; } break; } break; } } void EmitMethodOutParamDecl(std::ostream* file, const DdkGenerator::Member& member, bool emit_name = true) { const auto member_name = emit_name ? " out_" + member.name : ""; switch (member.kind) { case flat::Type::Kind::kArray: *file << member.type << member_name; for (uint32_t array_count : member.array_counts) { *file << "[" << array_count << "]"; } case flat::Type::Kind::kVector: { const auto buffer_name = emit_name ? " out_" + NameBuffer(member) : ""; const auto count_name = emit_name ? " " + NameCount(member) : ""; const auto actual_name = emit_name ? member_name + "_actual" : ""; switch (member.nullability) { case types::Nullability::kNullable: *file << member.element_type << "**" << buffer_name << ", " << "size_t*" << count_name; break; case types::Nullability::kNonnullable: *file << member.element_type << "*" << buffer_name << ", " << "size_t" << count_name << ", " << "size_t*" << actual_name; break; } break; } case flat::Type::Kind::kString: if (emit_name) { *file << "char*" << member_name << ", " << "size_t " << member.name << "_capacity"; } else { *file << "char*, size_t"; } break; case flat::Type::Kind::kHandle: case flat::Type::Kind::kRequestHandle: case flat::Type::Kind::kPrimitive: *file << member.type << "*" << member_name; break; case flat::Type::Kind::kIdentifier: switch (member.decl_kind) { case flat::Decl::Kind::kConst: assert(false && "bad decl kind for member"); break; case flat::Decl::Kind::kEnum: case flat::Decl::Kind::kInterface: case flat::Decl::Kind::kStruct: case flat::Decl::Kind::kUnion: *file << member.type << "*" << member_name; break; } break; } } void EmitMethodDeclHelper(std::ostream* file, StringView method_name, const std::vector& input, const std::vector& output, StringView ctx) { const bool return_first = ReturnFirst(output); if (return_first) { *file << output[0].type << " "; } else { *file << "void "; } *file << method_name << "("; if (!ctx.empty()) { *file << ctx; } bool first = ctx.empty(); for (const auto& member : input) { if (first) { first = false; } else { *file << ", "; } EmitMethodInParamDecl(file, member); } for (auto member = output.begin() + (return_first ? 1 : 0); member != output.end(); member++) { if (first) { first = false; } else { *file << ", "; } EmitMethodOutParamDecl(file, *member); } } void EmitProtocolMethodDecl(std::ostream* file, StringView method_name, const std::vector& input, const std::vector& output) { EmitMethodDeclHelper(file, method_name, input, output, ""); } void EmitProtocolMethodWithCtxDecl(std::ostream* file, StringView method_name, const std::vector& input, const std::vector& output) { EmitMethodDeclHelper(file, method_name, input, output, "void* ctx"); } void EmitProtocolMethodWithSpecificCtxDecl(std::ostream* file, const std::string& protocol_name, banjo::StringView method_name, const std::vector& input, const std::vector& output) { EmitMethodDeclHelper(file, method_name, input, output, "const " + protocol_name + "_t* proto"); } void EmitProtocolMethodPtrDecl(std::ostream* file, const std::string& method_name, const std::vector& input, const std::vector& output) { EmitMethodDeclHelper(file, "(*" + method_name + ")", input, output, "void* ctx"); } void EmitProtocolMethodTemplateDecl(std::ostream* file, const std::vector& input, const std::vector& output) { EmitMethodDeclHelper(file, "(C::*)", input, output, ""); *file << "));\n"; } void EmitMethodImplHelper(std::ostream* file, StringView method_name, const std::vector& input, const std::vector& output, StringView ctx, bool save_ret=false) { const bool return_first = ReturnFirst(output); if (return_first) *file << (save_ret ? "auto ret = " : "return "); *file << method_name << "("; if (!ctx.empty()) { *file << ctx; } bool first = ctx.empty(); for (const auto& member : input) { if (first) { first = false; } else { *file << ", "; } if (member.kind == flat::Type::Kind::kVector) { *file << NameBuffer(member) << ", " << NameCount(member); } else { *file << member.name; } } for (auto member = output.begin() + (return_first ? 1 : 0); member != output.end(); member++) { if (first) { first = false; } else { *file << ", "; } if (member->kind == flat::Type::Kind::kVector) { *file << "out_" << NameBuffer(*member) << ", " << NameCount(*member); if (member->nullability == types::Nullability::kNonnullable) { *file << ", out_" << member->name << "_actual"; } } else if (member->kind == flat::Type::Kind::kString) { *file << "out_" << member->name << ", " << member->name << "_capacity"; } else { *file << (member->address_of ? "&" : "") << "out_" << member->name; } } } void EmitDdkProtocolMethodImpl(std::ostream* file, const std::string& method_name, const std::vector& input, const std::vector& output) { EmitMethodImplHelper(file, "proto->ops->" + method_name, input, output, "proto->ctx"); *file << ");\n"; } void EmitDdktlProtocolMethodImpl(std::ostream* file, const std::string& method_name, std::vector input, std::vector output, bool handle_wrappers) { if (handle_wrappers) { for (auto& member : input) { if (member.kind == flat::Type::Kind::kHandle) { member.name = member.type + "(" + member.name + ")"; } } for (auto& member : output) { if (member.kind == flat::Type::Kind::kHandle) { *file << kIndent << kIndent << member.type << " out_" << member.name << "2;\n"; member.name = member.name + "2"; member.address_of = true; } } *file << kIndent << kIndent; EmitMethodImplHelper(file, "static_cast(ctx)->" + method_name, input, output, "", true); *file << ");\n"; for (auto& member : output) { if (member.kind == flat::Type::Kind::kHandle) { *file << kIndent << kIndent << "*out_" << member.name.substr(0, member.name.size() - 1) << " = out_" << member.name << ".release();\n"; } } if (ReturnFirst(output)) { *file << kIndent << kIndent << "return ret;\n"; } } else { *file << kIndent << kIndent; EmitMethodImplHelper(file, "static_cast(ctx)->" + method_name, input, output, ""); *file << ");\n"; } } void EmitClientMethodImpl(std::ostream* file, const std::string& method_name, std::vector& input, std::vector& output, bool handle_wrappers) { if (handle_wrappers) { for (auto& member : input) { if (member.kind == flat::Type::Kind::kHandle) { member.name = member.name + ".release()"; } } for (auto& member : output) { if (member.kind == flat::Type::Kind::kHandle) { member.name = member.name + "->reset_and_get_address()"; } } } EmitMethodImplHelper(file, "ops_->" + method_name, input, output, "ctx_"); *file << ");\n"; } void EmitCallbackMethodImpl(std::ostream* file, const std::string& method_name, const std::vector& members) { *file << kIndent << "struct " << method_name << "_callback_context* ctx = cookie;\n"; EmitBlank(file); for (const auto& member : members) { const auto& name = member.name; switch (member.kind) { case flat::Type::Kind::kArray: *file << kIndent << "memcpy(ctx->" << name << ", " << name << ", sizeof(" << name << "));\n"; break; case flat::Type::Kind::kVector: *file << kIndent << "memcpy(ctx->" << NameBuffer(member) << ", " << NameBuffer(member) << ", sizeof(*" << NameBuffer(member) << ") * " << NameCount(member) << ");\n"; *file << kIndent << "*ctx->" << name << "_actual = " << NameCount(member) << ";\n"; break; case flat::Type::Kind::kString: *file << kIndent << "strcpy(ctx->" << name << ", " << name << ");\n"; break; case flat::Type::Kind::kHandle: case flat::Type::Kind::kRequestHandle: case flat::Type::Kind::kPrimitive: *file << kIndent << "*ctx->" << name << " = " << name << ";\n"; break; case flat::Type::Kind::kIdentifier: switch (member.decl_kind) { case flat::Decl::Kind::kConst: assert(false && "bad decl kind for member"); break; case flat::Decl::Kind::kEnum: case flat::Decl::Kind::kInterface: *file << kIndent << "*ctx->" << name << " = " << name << ";\n"; break; case flat::Decl::Kind::kStruct: case flat::Decl::Kind::kUnion: switch (member.nullability) { case types::Nullability::kNullable: *file << kIndent << "if (" << name << ") {\n"; *file << kIndent << kIndent << "*ctx->" << name << " = *" << name << ";\n"; *file << kIndent << "} else {\n"; // We don't have a great way of signaling that the optional response member // was not in the message. That means these bindings aren't particularly // useful when the client needs to extract that bit. The best we can do is // zero out the value to make sure the client has defined behavior. // // In many cases, the response contains other information (e.g., a status code) // that lets the client do something reasonable. *file << kIndent << kIndent << "memset(ctx->" << name << ", 0, sizeof(*ctx->" << name << "));\n"; *file << kIndent << "}\n"; break; case types::Nullability::kNonnullable: *file << kIndent << "*ctx->" << name << " = *" << name << ";\n"; break; } break; } break; } } EmitBlank(file); *file << kIndent << "sync_completion_signal(&ctx->completion);\n"; } void EmitSyncMethodImpl(std::ostream* file, const std::string& protocol_name, const std::string& method_name, const std::vector& input, const std::vector& members) { *file << kIndent << "struct " << method_name << "_callback_context ctx;\n"; *file << kIndent << "sync_completion_reset(&ctx.completion);\n"; const bool return_first = ReturnFirst(members); if (return_first) { *file << kIndent << members[0].type << " _" << members[0].name << ";\n"; *file << kIndent << members[0].type << "* out_" << members[0].name << " = &_" << members[0].name << ";\n"; } EmitBlank(file); for (const auto& member : members) { const auto& name = member.name; switch (member.kind) { case flat::Type::Kind::kArray: *file << kIndent << "ctx." << name << " = out_" << name << "\n"; break; case flat::Type::Kind::kVector: *file << kIndent << "ctx." << NameBuffer(member) << " = out_" << NameBuffer(member) << ";\n"; *file << kIndent << "ctx." << NameCount(member) << " = " << NameCount(member) << ";\n"; *file << kIndent << "ctx." << name << "_actual = out_" << name << "_actual;\n"; break; case flat::Type::Kind::kString: *file << kIndent << "ctx." << name << " = out_" << name << ";\n"; *file << kIndent << "ctx." << name << "capacity = out_" << name << "capacity;\n"; break; case flat::Type::Kind::kHandle: case flat::Type::Kind::kRequestHandle: case flat::Type::Kind::kPrimitive: *file << kIndent << "ctx." << name << " = out_" << name << ";\n"; break; case flat::Type::Kind::kIdentifier: switch (member.decl_kind) { case flat::Decl::Kind::kConst: assert(false && "bad decl kind for member"); break; case flat::Decl::Kind::kEnum: case flat::Decl::Kind::kInterface: *file << kIndent << "ctx." << name << " = out_" << name << ";\n"; break; case flat::Decl::Kind::kStruct: case flat::Decl::Kind::kUnion: switch (member.nullability) { case types::Nullability::kNullable: *file << kIndent << "ctx." << name << " = out_" << name << ";\n"; break; case types::Nullability::kNonnullable: *file << kIndent << "ctx." << name << " = out_" << name << ";\n"; break; } break; } break; } } EmitBlank(file); *file << kIndent; EmitMethodImplHelper(file, method_name, input, {}, protocol_name); *file << ", " << method_name << "_cb, &ctx);\n"; *file << kIndent << "zx_status_t status = sync_completion_wait(&ctx.completion, ZX_TIME_INFINITE);\n"; if (return_first) { *file << kIndent << "if (status != ZX_OK) {\n"; *file << kIndent << kIndent << "return status;\n"; *file << kIndent << "}\n"; *file << kIndent << "return _" << members[0].name << ";\n"; } else { *file << kIndent << "assert(status == ZX_OK);\n"; } } // Various computational helper routines. void EnumValue(types::PrimitiveSubtype type, const flat::Constant* constant, const flat::Library* library, std::string* out_value) { std::ostringstream member_value; switch (type) { case types::PrimitiveSubtype::kInt8: { int8_t value; bool success = library->ParseIntegerConstant(constant, &value); if (!success) { __builtin_trap(); } // The char-sized overloads of operator<< here print // the character value, not the numeric value, so cast up. member_value << static_cast(value); break; } case types::PrimitiveSubtype::kInt16: { int16_t value; bool success = library->ParseIntegerConstant(constant, &value); if (!success) { __builtin_trap(); } member_value << value; break; } case types::PrimitiveSubtype::kInt32: { int32_t value; bool success = library->ParseIntegerConstant(constant, &value); if (!success) { __builtin_trap(); } member_value << value; break; } case types::PrimitiveSubtype::kInt64: { int64_t value; bool success = library->ParseIntegerConstant(constant, &value); if (!success) { __builtin_trap(); } member_value << value; break; } case types::PrimitiveSubtype::kUint8: { uint8_t value; bool success = library->ParseIntegerConstant(constant, &value); if (!success) { __builtin_trap(); } // The char-sized overloads of operator<< here print // the character value, not the numeric value, so cast up. member_value << static_cast(value); break; } case types::PrimitiveSubtype::kUint16: { uint16_t value; bool success = library->ParseIntegerConstant(constant, &value); if (!success) { __builtin_trap(); } member_value << value; break; } case types::PrimitiveSubtype::kUint32: { uint32_t value; bool success = library->ParseIntegerConstant(constant, &value); if (!success) { __builtin_trap(); } member_value << value; break; } case types::PrimitiveSubtype::kUint64: { uint64_t value; bool success = library->ParseIntegerConstant(constant, &value); if (!success) { __builtin_trap(); } member_value << value; break; } case types::PrimitiveSubtype::kBool: case types::PrimitiveSubtype::kFloat32: case types::PrimitiveSubtype::kFloat64: case types::PrimitiveSubtype::kUSize: case types::PrimitiveSubtype::kISize: case types::PrimitiveSubtype::kVoidPtr: assert(false && "bad primitive type for an enum"); break; } *out_value = member_value.str(); } std::vector ArrayCounts(const flat::Library* library, const flat::Type* type) { std::vector array_counts; for (;;) { switch (type->kind) { default: { return array_counts; } case flat::Type::Kind::kArray: { auto array_type = static_cast(type); uint32_t element_count = array_type->element_count.Value(); array_counts.push_back(element_count); type = array_type->element_type.get(); continue; } case flat::Type::Kind::kString: { auto str_type = static_cast(type); uint32_t max_size = str_type->max_size.Value(); if (max_size < flat::Size::Max().Value()) array_counts.push_back(max_size); return array_counts; } } } } flat::Decl::Kind GetDeclKind(const flat::Library* library, const flat::Type* type) { if (type->kind != flat::Type::Kind::kIdentifier) return flat::Decl::Kind::kConst; auto identifier_type = static_cast(type); auto named_decl = library->LookupDeclByName(identifier_type->name); assert(named_decl && "library must contain declaration"); return named_decl->kind; } std::string HandleToZxWrapper(const flat::HandleType* handle_type) { switch (handle_type->subtype) { case types::HandleSubtype::kHandle: return "zx::handle"; case types::HandleSubtype::kProcess: return "zx::process"; case types::HandleSubtype::kThread: return "zx::thread"; case types::HandleSubtype::kVmo: return "zx::vmo"; case types::HandleSubtype::kChannel: return "zx::channel"; case types::HandleSubtype::kEvent: return "zx::event"; case types::HandleSubtype::kPort: return "zx::port"; case types::HandleSubtype::kInterrupt: return "zx::interrupt"; case types::HandleSubtype::kLog: return "zx::debuglog"; case types::HandleSubtype::kSocket: return "zx::socket"; case types::HandleSubtype::kResource: return "zx::resource"; case types::HandleSubtype::kEventpair: return "zx::eventpair"; case types::HandleSubtype::kJob: return "zx::job"; case types::HandleSubtype::kVmar: return "zx::vmar"; case types::HandleSubtype::kFifo: return "zx::fifo"; case types::HandleSubtype::kGuest: return "zx::guest"; case types::HandleSubtype::kTimer: return "zx::timer"; case types::HandleSubtype::kBti: return "zx::bti"; case types::HandleSubtype::kProfile: return "zx::profile"; default: { abort(); } } } std::string NameType(const flat::Type* type, const flat::Decl::Kind& decl_kind, bool handle_wrappers=false) { for (;;) { switch (type->kind) { case flat::Type::Kind::kHandle: case flat::Type::Kind::kRequestHandle: { if (handle_wrappers) { auto handle_type = static_cast(type); return HandleToZxWrapper(handle_type); } else { return "zx_handle_t"; } } case flat::Type::Kind::kString: return "const char*"; case flat::Type::Kind::kPrimitive: { auto primitive_type = static_cast(type); if (primitive_type->subtype == types::PrimitiveSubtype::kInt32) return "zx_status_t"; return NamePrimitiveCType(primitive_type->subtype); } case flat::Type::Kind::kArray: { auto array_type = static_cast(type); type = array_type->element_type.get(); continue; } case flat::Type::Kind::kVector: { auto vector_type = static_cast(type); type = vector_type->element_type.get(); continue; } case flat::Type::Kind::kIdentifier: { auto identifier_type = static_cast(type); switch (decl_kind) { case flat::Decl::Kind::kConst: case flat::Decl::Kind::kEnum: case flat::Decl::Kind::kStruct: case flat::Decl::Kind::kUnion: { std::string name = identifier_type->name.name().data(); name = ToSnakeCase(name) + "_t"; if (identifier_type->nullability == types::Nullability::kNullable) { name.push_back('*'); } return name; } case flat::Decl::Kind::kInterface: { return std::string( "const " + ToSnakeCase(identifier_type->name.name().data()) + "_t"); } default: { abort(); } } } default: { abort(); } } } } template DdkGenerator::Member CreateMember(const flat::Library* library, const T& decl, bool handle_wrappers=false) { std::string name = NameIdentifier(decl.name); const flat::Type* type = decl.type.get(); auto decl_kind = GetDeclKind(library, type); auto type_name = NameType(type, decl_kind, handle_wrappers); std::vector array_counts = ArrayCounts(library, type); std::string element_type_name; std::string doc = decl.GetAttribute("Doc"); if (type->kind == flat::Type::Kind::kVector) { auto vector_type = static_cast(type); const flat::Type* element_type = vector_type->element_type.get(); element_type_name = NameType(element_type, GetDeclKind(library, element_type)); } types::Nullability nullability = types::Nullability::kNonnullable; if (type->kind == flat::Type::Kind::kIdentifier) { auto identifier_type = static_cast(type); nullability = identifier_type->nullability; } else if (type->kind == flat::Type::Kind::kVector) { auto identifier_type = static_cast(type); nullability = identifier_type->nullability; } return DdkGenerator::Member{ type->kind, decl_kind, std::move(type_name), std::move(name), std::move(element_type_name), std::move(doc), std::move(array_counts), nullability, }; } template std::vector GenerateMembers(const flat::Library* library, const std::vector& decl_members) { std::vector members; members.reserve(decl_members.size()); for (const auto& member : decl_members) { members.push_back(CreateMember(library, member, false)); } return members; } void GetMethodParameters(const flat::Library* library, const DdkGenerator::NamedMethod& method_info, std::vector* input, std::vector* output, bool handle_wrappers=false) { input->reserve(method_info.input_parameters.size() + (method_info.async ? 2 : 0)); for (const auto& parameter : method_info.input_parameters) { input->push_back(CreateMember(library, parameter, handle_wrappers)); } if (method_info.async) { input->push_back(DdkGenerator::Member{ .kind = flat::Type::Kind::kIdentifier, .decl_kind = flat::Decl::Kind::kStruct, .type = ToSnakeCase(method_info.protocol_name) + "_callback", .name = "callback", .element_type = "", .array_counts = {}, .nullability = types::Nullability::kNullable, }); input->push_back(DdkGenerator::Member{ .kind = flat::Type::Kind::kPrimitive, .decl_kind = flat::Decl::Kind::kStruct, .type = "void*", .name = "cookie", .element_type = "", .array_counts = {}, .nullability = types::Nullability::kNullable, }); } else { output->reserve(method_info.output_parameters.size()); for (const auto& parameter : method_info.output_parameters) { output->push_back(CreateMember(library, parameter, handle_wrappers)); } } } } // namespace void DdkGenerator::GeneratePrologues() { EmitFileComment(&file_, library_->name().back()); EmitHeaderGuard(&file_); EmitBlank(&file_); for (const auto& dep_library : library_->dependencies()) { if (dep_library == library_) continue; if (dep_library->HasAttribute("Internal")) continue; EmitIncludeHeader(&file_, "<" + ToLispCase(StringJoin(dep_library->name(), "/")) + ".h>"); } EmitIncludeHeader(&file_, ""); EmitIncludeHeader(&file_, ""); EmitBlank(&file_); file_ << "__BEGIN_CDECLS;\n"; } void DdktlGenerator::GeneratePrologues() { EmitFileComment(&file_, library_->name().back()); EmitHeaderGuard(&file_); EmitBlank(&file_); EmitIncludeHeader(&file_, ""); EmitIncludeHeader(&file_, "<" + ToLispCase(LibraryName(library_, "/")) + ".h>"); for (const auto& dep_library : library_->dependencies()) { if (dep_library == library_) continue; if (dep_library->HasAttribute("Internal")) continue; EmitIncludeHeader(&file_, "<" + ToLispCase(StringJoin(dep_library->name(), "/")) + ".h>"); } EmitIncludeHeader(&file_, ""); EmitIncludeHeader(&file_, ""); EmitIncludeHeader(&file_, ""); EmitIncludeHeader(&file_, ""); // Enumerate list of includes based on zx_handle_t wrappers. std::set includes; std::map named_interfaces = NameInterfaces(library_->interface_declarations_); for (const auto& named_interface : named_interfaces) { for (const auto& method_info : named_interface.second.methods) { std::vector input; std::vector output; GetMethodParameters(library_, method_info, &input, &output, true); for (const auto& member : input) { if (member.kind == flat::Type::Kind::kHandle) { includes.insert(std::string(member.type.substr(4))); } } for (const auto& member : output) { if (member.kind == flat::Type::Kind::kHandle) { includes.insert(std::string(member.type.substr(4))); } } } } for (const auto& include : includes) { EmitIncludeHeader(&file_, ""); } EmitBlank(&file_); const auto& libname = library_->name(); EmitIncludeHeader(&file_, "\"" + ToLispCase(libname.back()) + "-internal.h\""); EmitBlank(&file_); } void DdkGenerator::GenerateEpilogues() { file_ << "__END_CDECLS;\n"; } void DdktlGenerator::GenerateEpilogues() { EmitNamespaceEpilogue(&file_, "ddk"); } void DdkGenerator::GenerateIntegerDefine(StringView name, types::PrimitiveSubtype subtype, StringView value) { std::string literal_macro = NamePrimitiveIntegerCConstantMacro(subtype); file_ << "#define " << name << " " << literal_macro << "(" << value << ")\n"; } void DdkGenerator::GeneratePrimitiveDefine(StringView name, types::PrimitiveSubtype subtype, StringView value) { switch (subtype) { case types::PrimitiveSubtype::kInt8: case types::PrimitiveSubtype::kInt16: case types::PrimitiveSubtype::kInt32: case types::PrimitiveSubtype::kInt64: case types::PrimitiveSubtype::kUint8: case types::PrimitiveSubtype::kUint16: case types::PrimitiveSubtype::kUint32: case types::PrimitiveSubtype::kUint64: { std::string literal_macro = NamePrimitiveIntegerCConstantMacro(subtype); file_ << "#define " << name << " " << literal_macro << "(" << value << ")\n"; break; } case types::PrimitiveSubtype::kBool: case types::PrimitiveSubtype::kFloat32: case types::PrimitiveSubtype::kFloat64: { file_ << "#define " << name << " " << "(" << value << ")\n"; break; } default: abort(); } } void DdkGenerator::GenerateStringDefine(StringView name, StringView value) { file_ << "#define " << name << " " << value << "\n"; } #if 0 void DdktlGenerator::GeneratePrimitiveDefine(StringView name, types::PrimitiveSubtype subtype, StringView value) { std::string underlying_type = NamePrimitiveCType(subtype); file_ << "constexpr " << underlying_type << " " << name << " = " << value << ";\n"; } void DdktlGenerator::GenerateStringDefine(StringView name, StringView value) { file_ << "constexpr const char" << name << "[] = " << value << "\n"; } #endif void DdkGenerator::GenerateIntegerTypedef(types::PrimitiveSubtype subtype, StringView name) { std::string underlying_type = NamePrimitiveCType(subtype); file_ << "typedef " << underlying_type << " " << name << ";\n"; } void DdkGenerator::GenerateStructTypedef(StringView name, StringView type_name) { file_ << "typedef struct " << name << " " << type_name << ";\n"; } void DdkGenerator::GenerateUnionTypedef(StringView name, StringView type_name) { file_ << "typedef union " << name << " " << type_name << ";\n"; } void DdkGenerator::GenerateStructDeclaration(StringView name, const std::vector& members, bool packed, bool helper) { file_ << "struct " << name << " {\n"; bool first = true; for (const auto& member : members) { if (!helper) { EmitDocstring(&file_, member, true); } file_ << kIndent; EmitMemberDecl(&file_, member, helper && !first); file_ << ";\n"; if (first) first = false; } if (packed) { file_ << "} __attribute__((__packed__));\n"; } else { file_ << "};\n"; } } void DdkGenerator::GenerateTaggedUnionDeclaration(StringView name, const std::vector& members) { file_ << "union " << name << " {\n"; for (const auto& member : members) { EmitDocstring(&file_, member, true); file_ << kIndent; EmitMemberDecl(&file_, member); file_ << ";\n"; } file_ << "};\n"; } // TODO(TO-702) These should maybe check for global name // collisions? Otherwise, is there some other way they should fail? std::map DdkGenerator::NameConsts(const std::vector>& const_infos) { std::map named_consts; for (const auto& const_info : const_infos) { std::string doc = const_info->GetAttribute("Doc"); named_consts.emplace(const_info.get(), NamedConst{NameIdentifier(const_info->name.name()), std::move(doc), *const_info}); } return named_consts; } #if 0 std::map DdktlGenerator::NameConsts(const std::vector>& const_infos) { std::map named_consts; for (const auto& const_info : const_infos) { named_consts.emplace(const_info.get(), NamedConst{NameIdentifier(const_info->name.name()), *const_info}); } return named_consts; } #endif std::map DdkGenerator::NameEnums(const std::vector>& enum_infos) { std::map named_enums; for (const auto& enum_info : enum_infos) { std::string enum_name = ToSnakeCase(enum_info->name.name().data(), true); std::string type_name = ToSnakeCase(enum_info->name.name().data()) + "_t"; std::string doc = enum_info->GetAttribute("Doc"); named_enums.emplace(enum_info.get(), NamedEnum{std::move(enum_name), std::move(type_name), std::move(doc), *enum_info}); } return named_enums; } #if 0 std::map DdktlGenerator::NameEnums(const std::vector>& enum_infos) { std::map named_enums; for (const auto& enum_info : enum_infos) { std::string enum_name = enum_info->name.name().data(); named_enums.emplace(enum_info.get(), NamedEnum{enum_name, enum_name, *enum_info}); } return named_enums; } #endif std::map DdkGenerator::NameInterfaces(const std::vector>& interface_infos) { std::map named_interfaces; for (const auto& interface_info : interface_infos) { const auto layout = interface_info->GetAttribute("Layout"); InterfaceType intf_type; std::string name = interface_info->name.name().data(); if (layout == "ddk-protocol") { name += "Protocol"; intf_type = InterfaceType::kProtocol; } else if (layout == "ddk-interface") { intf_type = InterfaceType::kInterface; } else if (layout == "ddk-callback") { intf_type = InterfaceType::kCallback; } else { continue; } NamedInterface named_interface; named_interface.type = intf_type; named_interface.shortname = interface_info->name.name().data(); named_interface.camel_case_name = name; named_interface.snake_case_name = ToSnakeCase(std::move(name)); named_interface.doc = interface_info->GetAttribute("Doc"); for (const auto& method_pointer : interface_info->all_methods) { assert(method_pointer != nullptr); const auto& method = *method_pointer; std::string c_name = ToSnakeCase(method.name.data()); NamedMethod named_method = { .async = method.HasAttribute("Async"), .generate_sync_method = method.HasAttribute("GenerateSync"), .c_name = c_name, .protocol_name = ToSnakeCase(named_interface.shortname) + "_" + c_name, .proxy_name = "", .doc = method.GetAttribute("Doc"), .input_parameters = method.maybe_request->parameters, .output_parameters = method.maybe_response->parameters, }; named_interface.methods.push_back(std::move(named_method)); } named_interfaces.emplace(interface_info.get(), std::move(named_interface)); } return named_interfaces; } std::map DdktlGenerator::NameInterfaces(const std::vector>& interface_infos) { std::map named_interfaces; for (const auto& interface_info : interface_infos) { const auto layout = interface_info->GetAttribute("Layout"); InterfaceType intf_type; std::string name = interface_info->name.name().data(); if (layout == "ddk-protocol") { name += "Protocol"; intf_type = InterfaceType::kProtocol; } else if (layout == "ddk-interface") { intf_type = InterfaceType::kInterface; } else if (layout == "ddk-callback") { intf_type = InterfaceType::kCallback; } else { continue; } NamedInterface named_interface; named_interface.type = intf_type; named_interface.shortname = interface_info->name.name().data(); named_interface.camel_case_name = name; named_interface.snake_case_name = ToSnakeCase(std::move(name)); named_interface.doc = interface_info->GetAttribute("Doc"); named_interface.handle_wrappers = interface_info->HasAttribute("HandleWrappers"); for (const auto& method_pointer : interface_info->all_methods) { assert(method_pointer != nullptr); const auto& method = *method_pointer; std::string protocol_name = NameIdentifier(interface_info->name.name()) + NameIdentifier(method.name); std::string c_name = ToSnakeCase(method.name.data()); std::string proxy_name = NameIdentifier(method.name); NamedMethod named_method = { .async = method.HasAttribute("Async"), .generate_sync_method = method.HasAttribute("GenerateSync"), .c_name = c_name, .protocol_name = protocol_name, .proxy_name = proxy_name, .doc = method.GetAttribute("Doc"), .input_parameters = method.maybe_request->parameters, .output_parameters = method.maybe_response->parameters, }; named_interface.methods.push_back(std::move(named_method)); } named_interfaces.emplace(interface_info.get(), std::move(named_interface)); } return named_interfaces; } std::map DdkGenerator::NameStructs(const std::vector>& struct_infos) { std::map named_structs; for (const auto& struct_info : struct_infos) { const bool packed = struct_info->HasAttribute("Packed"); std::string name = ToSnakeCase(struct_info->name.name().data()); std::string type_name = name + "_t"; std::string doc = struct_info->GetAttribute("Doc"); named_structs.emplace(struct_info.get(), NamedStruct{std::move(name), std::move(type_name), std::move(doc), packed, *struct_info}); } return named_structs; } #if 0 std::map DdktlGenerator::NameStructs(const std::vector>& struct_infos) { std::map named_structs; for (const auto& struct_info : struct_infos) { const bool packed = struct_info->HasAttribute("Packed"); if (struct_info->GetAttribute("repr") == "C") { std::string name = ToSnakeCase(struct_info->name.name().data()); std::string type_name = name + "_t"; named_structs.emplace(struct_info.get(), NamedStruct{std::move(name), std::move(type_name), packed, *struct_info}); } else { const std::string name = struct_info->name.name().data(); named_structs.emplace(struct_info.get(), NamedStruct{name, name, packed, *struct_info}); } } return named_structs; } #endif std::map DdkGenerator::NameUnions(const std::vector>& union_infos) { std::map named_unions; for (const auto& union_info : union_infos) { std::string union_name = ToSnakeCase(union_info->name.name().data()); std::string type_name = union_name + "_t"; std::string doc = union_info->GetAttribute("Doc"); named_unions.emplace(union_info.get(), NamedUnion{std::move(union_name), std::move(type_name), std::move(doc), *union_info}); } return named_unions; } #if 0 std::map DdktlGenerator::NameUnions(const std::vector>& union_infos) { std::map named_unions; for (const auto& union_info : union_infos) { if (union_info->GetAttribute("repr") == "C") { std::string union_name = ToSnakeCase(union_info->name.name().data()); std::string type_name = union_name + "_t"; named_unions.emplace(union_info.get(), NamedUnion{std::move(union_name), std::move(type_name), *union_info}); } else { const std::string union_name = union_info->name.name().data(); named_unions.emplace(union_info.get(), NamedUnion{union_name, union_name, *union_info}); } } return named_unions; } #endif void DdkGenerator::ProduceConstForwardDeclaration(const NamedConst& named_const) { // TODO(TO-702) } void DdkGenerator::ProduceProtocolForwardDeclaration(const NamedInterface& named_interface) { GenerateStructTypedef(named_interface.snake_case_name, named_interface.snake_case_name + "_t"); for (const auto& method_info : named_interface.methods) { if (method_info.async) { std::vector input; input.reserve(method_info.output_parameters.size()); for (const auto& parameter : method_info.output_parameters) { input.push_back(CreateMember(library_, parameter)); } file_ << "typedef "; const auto method_name = method_info.protocol_name + "_callback"; EmitProtocolMethodPtrDecl(&file_, method_name, input, {}); file_ << ");\n"; } } } #if 0 void DdktlGenerator::ProduceProtocolForwardDeclaration(const NamedInterface& named_interface) {} #endif void DdkGenerator::ProduceEnumForwardDeclaration(const NamedEnum& named_enum) { types::PrimitiveSubtype subtype = named_enum.enum_info.type; EmitDocstring(&file_, named_enum, false); GenerateIntegerTypedef(subtype, named_enum.type_name); for (const auto& member : named_enum.enum_info.members) { std::string member_name = named_enum.name + "_" + NameIdentifier(member.name); std::string member_value; EnumValue(named_enum.enum_info.type, member.value.get(), library_, &member_value); struct { std::string doc; } temp = {member.GetAttribute("Doc")}; EmitDocstring(&file_, temp, true); GenerateIntegerDefine(member_name, subtype, std::move(member_value)); } EmitBlank(&file_); } #if 0 void DdktlGenerator::ProduceEnumForwardDeclaration(const NamedEnum& named_enum) { types::PrimitiveSubtype subtype = named_enum.enum_info.type; std::string underlying_type = NamePrimitiveCType(subtype); file_ << "enum class " << named_enum.name << " : " << underlying_type << " {\n"; for (const auto& member : named_enum.enum_info.members) { std::string member_name = NameIdentifier(member.name); std::string member_value; EnumValue(named_enum.enum_info.type, member.value.get(), library_, &member_value); file_ << kIndent << member_name << " = " << member_value << ",\n"; } file_ << "};\n"; } #endif void DdkGenerator::ProduceStructForwardDeclaration(const NamedStruct& named_struct) { // TODO: Hack - structs with no members are defined in a different header. if (named_struct.struct_info.members.empty()) return; GenerateStructTypedef(named_struct.name, named_struct.type_name); } void DdkGenerator::ProduceUnionForwardDeclaration(const NamedUnion& named_union) { GenerateUnionTypedef(named_union.name, named_union.type_name); } void DdkGenerator::ProduceConstDeclaration(const NamedConst& named_const) { const flat::Const& ci = named_const.const_info; // Some constants are not literals. Odd. if (ci.value->kind != flat::Constant::Kind::kLiteral) { return; } EmitDocstring(&file_, named_const, false); switch (ci.type->kind) { case flat::Type::Kind::kPrimitive: GeneratePrimitiveDefine( named_const.name, static_cast(ci.type.get())->subtype, static_cast(ci.value.get())->literal->location().data()); break; case flat::Type::Kind::kString: GenerateStringDefine( named_const.name, static_cast(ci.value.get())->literal->location().data()); break; default: abort(); } EmitBlank(&file_); } void DdkGenerator::ProduceProtocolImplementation(const NamedInterface& named_interface) { const auto& proto_name = named_interface.snake_case_name; if (named_interface.type == InterfaceType::kCallback) { assert(named_interface.methods.size() == 1 && "callback should only have 1 function"); file_ << "struct " << proto_name << " {\n"; const auto& method_info = named_interface.methods[0]; std::vector input; std::vector output; GetMethodParameters(library_, method_info, &input, &output, false); file_ << kIndent; EmitProtocolMethodPtrDecl(&file_, method_info.c_name, input, output); file_ << ");\n"; file_ << kIndent << "void* ctx;\n"; file_ << "};\n"; EmitBlank(&file_); return; } file_ << "typedef struct " << proto_name << "_ops {\n"; for (const auto& method_info : named_interface.methods) { std::vector input; std::vector output; GetMethodParameters(library_, method_info, &input, &output, false); file_ << kIndent; EmitProtocolMethodPtrDecl(&file_, method_info.c_name, input, output); file_ << ");\n"; } file_ << "} " << proto_name << "_ops_t;\n"; EmitBlank(&file_); // Emit Protocol. EmitDocstring(&file_, named_interface, false); file_ << "struct " << proto_name << " {\n"; file_ << kIndent << proto_name << "_ops_t* ops;\n"; file_ << kIndent << "void* ctx;\n"; file_ << "};\n"; EmitBlank(&file_); // Emit Protocol helper functions. for (const auto& method_info : named_interface.methods) { std::vector input; std::vector output; GetMethodParameters(library_, method_info, &input, &output, false); EmitDocstring(&file_, method_info, false); file_ << "static inline "; EmitProtocolMethodWithSpecificCtxDecl( &file_, proto_name, method_info.protocol_name, input, output); file_ << ") {\n" << kIndent; EmitDdkProtocolMethodImpl(&file_, method_info.c_name, input, output); file_ << "}\n"; } EmitBlank(&file_); // Emit Protocol async helper functions. for (const auto& method_info : named_interface.methods) { if (!method_info.async || !method_info.generate_sync_method) continue; // Generate context struct. std::vector members; members.reserve(method_info.output_parameters.size() + 1); members.push_back(DdkGenerator::Member{ .kind = flat::Type::Kind::kIdentifier, .decl_kind = flat::Decl::Kind::kStruct, .type = "sync_completion_t", .name = "completion", .element_type = "", .array_counts = {}, .nullability = types::Nullability::kNonnullable, }); for (const auto& member : method_info.output_parameters) { members.push_back(CreateMember(library_, member, false)); } GenerateStructDeclaration(method_info.protocol_name + "_callback_context", members, false, true); EmitBlank(&file_); // Generate callback function. members.erase(members.begin()); file_ << "static "; EmitMethodDeclHelper(&file_, method_info.protocol_name + "_cb", members, {}, "void* cookie"); file_ << ") {\n"; EmitCallbackMethodImpl(&file_, method_info.protocol_name, members); file_ << "}\n"; EmitBlank(&file_); // Generated sync version of helper function. auto method_info2 = method_info; method_info2.async = false; std::vector input; std::vector output; GetMethodParameters(library_, method_info2, &input, &output, false); file_ << "static inline "; EmitProtocolMethodWithSpecificCtxDecl( &file_, proto_name, method_info.protocol_name + "_sync", input, output); file_ << ") {\n"; input.clear(); output.clear(); GetMethodParameters(library_, method_info, &input, &output, false); EmitSyncMethodImpl(&file_, proto_name, method_info.protocol_name, input, members); file_ << "}\n"; } } void DdktlGenerator::ProduceExample(const NamedInterface& named_interface) { if (named_interface.type == InterfaceType::kCallback || named_interface.type == InterfaceType::kInterface) return; const auto& shortname = named_interface.shortname; const auto& sc_name = named_interface.snake_case_name; const auto& cc_name = named_interface.camel_case_name; const auto lc_name = ToLispCase(sc_name); file_ << "// DDK " << ToLispCase(sc_name) << " support\n"; file_ << "//\n"; file_ << "// :: Proxies ::\n"; file_ << "//\n"; file_ << "// ddk::" << cc_name << "Client is a simple wrapper around\n"; file_ << "// " << sc_name << "_t. It does not own the pointers passed to it\n"; file_ << "//\n"; file_ << "// :: Mixins ::\n"; file_ << "//\n"; file_ << "// ddk::" << cc_name << " is a mixin class that simplifies writing DDK " << "drivers\n"; file_ << "// that implement the " << ToLispCase(shortname) << " protocol. It doesn't set the base protocol.\n"; file_ << "//\n"; file_ << "// :: Examples ::\n"; file_ << "//\n"; file_ << "// // A driver that implements a ZX_PROTOCOL_" << ToSnakeCase(shortname, true) << " device.\n"; file_ << "// class " << shortname << "Device {\n"; file_ << "// using " << shortname << "DeviceType = ddk::Device<" << shortname << "Device, /* ddk mixins */>;\n"; file_ << "//\n"; file_ << "// class " << shortname << "Device : public " << shortname << "DeviceType,\n"; file_ << "// " << std::string(shortname.size() + 15, ' ') << "public ddk::" << cc_name << "<" << shortname << "Device> {\n"; file_ << "// public:\n"; file_ << "// " << kIndent << shortname << "Device(zx_device_t* parent)\n"; file_ << "// " << kIndent << kIndent << ": " << shortname << "DeviceType(\"my-" << lc_name << "-device\", parent) {}\n"; file_ << "//\n"; for (const auto& method_info : named_interface.methods) { std::vector input; std::vector output; GetMethodParameters(library_, method_info, &input, &output, named_interface.handle_wrappers); file_ << "// " << kIndent; EmitProtocolMethodDecl(&file_, method_info.protocol_name, input, output); file_ << ");\n"; file_ << "//\n"; } file_ << "// " << kIndent << "...\n"; file_ << "// };\n"; EmitBlank(&file_); } void DdktlGenerator::ProduceProtocolImplementation(const NamedInterface& named_interface) { if (named_interface.type == InterfaceType::kCallback) return; const auto& sc_name = named_interface.snake_case_name; const auto& cc_name = named_interface.camel_case_name; const auto& ops = sc_name + "_ops_"; EmitDocstring(&file_, named_interface, false); file_ << "template \n"; file_ << "class " << cc_name << " : public Base {\n"; file_ << "public:\n"; file_ << kIndent << cc_name << "() {\n"; file_ << kIndent << kIndent << "internal::Check" << cc_name << "Subclass();\n"; for (const auto& method_info : named_interface.methods) { file_ << kIndent << kIndent << ops << "." << method_info.c_name << " = " << method_info.protocol_name << ";\n"; } if (named_interface.type != InterfaceType::kInterface) { EmitBlank(&file_); file_ << kIndent << kIndent << "if constexpr (internal::is_base_proto::value) {\n"; file_ << kIndent << kIndent << kIndent << "auto dev = static_cast(this);\n"; file_ << kIndent << kIndent << kIndent << "// Can only inherit from one base_protocol implementation.\n"; file_ << kIndent << kIndent << kIndent << "ZX_ASSERT(dev->ddk_proto_id_ == 0);\n"; file_ << kIndent << kIndent << kIndent << "dev->ddk_proto_id_ = ZX_PROTOCOL_" << ToSnakeCase(named_interface.shortname, true) << ";\n"; file_ << kIndent << kIndent << kIndent << "dev->ddk_proto_ops_ = &" << ops << ";\n"; file_ << kIndent << kIndent << "}\n"; } file_ << kIndent << "}\n"; EmitBlank(&file_); file_ << "protected:\n"; file_ << kIndent << sc_name << "_ops_t " << ops << " = {};\n"; EmitBlank(&file_); file_ << "private:\n"; for (const auto& method_info : named_interface.methods) { std::vector input; std::vector output; GetMethodParameters(library_, method_info, &input, &output, false); EmitDocstring(&file_, method_info, true); file_ << kIndent << "static "; EmitProtocolMethodWithCtxDecl(&file_, method_info.protocol_name, input, output); file_ << ") {\n"; if (named_interface.handle_wrappers) { std::vector input2; std::vector output2; GetMethodParameters(library_, method_info, &input2, &output2, true); EmitDdktlProtocolMethodImpl(&file_, method_info.protocol_name, input2, output2, true); } else { EmitDdktlProtocolMethodImpl(&file_, method_info.protocol_name, input, output, false); } file_ << kIndent << "}\n"; } file_ << "};\n"; EmitBlank(&file_); ProduceClientImplementation(named_interface); } void DdktlGenerator::ProduceClientImplementation(const NamedInterface& named_interface) { if (named_interface.type == InterfaceType::kCallback) return; const auto& sc_name = named_interface.snake_case_name; const auto& cc_name = named_interface.camel_case_name; const auto type = sc_name + "_t"; const auto proto_id = "ZX_PROTOCOL_" + ToSnakeCase(named_interface.shortname, true); file_ << "class " << cc_name << "Client {\n"; file_ << "public:\n"; file_ << kIndent << cc_name << "Client()\n"; file_ << kIndent << kIndent << ": ops_(nullptr), ctx_(nullptr) {}\n"; file_ << kIndent << cc_name << "Client(const " << type << "* proto)\n"; file_ << kIndent << kIndent << ": ops_(proto->ops), ctx_(proto->ctx) {}\n"; if (named_interface.type != InterfaceType::kInterface) { EmitBlank(&file_); file_ << kIndent << cc_name << "Client(zx_device_t* parent) {\n"; file_ << kIndent << kIndent << type << " proto;\n"; file_ << kIndent << kIndent << "if (device_get_protocol(parent, " << proto_id << ", &proto) == ZX_OK) {\n"; file_ << kIndent << kIndent << kIndent << "ops_ = proto.ops;\n"; file_ << kIndent << kIndent << kIndent << "ctx_ = proto.ctx;\n"; file_ << kIndent << kIndent << "} else {\n"; file_ << kIndent << kIndent << kIndent << "ops_ = nullptr;\n"; file_ << kIndent << kIndent << kIndent << "ctx_ = nullptr;\n"; file_ << kIndent << kIndent << "}\n"; file_ << kIndent << "}\n"; } EmitBlank(&file_); file_ << kIndent << "void GetProto(" << type << "* proto) const {\n"; file_ << kIndent << kIndent << "proto->ctx = ctx_;\n"; file_ << kIndent << kIndent << "proto->ops = ops_;\n"; file_ << kIndent << "}\n"; file_ << kIndent << "bool is_valid() const {\n"; file_ << kIndent << kIndent << "return ops_ != nullptr;\n"; file_ << kIndent << "}\n"; file_ << kIndent << "void clear() {\n"; file_ << kIndent << kIndent << "ctx_ = nullptr;\n"; file_ << kIndent << kIndent << "ops_ = nullptr;\n"; file_ << kIndent << "}\n"; for (const auto& method_info : named_interface.methods) { std::vector input; std::vector output; GetMethodParameters(library_, method_info, &input, &output, named_interface.handle_wrappers); EmitDocstring(&file_, method_info, true); file_ << kIndent; EmitProtocolMethodDecl(&file_, method_info.proxy_name, input, output); file_ << ") const {\n" << kIndent << kIndent; EmitClientMethodImpl(&file_, method_info.c_name, input, output, named_interface.handle_wrappers); file_ << kIndent << "}\n"; } EmitBlank(&file_); file_ << "private:\n"; file_ << kIndent << sc_name << "_ops_t* ops_;\n"; file_ << kIndent << "void* ctx_;\n"; file_ << "};\n"; EmitBlank(&file_); } void DdktlGenerator::ProduceProtocolSubclass(const NamedInterface& named_interface) { if (named_interface.type == InterfaceType::kCallback) return; const auto& sc_name = named_interface.snake_case_name; const auto& cc_name = named_interface.camel_case_name; for (const auto& method_info : named_interface.methods) { std::vector input; std::vector output; GetMethodParameters(library_, method_info, &input, &output, named_interface.handle_wrappers); file_ << "DECLARE_HAS_MEMBER_FN_WITH_SIGNATURE(has_" << sc_name << "_" << method_info.c_name << ", " << method_info.protocol_name << ",\n"; file_ << kIndent << kIndent; EmitProtocolMethodTemplateDecl(&file_, input, output); } EmitBlank(&file_); file_ << "template \n"; file_ << "constexpr void Check" << cc_name << "Subclass() {\n"; for (const auto& method_info : named_interface.methods) { std::vector input; std::vector output; GetMethodParameters(library_, method_info, &input, &output, named_interface.handle_wrappers); file_ << kIndent << "static_assert(internal::has_" << sc_name << "_" << method_info.c_name << "::value,\n"; file_ << kIndent << kIndent << "\"" << cc_name << " subclasses must implement \"\n"; file_ << kIndent << kIndent << "\""; EmitProtocolMethodDecl(&file_, method_info.protocol_name, input, output); file_ << "\");\n"; } file_ << "}\n"; EmitBlank(&file_); } void DdkGenerator::ProduceStructDeclaration(const NamedStruct& named_struct) { // TODO: Hack - structs with no members are defined in a different header. if (named_struct.struct_info.members.empty()) return; std::vector members = GenerateMembers(library_, named_struct.struct_info.members); EmitDocstring(&file_, named_struct, false); GenerateStructDeclaration(named_struct.name, members, named_struct.packed); EmitBlank(&file_); } void DdkGenerator::ProduceUnionDeclaration(const NamedUnion& named_union) { std::vector members = GenerateMembers(library_, named_union.union_info.members); EmitDocstring(&file_, named_union, false); GenerateTaggedUnionDeclaration(named_union.name, members); EmitBlank(&file_); } std::ostringstream DdkGenerator::ProduceHeader() { std::map named_consts = NameConsts(library_->const_declarations_); std::map named_enums = NameEnums(library_->enum_declarations_); std::map named_interfaces = NameInterfaces(library_->interface_declarations_); std::map named_structs = NameStructs(library_->struct_declarations_); std::map named_unions = NameUnions(library_->union_declarations_); GeneratePrologues(); file_ << "\n// Forward declarations\n\n"; for (const auto* decl : library_->declaration_order_) { switch (decl->kind) { case flat::Decl::Kind::kConst: { auto iter = named_consts.find(decl); if (iter != named_consts.end()) { ProduceConstForwardDeclaration(iter->second); } break; } case flat::Decl::Kind::kEnum: { auto iter = named_enums.find(decl); if (iter != named_enums.end()) { ProduceEnumForwardDeclaration(iter->second); } break; } case flat::Decl::Kind::kInterface: { auto iter = named_interfaces.find(decl); if (iter != named_interfaces.end()) { ProduceProtocolForwardDeclaration(iter->second); } break; } case flat::Decl::Kind::kStruct: { auto iter = named_structs.find(decl); if (iter != named_structs.end()) { ProduceStructForwardDeclaration(iter->second); } break; } case flat::Decl::Kind::kUnion: { auto iter = named_unions.find(decl); if (iter != named_unions.end()) { ProduceUnionForwardDeclaration(iter->second); } break; } default: abort(); } } file_ << "\n// Declarations\n\n"; for (const auto* decl : library_->declaration_order_) { switch (decl->kind) { case flat::Decl::Kind::kConst: { auto iter = named_consts.find(decl); if (iter != named_consts.end()) { ProduceConstDeclaration(iter->second); } break; } case flat::Decl::Kind::kEnum: // Enums can be entirely forward declared, as they have no // dependencies other than standard headers. break; case flat::Decl::Kind::kInterface: { auto iter = named_interfaces.find(decl); if (iter != named_interfaces.end()) { ProduceProtocolImplementation(iter->second); } break; } case flat::Decl::Kind::kStruct: { auto iter = named_structs.find(decl); if (iter != named_structs.end()) { ProduceStructDeclaration(iter->second); } break; } case flat::Decl::Kind::kUnion: { auto iter = named_unions.find(decl); if (iter != named_unions.end()) { ProduceUnionDeclaration(iter->second); } break; } default: abort(); } } GenerateEpilogues(); return std::move(file_); } std::ostringstream DdktlGenerator::ProduceHeader() { std::map named_interfaces = NameInterfaces(library_->interface_declarations_); GeneratePrologues(); for (const auto& iter : named_interfaces) { ProduceExample(iter.second); } EmitNamespacePrologue(&file_, "ddk"); EmitBlank(&file_); for (const auto* decl : library_->declaration_order_) { switch (decl->kind) { case flat::Decl::Kind::kInterface: { auto iter = named_interfaces.find(decl); if (iter != named_interfaces.end()) { ProduceProtocolImplementation(iter->second); } break; } default: continue; } } GenerateEpilogues(); return std::move(file_); } std::ostringstream DdktlGenerator::ProduceInternalHeader() { std::map named_interfaces = NameInterfaces(library_->interface_declarations_); EmitFileComment(&file_, library_->name().back()); EmitHeaderGuard(&file_); EmitBlank(&file_); EmitIncludeHeader(&file_, "<" + ToLispCase(StringJoin(library_->name(), "/")) + ".h>"); EmitIncludeHeader(&file_, ""); EmitBlank(&file_); EmitNamespacePrologue(&file_, "ddk"); EmitNamespacePrologue(&file_, "internal"); EmitBlank(&file_); for (const auto* decl : library_->declaration_order_) { switch (decl->kind) { case flat::Decl::Kind::kInterface: { auto iter = named_interfaces.find(decl); if (iter != named_interfaces.end()) { ProduceProtocolSubclass(iter->second); } break; } default: break; } } EmitNamespaceEpilogue(&file_, "internal"); EmitNamespaceEpilogue(&file_, "ddk"); return std::move(file_); } } // namespace banjo