//===--- Failure.h - Failure to access remote memory ------------*- C++ -*-===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file provides a simple diagnostics library for the various // operations for working with a remote process. // //===----------------------------------------------------------------------===// #ifndef SWIFT_REMOTE_FAILURE_H #define SWIFT_REMOTE_FAILURE_H #include "swift/Remote/RemoteAddress.h" #include "llvm/Support/Compiler.h" #include "llvm/Support/ErrorHandling.h" #include #include #include #include namespace swift { namespace remote { class Failure { public: /// An enum which unifies all of the failure kinds into a single namespace. /// This is how kinds are stored internally. enum class Kind { #define FAILURE(KIND, TEXT, ARGTYS) KIND, #include "swift/Remote/FailureKinds.def" }; /// A bunch of enums for all the different kinds of failure. /// Users should construct a Failure by passing Failure::KIND instead /// of Failure::Kind::KIND. #define FAILURE(KIND, TEXT, ARGTYS) \ enum KIND##_t { KIND = unsigned(Kind::KIND) }; #include "swift/Remote/FailureKinds.def" private: // A whole lot of template machinery to validate that the right argument // types were passed: // A sequence of types. template struct TypeBundle {}; // A way of turning (X, Y, Z) into a TypeBundle: // typename TypeBundleForFunctionTypeParameters::types // Partially specialized at the end of this file. template struct TypeBundleForFunctionTypeParameters; // A way of getting the expected TypeBundle of (nonce) argument types // for a particular failure kind: // typename ArgTypesForFailureKind::types // Explicitly specialized for each kind type at the end of this file. template struct ArgTypesForFailureKind; // Nonce expected argument types. enum ArgType_String {}; enum ArgType_Address {}; // A predicate that decides whether the given argument type satisfies // the given nonce expected argument type. template struct IsAcceptableArgType { static constexpr bool value = false; }; template static void validateFailureArgsRecursive(TypeBundle expected, TypeBundle<> actual) { static_assert(std::is_same::value, "too few arguments to diagnostic"); } template static void validateFailureArgsRecursive( TypeBundle expected, TypeBundle actual) { using SimplifiedActual = typename std::decay::type; static_assert(IsAcceptableArgType::value, "argument does no match"); validateFailureArgsRecursive(TypeBundle(), TypeBundle()); } public: /// Validate that it's okay to construct a Failure with the given arguments. template static void validateFailureArgs(KindTy ty, ArgTys &&... argTys) { using ExpectedArgTypes = typename ArgTypesForFailureKind::types; validateFailureArgsRecursive(ExpectedArgTypes(), TypeBundle()); } private: static const char *getTextForKind(Kind kind) { switch (kind) { #define FAILURE(KIND, TEXT, ARGTYS) \ case Kind::KIND: return TEXT; #include "swift/Remote/FailureKinds.def" } llvm_unreachable("Unhandled FailureKind in switch."); } union ArgStorage { std::string String; RemoteAddress Address; ArgStorage() {} ArgStorage(const ArgStorage &other) = delete; ArgStorage &operator=(const ArgStorage &other) = delete; ~ArgStorage() {} }; enum class ArgStorageKind : char { None, String, // std::string Address, // RemoteAddress }; enum { MaxArgs = 4 }; Kind TheKind; ArgStorageKind ArgKinds[MaxArgs]; ArgStorage Args[MaxArgs]; template void initArgs() { static_assert(Index <= MaxArgs, "index out of bounds"); for (unsigned i = Index; i < MaxArgs; ++i) { ArgKinds[i] = ArgStorageKind::None; } } template void initArgs(T &&nextArg, Ts &&... restOfArgs) { static_assert(Index < MaxArgs, "index out of bounds"); initArg(Index, std::forward(nextArg)); initArgs(std::forward(restOfArgs)...); } void initArg(unsigned index, std::string &&string) { ArgKinds[index] = ArgStorageKind::String; new (&Args[index]) std::string(std::move(string)); } void initArg(unsigned index, const std::string &string) { ArgKinds[index] = ArgStorageKind::String; new (&Args[index]) std::string(string); } void initArg(unsigned index, RemoteAddress address) { ArgKinds[index] = ArgStorageKind::Address; new (&Args[index]) RemoteAddress(address); } public: template explicit Failure(KindTy kind, Ts &&...args) : TheKind(Kind(kind)) { validateFailureArgs(kind, std::forward(args)...); initArgs<0>(std::forward(args)...); } Failure(const Failure &other) : TheKind(other.TheKind) { for (unsigned i = 0; i != MaxArgs; ++i) { ArgKinds[i] = other.ArgKinds[i]; switch (ArgKinds[i]) { case ArgStorageKind::None: break; case ArgStorageKind::String: ::new (&Args[i].String) std::string(other.Args[i].String); break; case ArgStorageKind::Address: ::new (&Args[i].Address) RemoteAddress(other.Args[i].Address); break; } } } Failure(Failure &&other) : TheKind(other.TheKind) { for (unsigned i = 0; i != MaxArgs; ++i) { ArgKinds[i] = other.ArgKinds[i]; switch (ArgKinds[i]) { case ArgStorageKind::None: break; case ArgStorageKind::String: ::new (&Args[i].String) std::string(std::move(other.Args[i].String)); break; case ArgStorageKind::Address: ::new (&Args[i].Address) RemoteAddress(std::move(other.Args[i].Address)); break; } } } Failure &operator=(const Failure &other) { this->~Failure(); ::new (this) Failure(other); return *this; } Failure &operator=(Failure &&other) { this->~Failure(); ::new (this) Failure(std::move(other)); return *this; } ~Failure() { for (unsigned i = 0; i != MaxArgs; ++i) { switch (ArgKinds[i]) { case ArgStorageKind::None: break; case ArgStorageKind::String: Args[i].String.~basic_string(); break; case ArgStorageKind::Address: Args[i].Address.~RemoteAddress(); break; } } } /// Return the kind of failure. Kind getKind() const { return TheKind; } /// Return the number of arguments to the failure. unsigned getNumArgs() const { unsigned i = 0; for (; i != MaxArgs; ++i) { // Stop at the first missing argument. if (ArgKinds[i] == ArgStorageKind::None) break; } return i; } /// Render the failure as an error message. std::string render() const { std::string result; const char *text = getTextForKind(TheKind); while (const char *next = std::strchr(text, '%')) { // Append everything we just skipped over. result.append(text, next - text); // Skip the '%'. ++next; // Do something based on the character after '%'. char c = *next++; text = next; if (c == '%') { result += c; continue; } assert('0' <= c && c <= '9'); unsigned argIndex = c - '0'; assert(argIndex < MaxArgs); switch (ArgKinds[argIndex]) { case ArgStorageKind::None: LLVM_BUILTIN_UNREACHABLE; // Stringize a string argument by just appending it. case ArgStorageKind::String: result += Args[argIndex].String; continue; // Stringize an address argument as a hex number. We assume that an // address less than 2^32 is for a 32-bit target and accordingly // only print 8 digits for it. // // It is really silly to provide our own hex-stringization here, // but swift::remote is supposed to be a minimal, header-only library. case ArgStorageKind::Address: { result += '0'; result += 'x'; uint64_t address = Args[argIndex].Address.getRawAddress(); unsigned max = ((address >> 32) != 0 ? 16 : 8); for (unsigned i = 0; i != max; ++i) { result += "0123456789abcdef"[(address >> (max - 1 - i) * 4) & 0xF]; } continue; } } LLVM_BUILTIN_UNREACHABLE; } // Append the rest of the string. result.append(text); return result; } }; template struct Failure::TypeBundleForFunctionTypeParameters { using types = TypeBundle; }; template class TypeBundle {}; #define FAILURE(KIND, TEXT, ARGTYS) \ template <> struct Failure::ArgTypesForFailureKind { \ using String = ArgType_String; \ using Address = ArgType_Address; \ using types = TypeBundleForFunctionTypeParameters::types; \ }; #include "swift/Remote/FailureKinds.def" template struct Failure::IsAcceptableArgType { static constexpr bool value = std::is_convertible::value; }; template <> struct Failure::IsAcceptableArgType { static constexpr bool value = true; }; } // end namespace remote } // end namespace swift #endif // SWIFT_REMOTE_FAILURE_H