diff --git a/include/swift/Remote/Failure.h b/include/swift/Remote/Failure.h new file mode 100644 index 00000000000..df7d50f2453 --- /dev/null +++ b/include/swift/Remote/Failure.h @@ -0,0 +1,337 @@ +//===--- Failure.h - Failure to access remote memory ------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://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 +#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" + } + } + + 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++; + 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.getAddressData(); + 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 = true; +}; + +template <> +struct Failure::IsAcceptableArgType { + static constexpr bool value = true; +}; + +template <> +struct Failure::IsAcceptableArgType { + static constexpr bool value = true; +}; + +} // end namespace remote +} // end namespace swift + +#endif // SWIFT_REMOTE_FAILURE_H + diff --git a/include/swift/Remote/FailureKinds.def b/include/swift/Remote/FailureKinds.def new file mode 100644 index 00000000000..bd8c022808a --- /dev/null +++ b/include/swift/Remote/FailureKinds.def @@ -0,0 +1,19 @@ +//===--- FailureKinds.def - Remote failure definitions ----------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// + +// FAILURE(KIND, TEXT, ARGTYS) + +FAILURE(Unknown, "an unknown failure occurred", ()) +FAILURE(Memory, "an unknown failure occurred while reading %0 at address %1", + (String, Address)) + +#undef FAILURE \ No newline at end of file diff --git a/include/swift/Remote/MemoryReader.h b/include/swift/Remote/MemoryReader.h index 153c80141f6..3f57862401f 100644 --- a/include/swift/Remote/MemoryReader.h +++ b/include/swift/Remote/MemoryReader.h @@ -18,35 +18,13 @@ #ifndef SWIFT_REMOTE_MEMORYREADER_H #define SWIFT_REMOTE_MEMORYREADER_H -#include +#include "swift/Remote/RemoteAddress.h" + #include namespace swift { namespace remote { -/// An abstract address in the remote process's address space. -class RemoteAddress { - uint64_t Data; -public: - explicit RemoteAddress(const void *localPtr) - : Data(reinterpret_cast(localPtr)) {} - - explicit RemoteAddress(uint64_t addressData) : Data(addressData) {} - - explicit operator bool() const { - return Data != 0; - } - - template - const T *getLocalPointer() const { - return reinterpret_cast(static_cast(Data)); - } - - uint64_t getAddressData() const { - return Data; - } -}; - /// An abstract interface for reading memory. /// /// This abstraction presents memory as if it were a read-only diff --git a/include/swift/Remote/RemoteAddress.h b/include/swift/Remote/RemoteAddress.h new file mode 100644 index 00000000000..115b3c063ac --- /dev/null +++ b/include/swift/Remote/RemoteAddress.h @@ -0,0 +1,53 @@ +//===--- RemoteAddress.h - Address of remote memory -------------*- C++ -*-===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See http://swift.org/LICENSE.txt for license information +// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +// +// This file defines the RemoteAddress type, which abstracts over an +// address in a remote process. +// +//===----------------------------------------------------------------------===// + +#ifndef SWIFT_REMOTE_REMOTEADDRESS_H +#define SWIFT_REMOTE_REMOTEADDRESS_H + +#include + +namespace swift { +namespace remote { + +/// An abstract address in the remote process's address space. +class RemoteAddress { + uint64_t Data; +public: + explicit RemoteAddress(const void *localPtr) + : Data(reinterpret_cast(localPtr)) {} + + explicit RemoteAddress(uint64_t addressData) : Data(addressData) {} + + explicit operator bool() const { + return Data != 0; + } + + template + const T *getLocalPointer() const { + return reinterpret_cast(static_cast(Data)); + } + + uint64_t getAddressData() const { + return Data; + } +}; + +} // end namespace remote +} // end namespace swift + +#endif // SWIFT_REMOTE_REMOTEADDRESS_H + diff --git a/include/swift/RemoteAST/RemoteAST.h b/include/swift/RemoteAST/RemoteAST.h index ebcaa9ccd4f..2c52b9f4aa1 100644 --- a/include/swift/RemoteAST/RemoteAST.h +++ b/include/swift/RemoteAST/RemoteAST.h @@ -19,6 +19,7 @@ #ifndef SWIFT_REMOTEAST_REMOTEAST_H #define SWIFT_REMOTEAST_REMOTEAST_H +#include "swift/Remote/Failure.h" #include "swift/Remote/MemoryReader.h" #include "swift/Basic/LLVM.h" #include "llvm/ADT/Optional.h" @@ -33,6 +34,107 @@ class Type; namespace remoteAST { +template +class Result { + union Storage { + remote::Failure Failure; + T Success; + + Storage() {} + Storage(const Storage &) = delete; + Storage &operator=(const Storage &) = delete; + ~Storage() {} + }; + + Storage S; + bool IsSuccess; + + Result(bool isSuccess) {} + +public: + /*implicit*/ Result(const T &value) : IsSuccess(true) { + new (&S.Success) T(value); + } + + /*implicit*/ Result(T &&value) : IsSuccess(true) { + new (&S.Success) T(std::move(value)); + } + + /*implicit*/ Result(remote::Failure &&failure) : IsSuccess(false) { + new (&S.Failure) remote::Failure(std::move(failure)); + } + + Result(const Result &other) : IsSuccess(other.IsSuccess) { + if (IsSuccess) { + ::new (&S.Success) T(other.S.Success); + } else { + ::new (&S.Failure) remote::Failure(other.S.Failure); + } + } + + Result(Result &&other) : IsSuccess(other.IsSuccess) { + if (IsSuccess) { + ::new (&S.Success) T(std::move(other.S.Success)); + } else { + ::new (&S.Failure) remote::Failure(std::move(other.S.Failure)); + } + } + + Result &operator=(const Result &other) { + this->~Result(); + ::new (this) Result(other); + return *this; + } + + Result &operator=(Result &&other) { + this->~Result(); + ::new (this) Result(std::move(other)); + return *this; + } + + ~Result() { + if (IsSuccess) { + S.Success.~T(); + } else { + S.Failure.~Failure(); + } + } + + template + static Result emplaceSuccess(ArgTys &&...args) { + Result result(true); + ::new (&result.S.Success) T(std::forward(args)...); + return result; + } + + template + static Result emplaceFailure(KindTy kind, ArgTys &&...args) { + Result result(false); + ::new (&result.S.Failure) + remote::Failure(kind, std::forward(args)...); + return result; + } + + explicit operator bool() const { return isSuccess(); } + bool isSuccess() const { return IsSuccess; } + bool isFailure() const { return !IsSuccess; } + + const remote::Failure &getFailure() const { + assert(isFailure()); + return S.Failure; + } + + const T &getValue() const & { + assert(isSuccess()); + return S.Success; + } + + T &&getValue() && { + assert(isSuccess()); + return std::move(S.Success); + } +}; + /// A context for performing an operation relating the remote process with /// the AST. This may be discarded and recreated at any time without danger, /// but reusing a context across multiple calls may allow some redundant work @@ -65,16 +167,12 @@ public: /// Given an address which is supposedly of type metadata, try to /// resolve it to a specific type in the local AST. - /// - /// This may fail by returning a null type. - Type getTypeForRemoteTypeMetadata(remote::RemoteAddress address); + Result getTypeForRemoteTypeMetadata(remote::RemoteAddress address); /// Given an address which is supposedly of a nominal type descriptor, /// try to resolve it to a specific nominal type declaration in the /// local AST. - /// - /// This may fail by returning null. - NominalTypeDecl * + Result getDeclForRemoteNominalTypeDescriptor(remote::RemoteAddress address); /// Given a type in the local AST, try to resolve the offset of its @@ -83,7 +181,7 @@ public: /// This may fail by returning an empty optional. Failure may indicate /// that an offset for the property could not be resolved, or it may /// simply indicate that the property has a non-zero offset. - Optional getOffsetForProperty(Type type, StringRef propertyName); + Result getOffsetForProperty(Type type, StringRef propertyName); }; } // end namespace remoteAST diff --git a/lib/RemoteAST/RemoteAST.cpp b/lib/RemoteAST/RemoteAST.cpp index 4be99a6006b..607bef02437 100644 --- a/lib/RemoteAST/RemoteAST.cpp +++ b/lib/RemoteAST/RemoteAST.cpp @@ -506,26 +506,33 @@ public: } } - Type getTypeForRemoteTypeMetadata(RemoteAddress address) { + Result getTypeForRemoteTypeMetadata(RemoteAddress address) { + Type result; if (Is32) { - return Reader32->readTypeFromMetadata(address.getAddressData()); + result = Reader32->readTypeFromMetadata(address.getAddressData()); } else { - return Reader64->readTypeFromMetadata(address.getAddressData()); + result = Reader64->readTypeFromMetadata(address.getAddressData()); } + if (result) return result; + return Result::emplaceFailure(Failure::Unknown); } - NominalTypeDecl *getDeclForRemoteNominalTypeDescriptor(RemoteAddress address){ - + Result + getDeclForRemoteNominalTypeDescriptor(RemoteAddress address) { + NominalTypeDecl *result; if (Is32) { - return Reader32->readNominalTypeFromDescriptor(address.getAddressData()); + result =Reader32->readNominalTypeFromDescriptor(address.getAddressData()); } else { - return Reader64->readNominalTypeFromDescriptor(address.getAddressData()); + result =Reader64->readNominalTypeFromDescriptor(address.getAddressData()); } + + if (result) return result; + return Result::emplaceFailure(Failure::Unknown); } - Optional getOffsetForProperty(Type type, StringRef propertyName) { + Result getOffsetForProperty(Type type, StringRef propertyName) { // TODO - return None; + return Result::emplaceFailure(Failure::Unknown); } }; @@ -544,16 +551,17 @@ RemoteASTContext::~RemoteASTContext() { delete asImpl(Impl); } -Type RemoteASTContext::getTypeForRemoteTypeMetadata(RemoteAddress address) { +Result +RemoteASTContext::getTypeForRemoteTypeMetadata(RemoteAddress address) { return asImpl(Impl)->getTypeForRemoteTypeMetadata(address); } -NominalTypeDecl * +Result RemoteASTContext::getDeclForRemoteNominalTypeDescriptor(RemoteAddress address) { return asImpl(Impl)->getDeclForRemoteNominalTypeDescriptor(address); } -Optional +Result RemoteASTContext::getOffsetForProperty(Type type, StringRef propertyName) { return asImpl(Impl)->getOffsetForProperty(type, propertyName); }