blob: 0fb2903f122f8e83f157d7a0307f9678e3196834 [file] [log] [blame]
// 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 <iostream>
#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<DdkGenerator::Member>& 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<StringView> SplitString(const std::string& src, char delimiter) {
std::vector<StringView> 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 <typename T>
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<DdkGenerator::Member>& input,
const std::vector<DdkGenerator::Member>& 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<DdkGenerator::Member>& input,
const std::vector<DdkGenerator::Member>& output) {
EmitMethodDeclHelper(file, method_name, input, output, "");
}
void EmitProtocolMethodWithCtxDecl(std::ostream* file, StringView method_name,
const std::vector<DdkGenerator::Member>& input,
const std::vector<DdkGenerator::Member>& 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<DdkGenerator::Member>& input,
const std::vector<DdkGenerator::Member>& 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<DdkGenerator::Member>& input,
const std::vector<DdkGenerator::Member>& output) {
EmitMethodDeclHelper(file, "(*" + method_name + ")", input, output, "void* ctx");
}
void EmitProtocolMethodTemplateDecl(std::ostream* file,
const std::vector<DdkGenerator::Member>& input,
const std::vector<DdkGenerator::Member>& output) {
EmitMethodDeclHelper(file, "(C::*)", input, output, "");
*file << "));\n";
}
void EmitMethodImplHelper(std::ostream* file, StringView method_name,
const std::vector<DdkGenerator::Member>& input,
const std::vector<DdkGenerator::Member>& 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<DdkGenerator::Member>& input,
const std::vector<DdkGenerator::Member>& 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<DdkGenerator::Member> input,
std::vector<DdkGenerator::Member> 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<D*>(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<D*>(ctx)->" + method_name, input, output, "");
*file << ");\n";
}
}
void EmitClientMethodImpl(std::ostream* file, const std::string& method_name,
std::vector<DdkGenerator::Member>& input,
std::vector<DdkGenerator::Member>& 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<DdkGenerator::Member>& 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<DdkGenerator::Member>& input,
const std::vector<DdkGenerator::Member>& 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<int>(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<unsigned int>(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<uint32_t> ArrayCounts(const flat::Library* library, const flat::Type* type) {
std::vector<uint32_t> array_counts;
for (;;) {
switch (type->kind) {
default: { return array_counts; }
case flat::Type::Kind::kArray: {
auto array_type = static_cast<const flat::ArrayType*>(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<const flat::StringType*>(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<const flat::IdentifierType*>(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<const flat::HandleType*>(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<const flat::PrimitiveType*>(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<const flat::ArrayType*>(type);
type = array_type->element_type.get();
continue;
}
case flat::Type::Kind::kVector: {
auto vector_type = static_cast<const flat::VectorType*>(type);
type = vector_type->element_type.get();
continue;
}
case flat::Type::Kind::kIdentifier: {
auto identifier_type = static_cast<const flat::IdentifierType*>(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 <typename T>
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<uint32_t> 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<const flat::VectorType*>(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<const flat::IdentifierType*>(type);
nullability = identifier_type->nullability;
} else if (type->kind == flat::Type::Kind::kVector) {
auto identifier_type = static_cast<const flat::VectorType*>(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 <typename T>
std::vector<DdkGenerator::Member>
GenerateMembers(const flat::Library* library, const std::vector<T>& decl_members) {
std::vector<DdkGenerator::Member> 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<DdkGenerator::Member>* input,
std::vector<DdkGenerator::Member>* 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_, "<zircon/compiler.h>");
EmitIncludeHeader(&file_, "<zircon/types.h>");
EmitBlank(&file_);
file_ << "__BEGIN_CDECLS;\n";
}
void DdktlGenerator::GeneratePrologues() {
EmitFileComment(&file_, library_->name().back());
EmitHeaderGuard(&file_);
EmitBlank(&file_);
EmitIncludeHeader(&file_, "<ddk/driver.h>");
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_, "<ddktl/device-internal.h>");
EmitIncludeHeader(&file_, "<zircon/assert.h>");
EmitIncludeHeader(&file_, "<zircon/compiler.h>");
EmitIncludeHeader(&file_, "<zircon/types.h>");
// Enumerate list of includes based on zx_handle_t wrappers.
std::set<std::string> includes;
std::map<const flat::Decl*, NamedInterface> named_interfaces =
NameInterfaces(library_->interface_declarations_);
for (const auto& named_interface : named_interfaces) {
for (const auto& method_info : named_interface.second.methods) {
std::vector<Member> input;
std::vector<Member> 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_, "<lib/zx/" + include + ".h>");
}
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<Member>& 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<Member>& 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<const flat::Decl*, DdkGenerator::NamedConst>
DdkGenerator::NameConsts(const std::vector<std::unique_ptr<flat::Const>>& const_infos) {
std::map<const flat::Decl*, NamedConst> 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<const flat::Decl*, DdkGenerator::NamedConst>
DdktlGenerator::NameConsts(const std::vector<std::unique_ptr<flat::Const>>& const_infos) {
std::map<const flat::Decl*, NamedConst> 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<const flat::Decl*, DdkGenerator::NamedEnum>
DdkGenerator::NameEnums(const std::vector<std::unique_ptr<flat::Enum>>& enum_infos) {
std::map<const flat::Decl*, NamedEnum> 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<const flat::Decl*, DdkGenerator::NamedEnum>
DdktlGenerator::NameEnums(const std::vector<std::unique_ptr<flat::Enum>>& enum_infos) {
std::map<const flat::Decl*, NamedEnum> 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<const flat::Decl*, DdkGenerator::NamedInterface>
DdkGenerator::NameInterfaces(const std::vector<std::unique_ptr<flat::Interface>>& interface_infos) {
std::map<const flat::Decl*, NamedInterface> 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<const flat::Decl*, DdkGenerator::NamedInterface>
DdktlGenerator::NameInterfaces(const std::vector<std::unique_ptr<flat::Interface>>& interface_infos) {
std::map<const flat::Decl*, NamedInterface> 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<const flat::Decl*, DdkGenerator::NamedStruct>
DdkGenerator::NameStructs(const std::vector<std::unique_ptr<flat::Struct>>& struct_infos) {
std::map<const flat::Decl*, NamedStruct> 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<const flat::Decl*, DdkGenerator::NamedStruct>
DdktlGenerator::NameStructs(const std::vector<std::unique_ptr<flat::Struct>>& struct_infos) {
std::map<const flat::Decl*, NamedStruct> 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<const flat::Decl*, DdkGenerator::NamedUnion>
DdkGenerator::NameUnions(const std::vector<std::unique_ptr<flat::Union>>& union_infos) {
std::map<const flat::Decl*, NamedUnion> 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<const flat::Decl*, DdkGenerator::NamedUnion>
DdktlGenerator::NameUnions(const std::vector<std::unique_ptr<flat::Union>>& union_infos) {
std::map<const flat::Decl*, NamedUnion> 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<Member> 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<flat::PrimitiveType*>(ci.type.get())->subtype,
static_cast<flat::LiteralConstant*>(ci.value.get())->literal->location().data());
break;
case flat::Type::Kind::kString:
GenerateStringDefine(
named_const.name,
static_cast<flat::LiteralConstant*>(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<Member> input;
std::vector<Member> 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<Member> input;
std::vector<Member> 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<Member> input;
std::vector<Member> 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<DdkGenerator::Member> 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<Member> input;
std::vector<Member> 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(parent) {}\n";
file_ << "//\n";
for (const auto& method_info : named_interface.methods) {
std::vector<Member> input;
std::vector<Member> 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 <typename D, typename Base = internal::base_mixin>\n";
file_ << "class " << cc_name << " : public Base {\n";
file_ << "public:\n";
file_ << kIndent << cc_name << "() {\n";
file_ << kIndent << kIndent << "internal::Check" << cc_name << "Subclass<D>();\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<Base>::value) {\n";
file_ << kIndent << kIndent << kIndent << "auto dev = static_cast<D*>(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<Member> input;
std::vector<Member> 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<Member> input2;
std::vector<Member> 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<Member> input;
std::vector<Member> 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<Member> input;
std::vector<Member> 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 <typename D>\n";
file_ << "constexpr void Check" << cc_name << "Subclass() {\n";
for (const auto& method_info : named_interface.methods) {
std::vector<Member> input;
std::vector<Member> output;
GetMethodParameters(library_, method_info, &input, &output,
named_interface.handle_wrappers);
file_ << kIndent << "static_assert(internal::has_" << sc_name << "_" << method_info.c_name
<< "<D>::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<DdkGenerator::Member> 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<DdkGenerator::Member> 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<const flat::Decl*, NamedConst> named_consts =
NameConsts(library_->const_declarations_);
std::map<const flat::Decl*, NamedEnum> named_enums = NameEnums(library_->enum_declarations_);
std::map<const flat::Decl*, NamedInterface> named_interfaces =
NameInterfaces(library_->interface_declarations_);
std::map<const flat::Decl*, NamedStruct> named_structs =
NameStructs(library_->struct_declarations_);
std::map<const flat::Decl*, NamedUnion> 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<const flat::Decl*, NamedInterface> 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<const flat::Decl*, NamedInterface> named_interfaces =
NameInterfaces(library_->interface_declarations_);
EmitFileComment(&file_, library_->name().back());
EmitHeaderGuard(&file_);
EmitBlank(&file_);
EmitIncludeHeader(&file_, "<" + ToLispCase(StringJoin(library_->name(), "/")) + ".h>");
EmitIncludeHeader(&file_, "<type_traits>");
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