mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
We've decided to use the "invertible protocols" terminology throughout the runtime and compiler, so move over to that terminology consistently.
3475 lines
124 KiB
C++
3475 lines
124 KiB
C++
//===--- MetadataReader.h - Abstract access to remote metadata --*- 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 defines operations for reading metadata from a remote process.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#ifndef SWIFT_REMOTE_METADATAREADER_H
|
|
#define SWIFT_REMOTE_METADATAREADER_H
|
|
|
|
|
|
#include "swift/Runtime/Metadata.h"
|
|
#include "swift/Remote/MemoryReader.h"
|
|
#include "swift/Demangling/Demangler.h"
|
|
#include "swift/Demangling/TypeDecoder.h"
|
|
#include "swift/Basic/Defer.h"
|
|
#include "swift/Basic/ExternalUnion.h"
|
|
#include "swift/Basic/MathUtils.h"
|
|
#include "swift/Basic/Range.h"
|
|
#include "swift/Basic/LLVM.h"
|
|
#include "swift/ABI/TypeIdentity.h"
|
|
#include "swift/Runtime/ExistentialContainer.h"
|
|
#include "swift/Runtime/HeapObject.h"
|
|
#include "swift/Basic/Unreachable.h"
|
|
|
|
#include <type_traits>
|
|
#include <vector>
|
|
|
|
#include <inttypes.h>
|
|
|
|
namespace swift {
|
|
namespace remote {
|
|
|
|
template <typename BuiltType>
|
|
using FunctionParam = swift::Demangle::FunctionParam<BuiltType>;
|
|
|
|
template <typename BuilderType>
|
|
using TypeDecoder = swift::Demangle::TypeDecoder<BuilderType>;
|
|
|
|
/// The kind of mangled name to read.
|
|
enum class MangledNameKind {
|
|
Type,
|
|
Symbol,
|
|
};
|
|
|
|
/// A pointer to the local buffer of an object that also remembers the
|
|
/// address at which it was stored remotely.
|
|
template <typename T>
|
|
class RemoteRef {
|
|
private:
|
|
uint64_t Address;
|
|
const T *LocalBuffer;
|
|
|
|
public:
|
|
RemoteRef()
|
|
: Address(0), LocalBuffer(nullptr) {}
|
|
|
|
/*implicit*/
|
|
RemoteRef(std::nullptr_t _) : RemoteRef() {}
|
|
|
|
template<typename StoredPointer>
|
|
explicit RemoteRef(StoredPointer address, const T *localBuffer)
|
|
: Address((uint64_t)address), LocalBuffer(localBuffer) {}
|
|
|
|
// <rdar://99715218> Some versions of clang++ sometimes fail to generate the
|
|
// copy constructor for this type correctly - add a workaround
|
|
RemoteRef(const RemoteRef &other)
|
|
: Address(other.Address), LocalBuffer(other.LocalBuffer) {}
|
|
|
|
RemoteRef& operator=(const RemoteRef &other) {
|
|
Address = other.Address;
|
|
LocalBuffer = other.LocalBuffer;
|
|
return *this;
|
|
}
|
|
|
|
uint64_t getAddressData() const {
|
|
return Address;
|
|
}
|
|
|
|
const T *getLocalBuffer() const {
|
|
return LocalBuffer;
|
|
}
|
|
|
|
explicit operator bool() const {
|
|
return LocalBuffer != nullptr;
|
|
}
|
|
|
|
const T *operator->() const {
|
|
assert(LocalBuffer);
|
|
return LocalBuffer;
|
|
}
|
|
|
|
bool operator==(RemoteRef<T> other) const {
|
|
return Address == other.Address;
|
|
}
|
|
|
|
bool operator!=(RemoteRef<T> other) const {
|
|
return !operator==(other);
|
|
}
|
|
|
|
/// Project a reference for a field. The field must be projected from the same
|
|
/// LocalBuffer pointer as this RemoteRef.
|
|
template<typename U>
|
|
RemoteRef<U> getField(U &field) const {
|
|
auto offset = (intptr_t)&field - (intptr_t)LocalBuffer;
|
|
return RemoteRef<U>((uint64_t)(Address + (int64_t)offset), &field);
|
|
}
|
|
|
|
/// Resolve the remote address of a relative offset stored at the remote address.
|
|
uint64_t resolveRelativeAddressData() const {
|
|
int32_t offset;
|
|
memcpy(&offset, LocalBuffer, sizeof(int32_t));
|
|
if (offset == 0)
|
|
return 0;
|
|
return Address + (int64_t)offset;
|
|
}
|
|
|
|
template<typename U>
|
|
uint64_t resolveRelativeFieldData(U &field) const {
|
|
return getField(field).resolveRelativeAddressData();
|
|
}
|
|
|
|
RemoteRef atByteOffset(int64_t Offset) const {
|
|
return RemoteRef(Address + Offset,
|
|
(const T *)((intptr_t)LocalBuffer + Offset));
|
|
}
|
|
};
|
|
|
|
/// A structure, designed for use with std::unique_ptr, which destroys
|
|
/// a pointer by calling free on it (and not trying to call a destructor).
|
|
struct delete_with_free {
|
|
void operator()(const void *memory) {
|
|
free(const_cast<void*>(memory));
|
|
}
|
|
};
|
|
|
|
/// A structure representing an opened existential type.
|
|
struct RemoteExistential {
|
|
/// The payload's concrete type metadata.
|
|
RemoteAddress MetadataAddress;
|
|
|
|
/// The address of the payload value.
|
|
RemoteAddress PayloadAddress;
|
|
|
|
/// True if this is an NSError instance transparently bridged to an Error
|
|
/// existential.
|
|
bool IsBridgedError;
|
|
|
|
RemoteExistential(RemoteAddress MetadataAddress,
|
|
RemoteAddress PayloadAddress,
|
|
bool IsBridgedError=false)
|
|
: MetadataAddress(MetadataAddress),
|
|
PayloadAddress(PayloadAddress),
|
|
IsBridgedError(IsBridgedError) {}
|
|
};
|
|
|
|
/// A generic reader of metadata.
|
|
///
|
|
/// BuilderType must implement a particular interface which is currently
|
|
/// too fluid to allow useful documentation; consult the actual
|
|
/// implementations. The chief thing is that it provides several member
|
|
/// types which should obey the following constraints:
|
|
/// - T() yields a value which is false when contextually converted to bool
|
|
/// - a false value signals that an error occurred when building a value
|
|
template <typename Runtime, typename BuilderType>
|
|
class MetadataReader {
|
|
public:
|
|
using BuiltType = typename BuilderType::BuiltType;
|
|
using BuiltTypeDecl = typename BuilderType::BuiltTypeDecl;
|
|
using BuiltProtocolDecl = typename BuilderType::BuiltProtocolDecl;
|
|
using BuiltRequirement = typename BuilderType::BuiltRequirement;
|
|
using BuiltSubstitution = typename BuilderType::BuiltSubstitution;
|
|
using BuiltSubstitutionMap = typename BuilderType::BuiltSubstitutionMap;
|
|
using BuiltGenericSignature = typename BuilderType::BuiltGenericSignature;
|
|
using StoredPointer = typename Runtime::StoredPointer;
|
|
using StoredSignedPointer = typename Runtime::StoredSignedPointer;
|
|
using StoredSize = typename Runtime::StoredSize;
|
|
using TargetClassMetadata = TargetClassMetadataType<Runtime>;
|
|
|
|
static const int defaultTypeRecursionLimit = 50;
|
|
|
|
private:
|
|
/// The maximum number of bytes to read when reading metadata. Anything larger
|
|
/// will automatically return failure. This prevents us from reading absurd
|
|
/// amounts of data when we encounter corrupt values for sizes/counts.
|
|
static const uint64_t MaxMetadataSize = 1048576; // 1MB
|
|
|
|
/// The dense map info for a std::pair<StoredPointer, bool>.
|
|
struct DenseMapInfoTypeCacheKey {
|
|
using Pair = std::pair<StoredPointer, bool>;
|
|
using StoredPointerInfo = llvm::DenseMapInfo<StoredPointer>;
|
|
|
|
static inline Pair getEmptyKey() {
|
|
// Since bool doesn't have an empty key implementation, we only use the
|
|
// StoredPointer's empty key.
|
|
return std::make_pair(StoredPointerInfo::getEmptyKey(), false);
|
|
}
|
|
|
|
static inline Pair getTombstoneKey() {
|
|
// Since bool doesn't have a tombstone key implementation, we only use the
|
|
// StoredPointer's tombstone key.
|
|
return std::make_pair(StoredPointerInfo::getTombstoneKey(), false);
|
|
}
|
|
|
|
static unsigned getHashValue(const Pair &PairVal) {
|
|
return llvm::hash_combine(PairVal.first, PairVal.second);
|
|
}
|
|
|
|
static bool isEqual(const Pair &LHS, const Pair &RHS) {
|
|
return LHS.first == RHS.first && LHS.second == RHS.second;
|
|
}
|
|
};
|
|
|
|
/// A cache of built types, keyed by the address of the type and whether the
|
|
/// request ignored articial superclasses or not.
|
|
llvm::DenseMap<std::pair<StoredPointer, bool>, BuiltType,
|
|
DenseMapInfoTypeCacheKey>
|
|
TypeCache;
|
|
|
|
using MetadataRef = RemoteRef<const TargetMetadata<Runtime>>;
|
|
using OwnedMetadataRef = MemoryReader::ReadBytesResult;
|
|
|
|
/// A cache of read type metadata, keyed by the address of the metadata.
|
|
llvm::DenseMap<StoredPointer, OwnedMetadataRef> MetadataCache;
|
|
|
|
using ContextDescriptorRef =
|
|
RemoteRef<const TargetContextDescriptor<Runtime>>;
|
|
using OwnedContextDescriptorRef = MemoryReader::ReadBytesResult;
|
|
|
|
using ShapeRef =
|
|
RemoteRef<const TargetExtendedExistentialTypeShape<Runtime>>;
|
|
using OwnedShapeRef = MemoryReader::ReadBytesResult;
|
|
|
|
/// A reference to a context descriptor that may be in an unloaded image.
|
|
class ParentContextDescriptorRef {
|
|
bool IsResolved;
|
|
using Payloads = ExternalUnionMembers<std::string, ContextDescriptorRef>;
|
|
static typename Payloads::Index getPayloadIndex(bool IsResolved) {
|
|
return IsResolved ? Payloads::template indexOf<ContextDescriptorRef>()
|
|
: Payloads::template indexOf<std::string>();
|
|
}
|
|
|
|
ExternalUnion<bool, Payloads, getPayloadIndex> Payload;
|
|
|
|
public:
|
|
explicit ParentContextDescriptorRef(StringRef Symbol)
|
|
: IsResolved(false)
|
|
{
|
|
Payload.template emplace<std::string>(IsResolved, Symbol);
|
|
}
|
|
|
|
explicit ParentContextDescriptorRef(ContextDescriptorRef Resolved)
|
|
: IsResolved(true)
|
|
{
|
|
Payload.template emplace<ContextDescriptorRef>(IsResolved, Resolved);
|
|
}
|
|
|
|
ParentContextDescriptorRef()
|
|
: ParentContextDescriptorRef(ContextDescriptorRef())
|
|
{}
|
|
|
|
ParentContextDescriptorRef(const ParentContextDescriptorRef &o)
|
|
: IsResolved(o.IsResolved)
|
|
{
|
|
Payload.copyConstruct(IsResolved, o.Payload);
|
|
}
|
|
|
|
ParentContextDescriptorRef(ParentContextDescriptorRef &&o)
|
|
: IsResolved(o.IsResolved)
|
|
{
|
|
Payload.moveConstruct(IsResolved, std::move(o.Payload));
|
|
}
|
|
|
|
~ParentContextDescriptorRef() {
|
|
Payload.destruct(IsResolved);
|
|
}
|
|
|
|
ParentContextDescriptorRef &operator=(const ParentContextDescriptorRef &o) {
|
|
Payload.copyAssign(IsResolved, o.IsResolved, o.Payload);
|
|
IsResolved = o.isResolved();
|
|
return *this;
|
|
}
|
|
ParentContextDescriptorRef &operator=(ParentContextDescriptorRef &&o) {
|
|
Payload.moveAssign(IsResolved, o.IsResolved, std::move(o.Payload));
|
|
IsResolved = o.isResolved();
|
|
return *this;
|
|
}
|
|
|
|
bool isResolved() const { return IsResolved; }
|
|
|
|
StringRef getSymbol() const {
|
|
return Payload.template get<std::string>(IsResolved);
|
|
}
|
|
|
|
ContextDescriptorRef getResolved() const {
|
|
return Payload.template get<ContextDescriptorRef>(IsResolved);
|
|
}
|
|
|
|
explicit operator bool() const {
|
|
return !isResolved() || getResolved();
|
|
}
|
|
};
|
|
|
|
/// A cache of read nominal type descriptors, keyed by the address of the
|
|
/// nominal type descriptor.
|
|
llvm::DenseMap<StoredPointer, OwnedContextDescriptorRef>
|
|
ContextDescriptorCache;
|
|
|
|
using OwnedProtocolDescriptorRef =
|
|
std::unique_ptr<const TargetProtocolDescriptor<Runtime>, delete_with_free>;
|
|
/// A cache of read extended existential shape metadata, keyed by the
|
|
/// address of the shape metadata.
|
|
llvm::DenseMap<StoredPointer, OwnedShapeRef> ShapeCache;
|
|
|
|
enum class IsaEncodingKind {
|
|
/// We haven't checked yet.
|
|
Unknown,
|
|
|
|
/// There was an error trying to find out the isa encoding.
|
|
Error,
|
|
|
|
/// There's no special isa encoding.
|
|
None,
|
|
|
|
/// There's an unconditional mask to apply to the isa pointer.
|
|
/// - IsaMask stores the mask.
|
|
Masked,
|
|
|
|
/// Isa pointers are indexed. If applying a mask yields a magic value,
|
|
/// applying a different mask and shifting yields an index into a global
|
|
/// array of class pointers. Otherwise, the isa pointer is just a raw
|
|
/// class pointer.
|
|
/// - IsaIndexMask stores the index mask.
|
|
/// - IsaIndexShift stores the index shift.
|
|
/// - IsaMagicMask stores the magic value mask.
|
|
/// - IsaMagicValue stores the magic value.
|
|
/// - IndexedClassesPointer stores the pointer to the start of the
|
|
/// indexed classes array; this is constant throughout the program.
|
|
/// - IndexedClassesCountPointer stores a pointer to the number
|
|
/// of elements in the indexed classes array.
|
|
Indexed
|
|
};
|
|
|
|
IsaEncodingKind IsaEncoding = IsaEncodingKind::Unknown;
|
|
union {
|
|
StoredPointer IsaMask;
|
|
StoredPointer IsaIndexMask;
|
|
};
|
|
StoredPointer IsaIndexShift;
|
|
StoredPointer IsaMagicMask;
|
|
StoredPointer IsaMagicValue;
|
|
StoredPointer IndexedClassesPointer;
|
|
StoredPointer IndexedClassesCountPointer;
|
|
StoredPointer LastIndexedClassesCount = 0;
|
|
|
|
enum class TaggedPointerEncodingKind {
|
|
/// We haven't checked yet.
|
|
Unknown,
|
|
|
|
/// There was an error trying to find out the tagged pointer encoding.
|
|
Error,
|
|
|
|
/// The "extended" encoding.
|
|
///
|
|
/// 1 bit: is-tagged
|
|
/// 3 bits: class index (for objc_debug_taggedpointer_classes[])
|
|
/// 60 bits: payload
|
|
///
|
|
/// Class index 0b111 represents 256 additional classes:
|
|
///
|
|
/// 1 bit: is-tagged
|
|
/// 3 bits: 0b111
|
|
/// 8 bits: extended class index (for objc_debug_taggedpointer_ext_classes[])
|
|
/// 54 bits: payload
|
|
Extended
|
|
};
|
|
TaggedPointerEncodingKind TaggedPointerEncoding =
|
|
TaggedPointerEncodingKind::Unknown;
|
|
StoredPointer TaggedPointerMask;
|
|
StoredPointer TaggedPointerSlotShift;
|
|
StoredPointer TaggedPointerSlotMask;
|
|
StoredPointer TaggedPointerClasses;
|
|
StoredPointer TaggedPointerExtendedMask;
|
|
StoredPointer TaggedPointerExtendedSlotShift;
|
|
StoredPointer TaggedPointerExtendedSlotMask;
|
|
StoredPointer TaggedPointerExtendedClasses;
|
|
StoredPointer TaggedPointerObfuscator;
|
|
|
|
Demangle::NodeFactory Factory;
|
|
|
|
Demangle::NodeFactory &getNodeFactory() { return Factory; }
|
|
|
|
public:
|
|
BuilderType Builder;
|
|
|
|
BuilderType &getBuilder() {
|
|
return this->Builder;
|
|
}
|
|
|
|
std::shared_ptr<MemoryReader> Reader;
|
|
|
|
StoredPointer PtrAuthMask;
|
|
|
|
StoredPointer stripSignedPointer(StoredSignedPointer P) {
|
|
return P.SignedValue & PtrAuthMask;
|
|
}
|
|
|
|
RemoteAbsolutePointer stripSignedPointer(const RemoteAbsolutePointer &P) {
|
|
if (P.isResolved()) {
|
|
return RemoteAbsolutePointer("",
|
|
P.getResolvedAddress().getAddressData() & PtrAuthMask);
|
|
}
|
|
return P;
|
|
}
|
|
|
|
StoredPointer queryPtrAuthMask() {
|
|
StoredPointer QueryResult;
|
|
if (Reader->queryDataLayout(DataLayoutQueryType::DLQ_GetPtrAuthMask,
|
|
nullptr, &QueryResult)) {
|
|
return QueryResult;
|
|
}
|
|
return ~StoredPointer(0);
|
|
}
|
|
|
|
template <class... T>
|
|
MetadataReader(std::shared_ptr<MemoryReader> reader, T &&... args)
|
|
: Builder(std::forward<T>(args)...),
|
|
Reader(std::move(reader)),
|
|
PtrAuthMask(queryPtrAuthMask()) {
|
|
}
|
|
|
|
MetadataReader(const MetadataReader &other) = delete;
|
|
MetadataReader &operator=(const MetadataReader &other) = delete;
|
|
|
|
/// Clear all of the caches in this reader.
|
|
void clear() {
|
|
TypeCache.clear();
|
|
MetadataCache.clear();
|
|
ContextDescriptorCache.clear();
|
|
}
|
|
|
|
/// Demangle a mangled name that was read from the given remote address.
|
|
Demangle::NodePointer demangle(RemoteRef<char> mangledName,
|
|
MangledNameKind kind,
|
|
Demangler &dem,
|
|
bool useOpaqueTypeSymbolicReferences = false) {
|
|
// Symbolic reference resolver for the demangle operation below.
|
|
auto symbolicReferenceResolver = [&](SymbolicReferenceKind kind,
|
|
Directness directness,
|
|
int32_t offset,
|
|
const void *base) ->
|
|
swift::Demangle::NodePointer {
|
|
// Resolve the reference to a remote address.
|
|
auto offsetInMangledName =
|
|
(const char *)base - mangledName.getLocalBuffer();
|
|
auto remoteAddress =
|
|
mangledName.getAddressData() + offsetInMangledName + offset;
|
|
|
|
RemoteAbsolutePointer resolved;
|
|
if (directness == Directness::Indirect) {
|
|
if (auto indirectAddress = readPointer(remoteAddress)) {
|
|
resolved = stripSignedPointer(*indirectAddress);
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
} else {
|
|
resolved = Reader->getSymbol(RemoteAddress(remoteAddress));
|
|
}
|
|
|
|
switch (kind) {
|
|
case Demangle::SymbolicReferenceKind::Context: {
|
|
auto context = readContextDescriptor(resolved);
|
|
if (!context)
|
|
return nullptr;
|
|
// Try to preserve a reference to an OpaqueTypeDescriptor
|
|
// symbolically, since we'd like to read out and resolve the type ref
|
|
// to the underlying type if available.
|
|
if (useOpaqueTypeSymbolicReferences
|
|
&& context.isResolved()
|
|
&& context.getResolved()->getKind() == ContextDescriptorKind::OpaqueType){
|
|
return dem.createNode(
|
|
Node::Kind::OpaqueTypeDescriptorSymbolicReference,
|
|
context.getResolved().getAddressData());
|
|
}
|
|
|
|
return buildContextMangling(context, dem);
|
|
}
|
|
case Demangle::SymbolicReferenceKind::AccessorFunctionReference: {
|
|
// The symbolic reference points at a resolver function, but we can't
|
|
// execute code in the target process to resolve it from here.
|
|
return nullptr;
|
|
}
|
|
case Demangle::SymbolicReferenceKind::UniqueExtendedExistentialTypeShape: {
|
|
// The symbolic reference points at a unique extended
|
|
// existential type shape.
|
|
return dem.createNode(
|
|
Node::Kind::UniqueExtendedExistentialTypeShapeSymbolicReference,
|
|
resolved.getResolvedAddress().getAddressData());
|
|
}
|
|
case Demangle::SymbolicReferenceKind::NonUniqueExtendedExistentialTypeShape: {
|
|
// The symbolic reference points at a non-unique extended
|
|
// existential type shape.
|
|
return dem.createNode(
|
|
Node::Kind::NonUniqueExtendedExistentialTypeShapeSymbolicReference,
|
|
resolved.getResolvedAddress().getAddressData());
|
|
}
|
|
case Demangle::SymbolicReferenceKind::ObjectiveCProtocol: {
|
|
// 'resolved' points to a struct of two relative addresses.
|
|
// The second entry is a relative address to the mangled protocol
|
|
// without symbolic references.
|
|
|
|
// lldb might return an unresolved remote absolute pointer from its
|
|
// resolvePointerAsSymbol implementation -- workaround this.
|
|
if (!resolved.isResolved()) {
|
|
auto remoteAddr = RemoteAddress(remoteAddress);
|
|
resolved =
|
|
RemoteAbsolutePointer("", remoteAddr.getAddressData());
|
|
}
|
|
|
|
auto addr =
|
|
resolved.getResolvedAddress().getAddressData() + sizeof(int32_t);
|
|
int32_t offset;
|
|
Reader->readInteger(RemoteAddress(addr), &offset);
|
|
auto addrOfTypeRef = addr + offset;
|
|
resolved = Reader->getSymbol(RemoteAddress(addrOfTypeRef));
|
|
|
|
// lldb might return an unresolved remote absolute pointer from its
|
|
// resolvePointerAsSymbol implementation -- workaround this.
|
|
if (!resolved.isResolved()) {
|
|
auto remoteAddr = RemoteAddress(addrOfTypeRef);
|
|
resolved =
|
|
RemoteAbsolutePointer("", remoteAddr.getAddressData());
|
|
}
|
|
|
|
// Dig out the protocol from the protocol list.
|
|
auto protocolList = readMangledName(resolved.getResolvedAddress(),
|
|
MangledNameKind::Type, dem);
|
|
assert(protocolList && protocolList->getNumChildren());
|
|
if (!protocolList || !protocolList->getNumChildren())
|
|
return nullptr;
|
|
auto child = protocolList->getFirstChild();
|
|
assert(child && child->getNumChildren());
|
|
if (!child || !child->getNumChildren())
|
|
return nullptr;
|
|
child = child->getFirstChild();
|
|
assert(child && child->getNumChildren());
|
|
if (!child || !child->getNumChildren())
|
|
return nullptr;
|
|
assert(child && child->getNumChildren());
|
|
child = child->getFirstChild();
|
|
if (!child || !child->getNumChildren())
|
|
return nullptr;
|
|
child = child->getFirstChild();
|
|
assert(child && child->getKind() == Node::Kind::Protocol);
|
|
if (!child || child->getKind() != Node::Kind::Protocol)
|
|
return nullptr;
|
|
auto protocol = child;
|
|
auto protocolType = dem.createNode(Node::Kind::Type);
|
|
protocolType->addChild(protocol, dem);
|
|
return protocolType;
|
|
}
|
|
}
|
|
|
|
return nullptr;
|
|
};
|
|
|
|
auto mangledNameStr =
|
|
Demangle::makeSymbolicMangledNameStringRef(mangledName.getLocalBuffer());
|
|
|
|
swift::Demangle::NodePointer result;
|
|
switch (kind) {
|
|
case MangledNameKind::Type:
|
|
result = dem.demangleType(mangledNameStr, symbolicReferenceResolver);
|
|
break;
|
|
|
|
case MangledNameKind::Symbol:
|
|
result = dem.demangleSymbol(mangledNameStr, symbolicReferenceResolver);
|
|
break;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/// Demangle a mangled name from a potentially temporary std::string. The
|
|
/// demangler may produce pointers into the string data, so this copies the
|
|
/// string into the demangler's allocation first.
|
|
Demangle::NodePointer demangle(uint64_t remoteAddress,
|
|
const std::string &mangledName,
|
|
MangledNameKind kind,
|
|
Demangler &dem) {
|
|
StringRef mangledNameCopy = dem.copyString(mangledName);
|
|
return demangle(RemoteRef<char>(remoteAddress, mangledNameCopy.data()),
|
|
kind, dem);
|
|
}
|
|
|
|
/// Given a demangle tree, attempt to turn it into a type.
|
|
TypeLookupErrorOr<typename BuilderType::BuiltType>
|
|
decodeMangledType(NodePointer Node) {
|
|
return swift::Demangle::decodeMangledType(Builder, Node);
|
|
}
|
|
|
|
/// Get the remote process's swift_isaMask.
|
|
std::optional<StoredPointer> readIsaMask() {
|
|
auto encoding = getIsaEncoding();
|
|
if (encoding != IsaEncodingKind::Masked) {
|
|
// Still return success if there's no isa encoding at all.
|
|
if (encoding == IsaEncodingKind::None)
|
|
return 0;
|
|
else
|
|
return std::nullopt;
|
|
}
|
|
|
|
return IsaMask;
|
|
}
|
|
|
|
/// Given a remote pointer to metadata, attempt to discover its MetadataKind.
|
|
std::optional<MetadataKind>
|
|
readKindFromMetadata(StoredPointer MetadataAddress) {
|
|
auto meta = readMetadata(MetadataAddress);
|
|
if (!meta)
|
|
return std::nullopt;
|
|
|
|
return meta->getKind();
|
|
}
|
|
|
|
/// Given a remote pointer to class metadata, attempt to read its superclass.
|
|
StoredPointer
|
|
readSuperClassFromClassMetadata(StoredPointer MetadataAddress) {
|
|
auto meta = readMetadata(MetadataAddress);
|
|
if (!meta || meta->getKind() != MetadataKind::Class)
|
|
return StoredPointer();
|
|
|
|
auto classMeta = cast<TargetClassMetadata>(meta);
|
|
return stripSignedPointer(classMeta->Superclass);
|
|
}
|
|
|
|
/// Given a remote pointer to class metadata, attempt to discover its class
|
|
/// instance size and whether fields should use the resilient layout strategy.
|
|
std::optional<unsigned>
|
|
readInstanceStartFromClassMetadata(StoredPointer MetadataAddress) {
|
|
auto meta = readMetadata(MetadataAddress);
|
|
if (!meta || meta->getKind() != MetadataKind::Class)
|
|
return std::nullopt;
|
|
|
|
if (Runtime::ObjCInterop) {
|
|
// The following algorithm only works on the non-fragile Apple runtime.
|
|
|
|
// Grab the RO-data pointer. This part is not ABI.
|
|
StoredPointer roDataPtr = readObjCRODataPtr(MetadataAddress);
|
|
if (!roDataPtr)
|
|
return std::nullopt;
|
|
|
|
// Get the address of the InstanceStart field.
|
|
auto address = roDataPtr + sizeof(uint32_t) * 1;
|
|
|
|
unsigned start;
|
|
if (!Reader->readInteger(RemoteAddress(address), &start))
|
|
return std::nullopt;
|
|
|
|
return start;
|
|
} else {
|
|
// All swift class instances start with an isa pointer,
|
|
// followed by the retain counts (which are the size of a long long).
|
|
size_t isaAndRetainCountSize = sizeof(StoredSize) + sizeof(long long);
|
|
size_t start = isaAndRetainCountSize;
|
|
|
|
auto classMeta = cast<TargetClassMetadata>(meta);
|
|
while (stripSignedPointer(classMeta->Superclass)) {
|
|
meta = readMetadata(stripSignedPointer(classMeta->Superclass));
|
|
if (!meta || meta->getKind() != MetadataKind::Class)
|
|
return std::nullopt;
|
|
|
|
classMeta = cast<TargetClassMetadata>(meta);
|
|
|
|
// Subtract the size contribution of the isa and retain counts from
|
|
// the super class.
|
|
start += classMeta->InstanceSize - isaAndRetainCountSize;
|
|
}
|
|
return start;
|
|
}
|
|
}
|
|
|
|
/// Given a pointer to the metadata, attempt to read the value
|
|
/// witness table. Note that it's not safe to access any non-mandatory
|
|
/// members of the value witness table, like extra inhabitants or enum members.
|
|
std::optional<TargetValueWitnessTable<Runtime>>
|
|
readValueWitnessTable(StoredPointer MetadataAddress) {
|
|
// The value witness table pointer is at offset -1 from the metadata
|
|
// pointer, that is, the pointer-sized word immediately before the
|
|
// pointer's referenced address.
|
|
TargetValueWitnessTable<Runtime> VWT;
|
|
auto ValueWitnessTableAddrAddr = MetadataAddress - sizeof(StoredPointer);
|
|
StoredPointer ValueWitnessTableAddr;
|
|
if (!Reader->readInteger(RemoteAddress(ValueWitnessTableAddrAddr),
|
|
&ValueWitnessTableAddr))
|
|
return std::nullopt;
|
|
if (!Reader->readBytes(RemoteAddress(ValueWitnessTableAddr),
|
|
(uint8_t *)&VWT, sizeof(VWT)))
|
|
return std::nullopt;
|
|
return VWT;
|
|
}
|
|
|
|
/// Given a pointer to a known-error existential, attempt to discover the
|
|
/// pointer to its metadata address, its value address, and whether this
|
|
/// is a toll-free-bridged NSError or an actual Error existential wrapper
|
|
/// around a native Swift value.
|
|
std::optional<RemoteExistential>
|
|
readMetadataAndValueErrorExistential(RemoteAddress ExistentialAddress) {
|
|
// An pointer to an error existential is always an heap object.
|
|
auto MetadataAddress =
|
|
readMetadataFromInstance(ExistentialAddress.getAddressData());
|
|
if (!MetadataAddress)
|
|
return std::nullopt;
|
|
|
|
bool isObjC = false;
|
|
bool isBridged = false;
|
|
|
|
auto Meta = readMetadata(*MetadataAddress);
|
|
if (!Meta)
|
|
return std::nullopt;
|
|
|
|
if (auto ClassMeta = dyn_cast<TargetClassMetadata>(Meta)) {
|
|
if (ClassMeta->isPureObjC()) {
|
|
// If we can determine the Objective-C class name, this is probably an
|
|
// error existential with NSError-compatible layout.
|
|
std::string ObjCClassName;
|
|
if (readObjCClassName(*MetadataAddress, ObjCClassName)) {
|
|
if (ObjCClassName == "__SwiftNativeNSError")
|
|
isObjC = true;
|
|
else
|
|
isBridged = true;
|
|
}
|
|
} else {
|
|
isBridged = true;
|
|
}
|
|
}
|
|
|
|
if (isBridged) {
|
|
// NSError instances don't need to be unwrapped.
|
|
return RemoteExistential(RemoteAddress(*MetadataAddress),
|
|
ExistentialAddress,
|
|
isBridged);
|
|
}
|
|
|
|
// In addition to the isa pointer and two 32-bit reference counts, if the
|
|
// error existential is layout-compatible with NSError, we also need to
|
|
// skip over its three word-sized fields: the error code, the domain,
|
|
// and userInfo.
|
|
StoredPointer InstanceMetadataAddressAddress =
|
|
ExistentialAddress.getAddressData() +
|
|
(isObjC ? 5 : 2) * sizeof(StoredPointer);
|
|
|
|
// We need to get the instance's alignment info so we can get the exact
|
|
// offset of the start of its data in the class.
|
|
auto InstanceMetadataAddress =
|
|
readMetadataFromInstance(InstanceMetadataAddressAddress);
|
|
if (!InstanceMetadataAddress)
|
|
return std::nullopt;
|
|
|
|
// Read the value witness table.
|
|
auto VWT = readValueWitnessTable(*InstanceMetadataAddress);
|
|
if (!VWT)
|
|
return std::nullopt;
|
|
|
|
// Now we need to skip over the instance metadata pointer and instance's
|
|
// conformance pointer for Swift.Error.
|
|
StoredPointer InstanceAddress =
|
|
InstanceMetadataAddressAddress + 2 * sizeof(StoredPointer);
|
|
|
|
// When built with Objective-C interop, the runtime also stores a conformance
|
|
// to Hashable and the base type introducing the Hashable conformance.
|
|
if (isObjC)
|
|
InstanceAddress += 2 * sizeof(StoredPointer);
|
|
|
|
// Round up to alignment, and we have the start address of the
|
|
// instance payload.
|
|
auto AlignmentMask = VWT->getAlignmentMask();
|
|
InstanceAddress = (InstanceAddress + AlignmentMask) & ~AlignmentMask;
|
|
|
|
return RemoteExistential(
|
|
RemoteAddress(*InstanceMetadataAddress),
|
|
RemoteAddress(InstanceAddress),
|
|
isBridged);
|
|
}
|
|
|
|
/// Given a known-opaque existential, attempt to discover the pointer to its
|
|
/// metadata address and its value.
|
|
std::optional<RemoteExistential>
|
|
readMetadataAndValueOpaqueExistential(RemoteAddress ExistentialAddress) {
|
|
// OpaqueExistentialContainer is the layout of an opaque existential.
|
|
// `Type` is the pointer to the metadata.
|
|
TargetOpaqueExistentialContainer<Runtime> Container;
|
|
if (!Reader->readBytes(RemoteAddress(ExistentialAddress),
|
|
(uint8_t *)&Container, sizeof(Container)))
|
|
return std::nullopt;
|
|
auto MetadataAddress = static_cast<StoredPointer>(Container.Type);
|
|
auto Metadata = readMetadata(MetadataAddress);
|
|
if (!Metadata)
|
|
return std::nullopt;
|
|
|
|
auto VWT = readValueWitnessTable(MetadataAddress);
|
|
if (!VWT)
|
|
return std::nullopt;
|
|
|
|
// Inline representation (the value fits in the existential container).
|
|
// So, the value starts at the first word of the container.
|
|
if (VWT->isValueInline())
|
|
return RemoteExistential(RemoteAddress(MetadataAddress),
|
|
ExistentialAddress);
|
|
|
|
// Non-inline (box'ed) representation.
|
|
// The first word of the container stores the address to the box.
|
|
StoredPointer BoxAddress;
|
|
if (!Reader->readInteger(ExistentialAddress, &BoxAddress))
|
|
return std::nullopt;
|
|
|
|
auto AlignmentMask = VWT->getAlignmentMask();
|
|
auto Offset = (sizeof(HeapObject) + AlignmentMask) & ~AlignmentMask;
|
|
auto StartOfValue = BoxAddress + Offset;
|
|
return RemoteExistential(RemoteAddress(MetadataAddress),
|
|
RemoteAddress(StartOfValue));
|
|
}
|
|
|
|
/// Given a known-opaque existential, discover if its value is inlined in
|
|
/// the existential container.
|
|
std::optional<bool>
|
|
isValueInlinedInExistentialContainer(RemoteAddress ExistentialAddress) {
|
|
// OpaqueExistentialContainer is the layout of an opaque existential.
|
|
// `Type` is the pointer to the metadata.
|
|
TargetOpaqueExistentialContainer<Runtime> Container;
|
|
if (!Reader->readBytes(RemoteAddress(ExistentialAddress),
|
|
(uint8_t *)&Container, sizeof(Container)))
|
|
return std::nullopt;
|
|
auto MetadataAddress = static_cast<StoredPointer>(Container.Type);
|
|
auto Metadata = readMetadata(MetadataAddress);
|
|
if (!Metadata)
|
|
return std::nullopt;
|
|
|
|
auto VWT = readValueWitnessTable(MetadataAddress);
|
|
if (!VWT)
|
|
return std::nullopt;
|
|
|
|
return VWT->isValueInline();
|
|
}
|
|
|
|
/// Read a protocol from a reference to said protocol.
|
|
template<typename Resolver>
|
|
typename Resolver::Result readProtocol(
|
|
const TargetProtocolDescriptorRef<Runtime> &ProtocolAddress,
|
|
Demangler &dem,
|
|
Resolver resolver) {
|
|
#if SWIFT_OBJC_INTEROP
|
|
if (Runtime::ObjCInterop) {
|
|
// Check whether we have an Objective-C protocol.
|
|
if (ProtocolAddress.isObjC()) {
|
|
auto Name = readObjCProtocolName(ProtocolAddress.getObjCProtocol());
|
|
StringRef NameStr(Name);
|
|
|
|
// If this is a Swift-defined protocol, demangle it.
|
|
if (NameStr.starts_with("_TtP")) {
|
|
auto Demangled = dem.demangleSymbol(NameStr);
|
|
if (!Demangled)
|
|
return resolver.failure();
|
|
|
|
// FIXME: This appears in _swift_buildDemanglingForMetadata().
|
|
while (Demangled->getKind() == Node::Kind::Global ||
|
|
Demangled->getKind() == Node::Kind::TypeMangling ||
|
|
Demangled->getKind() == Node::Kind::Type ||
|
|
Demangled->getKind() == Node::Kind::ProtocolList ||
|
|
Demangled->getKind() == Node::Kind::TypeList ||
|
|
Demangled->getKind() == Node::Kind::Type) {
|
|
if (Demangled->getNumChildren() != 1)
|
|
return resolver.failure();
|
|
Demangled = Demangled->getFirstChild();
|
|
}
|
|
|
|
return resolver.swiftProtocol(Demangled);
|
|
}
|
|
|
|
// Otherwise, this is an imported protocol.
|
|
return resolver.objcProtocol(NameStr);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Swift-native protocol.
|
|
auto Demangled =
|
|
readDemanglingForContextDescriptor(
|
|
stripSignedPointer({ProtocolAddress.getSwiftProtocol()}), dem);
|
|
if (!Demangled)
|
|
return resolver.failure();
|
|
|
|
return resolver.swiftProtocol(Demangled);
|
|
}
|
|
|
|
/// Given a remote pointer to metadata, attempt to turn it into a type.
|
|
BuiltType
|
|
readTypeFromMetadata(StoredPointer MetadataAddress,
|
|
bool skipArtificialSubclasses = false,
|
|
int recursion_limit = defaultTypeRecursionLimit) {
|
|
std::pair<StoredPointer, bool> TypeCacheKey(MetadataAddress,
|
|
skipArtificialSubclasses);
|
|
auto Cached = TypeCache.find(TypeCacheKey);
|
|
if (Cached != TypeCache.end())
|
|
return Cached->second;
|
|
|
|
if (recursion_limit <= 0) {
|
|
return nullptr;
|
|
}
|
|
|
|
// readTypeFromMetadata calls out to various other functions which can call
|
|
// back to readTypeFromMetadata. We only want to bump the recursion limit
|
|
// down here, not in the other functions, so that we're only counting
|
|
// recursive calls to readTypeFromMetadata. This decrement is the only place
|
|
// where we'll subtract 1.
|
|
recursion_limit--;
|
|
|
|
// If we see garbage data in the process of building a BuiltType, and get
|
|
// the same metadata address again, we will hit an infinite loop.
|
|
// Insert a negative result into the cache now so that, if we recur with
|
|
// the same address, we will return the negative result with the check
|
|
// just above.
|
|
TypeCache.insert({TypeCacheKey, BuiltType()});
|
|
|
|
auto Meta = readMetadata(MetadataAddress);
|
|
if (!Meta) return BuiltType();
|
|
|
|
switch (Meta->getKind()) {
|
|
case MetadataKind::Class:
|
|
return readNominalTypeFromClassMetadata(Meta, recursion_limit,
|
|
skipArtificialSubclasses);
|
|
case MetadataKind::Struct:
|
|
case MetadataKind::Enum:
|
|
case MetadataKind::Optional:
|
|
return readNominalTypeFromMetadata(Meta, recursion_limit);
|
|
case MetadataKind::Tuple: {
|
|
auto tupleMeta = cast<TargetTupleTypeMetadata<Runtime>>(Meta);
|
|
|
|
std::vector<BuiltType> elementTypes;
|
|
elementTypes.reserve(tupleMeta->NumElements);
|
|
|
|
for (unsigned i = 0, n = tupleMeta->NumElements; i != n; ++i) {
|
|
auto &element = tupleMeta->getElement(i);
|
|
if (auto elementType =
|
|
readTypeFromMetadata(element.Type, false, recursion_limit))
|
|
elementTypes.push_back(elementType);
|
|
else
|
|
return BuiltType();
|
|
}
|
|
|
|
// Read the labels string.
|
|
std::string labelStr;
|
|
if (tupleMeta->Labels &&
|
|
!Reader->readString(RemoteAddress(tupleMeta->Labels), labelStr))
|
|
return BuiltType();
|
|
|
|
std::vector<llvm::StringRef> labels;
|
|
std::string::size_type end, start = 0;
|
|
while (true) {
|
|
end = labelStr.find(' ', start);
|
|
if (end == std::string::npos)
|
|
break;
|
|
labels.push_back(llvm::StringRef(labelStr.data() + start, end - start));
|
|
start = end + 1;
|
|
}
|
|
// Pad the vector with empty labels.
|
|
for (unsigned i = labels.size(); i < elementTypes.size(); ++i)
|
|
labels.push_back(StringRef());
|
|
|
|
auto BuiltTuple =
|
|
Builder.createTupleType(elementTypes, labels);
|
|
TypeCache[TypeCacheKey] = BuiltTuple;
|
|
return BuiltTuple;
|
|
}
|
|
case MetadataKind::Function: {
|
|
auto Function = cast<TargetFunctionTypeMetadata<Runtime>>(Meta);
|
|
|
|
std::vector<FunctionParam<BuiltType>> Parameters;
|
|
for (unsigned i = 0, n = Function->getNumParameters(); i != n; ++i) {
|
|
auto ParamTypeRef = readTypeFromMetadata(Function->getParameter(i),
|
|
false, recursion_limit);
|
|
if (!ParamTypeRef)
|
|
return BuiltType();
|
|
|
|
FunctionParam<BuiltType> Param;
|
|
Param.setType(ParamTypeRef);
|
|
Param.setFlags(Function->getParameterFlags(i));
|
|
Parameters.push_back(std::move(Param));
|
|
}
|
|
|
|
auto Result =
|
|
readTypeFromMetadata(Function->ResultType, false, recursion_limit);
|
|
if (!Result)
|
|
return BuiltType();
|
|
|
|
auto flags = FunctionTypeFlags::fromIntValue(Function->Flags.getIntValue());
|
|
auto extFlags = ExtendedFunctionTypeFlags();
|
|
if (flags.hasExtendedFlags())
|
|
extFlags = ExtendedFunctionTypeFlags::fromIntValue(
|
|
Function->getExtendedFlags().getIntValue());
|
|
|
|
BuiltType globalActor = BuiltType();
|
|
if (Function->hasGlobalActor()) {
|
|
globalActor = readTypeFromMetadata(Function->getGlobalActor(), false,
|
|
recursion_limit);
|
|
if (!globalActor)
|
|
return BuiltType();
|
|
}
|
|
|
|
FunctionMetadataDifferentiabilityKind diffKind;
|
|
switch (Function->getDifferentiabilityKind().Value) {
|
|
#define CASE(X) \
|
|
case TargetFunctionMetadataDifferentiabilityKind< \
|
|
typename Runtime::StoredSize>::X: \
|
|
diffKind = FunctionMetadataDifferentiabilityKind::X; \
|
|
break;
|
|
CASE(NonDifferentiable)
|
|
CASE(Forward)
|
|
CASE(Reverse)
|
|
CASE(Normal)
|
|
CASE(Linear)
|
|
#undef CASE
|
|
}
|
|
|
|
BuiltType thrownError = BuiltType();
|
|
if (Function->hasThrownError()) {
|
|
thrownError = readTypeFromMetadata(Function->getThrownError(), false,
|
|
recursion_limit);
|
|
if (!thrownError)
|
|
return BuiltType();
|
|
}
|
|
|
|
auto BuiltFunction = Builder.createFunctionType(
|
|
Parameters, Result, flags, extFlags, diffKind, globalActor, thrownError);
|
|
TypeCache[TypeCacheKey] = BuiltFunction;
|
|
return BuiltFunction;
|
|
}
|
|
case MetadataKind::Existential: {
|
|
auto Exist = cast<TargetExistentialTypeMetadata<Runtime>>(Meta);
|
|
|
|
bool HasExplicitAnyObject = false;
|
|
if (Exist->isClassBounded())
|
|
HasExplicitAnyObject = true;
|
|
|
|
BuiltType SuperclassType = BuiltType();
|
|
if (Exist->Flags.hasSuperclassConstraint()) {
|
|
// The superclass is stored after the list of protocols.
|
|
SuperclassType = readTypeFromMetadata(Exist->getSuperclassConstraint(),
|
|
false, recursion_limit);
|
|
if (!SuperclassType) return BuiltType();
|
|
|
|
HasExplicitAnyObject = true;
|
|
}
|
|
|
|
/// Resolver to turn a protocol reference into a protocol declaration.
|
|
struct ProtocolResolver {
|
|
using Result = BuiltProtocolDecl;
|
|
|
|
BuilderType &builder;
|
|
|
|
BuiltProtocolDecl failure() const {
|
|
return BuiltProtocolDecl();
|
|
}
|
|
|
|
BuiltProtocolDecl swiftProtocol(Demangle::Node *node) {
|
|
return builder.createProtocolDecl(node);
|
|
}
|
|
|
|
#if SWIFT_OBJC_INTEROP
|
|
BuiltProtocolDecl objcProtocol(StringRef name) {
|
|
return builder.createObjCProtocolDecl(name.str());
|
|
}
|
|
#endif
|
|
} resolver{Builder};
|
|
|
|
Demangler dem;
|
|
std::vector<BuiltProtocolDecl> Protocols;
|
|
for (auto ProtocolAddress : Exist->getProtocols()) {
|
|
if (auto Protocol = readProtocol(ProtocolAddress, dem, resolver))
|
|
Protocols.push_back(Protocol);
|
|
else
|
|
return BuiltType();
|
|
}
|
|
auto BuiltExist = Builder.createProtocolCompositionType(
|
|
Protocols, SuperclassType, HasExplicitAnyObject);
|
|
TypeCache[TypeCacheKey] = BuiltExist;
|
|
return BuiltExist;
|
|
}
|
|
case MetadataKind::ExtendedExistential: {
|
|
auto Exist = cast<TargetExtendedExistentialTypeMetadata<Runtime>>(Meta);
|
|
|
|
// Read the shape for this existential.
|
|
StoredPointer shapeAddress = stripSignedPointer(Exist->Shape);
|
|
ShapeRef Shape = readShape(shapeAddress);
|
|
if (!Shape)
|
|
return BuiltType();
|
|
|
|
const unsigned shapeArgumentCount
|
|
= Shape->getGenSigArgumentLayoutSizeInWords();
|
|
// Pull out the arguments to the generalization signature.
|
|
assert(Shape->hasGeneralizationSignature());
|
|
std::vector<BuiltType> builtArgs;
|
|
for (unsigned i = 0; i < shapeArgumentCount; ++i) {
|
|
auto remoteArg = Exist->getGeneralizationArguments()[i];
|
|
auto builtArg = readTypeFromMetadata(remoteArg, false, recursion_limit);
|
|
if (!builtArg)
|
|
return BuiltType();
|
|
builtArgs.push_back(builtArg);
|
|
}
|
|
|
|
// Pull out the existential type from the mangled type name.
|
|
Demangler dem;
|
|
auto mangledExistentialAddr =
|
|
resolveRelativeField(Shape, Shape->ExistentialType);
|
|
auto node = readMangledName(RemoteAddress(mangledExistentialAddr),
|
|
MangledNameKind::Type, dem);
|
|
if (!node)
|
|
return BuiltType();
|
|
|
|
BuiltType builtProto = decodeMangledType(node).getType();
|
|
if (!builtProto)
|
|
return BuiltType();
|
|
|
|
// Build up a substitution map for the generalized signature.
|
|
BuiltGenericSignature sig =
|
|
decodeRuntimeGenericSignature(Shape,
|
|
Shape->getGeneralizationSignature())
|
|
.getType();
|
|
if (!sig)
|
|
return BuiltType();
|
|
|
|
BuiltSubstitutionMap subst =
|
|
Builder.createSubstitutionMap(sig, builtArgs);
|
|
if (subst.empty())
|
|
return BuiltType();
|
|
|
|
builtProto = Builder.subst(builtProto, subst);
|
|
if (!builtProto)
|
|
return BuiltType();
|
|
|
|
// Read the type expression to build up any remaining layers of
|
|
// existential metatype.
|
|
if (Shape->Flags.hasTypeExpression()) {
|
|
Demangler dem;
|
|
|
|
// Read the mangled name.
|
|
auto mangledContextName = Shape->getTypeExpression();
|
|
auto mangledNameAddress =
|
|
resolveRelativeField(Shape, mangledContextName->name);
|
|
auto node = readMangledName(RemoteAddress(mangledNameAddress),
|
|
MangledNameKind::Type, dem);
|
|
if (!node)
|
|
return BuiltType();
|
|
|
|
while (node->getKind() == Demangle::Node::Kind::Type &&
|
|
node->getNumChildren() &&
|
|
node->getChild(0)->getKind() == Demangle::Node::Kind::Metatype &&
|
|
node->getChild(0)->getNumChildren()) {
|
|
builtProto = Builder.createExistentialMetatypeType(builtProto);
|
|
node = node->getChild(0)->getChild(0);
|
|
}
|
|
}
|
|
|
|
TypeCache[TypeCacheKey] = builtProto;
|
|
return builtProto;
|
|
}
|
|
|
|
case MetadataKind::Metatype: {
|
|
auto Metatype = cast<TargetMetatypeMetadata<Runtime>>(Meta);
|
|
auto Instance =
|
|
readTypeFromMetadata(Metatype->InstanceType, false, recursion_limit);
|
|
if (!Instance) return BuiltType();
|
|
auto BuiltMetatype = Builder.createMetatypeType(Instance);
|
|
TypeCache[TypeCacheKey] = BuiltMetatype;
|
|
return BuiltMetatype;
|
|
}
|
|
case MetadataKind::ObjCClassWrapper: {
|
|
auto objcWrapper = cast<TargetObjCClassWrapperMetadata<Runtime>>(Meta);
|
|
auto classAddress = objcWrapper->Class;
|
|
|
|
std::string className;
|
|
if (!readObjCClassName(classAddress, className))
|
|
return BuiltType();
|
|
|
|
auto BuiltObjCClass = Builder.createObjCClassType(std::move(className));
|
|
TypeCache[TypeCacheKey] = BuiltObjCClass;
|
|
return BuiltObjCClass;
|
|
}
|
|
case MetadataKind::ExistentialMetatype: {
|
|
auto Exist = cast<TargetExistentialMetatypeMetadata<Runtime>>(Meta);
|
|
auto Instance =
|
|
readTypeFromMetadata(Exist->InstanceType, false, recursion_limit);
|
|
if (!Instance) return BuiltType();
|
|
auto BuiltExist = Builder.createExistentialMetatypeType(Instance);
|
|
TypeCache[TypeCacheKey] = BuiltExist;
|
|
return BuiltExist;
|
|
}
|
|
case MetadataKind::ForeignReferenceType:
|
|
case MetadataKind::ForeignClass: {
|
|
auto descriptorAddr = readAddressOfNominalTypeDescriptor(Meta);
|
|
if (!descriptorAddr)
|
|
return BuiltType();
|
|
auto descriptor = readContextDescriptor(descriptorAddr);
|
|
if (!descriptor)
|
|
return BuiltType();
|
|
|
|
// Build the demangling tree from the context tree.
|
|
Demangler dem;
|
|
auto node = buildContextMangling(descriptor, dem);
|
|
if (!node || node->getKind() != Node::Kind::Type)
|
|
return BuiltType();
|
|
|
|
auto mangling = Demangle::mangleNode(node);
|
|
if (!mangling.isSuccess())
|
|
return BuiltType();
|
|
auto name = mangling.result();
|
|
|
|
auto BuiltForeign = Builder.createForeignClassType(std::move(name));
|
|
TypeCache[TypeCacheKey] = BuiltForeign;
|
|
return BuiltForeign;
|
|
}
|
|
case MetadataKind::HeapLocalVariable:
|
|
case MetadataKind::HeapGenericLocalVariable:
|
|
case MetadataKind::ErrorObject:
|
|
// Treat these all as Builtin.NativeObject for type lowering purposes.
|
|
return Builder.createBuiltinType("Builtin.NativeObject", "Bo");
|
|
case MetadataKind::Opaque:
|
|
default: {
|
|
auto BuiltOpaque = Builder.getOpaqueType();
|
|
TypeCache[TypeCacheKey] = BuiltOpaque;
|
|
return BuiltOpaque;
|
|
}
|
|
}
|
|
|
|
swift_unreachable("Unhandled MetadataKind in switch");
|
|
}
|
|
|
|
TypeLookupErrorOr<typename BuilderType::BuiltGenericSignature>
|
|
decodeRuntimeGenericSignature(ShapeRef contextRef,
|
|
const RuntimeGenericSignature<Runtime> &Sig) {
|
|
std::vector<BuiltType> params;
|
|
for (unsigned sigIdx : indices(Sig.getParams())) {
|
|
auto param =
|
|
Builder.createGenericTypeParameterType(/*depth*/ 0, /*index*/ sigIdx);
|
|
if (!param)
|
|
return TypeLookupError("Failed to read generic parameter type in "
|
|
"runtime generic signature.");
|
|
params.push_back(param);
|
|
}
|
|
|
|
std::vector<BuiltRequirement> reqs;
|
|
for (auto &req : Sig.getRequirements()) {
|
|
if (!req.hasKnownKind()) {
|
|
return TypeLookupError("unknown kind");
|
|
}
|
|
|
|
Demangler ldem;
|
|
auto lhsTypeNode = ldem.demangleType(req.getParam());
|
|
if (!lhsTypeNode) {
|
|
return TypeLookupError("Failed to read subject type in requirement of "
|
|
"runtime generic signature.");
|
|
}
|
|
|
|
BuiltType subjectType = decodeMangledType(lhsTypeNode).getType();
|
|
if (!subjectType)
|
|
return TypeLookupError("Failed to read subject type in requirement of "
|
|
"runtime generic signature.");
|
|
|
|
switch (req.Flags.getKind()) {
|
|
case GenericRequirementKind::SameType: {
|
|
Demangler rdem;
|
|
auto demangledConstraint =
|
|
demangle(RemoteRef<char>(req.getMangledTypeName().data(),
|
|
req.getMangledTypeName().data()),
|
|
MangledNameKind::Type, rdem);
|
|
auto constraintType = decodeMangledType(demangledConstraint);
|
|
if (auto *error = constraintType.getError()) {
|
|
return *error;
|
|
}
|
|
|
|
reqs.push_back(BuiltRequirement(RequirementKind::SameType, subjectType,
|
|
constraintType.getType()));
|
|
break;
|
|
}
|
|
case GenericRequirementKind::Protocol: {
|
|
/// Resolver to turn a protocol reference into an existential type.
|
|
struct ProtocolReferenceResolver {
|
|
using Result = BuiltType;
|
|
|
|
BuilderType &builder;
|
|
|
|
BuiltType failure() const { return BuiltType(); }
|
|
|
|
BuiltType swiftProtocol(Demangle::Node *node) {
|
|
auto decl = builder.createProtocolDecl(node);
|
|
if (!decl)
|
|
return failure();
|
|
return builder.createProtocolTypeFromDecl(decl);
|
|
}
|
|
|
|
#if SWIFT_OBJC_INTEROP
|
|
BuiltType objcProtocol(StringRef name) {
|
|
auto decl = builder.createObjCProtocolDecl(name.str());
|
|
if (!decl)
|
|
return failure();
|
|
return builder.createProtocolTypeFromDecl(decl);
|
|
}
|
|
#endif
|
|
} resolver{Builder};
|
|
|
|
Demangler dem;
|
|
auto protocolAddress =
|
|
resolveRelativeIndirectProtocol(contextRef, req.Protocol);
|
|
auto protocol = readProtocol(protocolAddress, dem, resolver);
|
|
if (!protocol) {
|
|
return TypeLookupError("Failed to read protocol type in conformance "
|
|
"requirement of runtime generic signature.");
|
|
}
|
|
|
|
reqs.push_back(BuiltRequirement(RequirementKind::Conformance,
|
|
subjectType, protocol));
|
|
break;
|
|
}
|
|
case GenericRequirementKind::BaseClass: {
|
|
Demangler rdem;
|
|
auto demangledConstraint =
|
|
demangle(RemoteRef<char>(req.getMangledTypeName().data(),
|
|
req.getMangledTypeName().data()),
|
|
MangledNameKind::Type, rdem);
|
|
auto constraintType = decodeMangledType(demangledConstraint);
|
|
if (auto *error = constraintType.getError()) {
|
|
return *error;
|
|
}
|
|
|
|
reqs.push_back(BuiltRequirement(RequirementKind::Superclass,
|
|
subjectType, constraintType.getType()));
|
|
break;
|
|
}
|
|
case GenericRequirementKind::SameConformance:
|
|
return TypeLookupError("Unexpected same conformance requirement in "
|
|
"runtime generic signature");
|
|
case GenericRequirementKind::Layout:
|
|
return TypeLookupError(
|
|
"Unexpected layout requirement in runtime generic signature");
|
|
case GenericRequirementKind::SameShape:
|
|
return TypeLookupError(
|
|
"Unexpected same-shape requirement in runtime generic signature");
|
|
case GenericRequirementKind::InvertedProtocols:
|
|
return TypeLookupError(
|
|
"Unexpected invertible protocol in runtime generic signature");
|
|
}
|
|
}
|
|
|
|
return Builder.createGenericSignature(params, reqs);
|
|
}
|
|
|
|
TypeLookupErrorOr<typename BuilderType::BuiltType>
|
|
readTypeFromMangledName(const char *MangledTypeName, size_t Length) {
|
|
Demangle::Demangler Dem;
|
|
Demangle::NodePointer Demangled =
|
|
Dem.demangleSymbol(StringRef(MangledTypeName, Length));
|
|
return decodeMangledType(Demangled);
|
|
}
|
|
|
|
/// Given the address of a context descriptor, attempt to read it, or
|
|
/// represent it symbolically.
|
|
ParentContextDescriptorRef
|
|
readContextDescriptor(const RemoteAbsolutePointer &address) {
|
|
// Map an unresolved pointer to an unresolved context ref.
|
|
if (!address.isResolved()) {
|
|
// We can only handle references to a symbol without an offset currently.
|
|
if (address.getOffset() != 0) {
|
|
return ParentContextDescriptorRef();
|
|
}
|
|
return ParentContextDescriptorRef(address.getSymbol());
|
|
}
|
|
|
|
return ParentContextDescriptorRef(
|
|
readContextDescriptor(address.getResolvedAddress().getAddressData()));
|
|
}
|
|
|
|
ShapeRef
|
|
readShape(StoredPointer address) {
|
|
if (address == 0)
|
|
return nullptr;
|
|
|
|
auto cached = ShapeCache.find(address);
|
|
if (cached != ShapeCache.end())
|
|
return ShapeRef(address,
|
|
reinterpret_cast<const TargetExtendedExistentialTypeShape<Runtime> *>(
|
|
cached->second.get()));
|
|
|
|
ExtendedExistentialTypeShapeFlags flags;
|
|
if (!Reader->readBytes(RemoteAddress(address), (uint8_t*)&flags,
|
|
sizeof(flags)))
|
|
return nullptr;
|
|
|
|
// Read the size of the requirement signature.
|
|
uint64_t reqSigGenericSize = 0;
|
|
uint64_t genericHeaderSize = sizeof(GenericContextDescriptorHeader);
|
|
{
|
|
GenericContextDescriptorHeader header;
|
|
auto headerAddr = address + sizeof(flags);
|
|
|
|
if (!Reader->readBytes(RemoteAddress(headerAddr),
|
|
(uint8_t*)&header, sizeof(header)))
|
|
return nullptr;
|
|
|
|
reqSigGenericSize = reqSigGenericSize
|
|
+ (header.NumParams + 3u & ~3u)
|
|
+ header.NumRequirements
|
|
* sizeof(TargetGenericRequirementDescriptor<Runtime>);
|
|
}
|
|
uint64_t typeExprSize = flags.hasTypeExpression() ? sizeof(StoredPointer) : 0;
|
|
uint64_t suggestedVWSize = flags.hasSuggestedValueWitnesses() ? sizeof(StoredPointer) : 0;
|
|
|
|
uint64_t size = sizeof(ExtendedExistentialTypeShapeFlags) +
|
|
sizeof(TargetRelativeDirectPointer<Runtime, const char,
|
|
/*nullable*/ false>) +
|
|
genericHeaderSize + typeExprSize + suggestedVWSize +
|
|
reqSigGenericSize;
|
|
if (size > MaxMetadataSize)
|
|
return nullptr;
|
|
auto readResult = Reader->readBytes(RemoteAddress(address), size);
|
|
if (!readResult)
|
|
return nullptr;
|
|
|
|
auto descriptor =
|
|
reinterpret_cast<const TargetExtendedExistentialTypeShape<Runtime> *>(
|
|
readResult.get());
|
|
|
|
ShapeCache.insert(
|
|
std::make_pair(address, std::move(readResult)));
|
|
return ShapeRef(address, descriptor);
|
|
}
|
|
|
|
/// Given the address of a context descriptor, attempt to read it.
|
|
ContextDescriptorRef
|
|
readContextDescriptor(StoredPointer address) {
|
|
if (address == 0)
|
|
return nullptr;
|
|
|
|
auto cached = ContextDescriptorCache.find(address);
|
|
if (cached != ContextDescriptorCache.end())
|
|
return ContextDescriptorRef(
|
|
address, reinterpret_cast<const TargetContextDescriptor<Runtime> *>(
|
|
cached->second.get()));
|
|
|
|
// Read the flags to figure out how much space we should read.
|
|
ContextDescriptorFlags flags;
|
|
if (!Reader->readBytes(RemoteAddress(address), (uint8_t*)&flags,
|
|
sizeof(flags)))
|
|
return nullptr;
|
|
|
|
TypeContextDescriptorFlags typeFlags(flags.getKindSpecificFlags());
|
|
uint64_t baseSize = 0;
|
|
uint64_t genericHeaderSize = sizeof(GenericContextDescriptorHeader);
|
|
uint64_t metadataInitSize = 0;
|
|
bool hasVTable = false;
|
|
|
|
auto readMetadataInitSize = [&]() -> unsigned {
|
|
switch (typeFlags.getMetadataInitialization()) {
|
|
case TypeContextDescriptorFlags::NoMetadataInitialization:
|
|
return 0;
|
|
case TypeContextDescriptorFlags::SingletonMetadataInitialization:
|
|
// FIXME: classes
|
|
return sizeof(TargetSingletonMetadataInitialization<Runtime>);
|
|
case TypeContextDescriptorFlags::ForeignMetadataInitialization:
|
|
return sizeof(TargetForeignMetadataInitialization<Runtime>);
|
|
}
|
|
return 0;
|
|
};
|
|
|
|
switch (auto kind = flags.getKind()) {
|
|
case ContextDescriptorKind::Module:
|
|
baseSize = sizeof(TargetModuleContextDescriptor<Runtime>);
|
|
break;
|
|
// TODO: Should we include trailing generic arguments in this load?
|
|
case ContextDescriptorKind::Extension:
|
|
baseSize = sizeof(TargetExtensionContextDescriptor<Runtime>);
|
|
break;
|
|
case ContextDescriptorKind::Anonymous:
|
|
baseSize = sizeof(TargetAnonymousContextDescriptor<Runtime>);
|
|
if (AnonymousContextDescriptorFlags(flags.getKindSpecificFlags())
|
|
.hasMangledName()) {
|
|
metadataInitSize = sizeof(TargetMangledContextName<Runtime>);
|
|
}
|
|
break;
|
|
case ContextDescriptorKind::Class:
|
|
baseSize = sizeof(TargetClassDescriptor<Runtime>);
|
|
genericHeaderSize = sizeof(TypeGenericContextDescriptorHeader);
|
|
hasVTable = typeFlags.class_hasVTable();
|
|
metadataInitSize = readMetadataInitSize();
|
|
break;
|
|
case ContextDescriptorKind::Enum:
|
|
baseSize = sizeof(TargetEnumDescriptor<Runtime>);
|
|
genericHeaderSize = sizeof(TypeGenericContextDescriptorHeader);
|
|
metadataInitSize = readMetadataInitSize();
|
|
break;
|
|
case ContextDescriptorKind::Struct:
|
|
baseSize = sizeof(TargetStructDescriptor<Runtime>);
|
|
genericHeaderSize = sizeof(TypeGenericContextDescriptorHeader);
|
|
metadataInitSize = readMetadataInitSize();
|
|
break;
|
|
case ContextDescriptorKind::Protocol:
|
|
baseSize = sizeof(TargetProtocolDescriptor<Runtime>);
|
|
break;
|
|
case ContextDescriptorKind::OpaqueType:
|
|
baseSize = sizeof(TargetOpaqueTypeDescriptor<Runtime>);
|
|
metadataInitSize =
|
|
sizeof(typename Runtime::template RelativeDirectPointer<const char>)
|
|
* flags.getKindSpecificFlags();
|
|
break;
|
|
default:
|
|
// We don't know about this kind of context.
|
|
return nullptr;
|
|
}
|
|
|
|
// Determine the full size of the descriptor. This is reimplementing a fair
|
|
// bit of TrailingObjects but for out-of-process; maybe there's a way to
|
|
// factor the layout stuff out...
|
|
uint64_t genericsSize = 0;
|
|
if (flags.isGeneric()) {
|
|
GenericContextDescriptorHeader header;
|
|
auto headerAddr = address
|
|
+ baseSize
|
|
+ genericHeaderSize
|
|
- sizeof(header);
|
|
|
|
if (!Reader->readBytes(RemoteAddress(headerAddr),
|
|
(uint8_t*)&header, sizeof(header)))
|
|
return nullptr;
|
|
|
|
genericsSize = genericHeaderSize
|
|
+ (header.NumParams + 3u & ~3u)
|
|
+ header.NumRequirements
|
|
* sizeof(TargetGenericRequirementDescriptor<Runtime>);
|
|
}
|
|
|
|
uint64_t vtableSize = 0;
|
|
if (hasVTable) {
|
|
TargetVTableDescriptorHeader<Runtime> header;
|
|
auto headerAddr = address
|
|
+ baseSize
|
|
+ genericsSize
|
|
+ metadataInitSize;
|
|
|
|
if (!Reader->readBytes(RemoteAddress(headerAddr),
|
|
(uint8_t*)&header, sizeof(header)))
|
|
return nullptr;
|
|
|
|
vtableSize = sizeof(header)
|
|
+ header.VTableSize * sizeof(TargetMethodDescriptor<Runtime>);
|
|
}
|
|
|
|
uint64_t size = baseSize + genericsSize + metadataInitSize + vtableSize;
|
|
if (size > MaxMetadataSize)
|
|
return nullptr;
|
|
auto readResult = Reader->readBytes(RemoteAddress(address), size);
|
|
if (!readResult)
|
|
return nullptr;
|
|
|
|
auto descriptor =
|
|
reinterpret_cast<const TargetContextDescriptor<Runtime> *>(
|
|
readResult.get());
|
|
|
|
ContextDescriptorCache.insert(
|
|
std::make_pair(address, std::move(readResult)));
|
|
return ContextDescriptorRef(address, descriptor);
|
|
}
|
|
|
|
/// Demangle the entity represented by a symbolic reference to a given symbol name.
|
|
Demangle::NodePointer
|
|
buildContextManglingForSymbol(StringRef symbol, Demangler &dem) {
|
|
auto demangledSymbol = dem.demangleSymbol(symbol);
|
|
if (demangledSymbol->getKind() == Demangle::Node::Kind::Global) {
|
|
demangledSymbol = demangledSymbol->getChild(0);
|
|
}
|
|
|
|
switch (demangledSymbol->getKind()) {
|
|
// Pointers to nominal type or protocol descriptors would demangle to
|
|
// the type they represent.
|
|
case Demangle::Node::Kind::NominalTypeDescriptor:
|
|
case Demangle::Node::Kind::ProtocolDescriptor:
|
|
demangledSymbol = demangledSymbol->getChild(0);
|
|
assert(demangledSymbol->getKind() == Demangle::Node::Kind::Type);
|
|
break;
|
|
// Pointers to opaque type descriptors demangle to the name of the opaque
|
|
// type declaration.
|
|
case Demangle::Node::Kind::OpaqueTypeDescriptor:
|
|
demangledSymbol = demangledSymbol->getChild(0);
|
|
break;
|
|
// We don't handle pointers to other symbols yet.
|
|
default:
|
|
return nullptr;
|
|
}
|
|
|
|
return demangledSymbol;
|
|
}
|
|
|
|
Demangle::NodePointer buildContextManglingForSymbol(const std::string &symbol,
|
|
Demangler &dem) {
|
|
return buildContextManglingForSymbol(dem.copyString(symbol), dem);
|
|
}
|
|
|
|
/// Given a read context descriptor, attempt to build a demangling tree
|
|
/// for it.
|
|
Demangle::NodePointer
|
|
buildContextMangling(const ParentContextDescriptorRef &descriptor,
|
|
Demangler &dem) {
|
|
if (descriptor.isResolved()) {
|
|
return buildContextMangling(descriptor.getResolved(), dem);
|
|
}
|
|
|
|
// Try to demangle the symbol name to figure out what context it would
|
|
// point to.
|
|
return buildContextManglingForSymbol(descriptor.getSymbol(), dem);
|
|
}
|
|
|
|
/// Given a read context descriptor, attempt to build a demangling tree
|
|
/// for it.
|
|
Demangle::NodePointer
|
|
buildContextMangling(ContextDescriptorRef descriptor,
|
|
Demangler &dem) {
|
|
auto demangling = buildContextDescriptorMangling(descriptor, dem, 50);
|
|
if (!demangling)
|
|
return nullptr;
|
|
|
|
Demangle::NodePointer top;
|
|
// References to type nodes behave as types in the mangling.
|
|
if (isa<TargetTypeContextDescriptor<Runtime>>(descriptor.getLocalBuffer()) ||
|
|
isa<TargetProtocolDescriptor<Runtime>>(descriptor.getLocalBuffer())) {
|
|
top = dem.createNode(Node::Kind::Type);
|
|
top->addChild(demangling, dem);
|
|
} else {
|
|
top = demangling;
|
|
}
|
|
|
|
return top;
|
|
}
|
|
|
|
/// Read a context descriptor from the given address and build a mangling
|
|
/// tree representing it.
|
|
Demangle::NodePointer
|
|
readDemanglingForContextDescriptor(StoredPointer contextAddress,
|
|
Demangler &Dem) {
|
|
auto context = readContextDescriptor(contextAddress);
|
|
if (!context)
|
|
return nullptr;
|
|
return buildContextMangling(context, Dem);
|
|
}
|
|
|
|
/// Read the mangled underlying type from an opaque type descriptor.
|
|
Demangle::NodePointer
|
|
readUnderlyingTypeManglingForOpaqueTypeDescriptor(StoredPointer contextAddr,
|
|
unsigned ordinal,
|
|
Demangler &Dem) {
|
|
auto context = readContextDescriptor(contextAddr);
|
|
if (!context)
|
|
return nullptr;
|
|
if (context->getKind() != ContextDescriptorKind::OpaqueType)
|
|
return nullptr;
|
|
|
|
auto opaqueType =
|
|
reinterpret_cast<const TargetOpaqueTypeDescriptor<Runtime> *>(
|
|
context.getLocalBuffer());
|
|
|
|
if (ordinal >= opaqueType->getNumUnderlyingTypeArguments())
|
|
return nullptr;
|
|
|
|
auto nameAddr = resolveRelativeField(context,
|
|
opaqueType->getUnderlyingTypeArgumentMangledName(ordinal));
|
|
|
|
return readMangledName(RemoteAddress(nameAddr),
|
|
MangledNameKind::Type, Dem);
|
|
}
|
|
|
|
TypeLookupErrorOr<typename BuilderType::BuiltType>
|
|
readUnderlyingTypeForOpaqueTypeDescriptor(StoredPointer contextAddr,
|
|
unsigned ordinal) {
|
|
Demangle::Demangler Dem;
|
|
auto node = readUnderlyingTypeManglingForOpaqueTypeDescriptor(contextAddr,
|
|
ordinal, Dem);
|
|
if (!node)
|
|
return TypeLookupError("Failed to read type mangling for descriptor.");
|
|
return decodeMangledType(node);
|
|
}
|
|
|
|
bool isTaggedPointer(StoredPointer objectAddress) {
|
|
if (getTaggedPointerEncoding() != TaggedPointerEncodingKind::Extended)
|
|
return false;
|
|
|
|
return (objectAddress ^ TaggedPointerObfuscator) & TaggedPointerMask;
|
|
}
|
|
|
|
/// Read the isa pointer of an Object-C tagged pointer value.
|
|
std::optional<StoredPointer>
|
|
readMetadataFromTaggedPointer(StoredPointer objectAddress) {
|
|
auto readArrayElement =
|
|
[&](StoredPointer base,
|
|
StoredPointer tag) -> std::optional<StoredPointer> {
|
|
StoredPointer addr = base + tag * sizeof(StoredPointer);
|
|
StoredPointer isa;
|
|
if (!Reader->readInteger(RemoteAddress(addr), &isa))
|
|
return std::nullopt;
|
|
return isa;
|
|
};
|
|
|
|
// Extended pointers have a tag of 0b111, using 8 additional bits
|
|
// to specify the class.
|
|
if (TaggedPointerExtendedMask != 0 &&
|
|
(((objectAddress ^ TaggedPointerObfuscator) & TaggedPointerExtendedMask)
|
|
== TaggedPointerExtendedMask)) {
|
|
auto tag = ((objectAddress >> TaggedPointerExtendedSlotShift) &
|
|
TaggedPointerExtendedSlotMask);
|
|
return readArrayElement(TaggedPointerExtendedClasses, tag);
|
|
}
|
|
|
|
// Basic tagged pointers use a 3 bit tag to specify the class.
|
|
auto tag = ((objectAddress >> TaggedPointerSlotShift) &
|
|
TaggedPointerSlotMask);
|
|
return readArrayElement(TaggedPointerClasses, tag);
|
|
}
|
|
|
|
/// Read the isa pointer of a class or closure context instance and apply
|
|
/// the isa mask.
|
|
std::optional<StoredPointer>
|
|
readMetadataFromInstance(StoredPointer objectAddress) {
|
|
if (isTaggedPointer(objectAddress))
|
|
return readMetadataFromTaggedPointer(objectAddress);
|
|
|
|
StoredPointer isa;
|
|
if (!Reader->readInteger(RemoteAddress(objectAddress), &isa))
|
|
return std::nullopt;
|
|
|
|
switch (getIsaEncoding()) {
|
|
case IsaEncodingKind::Unknown:
|
|
case IsaEncodingKind::Error:
|
|
return std::nullopt;
|
|
|
|
case IsaEncodingKind::None:
|
|
return isa;
|
|
|
|
case IsaEncodingKind::Masked:
|
|
return isa & IsaMask;
|
|
|
|
case IsaEncodingKind::Indexed: {
|
|
// If applying the magic mask doesn't give us the magic value,
|
|
// it's not an indexed isa.
|
|
if ((isa & IsaMagicMask) != IsaMagicValue)
|
|
return isa;
|
|
|
|
// Extract the index.
|
|
auto classIndex = (isa & IsaIndexMask) >> IsaIndexShift;
|
|
|
|
// 0 is never a valid index.
|
|
if (classIndex == 0) {
|
|
return std::nullopt;
|
|
|
|
// If the index is out of range, it's an error; but check for an
|
|
// update first. (This will also trigger the first time because
|
|
// we initialize LastIndexedClassesCount to 0).
|
|
} else if (classIndex >= LastIndexedClassesCount) {
|
|
StoredPointer count;
|
|
if (!Reader->readInteger(RemoteAddress(IndexedClassesCountPointer),
|
|
&count)) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
LastIndexedClassesCount = count;
|
|
if (classIndex >= count) {
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
|
|
// Find the address of the appropriate array element.
|
|
RemoteAddress eltPointer =
|
|
RemoteAddress(IndexedClassesPointer
|
|
+ classIndex * sizeof(StoredPointer));
|
|
StoredPointer metadataPointer;
|
|
if (!Reader->readInteger(eltPointer, &metadataPointer)) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
return metadataPointer;
|
|
}
|
|
}
|
|
|
|
swift_unreachable("Unhandled IsaEncodingKind in switch.");
|
|
}
|
|
|
|
/// Read the offset of the generic parameters of a class from the nominal
|
|
/// type descriptor. If the class has a resilient superclass, we also
|
|
/// have to read the superclass size and add that to the offset.
|
|
///
|
|
/// The offset is in units of words, from the start of the class's
|
|
/// metadata.
|
|
std::optional<int32_t>
|
|
readGenericArgsOffset(MetadataRef metadata, ContextDescriptorRef descriptor) {
|
|
switch (descriptor->getKind()) {
|
|
case ContextDescriptorKind::Class: {
|
|
auto type = cast<TargetClassDescriptor<Runtime>>(descriptor);
|
|
|
|
if (!type->hasResilientSuperclass())
|
|
return type->getNonResilientGenericArgumentOffset();
|
|
|
|
auto bounds = readMetadataBoundsOfSuperclass(descriptor);
|
|
if (!bounds)
|
|
return std::nullopt;
|
|
|
|
bounds->adjustForSubclass(type->areImmediateMembersNegative(),
|
|
type->NumImmediateMembers);
|
|
|
|
return bounds->ImmediateMembersOffset / sizeof(StoredPointer);
|
|
}
|
|
|
|
case ContextDescriptorKind::Enum: {
|
|
auto type = cast<TargetEnumDescriptor<Runtime>>(descriptor);
|
|
return type->getGenericArgumentOffset();
|
|
}
|
|
|
|
case ContextDescriptorKind::Struct: {
|
|
auto type = cast<TargetStructDescriptor<Runtime>>(descriptor);
|
|
return type->getGenericArgumentOffset();
|
|
}
|
|
|
|
default:
|
|
return std::nullopt;
|
|
}
|
|
}
|
|
|
|
using ClassMetadataBounds = TargetClassMetadataBounds<Runtime>;
|
|
|
|
// This follows computeMetadataBoundsForSuperclass.
|
|
std::optional<ClassMetadataBounds>
|
|
readMetadataBoundsOfSuperclass(ContextDescriptorRef subclassRef) {
|
|
auto subclass = cast<TargetClassDescriptor<Runtime>>(subclassRef);
|
|
if (!subclass->hasResilientSuperclass())
|
|
return ClassMetadataBounds::forSwiftRootClass();
|
|
|
|
auto rawSuperclass =
|
|
resolveRelativeField(subclassRef, subclass->getResilientSuperclass());
|
|
if (!rawSuperclass) {
|
|
return ClassMetadataBounds::forSwiftRootClass();
|
|
}
|
|
|
|
return forTypeReference<ClassMetadataBounds>(
|
|
subclass->getResilientSuperclassReferenceKind(), rawSuperclass,
|
|
[&](ContextDescriptorRef superclass)
|
|
-> std::optional<ClassMetadataBounds> {
|
|
if (!isa<TargetClassDescriptor<Runtime>>(superclass))
|
|
return std::nullopt;
|
|
return readMetadataBoundsOfSuperclass(superclass);
|
|
},
|
|
[&](MetadataRef metadata) -> std::optional<ClassMetadataBounds> {
|
|
auto cls = dyn_cast<TargetClassMetadata>(metadata);
|
|
if (!cls)
|
|
return std::nullopt;
|
|
|
|
return cls->getClassBoundsAsSwiftSuperclass();
|
|
},
|
|
[](StoredPointer objcClassName) -> std::optional<ClassMetadataBounds> {
|
|
// We have no ability to look up an ObjC class by name.
|
|
// FIXME: add a query for this; clients may have a way to do it.
|
|
return std::nullopt;
|
|
});
|
|
}
|
|
|
|
template <class Result, class DescriptorFn, class MetadataFn,
|
|
class ClassNameFn>
|
|
std::optional<Result> forTypeReference(TypeReferenceKind refKind,
|
|
StoredPointer ref,
|
|
const DescriptorFn &descriptorFn,
|
|
const MetadataFn &metadataFn,
|
|
const ClassNameFn &classNameFn) {
|
|
switch (refKind) {
|
|
case TypeReferenceKind::IndirectTypeDescriptor: {
|
|
StoredPointer descriptorAddress = 0;
|
|
if (!Reader->readInteger(RemoteAddress(ref), &descriptorAddress))
|
|
return std::nullopt;
|
|
|
|
ref = descriptorAddress;
|
|
LLVM_FALLTHROUGH;
|
|
}
|
|
|
|
case TypeReferenceKind::DirectTypeDescriptor: {
|
|
auto descriptor = readContextDescriptor(ref);
|
|
if (!descriptor)
|
|
return std::nullopt;
|
|
|
|
return descriptorFn(descriptor);
|
|
}
|
|
|
|
case TypeReferenceKind::DirectObjCClassName:
|
|
return classNameFn(ref);
|
|
|
|
case TypeReferenceKind::IndirectObjCClass: {
|
|
StoredPointer classRef = 0;
|
|
if (!Reader->readInteger(RemoteAddress(ref), &classRef))
|
|
return std::nullopt;
|
|
|
|
auto metadata = readMetadata(classRef);
|
|
if (!metadata)
|
|
return std::nullopt;
|
|
|
|
return metadataFn(metadata);
|
|
}
|
|
}
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
/// Read a single generic type argument from a bound generic type
|
|
/// metadata.
|
|
std::optional<StoredPointer>
|
|
readGenericArgFromMetadata(StoredPointer metadata, unsigned index) {
|
|
auto Meta = readMetadata(metadata);
|
|
if (!Meta)
|
|
return std::nullopt;
|
|
|
|
auto descriptorAddress = readAddressOfNominalTypeDescriptor(Meta);
|
|
if (!descriptorAddress)
|
|
return std::nullopt;
|
|
|
|
// Read the nominal type descriptor.
|
|
auto descriptor = readContextDescriptor(descriptorAddress);
|
|
if (!descriptor)
|
|
return std::nullopt;
|
|
|
|
auto generics = descriptor->getGenericContext();
|
|
if (!generics)
|
|
return std::nullopt;
|
|
|
|
auto offsetToGenericArgs = readGenericArgsOffset(Meta, descriptor);
|
|
if (!offsetToGenericArgs)
|
|
return std::nullopt;
|
|
|
|
auto addressOfGenericArgAddress =
|
|
(getAddress(Meta) +
|
|
*offsetToGenericArgs * sizeof(StoredPointer) +
|
|
index * sizeof(StoredPointer));
|
|
|
|
if (index >= generics->getGenericContextHeader().getNumArguments())
|
|
return std::nullopt;
|
|
|
|
StoredPointer genericArgAddress;
|
|
if (!Reader->readInteger(RemoteAddress(addressOfGenericArgAddress),
|
|
&genericArgAddress))
|
|
return std::nullopt;
|
|
|
|
return genericArgAddress;
|
|
}
|
|
|
|
/// Given the address of a nominal type descriptor, attempt to resolve
|
|
/// its nominal type declaration.
|
|
BuiltTypeDecl readNominalTypeFromDescriptor(StoredPointer address) {
|
|
auto descriptor = readContextDescriptor(address);
|
|
if (!descriptor)
|
|
return BuiltTypeDecl();
|
|
|
|
return buildNominalTypeDecl(descriptor);
|
|
}
|
|
|
|
/// Try to read the offset of a tuple element from a tuple metadata.
|
|
bool readTupleElementOffset(StoredPointer metadataAddress, unsigned eltIndex,
|
|
StoredSize *offset) {
|
|
// Read the metadata.
|
|
auto metadata = readMetadata(metadataAddress);
|
|
if (!metadata)
|
|
return false;
|
|
|
|
// Ensure that the metadata actually is tuple metadata.
|
|
auto tupleMetadata = dyn_cast<TargetTupleTypeMetadata<Runtime>>(metadata);
|
|
if (!tupleMetadata)
|
|
return false;
|
|
|
|
// Ensure that the element is in-bounds.
|
|
if (eltIndex >= tupleMetadata->NumElements)
|
|
return false;
|
|
|
|
// Read the offset.
|
|
const auto &element = tupleMetadata->getElement(eltIndex);
|
|
*offset = element.Offset;
|
|
return true;
|
|
}
|
|
|
|
/// Given a remote pointer to class metadata, attempt to read its superclass.
|
|
std::optional<StoredPointer>
|
|
readOffsetToFirstCaptureFromMetadata(StoredPointer MetadataAddress) {
|
|
auto meta = readMetadata(MetadataAddress);
|
|
if (!meta || meta->getKind() != MetadataKind::HeapLocalVariable)
|
|
return std::nullopt;
|
|
|
|
auto heapMeta = cast<TargetHeapLocalVariableMetadata<Runtime>>(meta);
|
|
return heapMeta->OffsetToFirstCapture;
|
|
}
|
|
|
|
std::optional<RemoteAbsolutePointer> readPointer(StoredPointer address) {
|
|
return Reader->readPointer(RemoteAddress(address), sizeof(StoredPointer));
|
|
}
|
|
|
|
std::optional<StoredPointer> readResolvedPointerValue(StoredPointer address) {
|
|
if (auto pointer = readPointer(address)) {
|
|
if (!pointer->isResolved())
|
|
return std::nullopt;
|
|
return (StoredPointer)pointer->getResolvedAddress().getAddressData();
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
template<typename T, typename U>
|
|
RemoteAbsolutePointer resolvePointerField(RemoteRef<T> base,
|
|
const U &field) {
|
|
auto pointerRef = base.getField(field);
|
|
return Reader->resolvePointer(RemoteAddress(getAddress(pointerRef)),
|
|
*pointerRef.getLocalBuffer());
|
|
}
|
|
|
|
/// Given a remote pointer to class metadata, attempt to read its superclass.
|
|
std::optional<RemoteAbsolutePointer>
|
|
readCaptureDescriptorFromMetadata(StoredPointer MetadataAddress) {
|
|
auto meta = readMetadata(MetadataAddress);
|
|
if (!meta || meta->getKind() != MetadataKind::HeapLocalVariable)
|
|
return std::nullopt;
|
|
|
|
auto heapMeta = cast<TargetHeapLocalVariableMetadata<Runtime>>(meta);
|
|
return resolvePointerField(meta, heapMeta->CaptureDescription);
|
|
}
|
|
|
|
protected:
|
|
template<typename Base>
|
|
StoredPointer getAddress(RemoteRef<Base> base) {
|
|
return (StoredPointer)base.getAddressData();
|
|
}
|
|
|
|
template<typename Base, typename Field>
|
|
StoredPointer resolveRelativeField(
|
|
RemoteRef<Base> base, const Field &field) {
|
|
return (StoredPointer)base.resolveRelativeFieldData(field);
|
|
}
|
|
|
|
template <typename Base, typename Field>
|
|
std::optional<RemoteAbsolutePointer>
|
|
resolveRelativeIndirectableField(RemoteRef<Base> base, const Field &field) {
|
|
auto fieldRef = base.getField(field);
|
|
int32_t offset;
|
|
memcpy(&offset, fieldRef.getLocalBuffer(), sizeof(int32_t));
|
|
|
|
if (offset == 0)
|
|
return std::optional<RemoteAbsolutePointer>(nullptr);
|
|
bool indirect = offset & 1;
|
|
offset &= ~1u;
|
|
|
|
using SignedPointer = typename std::make_signed<StoredPointer>::type;
|
|
|
|
StoredPointer resultAddress = getAddress(fieldRef) + (SignedPointer)offset;
|
|
|
|
// Low bit set in the offset indicates that the offset leads to the absolute
|
|
// address in memory.
|
|
if (indirect) {
|
|
if (auto ptr = readPointer(resultAddress)) {
|
|
return stripSignedPointer(*ptr);
|
|
}
|
|
return std::nullopt;
|
|
}
|
|
|
|
return RemoteAbsolutePointer("", resultAddress);
|
|
}
|
|
|
|
/// Given a pointer to an Objective-C class, try to read its class name.
|
|
bool readObjCClassName(StoredPointer classAddress, std::string &className) {
|
|
// The following algorithm only works on the non-fragile Apple runtime.
|
|
|
|
// Grab the RO-data pointer. This part is not ABI.
|
|
StoredPointer roDataPtr = readObjCRODataPtr(classAddress);
|
|
if (!roDataPtr) return false;
|
|
|
|
// This is ABI.
|
|
static constexpr auto OffsetToName =
|
|
roundUpToAlignment(size_t(12), sizeof(StoredPointer))
|
|
+ sizeof(StoredPointer);
|
|
|
|
// Read the name pointer.
|
|
StoredPointer namePtr;
|
|
if (!Reader->readInteger(RemoteAddress(roDataPtr + OffsetToName), &namePtr))
|
|
return false;
|
|
|
|
// If the name pointer is null, treat that as an error.
|
|
if (!namePtr)
|
|
return false;
|
|
|
|
return Reader->readString(RemoteAddress(namePtr), className);
|
|
}
|
|
|
|
MetadataRef readMetadata(StoredPointer address) {
|
|
auto cached = MetadataCache.find(address);
|
|
if (cached != MetadataCache.end())
|
|
return MetadataRef(address,
|
|
reinterpret_cast<const TargetMetadata<Runtime> *>(
|
|
cached->second.get()));
|
|
|
|
StoredPointer KindValue = 0;
|
|
if (!Reader->readInteger(RemoteAddress(address), &KindValue))
|
|
return nullptr;
|
|
|
|
switch (getEnumeratedMetadataKind(KindValue)) {
|
|
case MetadataKind::Class:
|
|
|
|
return _readMetadata<TargetClassMetadataType>(address);
|
|
|
|
case MetadataKind::Enum:
|
|
return _readMetadata<TargetEnumMetadata>(address);
|
|
case MetadataKind::ErrorObject:
|
|
return _readMetadata<TargetEnumMetadata>(address);
|
|
case MetadataKind::Existential: {
|
|
StoredPointer flagsAddress = address +
|
|
sizeof(StoredPointer);
|
|
|
|
ExistentialTypeFlags::int_type flagsData;
|
|
if (!Reader->readInteger(RemoteAddress(flagsAddress),
|
|
&flagsData))
|
|
return nullptr;
|
|
|
|
ExistentialTypeFlags flags(flagsData);
|
|
|
|
StoredPointer numProtocolsAddress = flagsAddress + sizeof(flagsData);
|
|
uint32_t numProtocols;
|
|
if (!Reader->readInteger(RemoteAddress(numProtocolsAddress),
|
|
&numProtocols))
|
|
return nullptr;
|
|
|
|
// Make sure the number of protocols is reasonable
|
|
if (numProtocols >= 256)
|
|
return nullptr;
|
|
|
|
auto totalSize = sizeof(TargetExistentialTypeMetadata<Runtime>)
|
|
+ numProtocols *
|
|
sizeof(ConstTargetMetadataPointer<Runtime, TargetProtocolDescriptor>);
|
|
|
|
if (flags.hasSuperclassConstraint())
|
|
totalSize += sizeof(StoredPointer);
|
|
|
|
return _readMetadata(address, totalSize);
|
|
}
|
|
case MetadataKind::ExistentialMetatype:
|
|
return _readMetadata<TargetExistentialMetatypeMetadata>(address);
|
|
case MetadataKind::ExtendedExistential: {
|
|
// We need to read the shape in order to figure out how large
|
|
// the generalization arguments are.
|
|
StoredPointer shapeAddress = address + sizeof(StoredPointer);
|
|
StoredSignedPointer signedShapePtr;
|
|
if (!Reader->readInteger(RemoteAddress(shapeAddress), &signedShapePtr))
|
|
return nullptr;
|
|
auto shapePtr = stripSignedPointer(signedShapePtr);
|
|
|
|
auto shape = readShape(shapePtr);
|
|
if (!shape)
|
|
return nullptr;
|
|
|
|
auto totalSize =
|
|
sizeof(TargetExtendedExistentialTypeMetadata<Runtime>)
|
|
+ shape->getGeneralizationSignature().getArgumentLayoutSizeInWords()
|
|
* sizeof(StoredPointer);
|
|
return _readMetadata(address, totalSize);
|
|
}
|
|
case MetadataKind::ForeignClass:
|
|
return _readMetadata<TargetForeignClassMetadata>(address);
|
|
case MetadataKind::ForeignReferenceType:
|
|
return _readMetadata<TargetForeignReferenceTypeMetadata>(address);
|
|
case MetadataKind::Function: {
|
|
StoredSize flagsValue;
|
|
auto flagsAddr =
|
|
address + TargetFunctionTypeMetadata<Runtime>::OffsetToFlags;
|
|
if (!Reader->readInteger(RemoteAddress(flagsAddr), &flagsValue))
|
|
return nullptr;
|
|
|
|
auto flags =
|
|
TargetFunctionTypeFlags<StoredSize>::fromIntValue(flagsValue);
|
|
|
|
auto totalSize =
|
|
sizeof(TargetFunctionTypeMetadata<Runtime>) +
|
|
flags.getNumParameters() * sizeof(FunctionTypeMetadata::Parameter);
|
|
|
|
if (flags.hasParameterFlags())
|
|
totalSize += flags.getNumParameters() * sizeof(uint32_t);
|
|
|
|
if (flags.isDifferentiable())
|
|
totalSize = roundUpToAlignment(totalSize, sizeof(StoredPointer)) +
|
|
sizeof(TargetFunctionMetadataDifferentiabilityKind<
|
|
typename Runtime::StoredSize>);
|
|
|
|
return _readMetadata(address,
|
|
roundUpToAlignment(totalSize, sizeof(StoredPointer)));
|
|
}
|
|
case MetadataKind::HeapGenericLocalVariable:
|
|
return _readMetadata<TargetGenericBoxHeapMetadata>(address);
|
|
case MetadataKind::HeapLocalVariable:
|
|
return _readMetadata<TargetHeapLocalVariableMetadata>(address);
|
|
case MetadataKind::Metatype:
|
|
return _readMetadata<TargetMetatypeMetadata>(address);
|
|
case MetadataKind::ObjCClassWrapper:
|
|
return _readMetadata<TargetObjCClassWrapperMetadata>(address);
|
|
case MetadataKind::Optional:
|
|
return _readMetadata<TargetEnumMetadata>(address);
|
|
case MetadataKind::Struct:
|
|
return _readMetadata<TargetStructMetadata>(address);
|
|
case MetadataKind::Tuple: {
|
|
auto numElementsAddress = address +
|
|
TargetTupleTypeMetadata<Runtime>::getOffsetToNumElements();
|
|
StoredSize numElements;
|
|
if (!Reader->readInteger(RemoteAddress(numElementsAddress),
|
|
&numElements))
|
|
return nullptr;
|
|
auto totalSize = sizeof(TargetTupleTypeMetadata<Runtime>) +
|
|
numElements * sizeof(TupleTypeMetadata::Element);
|
|
|
|
// Make sure the number of elements is reasonable
|
|
if (numElements >= 256)
|
|
return nullptr;
|
|
|
|
return _readMetadata(address, totalSize);
|
|
}
|
|
case MetadataKind::Opaque:
|
|
default:
|
|
return _readMetadata<TargetOpaqueMetadata>(address);
|
|
}
|
|
|
|
// We can fall out here if the value wasn't actually a valid
|
|
// MetadataKind.
|
|
return nullptr;
|
|
}
|
|
|
|
StoredPointer
|
|
readAddressOfNominalTypeDescriptor(MetadataRef &metadata,
|
|
bool skipArtificialSubclasses = false) {
|
|
switch (metadata->getKind()) {
|
|
case MetadataKind::Class: {
|
|
auto classMeta = cast<TargetClassMetadata>(metadata);
|
|
while (true) {
|
|
if (!classMeta->isTypeMetadata())
|
|
return 0;
|
|
|
|
StoredSignedPointer descriptorAddressSigned = classMeta->getDescriptionAsSignedPointer();
|
|
StoredPointer descriptorAddress = stripSignedPointer(descriptorAddressSigned);
|
|
|
|
// If this class has a null descriptor, it's artificial,
|
|
// and we need to skip it upon request. Otherwise, we're done.
|
|
if (descriptorAddress || !skipArtificialSubclasses)
|
|
return static_cast<StoredPointer>(descriptorAddress);
|
|
|
|
auto superclassMetadataAddress =
|
|
stripSignedPointer(classMeta->Superclass);
|
|
if (!superclassMetadataAddress)
|
|
return 0;
|
|
|
|
auto superMeta = readMetadata(superclassMetadataAddress);
|
|
if (!superMeta)
|
|
return 0;
|
|
|
|
auto superclassMeta = dyn_cast<TargetClassMetadata>(superMeta);
|
|
if (!superclassMeta)
|
|
return 0;
|
|
|
|
classMeta = superclassMeta;
|
|
metadata = superMeta;
|
|
}
|
|
}
|
|
|
|
case MetadataKind::Struct:
|
|
case MetadataKind::Optional:
|
|
case MetadataKind::Enum: {
|
|
auto valueMeta = cast<TargetValueMetadata<Runtime>>(metadata);
|
|
StoredSignedPointer descriptorAddressSigned = valueMeta->getDescriptionAsSignedPointer();
|
|
StoredPointer descriptorAddress = stripSignedPointer(descriptorAddressSigned);
|
|
return descriptorAddress;
|
|
}
|
|
|
|
case MetadataKind::ForeignClass: {
|
|
auto foreignMeta = cast<TargetForeignClassMetadata<Runtime>>(metadata);
|
|
StoredSignedPointer descriptorAddressSigned = foreignMeta->getDescriptionAsSignedPointer();
|
|
StoredPointer descriptorAddress = stripSignedPointer(descriptorAddressSigned);
|
|
return descriptorAddress;
|
|
}
|
|
|
|
case MetadataKind::ForeignReferenceType: {
|
|
auto foreignMeta = cast<TargetForeignReferenceTypeMetadata<Runtime>>(metadata);
|
|
StoredSignedPointer descriptorAddressSigned = foreignMeta->getDescriptionAsSignedPointer();
|
|
StoredPointer descriptorAddress = stripSignedPointer(descriptorAddressSigned);
|
|
return descriptorAddress;
|
|
}
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
private:
|
|
template <template <class R> class M>
|
|
MetadataRef _readMetadata(StoredPointer address) {
|
|
return _readMetadata(address, sizeof(M<Runtime>));
|
|
}
|
|
|
|
MetadataRef _readMetadata(StoredPointer address, size_t sizeAfter) {
|
|
if (sizeAfter > MaxMetadataSize)
|
|
return nullptr;
|
|
auto readResult = Reader->readBytes(RemoteAddress(address), sizeAfter);
|
|
if (!readResult)
|
|
return nullptr;
|
|
|
|
auto metadata =
|
|
reinterpret_cast<const TargetMetadata<Runtime> *>(readResult.get());
|
|
MetadataCache.insert(std::make_pair(address, std::move(readResult)));
|
|
return MetadataRef(address, metadata);
|
|
}
|
|
|
|
/// Returns Optional(ParentContextDescriptorRef()) if there's no parent descriptor.
|
|
/// Returns None if there was an error reading the parent descriptor.
|
|
std::optional<ParentContextDescriptorRef>
|
|
readParentContextDescriptor(ContextDescriptorRef base) {
|
|
auto parentAddress = resolveRelativeIndirectableField(base, base->Parent);
|
|
if (!parentAddress)
|
|
return std::nullopt;
|
|
if (!parentAddress->isResolved()) {
|
|
// Currently we can only handle references directly to a symbol without
|
|
// an offset.
|
|
if (parentAddress->getOffset() != 0) {
|
|
return std::nullopt;
|
|
}
|
|
return ParentContextDescriptorRef(parentAddress->getSymbol());
|
|
}
|
|
auto addr = parentAddress->getResolvedAddress();
|
|
if (!addr)
|
|
return ParentContextDescriptorRef();
|
|
if (auto parentDescriptor = readContextDescriptor(addr.getAddressData()))
|
|
return ParentContextDescriptorRef(parentDescriptor);
|
|
return std::nullopt;
|
|
}
|
|
|
|
static bool isCImportedContext(Demangle::NodePointer node) {
|
|
do {
|
|
if (node->getKind() == Demangle::Node::Kind::Module) {
|
|
return isCImportedModuleName(node->getText());
|
|
}
|
|
|
|
// Continue to the parent.
|
|
node = node->getChild(0);
|
|
} while (node);
|
|
return false;
|
|
}
|
|
|
|
/// Read the name from a module, type, or protocol context descriptor.
|
|
std::optional<std::string> readContextDescriptorName(
|
|
ContextDescriptorRef descriptor,
|
|
std::optional<TypeImportInfo<std::string>> &importInfo) {
|
|
std::string name;
|
|
auto context = descriptor.getLocalBuffer();
|
|
|
|
// Read the name of a protocol.
|
|
if (auto protoBuffer =
|
|
dyn_cast<TargetProtocolDescriptor<Runtime>>(context)) {
|
|
auto nameAddress = resolveRelativeField(descriptor, protoBuffer->Name);
|
|
if (Reader->readString(RemoteAddress(nameAddress), name))
|
|
return name;
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
// Read the name of a module.
|
|
if (auto moduleBuffer =
|
|
dyn_cast<TargetModuleContextDescriptor<Runtime>>(context)) {
|
|
auto nameAddress = resolveRelativeField(descriptor, moduleBuffer->Name);
|
|
if (Reader->readString(RemoteAddress(nameAddress), name))
|
|
return name;
|
|
|
|
return std::nullopt;
|
|
}
|
|
|
|
// Only type contexts remain.
|
|
auto typeBuffer = dyn_cast<TargetTypeContextDescriptor<Runtime>>(context);
|
|
if (!typeBuffer)
|
|
return std::nullopt;
|
|
|
|
auto nameAddress = resolveRelativeField(descriptor, typeBuffer->Name);
|
|
if (!Reader->readString(RemoteAddress(nameAddress), name))
|
|
return std::nullopt;
|
|
|
|
// Read the TypeImportInfo if present.
|
|
if (typeBuffer->getTypeContextDescriptorFlags().hasImportInfo()) {
|
|
importInfo.emplace();
|
|
nameAddress += name.size() + 1;
|
|
|
|
while (true) {
|
|
// Read the next string.
|
|
std::string temp;
|
|
if (!Reader->readString(RemoteAddress(nameAddress), temp))
|
|
return std::nullopt;
|
|
|
|
// If we read an empty string, we're done.
|
|
if (temp.empty())
|
|
break;
|
|
|
|
// Advance past the string.
|
|
nameAddress += temp.size() + 1;
|
|
|
|
// Collect the import information. Ignore anything we don't
|
|
// understand.
|
|
importInfo->collect</*asserting*/false>(std::move(temp));
|
|
}
|
|
|
|
// Ignore the original if we have an ABI name override.
|
|
if (!importInfo->ABIName.empty())
|
|
name = std::move(importInfo->ABIName);
|
|
}
|
|
|
|
return name;
|
|
}
|
|
|
|
/// Clone the given demangle node into the demangler \c dem.
|
|
static Demangle::NodePointer cloneDemangleNode(Demangle::NodePointer node,
|
|
Demangler &dem) {
|
|
if (!node)
|
|
return nullptr;
|
|
|
|
Demangle::NodePointer newNode;
|
|
if (node->hasText())
|
|
newNode = dem.createNode(node->getKind(), node->getText());
|
|
else if (node->hasIndex())
|
|
newNode = dem.createNode(node->getKind(), node->getIndex());
|
|
else
|
|
newNode = dem.createNode(node->getKind());
|
|
|
|
for (auto child : *node) {
|
|
newNode->addChild(cloneDemangleNode(child, dem), dem);
|
|
}
|
|
return newNode;
|
|
}
|
|
|
|
/// Read a mangled name at the given remote address and return the
|
|
/// demangle tree.
|
|
Demangle::NodePointer readMangledName(RemoteAddress address,
|
|
MangledNameKind kind,
|
|
Demangler &dem) {
|
|
// Read chunks of the mangled name, for which the string can be
|
|
// prematurely terminated by a symbolic reference.
|
|
std::string mangledName;
|
|
RemoteAddress currentAddress = address;
|
|
unsigned index = 0;
|
|
while (true) {
|
|
// Read the next chunk.
|
|
std::string chunk;
|
|
if (!Reader->readString(currentAddress, chunk))
|
|
return nullptr;
|
|
|
|
// Move the address forward and add the next chunk.
|
|
currentAddress =
|
|
RemoteAddress(currentAddress.getAddressData() + chunk.size() + 1);
|
|
mangledName += std::move(chunk);
|
|
|
|
// Scan through the mangled name to skip over symbolic references.
|
|
unsigned end = mangledName.size();
|
|
while (index < end) {
|
|
char c = mangledName[index];
|
|
|
|
// Figure out how far to step in the string.
|
|
unsigned step = 1;
|
|
if (c >= '\x01' && c <= '\x17') {
|
|
step += sizeof(uint32_t);
|
|
}
|
|
else if (c >= '\x18' && c <= '\x1F') {
|
|
step += sizeof(typename Runtime::StoredPointer);
|
|
}
|
|
|
|
// If we would be stepping past the end, break out.
|
|
if (index + step > end)
|
|
break;
|
|
|
|
index += step;
|
|
}
|
|
|
|
// If we didn't make it to the end, the '\0' is significant. Add it to the
|
|
// mangled name and
|
|
if (index < end) {
|
|
mangledName.push_back('\0');
|
|
continue;
|
|
}
|
|
|
|
// We're done.
|
|
break;
|
|
}
|
|
return demangle(address.getAddressData(), mangledName, kind, dem);
|
|
}
|
|
|
|
/// Read and demangle the name of an anonymous context.
|
|
Demangle::NodePointer demangleAnonymousContextName(
|
|
ContextDescriptorRef contextRef,
|
|
Demangler &dem) {
|
|
auto anonymousBuffer = cast<TargetAnonymousContextDescriptor<Runtime>>(
|
|
contextRef.getLocalBuffer());
|
|
|
|
if (!anonymousBuffer->hasMangledName())
|
|
return nullptr;
|
|
|
|
// Read the mangled name.
|
|
auto mangledContextName = anonymousBuffer->getMangledContextName();
|
|
auto mangledNameAddress = resolveRelativeField(contextRef,
|
|
mangledContextName->name);
|
|
return readMangledName(RemoteAddress(mangledNameAddress),
|
|
MangledNameKind::Symbol,
|
|
dem);
|
|
}
|
|
|
|
/// If we have a context whose parent context is an anonymous context
|
|
/// that provides the local/private name for the current context,
|
|
/// produce a mangled node describing the name of \c context.
|
|
Demangle::NodePointer adoptAnonymousContextName(
|
|
ContextDescriptorRef contextRef,
|
|
std::optional<ParentContextDescriptorRef> &parentContextRef,
|
|
Demangler &dem, Demangle::NodePointer &outerNode) {
|
|
outerNode = nullptr;
|
|
|
|
// Bail if there is no parent, or if the parent is in another image.
|
|
// (Anonymous contexts should always be emitted in the same image as their
|
|
// children.)
|
|
if (!parentContextRef
|
|
|| !*parentContextRef
|
|
|| !parentContextRef->isResolved())
|
|
return nullptr;
|
|
|
|
auto parentContextLocalRef = parentContextRef->getResolved();
|
|
|
|
auto context = contextRef.getLocalBuffer();
|
|
auto typeContext = dyn_cast<TargetTypeContextDescriptor<Runtime>>(context);
|
|
auto protoContext = dyn_cast<TargetProtocolDescriptor<Runtime>>(context);
|
|
if (!typeContext && !protoContext)
|
|
return nullptr;
|
|
|
|
auto anonymousParent = dyn_cast_or_null<TargetAnonymousContextDescriptor<Runtime>>(
|
|
parentContextLocalRef.getLocalBuffer());
|
|
if (!anonymousParent)
|
|
return nullptr;
|
|
|
|
auto mangledNode = demangleAnonymousContextName(parentContextLocalRef, dem);
|
|
if (!mangledNode)
|
|
return nullptr;
|
|
|
|
if (mangledNode->getKind() == Demangle::Node::Kind::Global)
|
|
mangledNode = mangledNode->getFirstChild();
|
|
|
|
if (mangledNode->getNumChildren() < 2)
|
|
return nullptr;
|
|
|
|
// Dig out the name of the entity.
|
|
swift::Demangle::NodePointer nameChild = mangledNode->getChild(1);
|
|
if ((nameChild->getKind() != Node::Kind::PrivateDeclName &&
|
|
nameChild->getKind() != Node::Kind::LocalDeclName) ||
|
|
nameChild->getNumChildren() < 2)
|
|
return nullptr;
|
|
|
|
// Make sure we have an identifier where we expect it.
|
|
auto identifierNode = nameChild->getChild(1);
|
|
if (identifierNode->getKind() != Node::Kind::Identifier ||
|
|
!identifierNode->hasText())
|
|
return nullptr;
|
|
|
|
// Read the name of the current context.
|
|
std::optional<TypeImportInfo<std::string>> importInfo;
|
|
auto contextName = readContextDescriptorName(contextRef, importInfo);
|
|
if (!contextName)
|
|
return nullptr;
|
|
|
|
// Make sure the name of the current context matches the one in the mangled
|
|
// name of the parent anonymous context.
|
|
// FIXME: Use the ABI name here.
|
|
if (*contextName != identifierNode->getText())
|
|
return nullptr;
|
|
|
|
// We have a match. Update the parent context to skip the anonymous
|
|
// context entirely.
|
|
parentContextRef = readParentContextDescriptor(parentContextLocalRef);
|
|
|
|
// The outer node is the first child.
|
|
outerNode = mangledNode->getChild(0);
|
|
|
|
// Return the name.
|
|
return nameChild;
|
|
}
|
|
|
|
/// Resolve a relative target protocol descriptor pointer, which uses
|
|
/// the lowest bit to indicate an indirect vs. direct relative reference and
|
|
/// the second lowest bit to indicate whether it is an Objective-C protocol.
|
|
template<typename Base>
|
|
StoredPointer resolveRelativeIndirectProtocol(
|
|
RemoteRef<Base> descriptor,
|
|
const RelativeTargetProtocolDescriptorPointer<Runtime> &protocol) {
|
|
// Map the offset from within our local buffer to the remote address.
|
|
auto distance = (intptr_t)&protocol - (intptr_t)descriptor.getLocalBuffer();
|
|
StoredPointer targetAddress(getAddress(descriptor) + distance);
|
|
|
|
// Read the relative offset.
|
|
int32_t relative;
|
|
if (!Reader->readInteger(RemoteAddress(targetAddress), &relative))
|
|
return StoredPointer();
|
|
|
|
// Collect and mask off the 'indirect' and 'isObjC' bits.
|
|
bool indirect = relative & 1;
|
|
relative &= ~1u;
|
|
bool isObjC = relative & 2;
|
|
relative &= ~2u;
|
|
|
|
using SignedPointer = typename std::make_signed<StoredPointer>::type;
|
|
auto signext = (SignedPointer)(int32_t)relative;
|
|
|
|
StoredPointer resultAddress = targetAddress + signext;
|
|
|
|
// Low bit set in the offset indicates that the offset leads to the absolute
|
|
// address in memory.
|
|
if (indirect) {
|
|
if (!Reader->readBytes(RemoteAddress(resultAddress),
|
|
(uint8_t *)&resultAddress,
|
|
sizeof(StoredPointer)))
|
|
return StoredPointer();
|
|
}
|
|
|
|
// Add back the Objective-C bit.
|
|
if (isObjC)
|
|
resultAddress |= 0x1;
|
|
|
|
return resultAddress;
|
|
}
|
|
|
|
Demangle::NodePointer
|
|
buildContextDescriptorMangling(const ParentContextDescriptorRef &descriptor,
|
|
Demangler &dem, int recursion_limit) {
|
|
if (recursion_limit <= 0) {
|
|
return nullptr;
|
|
}
|
|
|
|
if (descriptor.isResolved()) {
|
|
return buildContextDescriptorMangling(descriptor.getResolved(), dem, recursion_limit);
|
|
}
|
|
|
|
// Try to demangle the symbol name to figure out what context it would
|
|
// point to.
|
|
auto demangledSymbol = buildContextManglingForSymbol(descriptor.getSymbol(),
|
|
dem);
|
|
if (!demangledSymbol)
|
|
return nullptr;
|
|
// Look through Type notes since we're building up a mangling here.
|
|
if (demangledSymbol->getKind() == Demangle::Node::Kind::Type){
|
|
demangledSymbol = demangledSymbol->getChild(0);
|
|
}
|
|
return demangledSymbol;
|
|
}
|
|
|
|
Demangle::NodePointer
|
|
buildContextDescriptorMangling(ContextDescriptorRef descriptor,
|
|
Demangler &dem, int recursion_limit) {
|
|
if (recursion_limit <= 0) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Read the parent descriptor.
|
|
auto parentDescriptorResult = readParentContextDescriptor(descriptor);
|
|
|
|
// If the parent is an anonymous context that provides a complete
|
|
// name for this node, note that.
|
|
Demangle::NodePointer demangledParentNode = nullptr;
|
|
auto nameNode = adoptAnonymousContextName(
|
|
descriptor, parentDescriptorResult, dem, demangledParentNode);
|
|
|
|
// If there was a problem reading the parent descriptor, we're done.
|
|
if (!parentDescriptorResult) return nullptr;
|
|
|
|
// Try to produce a mangle-tree for the parent.
|
|
Demangle::NodePointer parentDemangling = nullptr;
|
|
if (auto parentDescriptor = *parentDescriptorResult) {
|
|
parentDemangling =
|
|
buildContextDescriptorMangling(parentDescriptor, dem, recursion_limit - 1);
|
|
if (!parentDemangling && !demangledParentNode)
|
|
return nullptr;
|
|
}
|
|
|
|
// If we have a better parent node produced from an enclosing nominal
|
|
// context, use that.
|
|
if (demangledParentNode &&
|
|
(!parentDemangling ||
|
|
parentDemangling->getKind() == Node::Kind::AnonymousContext)) {
|
|
parentDemangling = demangledParentNode;
|
|
}
|
|
|
|
Demangle::Node::Kind nodeKind;
|
|
std::optional<TypeImportInfo<std::string>> importInfo;
|
|
|
|
auto getContextName = [&]() -> bool {
|
|
if (nameNode)
|
|
return true;
|
|
|
|
if (auto name = readContextDescriptorName(descriptor, importInfo)) {
|
|
nameNode = dem.createNode(Node::Kind::Identifier, std::move(*name));
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
};
|
|
|
|
switch (auto contextKind = descriptor->getKind()) {
|
|
case ContextDescriptorKind::Class:
|
|
if (!getContextName())
|
|
return nullptr;
|
|
nodeKind = Demangle::Node::Kind::Class;
|
|
break;
|
|
case ContextDescriptorKind::Struct:
|
|
if (!getContextName())
|
|
return nullptr;
|
|
nodeKind = Demangle::Node::Kind::Structure;
|
|
break;
|
|
case ContextDescriptorKind::Enum:
|
|
if (!getContextName())
|
|
return nullptr;
|
|
nodeKind = Demangle::Node::Kind::Enum;
|
|
break;
|
|
case ContextDescriptorKind::Protocol: {
|
|
if (!getContextName())
|
|
return nullptr;
|
|
|
|
nodeKind = Demangle::Node::Kind::Protocol;
|
|
break;
|
|
}
|
|
case ContextDescriptorKind::Extension: {
|
|
// There should always be a parent describing where the extension
|
|
// lives.
|
|
if (!parentDemangling) {
|
|
return nullptr;
|
|
}
|
|
|
|
auto extensionBuffer =
|
|
reinterpret_cast<const TargetExtensionContextDescriptor<Runtime> *>(
|
|
descriptor.getLocalBuffer());
|
|
|
|
auto extendedContextAddress =
|
|
resolveRelativeField(descriptor, extensionBuffer->ExtendedContext);
|
|
|
|
auto demangledExtendedContext =
|
|
readMangledName(RemoteAddress(extendedContextAddress),
|
|
MangledNameKind::Type,
|
|
dem);
|
|
if (!demangledExtendedContext)
|
|
return nullptr;
|
|
|
|
auto demangling = dem.createNode(Node::Kind::Extension);
|
|
demangling->addChild(parentDemangling, dem);
|
|
demangling->addChild(demangledExtendedContext, dem);
|
|
|
|
/// Resolver to turn a protocol reference into a demangling.
|
|
struct ProtocolResolver {
|
|
using Result = Demangle::Node *;
|
|
|
|
Demangler &dem;
|
|
|
|
Result failure() const {
|
|
return nullptr;
|
|
}
|
|
|
|
Result swiftProtocol(Demangle::Node *node) {
|
|
return node;
|
|
}
|
|
|
|
#if SWIFT_OBJC_INTEROP
|
|
Result objcProtocol(StringRef name) {
|
|
// FIXME: Unify this with the runtime's Demangle.cpp
|
|
auto module = dem.createNode(Node::Kind::Module,
|
|
MANGLING_MODULE_OBJC);
|
|
auto node = dem.createNode(Node::Kind::Protocol);
|
|
node->addChild(module, dem);
|
|
node->addChild(dem.createNode(Node::Kind::Identifier, name),
|
|
dem);
|
|
return node;
|
|
}
|
|
#endif
|
|
} protocolResolver{dem};
|
|
|
|
// If there are generic requirements, form the generic signature.
|
|
auto requirements = extensionBuffer->getGenericRequirements();
|
|
if (!requirements.empty()) {
|
|
auto signatureNode =
|
|
dem.createNode(Node::Kind::DependentGenericSignature);
|
|
bool failed = false;
|
|
for (const auto &req : requirements) {
|
|
if (failed)
|
|
break;
|
|
|
|
// Demangle the subject.
|
|
auto subjectAddress = resolveRelativeField(descriptor, req.Param);
|
|
swift::Demangle::NodePointer subject =
|
|
readMangledName(RemoteAddress(subjectAddress),
|
|
MangledNameKind::Type, dem);
|
|
if (!subject) {
|
|
failed = true;
|
|
break;
|
|
}
|
|
|
|
switch (req.Flags.getKind()) {
|
|
case GenericRequirementKind::Protocol: {
|
|
auto protocolAddress =
|
|
resolveRelativeIndirectProtocol(descriptor, req.Protocol);
|
|
auto protocol = readProtocol(protocolAddress, dem,
|
|
protocolResolver);
|
|
if (!protocol) {
|
|
failed = true;
|
|
break;
|
|
}
|
|
|
|
auto reqNode =
|
|
dem.createNode(
|
|
Node::Kind::DependentGenericConformanceRequirement);
|
|
reqNode->addChild(subject, dem);
|
|
reqNode->addChild(protocol, dem);
|
|
signatureNode->addChild(reqNode, dem);
|
|
break;
|
|
}
|
|
|
|
case GenericRequirementKind::SameType:
|
|
case GenericRequirementKind::BaseClass: {
|
|
// Demangle the right-hand type.
|
|
auto typeAddress = resolveRelativeField(descriptor, req.Type);
|
|
swift::Demangle::NodePointer type =
|
|
readMangledName(RemoteAddress(typeAddress),
|
|
MangledNameKind::Type, dem);
|
|
if (!type) {
|
|
failed = true;
|
|
break;
|
|
}
|
|
|
|
Node::Kind nodeKind;
|
|
if (req.Flags.getKind() == GenericRequirementKind::SameType)
|
|
nodeKind = Node::Kind::DependentGenericSameTypeRequirement;
|
|
else
|
|
nodeKind = Node::Kind::DependentGenericConformanceRequirement;
|
|
|
|
auto reqNode = dem.createNode(nodeKind);
|
|
reqNode->addChild(subject, dem);
|
|
reqNode->addChild(type, dem);
|
|
signatureNode->addChild(reqNode, dem);
|
|
break;
|
|
}
|
|
|
|
case GenericRequirementKind::SameConformance:
|
|
// Do nothing
|
|
break;
|
|
|
|
case GenericRequirementKind::Layout: {
|
|
// Map the offset from within our local buffer to the remote
|
|
// address.
|
|
auto distance =
|
|
(intptr_t)&req.Layout - (intptr_t)descriptor.getLocalBuffer();
|
|
StoredPointer targetAddress(getAddress(descriptor) + distance);
|
|
|
|
GenericRequirementLayoutKind kind;
|
|
if (!Reader->readBytes(RemoteAddress(targetAddress),
|
|
(uint8_t *)&kind, sizeof(kind))) {
|
|
failed = true;
|
|
break;
|
|
}
|
|
|
|
if (kind == GenericRequirementLayoutKind::Class) {
|
|
auto reqNode =
|
|
dem.createNode(Node::Kind::DependentGenericLayoutRequirement);
|
|
reqNode->addChild(subject, dem);
|
|
auto idNode = dem.createNode(Node::Kind::Identifier, "C");
|
|
reqNode->addChild(idNode, dem);
|
|
signatureNode->addChild(reqNode, dem);
|
|
} else {
|
|
failed = true;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case GenericRequirementKind::SameShape:
|
|
llvm_unreachable("Implement me");
|
|
case GenericRequirementKind::InvertedProtocols:
|
|
llvm_unreachable("Implement me");
|
|
}
|
|
}
|
|
|
|
if (!failed) {
|
|
demangling->addChild(signatureNode, dem);
|
|
}
|
|
}
|
|
return demangling;
|
|
}
|
|
|
|
case ContextDescriptorKind::Anonymous: {
|
|
// Use the remote address to identify the anonymous context.
|
|
char addressBuf[18];
|
|
snprintf(addressBuf, sizeof(addressBuf), "$%" PRIx64,
|
|
(uint64_t)descriptor.getAddressData());
|
|
auto anonNode = dem.createNode(Node::Kind::AnonymousContext);
|
|
CharVector addressStr;
|
|
addressStr.append(addressBuf, dem);
|
|
auto name = dem.createNode(Node::Kind::Identifier, addressStr);
|
|
anonNode->addChild(name, dem);
|
|
if (parentDemangling)
|
|
anonNode->addChild(parentDemangling, dem);
|
|
|
|
return anonNode;
|
|
}
|
|
|
|
case ContextDescriptorKind::Module: {
|
|
// Modules shouldn't have a parent.
|
|
if (parentDemangling) {
|
|
return nullptr;
|
|
}
|
|
|
|
nodeKind = Demangle::Node::Kind::Module;
|
|
auto moduleBuffer =
|
|
reinterpret_cast<const TargetModuleContextDescriptor<Runtime> *>
|
|
(descriptor.getLocalBuffer());
|
|
auto nameAddress
|
|
= resolveRelativeField(descriptor, moduleBuffer->Name);
|
|
std::string moduleName;
|
|
if (!Reader->readString(RemoteAddress(nameAddress), moduleName))
|
|
return nullptr;
|
|
|
|
// The form of module contexts is a little different from other
|
|
// contexts; just create the node directly here and return.
|
|
return dem.createNode(nodeKind, std::move(moduleName));
|
|
}
|
|
|
|
case ContextDescriptorKind::OpaqueType: {
|
|
// The opaque type may have a named anonymous context for us to map
|
|
// back to its defining decl.
|
|
if (!parentDescriptorResult
|
|
|| !*parentDescriptorResult
|
|
|| !parentDescriptorResult->isResolved())
|
|
return nullptr;
|
|
|
|
if (parentDemangling->getKind() == Node::Kind::AnonymousContext) {
|
|
auto mangledNode =
|
|
demangleAnonymousContextName(parentDescriptorResult->getResolved(), dem);
|
|
if (!mangledNode)
|
|
return nullptr;
|
|
if (mangledNode->getKind() == Node::Kind::Global)
|
|
mangledNode = mangledNode->getChild(0);
|
|
|
|
auto opaqueNode = dem.createNode(Node::Kind::OpaqueReturnTypeOf);
|
|
opaqueNode->addChild(mangledNode, dem);
|
|
return opaqueNode;
|
|
} else if (parentDemangling->getKind() == Node::Kind::Module) {
|
|
auto opaqueNode = dem.createNode(Node::Kind::OpaqueReturnTypeOf);
|
|
opaqueNode->addChild(parentDemangling, dem);
|
|
return opaqueNode;
|
|
} else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
default:
|
|
// Not a kind of context we know about.
|
|
return nullptr;
|
|
}
|
|
|
|
// The root context should be a module context, which we handled directly
|
|
// in the switch, so if we got here without a parent, we're ill-formed.
|
|
if (!parentDemangling) {
|
|
return nullptr;
|
|
}
|
|
|
|
// Override various aspects of the mangling for imported types.
|
|
if (importInfo && isCImportedContext(parentDemangling)) {
|
|
if (importInfo->SymbolNamespace == TypeImportSymbolNamespace::CTypedef)
|
|
nodeKind = Demangle::Node::Kind::TypeAlias;
|
|
|
|
// As a special case, use the struct mangling for C-imported value
|
|
// types that don't have a special namespace and aren't a related
|
|
// entity.
|
|
//
|
|
// This should be kept in sync with the AST mangler and with
|
|
// _isCImportedTagType in the runtime.
|
|
else if (importInfo->SymbolNamespace.empty() &&
|
|
importInfo->RelatedEntityName.empty() &&
|
|
nodeKind == Demangle::Node::Kind::Enum)
|
|
nodeKind = Demangle::Node::Kind::Structure;
|
|
}
|
|
|
|
// Use private declaration names for anonymous context references.
|
|
if (parentDemangling->getKind() == Node::Kind::AnonymousContext
|
|
&& nameNode->getKind() == Node::Kind::Identifier) {
|
|
if (parentDemangling->getNumChildren() < 2)
|
|
return nullptr;
|
|
|
|
auto privateDeclName =
|
|
dem.createNode(Node::Kind::PrivateDeclName);
|
|
privateDeclName->addChild(parentDemangling->getChild(0), dem);
|
|
privateDeclName->addChild(nameNode, dem);
|
|
|
|
nameNode = privateDeclName;
|
|
parentDemangling = parentDemangling->getChild(1);
|
|
}
|
|
|
|
if (importInfo && !importInfo->RelatedEntityName.empty()) {
|
|
auto kindNode = dem.createNode(Node::Kind::Identifier,
|
|
std::move(importInfo->RelatedEntityName));
|
|
auto relatedNode = dem.createNode(Node::Kind::RelatedEntityDeclName);
|
|
relatedNode->addChild(kindNode, dem);
|
|
relatedNode->addChild(nameNode, dem);
|
|
nameNode = relatedNode;
|
|
}
|
|
|
|
auto demangling = dem.createNode(nodeKind);
|
|
demangling->addChild(parentDemangling, dem);
|
|
demangling->addChild(nameNode, dem);
|
|
return demangling;
|
|
}
|
|
|
|
/// Given a read nominal type descriptor, attempt to build a
|
|
/// nominal type decl from it.
|
|
template <
|
|
typename T = BuilderType,
|
|
typename std::enable_if_t<
|
|
!(std::is_same<
|
|
const bool,
|
|
decltype(T::needsToPrecomputeParentGenericContextShapes)>::
|
|
value &&
|
|
T::needsToPrecomputeParentGenericContextShapes),
|
|
bool> = true>
|
|
BuiltTypeDecl buildNominalTypeDecl(ContextDescriptorRef descriptor) {
|
|
// Build the demangling tree from the context tree.
|
|
Demangler dem;
|
|
auto node = buildContextMangling(descriptor, dem);
|
|
if (!node || node->getKind() != Node::Kind::Type)
|
|
return BuiltTypeDecl();
|
|
bool typeAlias = false;
|
|
BuiltTypeDecl decl = Builder.createTypeDecl(node, typeAlias);
|
|
return decl;
|
|
}
|
|
|
|
template <typename T = BuilderType,
|
|
typename std::enable_if_t<
|
|
std::is_same<
|
|
const bool,
|
|
decltype(T::needsToPrecomputeParentGenericContextShapes)>::
|
|
value &&
|
|
T::needsToPrecomputeParentGenericContextShapes,
|
|
bool> = true>
|
|
BuiltTypeDecl buildNominalTypeDecl(ContextDescriptorRef descriptor) {
|
|
// Build the demangling tree from the context tree.
|
|
Demangler dem;
|
|
auto node = buildContextMangling(descriptor, dem);
|
|
if (!node || node->getKind() != Node::Kind::Type)
|
|
return BuiltTypeDecl();
|
|
std::vector<size_t> paramsPerLevel;
|
|
size_t runningCount = 0;
|
|
std::function<void(ContextDescriptorRef current, size_t &)> countLevels =
|
|
[&](ContextDescriptorRef current, size_t &runningCount) {
|
|
if (auto parentContextRef = readParentContextDescriptor(current))
|
|
if (parentContextRef->isResolved())
|
|
if (auto parentContext = parentContextRef->getResolved())
|
|
countLevels(parentContext, runningCount);
|
|
|
|
auto genericContext = current->getGenericContext();
|
|
// Only consider generic contexts of type class, enum or struct.
|
|
// There are other context types that can be generic, but they should
|
|
// not affect the generic shape.
|
|
if (genericContext &&
|
|
(current->getKind() == ContextDescriptorKind::Class ||
|
|
current->getKind() == ContextDescriptorKind::Enum ||
|
|
current->getKind() == ContextDescriptorKind::Struct)) {
|
|
auto contextHeader = genericContext->getGenericContextHeader();
|
|
paramsPerLevel.emplace_back(contextHeader.NumParams - runningCount);
|
|
runningCount += paramsPerLevel.back();
|
|
}
|
|
};
|
|
countLevels(descriptor, runningCount);
|
|
BuiltTypeDecl decl = Builder.createTypeDecl(node, paramsPerLevel);
|
|
return decl;
|
|
}
|
|
|
|
#if SWIFT_OBJC_INTEROP
|
|
std::string readObjCProtocolName(StoredPointer Address) {
|
|
auto Size = sizeof(TargetObjCProtocolPrefix<Runtime>);
|
|
auto Buffer = (uint8_t *)malloc(Size);
|
|
if (!Buffer)
|
|
return std::string();
|
|
SWIFT_DEFER {
|
|
free(Buffer);
|
|
};
|
|
|
|
if (!Reader->readBytes(RemoteAddress(Address), Buffer, Size))
|
|
return std::string();
|
|
|
|
auto ProtocolDescriptor
|
|
= reinterpret_cast<TargetObjCProtocolPrefix<Runtime> *>(Buffer);
|
|
std::string Name;
|
|
if (!Reader->readString(RemoteAddress(ProtocolDescriptor->Name), Name))
|
|
return std::string();
|
|
|
|
return Name;
|
|
}
|
|
#endif
|
|
|
|
// TODO: We need to be able to produce protocol conformances for each
|
|
// substitution type as well in order to accurately rebuild bound generic
|
|
// types or types in protocol-constrained inner contexts.
|
|
std::vector<BuiltType> getGenericSubst(MetadataRef metadata,
|
|
ContextDescriptorRef descriptor,
|
|
int recursion_limit) {
|
|
auto generics = descriptor->getGenericContext();
|
|
if (!generics)
|
|
return {};
|
|
|
|
auto numGenericArgs =
|
|
generics->getGenericContextHeader().getNumArguments();
|
|
|
|
auto offsetToGenericArgs = readGenericArgsOffset(metadata, descriptor);
|
|
if (!offsetToGenericArgs)
|
|
return {};
|
|
|
|
auto genericArgsAddr = getAddress(metadata)
|
|
+ sizeof(StoredPointer) * *offsetToGenericArgs;
|
|
|
|
std::vector<BuiltType> builtSubsts;
|
|
for (auto param : generics->getGenericParams()) {
|
|
switch (param.getKind()) {
|
|
case GenericParamKind::Type:
|
|
// The type should have a key argument unless it's been same-typed
|
|
// to another type.
|
|
if (param.hasKeyArgument()) {
|
|
if (numGenericArgs == 0)
|
|
return {};
|
|
--numGenericArgs;
|
|
|
|
StoredPointer arg;
|
|
if (!Reader->readBytes(RemoteAddress(genericArgsAddr),
|
|
(uint8_t*)&arg, sizeof(arg))) {
|
|
return {};
|
|
}
|
|
genericArgsAddr += sizeof(StoredPointer);
|
|
|
|
auto builtArg = readTypeFromMetadata(arg, false, recursion_limit);
|
|
if (!builtArg)
|
|
return {};
|
|
builtSubsts.push_back(builtArg);
|
|
} else {
|
|
// TODO: If the key argument has been concretized by a same-type
|
|
// constraint, that should be reflected in the built nominal type
|
|
// decl's generic constraints. This isn't handled correctly yet.
|
|
return {};
|
|
}
|
|
break;
|
|
|
|
case GenericParamKind::TypePack:
|
|
// assert(false && "Packs not supported here yet");
|
|
return {};
|
|
|
|
default:
|
|
// We don't know about this kind of parameter.
|
|
return {};
|
|
}
|
|
}
|
|
return builtSubsts;
|
|
}
|
|
|
|
BuiltType
|
|
readNominalTypeFromMetadata(MetadataRef origMetadata,
|
|
int recursion_limit = defaultTypeRecursionLimit,
|
|
bool skipArtificialSubclasses = false) {
|
|
auto metadata = origMetadata;
|
|
auto descriptorAddress =
|
|
readAddressOfNominalTypeDescriptor(metadata,
|
|
skipArtificialSubclasses);
|
|
if (!descriptorAddress)
|
|
return BuiltType();
|
|
|
|
// If we've skipped an artificial subclasses, check the cache at
|
|
// the superclass. (This also protects against recursion.)
|
|
if (skipArtificialSubclasses && metadata != origMetadata) {
|
|
auto it =
|
|
TypeCache.find({getAddress(metadata), skipArtificialSubclasses});
|
|
if (it != TypeCache.end()) {
|
|
TypeCache.erase({getAddress(origMetadata), skipArtificialSubclasses});
|
|
return it->second;
|
|
}
|
|
}
|
|
|
|
// Read the nominal type descriptor.
|
|
ContextDescriptorRef descriptor = readContextDescriptor(descriptorAddress);
|
|
if (!descriptor)
|
|
return BuiltType();
|
|
|
|
// From that, attempt to resolve a nominal type.
|
|
BuiltTypeDecl typeDecl = buildNominalTypeDecl(descriptor);
|
|
if (!typeDecl)
|
|
return BuiltType();
|
|
|
|
// Build the nominal type.
|
|
BuiltType nominal;
|
|
if (descriptor->isGeneric()) {
|
|
// Resolve the generic arguments.
|
|
auto builtGenerics =
|
|
getGenericSubst(metadata, descriptor, recursion_limit);
|
|
if (builtGenerics.empty())
|
|
return BuiltType();
|
|
nominal = Builder.createBoundGenericType(typeDecl, builtGenerics);
|
|
} else {
|
|
nominal = Builder.createNominalType(typeDecl);
|
|
}
|
|
|
|
if (!nominal)
|
|
return BuiltType();
|
|
|
|
TypeCache[{getAddress(metadata), skipArtificialSubclasses}] = nominal;
|
|
|
|
// If we've skipped an artificial subclass, remove the
|
|
// recursion-protection entry we made for it.
|
|
if (skipArtificialSubclasses && metadata != origMetadata) {
|
|
TypeCache.erase({getAddress(origMetadata), skipArtificialSubclasses});
|
|
}
|
|
|
|
return nominal;
|
|
}
|
|
|
|
BuiltType
|
|
readNominalTypeFromClassMetadata(MetadataRef origMetadata,
|
|
int recursion_limit,
|
|
bool skipArtificialSubclasses = false) {
|
|
auto classMeta = cast<TargetClassMetadata>(origMetadata);
|
|
if (classMeta->isTypeMetadata())
|
|
return readNominalTypeFromMetadata(origMetadata, recursion_limit,
|
|
skipArtificialSubclasses);
|
|
|
|
std::string className;
|
|
auto origMetadataPtr = getAddress(origMetadata);
|
|
if (!readObjCClassName(origMetadataPtr, className))
|
|
return BuiltType();
|
|
|
|
BuiltType BuiltObjCClass = Builder.createObjCClassType(std::move(className));
|
|
if (!BuiltObjCClass) {
|
|
// Try the superclass.
|
|
if (!stripSignedPointer(classMeta->Superclass))
|
|
return BuiltType();
|
|
|
|
BuiltObjCClass =
|
|
readTypeFromMetadata(stripSignedPointer(classMeta->Superclass),
|
|
skipArtificialSubclasses, recursion_limit);
|
|
}
|
|
|
|
TypeCache[{origMetadataPtr, skipArtificialSubclasses}] = BuiltObjCClass;
|
|
return BuiltObjCClass;
|
|
}
|
|
|
|
using TargetClassMetadataObjCInterop =
|
|
swift::TargetClassMetadata<Runtime,
|
|
TargetAnyClassMetadataObjCInterop<Runtime>>;
|
|
|
|
/// Given that the remote process is running the non-fragile Apple runtime,
|
|
/// grab the ro-data from a class pointer.
|
|
StoredPointer readObjCRODataPtr(StoredPointer classAddress) {
|
|
// WARNING: the following algorithm works on current modern Apple
|
|
// runtimes but is not actually ABI. But it is pretty reliable.
|
|
|
|
#if SWIFT_OBJC_INTEROP
|
|
StoredPointer dataPtr;
|
|
if (!Reader->readInteger(
|
|
RemoteAddress(classAddress +
|
|
TargetClassMetadataObjCInterop::offsetToData()),
|
|
&dataPtr))
|
|
return StoredPointer();
|
|
|
|
// Apply the data-pointer mask.
|
|
// These values have been stolen from the runtime source.
|
|
static constexpr uint64_t DataPtrMask =
|
|
(Runtime::PointerSize == 8 ? 0x00007ffffffffff8ULL : 0xfffffffcULL);
|
|
dataPtr &= StoredPointer(DataPtrMask);
|
|
if (!dataPtr)
|
|
return StoredPointer();
|
|
|
|
// Read the flags, which is a 32-bit header on both formats.
|
|
uint32_t flags;
|
|
if (!Reader->readInteger(RemoteAddress(dataPtr), &flags))
|
|
return StoredPointer();
|
|
|
|
// If the type is not realized, this is the RO-data.
|
|
static constexpr uint32_t RO_REALIZED = 0x80000000U;
|
|
if (!(flags & RO_REALIZED))
|
|
return dataPtr;
|
|
|
|
// Otherwise, it's the RW-data; read the RO-data pointer from a
|
|
// well-known position within the RW-data.
|
|
StoredSignedPointer signedDataPtr;
|
|
static constexpr uint32_t OffsetToROPtr = 8;
|
|
if (!Reader->readInteger(RemoteAddress(dataPtr + OffsetToROPtr), &signedDataPtr))
|
|
return StoredPointer();
|
|
dataPtr = stripSignedPointer(signedDataPtr);
|
|
|
|
// Newer Objective-C runtimes implement a size optimization where the RO
|
|
// field is a tagged union. If the low-bit is set, then the pointer needs
|
|
// to be dereferenced once more to yield the real class_ro_t pointer.
|
|
if (dataPtr & 1) {
|
|
if (!Reader->readInteger(RemoteAddress(dataPtr^1), &signedDataPtr))
|
|
return StoredPointer();
|
|
dataPtr = stripSignedPointer(signedDataPtr);
|
|
}
|
|
|
|
return dataPtr;
|
|
#else
|
|
return StoredPointer();
|
|
#endif
|
|
}
|
|
|
|
IsaEncodingKind getIsaEncoding() {
|
|
if (IsaEncoding != IsaEncodingKind::Unknown)
|
|
return IsaEncoding;
|
|
|
|
auto finish = [&](IsaEncodingKind result) -> IsaEncodingKind {
|
|
IsaEncoding = result;
|
|
return result;
|
|
};
|
|
|
|
/// Look up the given global symbol and bind 'varname' to its
|
|
/// address if its exists.
|
|
# define tryFindSymbol(varname, symbolName) \
|
|
auto varname = Reader->getSymbolAddress(symbolName); \
|
|
if (!varname) \
|
|
return finish(IsaEncodingKind::Error)
|
|
/// Read from the given pointer into 'dest'.
|
|
# define tryReadSymbol(varname, dest) do { \
|
|
if (!Reader->readInteger(varname, &dest)) \
|
|
return finish(IsaEncodingKind::Error); \
|
|
} while (0)
|
|
/// Read from the given global symbol into 'dest'.
|
|
# define tryFindAndReadSymbol(dest, symbolName) do { \
|
|
tryFindSymbol(_address, symbolName); \
|
|
tryReadSymbol(_address, dest); \
|
|
} while (0)
|
|
|
|
// Check for the magic-mask symbol that indicates that the ObjC
|
|
// runtime is using indexed ISAs.
|
|
if (auto magicMaskAddress =
|
|
Reader->getSymbolAddress("objc_debug_indexed_isa_magic_mask")) {
|
|
tryReadSymbol(magicMaskAddress, IsaMagicMask);
|
|
if (IsaMagicMask != 0) {
|
|
tryFindAndReadSymbol(IsaMagicValue,
|
|
"objc_debug_indexed_isa_magic_value");
|
|
tryFindAndReadSymbol(IsaIndexMask,
|
|
"objc_debug_indexed_isa_index_mask");
|
|
tryFindAndReadSymbol(IsaIndexShift,
|
|
"objc_debug_indexed_isa_index_shift");
|
|
tryFindSymbol(indexedClasses, "objc_indexed_classes");
|
|
IndexedClassesPointer = indexedClasses.getAddressData();
|
|
tryFindSymbol(indexedClassesCount, "objc_indexed_classes_count");
|
|
IndexedClassesCountPointer = indexedClassesCount.getAddressData();
|
|
|
|
return finish(IsaEncodingKind::Indexed);
|
|
}
|
|
}
|
|
|
|
// Check for the ISA mask symbol. This has to come second because
|
|
// the standard library will define this even if the ObjC runtime
|
|
// doesn't use it.
|
|
if (auto maskAddress = Reader->getSymbolAddress("swift_isaMask")) {
|
|
tryReadSymbol(maskAddress, IsaMask);
|
|
if (IsaMask != 0) {
|
|
return finish(IsaEncodingKind::Masked);
|
|
}
|
|
}
|
|
|
|
# undef tryFindSymbol
|
|
# undef tryReadSymbol
|
|
# undef tryFindAndReadSymbol
|
|
|
|
return finish(IsaEncodingKind::None);
|
|
}
|
|
|
|
TaggedPointerEncodingKind getTaggedPointerEncoding() {
|
|
if (TaggedPointerEncoding != TaggedPointerEncodingKind::Unknown)
|
|
return TaggedPointerEncoding;
|
|
|
|
auto finish = [&](TaggedPointerEncodingKind result)
|
|
-> TaggedPointerEncodingKind {
|
|
TaggedPointerEncoding = result;
|
|
return result;
|
|
};
|
|
|
|
/// Look up the given global symbol and bind 'varname' to its
|
|
/// address if its exists.
|
|
# define tryFindSymbol(varname, symbolName) \
|
|
auto varname = Reader->getSymbolAddress(symbolName); \
|
|
if (!varname) \
|
|
return finish(TaggedPointerEncodingKind::Error)
|
|
/// Read from the given pointer into 'dest'.
|
|
# define tryReadSymbol(varname, dest) do { \
|
|
if (!Reader->readInteger(varname, &dest)) \
|
|
return finish(TaggedPointerEncodingKind::Error); \
|
|
} while (0)
|
|
/// Read from the given global symbol into 'dest'.
|
|
# define tryFindAndReadSymbol(dest, symbolName) do { \
|
|
tryFindSymbol(_address, symbolName); \
|
|
tryReadSymbol(_address, dest); \
|
|
} while (0)
|
|
# define tryFindAndReadSymbolWithDefault(dest, symbolName, default) do { \
|
|
dest = default; \
|
|
auto _address = Reader->getSymbolAddress(symbolName); \
|
|
if (_address) \
|
|
tryReadSymbol(_address, dest); \
|
|
} while (0)
|
|
|
|
tryFindAndReadSymbol(TaggedPointerMask,
|
|
"objc_debug_taggedpointer_mask");
|
|
tryFindAndReadSymbol(TaggedPointerSlotShift,
|
|
"objc_debug_taggedpointer_slot_shift");
|
|
tryFindAndReadSymbol(TaggedPointerSlotMask,
|
|
"objc_debug_taggedpointer_slot_mask");
|
|
tryFindSymbol(TaggedPointerClassesAddr,
|
|
"objc_debug_taggedpointer_classes");
|
|
if (!TaggedPointerClassesAddr)
|
|
finish(TaggedPointerEncodingKind::Error);
|
|
TaggedPointerClasses = TaggedPointerClassesAddr.getAddressData();
|
|
|
|
// Extended tagged pointers don't exist on older OSes. Handle those
|
|
// by setting the variables to zero.
|
|
tryFindAndReadSymbolWithDefault(TaggedPointerExtendedMask,
|
|
"objc_debug_taggedpointer_ext_mask",
|
|
0);
|
|
tryFindAndReadSymbolWithDefault(TaggedPointerExtendedSlotShift,
|
|
"objc_debug_taggedpointer_ext_slot_shift",
|
|
0);
|
|
tryFindAndReadSymbolWithDefault(TaggedPointerExtendedSlotMask,
|
|
"objc_debug_taggedpointer_ext_slot_mask",
|
|
0);
|
|
auto TaggedPointerExtendedClassesAddr =
|
|
Reader->getSymbolAddress("objc_debug_taggedpointer_ext_classes");
|
|
if (TaggedPointerExtendedClassesAddr)
|
|
TaggedPointerExtendedClasses =
|
|
TaggedPointerExtendedClassesAddr.getAddressData();
|
|
|
|
// The tagged pointer obfuscator is not present on older OSes, in
|
|
// which case we can treat it as zero.
|
|
tryFindAndReadSymbolWithDefault(TaggedPointerObfuscator,
|
|
"objc_debug_taggedpointer_obfuscator",
|
|
0);
|
|
|
|
# undef tryFindSymbol
|
|
# undef tryReadSymbol
|
|
# undef tryFindAndReadSymbol
|
|
# undef tryFindAndReadSymbolWithDefault
|
|
|
|
return finish(TaggedPointerEncodingKind::Extended);
|
|
}
|
|
};
|
|
|
|
} // end namespace remote
|
|
} // end namespace swift
|
|
|
|
namespace llvm {
|
|
template<typename T>
|
|
struct simplify_type<swift::remote::RemoteRef<T>> {
|
|
using SimpleType = const T *;
|
|
static SimpleType
|
|
getSimplifiedValue(swift::remote::RemoteRef<T> value) {
|
|
return value.getLocalBuffer();
|
|
}
|
|
};
|
|
}
|
|
|
|
#endif // SWIFT_REFLECTION_READER_H
|