blob: e28c65c97195d419acb9bdcdff9abf5241892f4b [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <algorithm>
#include <optional>
#include <string_view>
#include <utility>
#include "tools/kazoo/output_util.h"
#include "tools/kazoo/outputs.h"
using namespace std::literals;
namespace {
constexpr std::pair<const char*, std::string_view> kFunctionAttributes[] = {
{"const", "__CONST"},
};
constexpr std::pair<std::string_view, std::string_view> kHandleAttributes[] = {
{"Acquire", "acquire_handle"},
{"Release", "release_handle"},
{"Use", "use_handle"},
};
bool IsHandleType(const Type& type) {
if (type.IsPointer()) {
return IsHandleType(type.DataAsPointer().pointed_to_type());
}
if (type.IsVector()) {
return IsHandleType(type.DataAsVector().contained_type());
}
if (type.IsStruct()) {
const auto& s = type.DataAsStruct().struct_data();
return std::any_of(s.members().begin(), s.members().end(),
[](const auto& m) { return IsHandleType(m.type()); });
}
return type.IsHandle();
}
std::optional<std::string_view> HandleAnnotation(const StructMember& arg) {
if (IsHandleType(arg.type())) {
for (const auto [attr, anno] : kHandleAttributes) {
if (arg.attributes().count(std::string(attr))) {
return anno;
}
}
switch (arg.type().optionality()) {
case Optionality::kOutputOptional:
case Optionality::kOutputNonOptional:
return "acquire_handle"sv;
default:
return "use_handle"sv;
}
}
return {};
}
void CDeclarationMacro(const Syscall& syscall, std::string_view macro,
std::string (*type_name)(const Type&), Writer* writer) {
std::string decl(macro);
decl += "(";
decl += syscall.name();
decl += ", ";
// First the return type.
decl += type_name(syscall.kernel_return_type());
decl += ",";
{
// Now the function attributes.
std::string attrs;
if (syscall.is_noreturn()) {
attrs += " __NO_RETURN";
}
for (const auto [attr, anno] : kFunctionAttributes) {
if (syscall.HasAttribute(attr)) {
attrs += " ";
attrs += anno;
}
}
if (attrs.empty()) {
decl += " /* no attributes */";
} else {
decl += attrs;
}
}
decl += ", ";
// Now the argument count, used in assembly macros.
decl += std::to_string(syscall.num_kernel_args());
decl += ",\n ";
{
// Now the argument list, just the names between parentheses.
decl += "(";
bool first = true;
for (const auto& arg : syscall.kernel_arguments()) {
if (!first) {
decl += ", ";
}
first = false;
decl += arg.name();
}
decl += ")";
}
decl += ", ";
// Finally, the full prototype.
if (syscall.kernel_arguments().empty()) {
decl += "(void)";
} else {
const bool unchecked = syscall.HasAttribute("HandleUnchecked");
bool first = true;
for (const auto& arg : syscall.kernel_arguments()) {
decl += first ? "("sv : ","sv;
first = false;
decl += "\n ";
auto anno = HandleAnnotation(arg);
if (anno) {
decl += "_ZX_SYSCALL_ANNO(";
decl += *anno;
decl += unchecked ? "(\"FuchsiaUnchecked\")"sv : "(\"Fuchsia\")"sv;
decl += ") ";
}
decl += type_name(arg.type());
decl += " ";
decl += arg.name();
}
decl += ")";
}
decl += ")\n\n";
writer->Puts(decl.c_str());
}
constexpr std::string_view PrivateMacro(const Syscall& syscall) {
if (syscall.HasAttribute("vdsocall"))
return "VDSO_SYSCALL";
if (syscall.HasAttribute("blocking"))
return "BLOCKING_SYSCALL";
if (syscall.HasAttribute("internal"))
return "INTERNAL_SYSCALL";
return "KERNEL_SYSCALL";
}
} // namespace
bool PublicDeclarationsOutput(const SyscallLibrary& library, Writer* writer) {
CopyrightHeaderWithCppComments(writer);
writer->Puts(R"(#ifndef _ZX_SYSCALL_DECL
#error "<zircon/syscalls.h> is the public API header"
#endif
)");
for (const auto& syscall : library.syscalls()) {
if (!syscall->HasAttribute("internal") && !syscall->HasAttribute("testonly")) {
CDeclarationMacro(*syscall, "_ZX_SYSCALL_DECL", GetCUserModeName, writer);
}
}
return true;
}
bool TestonlyPublicDeclarationsOutput(const SyscallLibrary& library, Writer* writer) {
CopyrightHeaderWithCppComments(writer);
writer->Puts(R"(#ifndef _ZX_SYSCALL_DECL
#error "<zircon/testonly-syscalls.h> is the public API header"
#endif
)");
for (const auto& syscall : library.syscalls()) {
if (!syscall->HasAttribute("internal") && syscall->HasAttribute("testonly")) {
CDeclarationMacro(*syscall, "_ZX_SYSCALL_DECL", GetCUserModeName, writer);
}
}
return true;
}
bool PrivateDeclarationsOutput(const SyscallLibrary& library, Writer* writer) {
CopyrightHeaderWithCppComments(writer);
for (const auto& syscall : library.syscalls()) {
CDeclarationMacro(*syscall, PrivateMacro(*syscall), GetCUserModeName, writer);
}
return true;
}
bool KernelDeclarationsOutput(const SyscallLibrary& library, Writer* writer) {
CopyrightHeaderWithCppComments(writer);
for (const auto& syscall : library.syscalls()) {
CDeclarationMacro(*syscall, PrivateMacro(*syscall), GetCKernelModeName, writer);
}
return true;
}