| // Copyright 2019 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 "tools/kazoo/output_util.h" |
| |
| #include <zircon/assert.h> |
| |
| #include <string_view> |
| |
| #include "tools/kazoo/string_util.h" |
| |
| void CopyrightHeaderWithCppComments(Writer* writer) { |
| writer->Puts(R"(// Copyright 2019 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. |
| |
| // WARNING: THIS FILE IS MACHINE GENERATED BY //tools/kazoo. DO NOT EDIT. |
| |
| )"); |
| } |
| |
| void CopyrightHeaderWithHashComments(Writer* writer) { |
| writer->Puts(R"(# Copyright 2019 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. |
| |
| # WARNING: THIS FILE IS MACHINE GENERATED BY //tools/kazoo. DO NOT EDIT. |
| |
| )"); |
| } |
| |
| std::string ToLowerAscii(const std::string& input) { |
| std::string ret = input; |
| std::transform(ret.begin(), ret.end(), ret.begin(), ToLowerASCII); |
| return ret; |
| } |
| |
| std::string ToUpperAscii(const std::string& input) { |
| std::string ret = input; |
| std::transform(ret.begin(), ret.end(), ret.begin(), ToUpperASCII); |
| return ret; |
| } |
| |
| std::string CamelToSnake(const std::string& camel_fidl) { |
| auto is_transition = [](char prev, char cur, char peek) { |
| enum { Upper, Lower, Other }; |
| auto categorize = [](char c) { |
| if (c == 0) |
| return Other; |
| if (c >= 'a' && c <= 'z') |
| return Lower; |
| if (c >= 'A' && c <= 'Z') |
| return Upper; |
| if ((c >= '0' && c <= '9') || c == '_') |
| return Other; |
| ZX_ASSERT_MSG(false, "%c is unexpected character", c); |
| return Other; |
| }; |
| auto prev_type = categorize(prev); |
| auto cur_type = categorize(cur); |
| auto peek_type = categorize(peek); |
| |
| bool lower_to_upper = prev_type != Upper && cur_type == Upper; |
| bool multiple_caps_to_lower = |
| peek && (prev_type == Upper && cur_type == Upper && peek_type == Lower); |
| |
| return lower_to_upper || multiple_caps_to_lower; |
| }; |
| std::vector<std::string> parts; |
| char prev = 0; |
| std::string current_word; |
| for (size_t i = 0; i < camel_fidl.size(); ++i) { |
| char cur = camel_fidl[i]; |
| char peek = i + 1 < camel_fidl.size() ? camel_fidl[i + 1] : 0; |
| if (current_word.size() > 1 && is_transition(prev, cur, peek)) { |
| parts.push_back(ToLowerAscii(current_word)); |
| current_word = cur; |
| } else { |
| current_word += cur; |
| } |
| prev = cur; |
| } |
| |
| if (!current_word.empty()) { |
| parts.push_back(ToLowerAscii(current_word)); |
| } |
| |
| return JoinStrings(parts, "_"); |
| } |
| |
| namespace { |
| |
| // Most of the implementation of GetCUserModeName() and GetCKernelModeName(), other than for |
| // pointers. |
| std::string CNameImpl(const Type& type) { |
| struct { |
| public: |
| void operator()(const std::monostate&) { ret = "<TODO!>"; } |
| void operator()(const TypeBool&) { ret = "bool"; } |
| void operator()(const TypeChar&) { ret = "char"; } |
| void operator()(const TypeInt32&) { ret = "int32_t"; } |
| void operator()(const TypeInt64&) { ret = "int64_t"; } |
| void operator()(const TypeSizeT&) { ret = "size_t"; } |
| void operator()(const TypeUint16&) { ret = "uint16_t"; } |
| void operator()(const TypeUint32&) { ret = "uint32_t"; } |
| void operator()(const TypeUint64&) { ret = "uint64_t"; } |
| void operator()(const TypeUint8&) { ret = "uint8_t"; } |
| void operator()(const TypeUintptrT&) { ret = "uintptr_t"; } |
| void operator()(const TypeVoid&) { ret = "void"; } |
| void operator()(const TypeZxBasicAlias& zx_basic_alias) { ret = zx_basic_alias.name(); } |
| |
| void operator()(const TypeAlias& alias) { ret = "zx_" + alias.alias_data().base_name() + "_t"; } |
| void operator()(const TypeEnum& enm) { ret = "zx_" + enm.enum_data().base_name() + "_t"; } |
| void operator()(const TypeHandle& handle) { |
| ret = "zx_handle_t"; |
| // TOOD(syscall-fidl-transition): Once we're not trying to match abigen, it might be nice to |
| // add the underlying handle type here like "zx_handle_t /*vmo*/ handle" or similar. |
| } |
| void operator()(const TypePointer& pointer) { |
| ZX_ASSERT(false && "pointers should be handled by caller"); |
| ret = "<!>"; |
| } |
| void operator()(const TypeString&) { |
| ZX_ASSERT(false && "can't convert string to C directly"); |
| ret = "<!>"; |
| } |
| void operator()(const TypeStruct& strukt) { |
| ret = "zx_" + strukt.struct_data().base_name() + "_t"; |
| } |
| void operator()(const TypeVector&) { |
| ZX_ASSERT(false && "can't convert vector to C directly"); |
| ret = "<!>"; |
| } |
| |
| Constness constness; |
| std::string ret; |
| } name_visitor; |
| name_visitor.constness = type.constness(); |
| std::visit(name_visitor, type.type_data()); |
| return name_visitor.ret; |
| } |
| |
| } // namespace |
| |
| std::string GetCUserModeName(const Type& type) { |
| if (type.IsPointer()) { |
| ZX_ASSERT(type.constness() != Constness::kUnspecified && |
| "Pointer should be explicitly const or mutable by output time"); |
| return (type.constness() == Constness::kConst ? "const " : "") + |
| GetCUserModeName(type.DataAsPointer().pointed_to_type()) + "*"; |
| } |
| return CNameImpl(type); |
| } |
| |
| std::string GetCKernelModeName(const Type& type) { |
| if (type.IsPointer()) { |
| ZX_ASSERT(type.constness() != Constness::kUnspecified && |
| "Pointer should be explictly const or mutable by output time"); |
| std::string pointed_to = GetCKernelModeName(type.DataAsPointer().pointed_to_type()); |
| if (type.constness() == Constness::kConst) { |
| return StringPrintf("user_in_ptr<const %s>", pointed_to.c_str()); |
| } else if (type.constness() == Constness::kMutable) { |
| if (pointed_to == "zx_handle_t" && !type.DataAsPointer().was_vector()) { |
| return "user_out_handle*"; |
| } |
| if (type.optionality() == Optionality::kInputArgument) { |
| return StringPrintf("user_inout_ptr<%s>", pointed_to.c_str()); |
| } else { |
| return StringPrintf("user_out_ptr<%s>", pointed_to.c_str()); |
| } |
| } |
| } |
| return CNameImpl(type); |
| } |
| |
| JsonTypeNameData GetJsonName(const Type& type) { |
| JsonTypeNameData ret; |
| if (type.IsPointer()) { |
| ret.name = GetCUserModeName(type.DataAsPointer().pointed_to_type()); |
| ret.is_pointer = true; |
| if (type.constness() == Constness::kConst) { |
| ret.attribute = "IN"; |
| } else if (type.constness() == Constness::kMutable) { |
| if (type.optionality() == Optionality::kInputArgument) { |
| ret.attribute = "INOUT"; |
| } else if (type.optionality() == Optionality::kOutputNonOptional) { |
| ret.attribute = "OUT"; |
| } else if (type.optionality() == Optionality::kOutputOptional) { |
| ret.attribute = "optional"; |
| } |
| } |
| |
| if (ret.name == "void") { |
| ret.name = "any"; |
| } |
| } else { |
| ret.name = GetCUserModeName(type); |
| } |
| return ret; |
| } |
| |
| namespace { |
| |
| std::string GetGoNameImpl(const Type& type) { |
| struct { |
| public: |
| void operator()(const std::monostate&) { ret = "<TODO!>"; } |
| void operator()(const TypeBool&) { ret = "bool"; } |
| void operator()(const TypeChar&) { ret = "uint8"; } |
| void operator()(const TypeInt32&) { ret = "int32"; } |
| void operator()(const TypeInt64&) { ret = "int64"; } |
| void operator()(const TypeSizeT&) { ret = "uint"; } |
| void operator()(const TypeUint16&) { ret = "uint16"; } |
| void operator()(const TypeUint32&) { ret = "uint32"; } |
| void operator()(const TypeUint64&) { ret = "uint64"; } |
| void operator()(const TypeUint8&) { ret = "uint8"; } |
| void operator()(const TypeUintptrT&) { ret = "uintptr"; } |
| void operator()(const TypeVoid&) { ret = "void"; } |
| void operator()(const TypeZxBasicAlias& zx_basic_alias) { ret = zx_basic_alias.go_name(); } |
| |
| void operator()(const TypeAlias& alias) { ret = "<TODO!>"; } |
| void operator()(const TypeEnum& enm) { ret = "zx_" + enm.enum_data().base_name() + "_t"; } |
| void operator()(const TypeHandle& handle) { ret = "Handle"; } |
| void operator()(const TypePointer& pointer) { |
| if (pointer.pointed_to_type().IsVoid()) { |
| ret = "unsafe.Pointer"; |
| } else { |
| ret = "*" + GetGoName(pointer.pointed_to_type()); |
| } |
| } |
| void operator()(const TypeString&) { |
| ZX_ASSERT(false && "can't convert string directly"); |
| ret = "<!>"; |
| } |
| void operator()(const TypeStruct& strukt) { |
| ret = "zx_" + strukt.struct_data().base_name() + "_t"; |
| } |
| void operator()(const TypeVector&) { |
| ZX_ASSERT(false && "can't convert vector directly"); |
| ret = "<!>"; |
| } |
| |
| Constness constness; |
| std::string ret; |
| } name_visitor; |
| name_visitor.constness = type.constness(); |
| std::visit(name_visitor, type.type_data()); |
| return name_visitor.ret; |
| } |
| |
| } // namespace |
| |
| std::string GetGoName(const Type& type) { |
| // Most of the "normal" implementation is in GetGoNameImpl(), and then we do |
| // a few hacks here to make it match the previous output of the Go-written |
| // tool that parsed abigen directly. |
| // TODO(scottmg|dhobsd): Remove or rationalize these over time. |
| std::string name = GetGoNameImpl(type); |
| if (name == "Futex") |
| return "int32"; |
| if (name == "Koid") |
| return "uint64"; |
| if (name == "Ticks") |
| return "int64"; |
| if (name == "Vm_option") |
| return "VMFlag"; |
| if (name == "zx_channel_call_args_t") |
| return "ChannelCallArgs"; |
| if (name == "zx_clock_t") |
| return "uint32"; |
| if (name == "zx_handle_disposition_t") |
| return "HandleDisposition"; |
| if (name == "zx_handle_info_t") |
| return "int"; |
| if (name == "zx_pci_bar_t") |
| return "uintptr"; |
| if (name == "zx_pci_init_arg_t") |
| return "uintptr"; |
| if (name == "zx_pcie_device_info_t") |
| return "uintptr"; |
| if (name == "zx_port_packet_t") |
| return "int"; |
| if (name == "zx_profile_info_t") |
| return "int"; |
| if (name == "zx_rights_t") |
| return "Rights"; |
| if (name == "zx_smc_parameters_t") |
| return "SMCParameters"; |
| if (name == "zx_smc_result_t") |
| return "SMCResult"; |
| if (name == "zx_system_powerctl_arg_t") |
| return "int"; |
| if (name == "zx_wait_item_t") |
| return "WaitItem"; |
| return name; |
| } |
| |
| // Returns a size-compatible Go native type. |
| std::string GetNativeGoName(const Type& type) { |
| if (type.IsPointer()) { |
| return "unsafe.Pointer"; |
| } |
| std::string name = GetGoNameImpl(type); |
| |
| // Most of the "normal" implementation is in GetGoNameImpl(), and then we do |
| // a few hacks here to make it match the previous output of the Go-written |
| // tool that parsed abigen directly. |
| // TODO(scottmg|dhobsd): Remove or rationalize these over time. |
| if (name == "Duration") |
| return "int64"; |
| if (name == "Futex") |
| return "int32"; |
| if (name == "Handle") |
| return "uint32"; |
| if (name == "Koid") |
| return "uint64"; |
| if (name == "Paddr") |
| return "uintptr"; |
| if (name == "Signals") |
| return "uint32"; |
| if (name == "Status") |
| return "int32"; |
| if (name == "Ticks") |
| return "int64"; |
| if (name == "Time") |
| return "int64"; |
| if (name == "Vaddr") |
| return "uintptr"; |
| if (name == "Vm_option") |
| return "uint32"; |
| if (name == "zx_channel_call_args_t") |
| return "uintptr"; |
| if (name == "zx_clock_t") |
| return "uint32"; |
| if (name == "zx_handle_disposition_t") |
| return "uintptr"; |
| if (name == "zx_handle_info_t") |
| return "int"; |
| if (name == "zx_pci_bar_t") |
| return "uintptr"; |
| if (name == "zx_pci_init_arg_t") |
| return "uintptr"; |
| if (name == "zx_pcie_device_info_t") |
| return "uintptr"; |
| if (name == "zx_port_packet_t") |
| return "int"; |
| if (name == "zx_profile_info_t") |
| return "int"; |
| if (name == "zx_rights_t") |
| return "uint32"; |
| if (name == "zx_smc_parameters_t") |
| return "uintptr"; |
| if (name == "zx_smc_result_t") |
| return "uintptr"; |
| if (name == "zx_system_powerctl_arg_t") |
| return "int"; |
| if (name == "zx_wait_item_t") |
| return "uintptr"; |
| return name; |
| } |
| |
| std::string RemapReservedGoName(const std::string& name) { |
| // Probably more of these and/or a better way to do this, but this handles current syscalls. |
| if (name == "type") |
| return "typ"; |
| if (name == "func") |
| return "funk"; |
| if (name == "g") |
| return "g_"; |
| return name; |
| } |
| |
| // http://www.cse.yorku.ca/~oz/hash.html |
| // Used by Go output, taken from |
| // https://fuchsia.googlesource.com/third_party/go/+/ae00e6ff359966a496acacc3aa386c79e972279d/src/runtime/mkfuchsiavdso.go#36 |
| // TODO(scottmg): Not clear on whether this must match something in Go internals. |
| uint32_t DJBHash(const std::string& str) { |
| uint32_t h = 5381; |
| for (const auto& c : str) { |
| h = (h << 5) + h + static_cast<uint32_t>(c); |
| } |
| return h; |
| } |