| // 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; |
| } |