| //===--- ReflectionContext.h - Swift Type Reflection Context ----*- 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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Implements the context for reflection of values in the address space of a |
| // remote process. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef SWIFT_REFLECTION_REFLECTIONCONTEXT_H |
| #define SWIFT_REFLECTION_REFLECTIONCONTEXT_H |
| |
| #include "llvm/BinaryFormat/MachO.h" |
| #include "llvm/BinaryFormat/ELF.h" |
| |
| #include "swift/Remote/MemoryReader.h" |
| #include "swift/Remote/MetadataReader.h" |
| #include "swift/Reflection/Records.h" |
| #include "swift/Reflection/TypeLowering.h" |
| #include "swift/Reflection/TypeRef.h" |
| #include "swift/Reflection/TypeRefBuilder.h" |
| #include "swift/Runtime/Unreachable.h" |
| |
| #include <iostream> |
| #include <set> |
| #include <vector> |
| #include <unordered_map> |
| #include <utility> |
| |
| namespace { |
| |
| template <unsigned PointerSize> struct MachOTraits; |
| |
| template <> struct MachOTraits<4> { |
| using Header = const struct llvm::MachO::mach_header; |
| using SegmentCmd = const struct llvm::MachO::segment_command; |
| using Section = const struct llvm::MachO::section; |
| static constexpr size_t MagicNumber = llvm::MachO::MH_MAGIC; |
| }; |
| |
| template <> struct MachOTraits<8> { |
| using Header = const struct llvm::MachO::mach_header_64; |
| using SegmentCmd = const struct llvm::MachO::segment_command_64; |
| using Section = const struct llvm::MachO::section_64; |
| static constexpr size_t MagicNumber = llvm::MachO::MH_MAGIC_64; |
| }; |
| } // namespace |
| |
| namespace swift { |
| namespace reflection { |
| |
| using swift::remote::MemoryReader; |
| using swift::remote::RemoteAddress; |
| |
| template <typename Runtime> |
| class ReflectionContext |
| : public remote::MetadataReader<Runtime, TypeRefBuilder> { |
| using super = remote::MetadataReader<Runtime, TypeRefBuilder>; |
| using super::readMetadata; |
| using super::readObjCClassName; |
| |
| std::unordered_map<typename super::StoredPointer, const TypeInfo *> Cache; |
| |
| /// All buffers we need to keep around long term. This will automatically free them |
| /// when this object is destroyed. |
| std::vector<MemoryReader::ReadBytesResult> savedBuffers; |
| std::vector<std::tuple<RemoteAddress, RemoteAddress>> imageRanges; |
| |
| public: |
| using super::getBuilder; |
| using super::readDemanglingForContextDescriptor; |
| using super::readGenericArgFromMetadata; |
| using super::readIsaMask; |
| using super::readMetadataAndValueErrorExistential; |
| using super::readMetadataAndValueOpaqueExistential; |
| using super::readMetadataFromInstance; |
| using super::readTypeFromMetadata; |
| using typename super::StoredPointer; |
| |
| explicit ReflectionContext(std::shared_ptr<MemoryReader> reader) |
| : super(std::move(reader)) { |
| getBuilder().setSymbolicReferenceResolverReader(*this); |
| } |
| |
| ReflectionContext(const ReflectionContext &other) = delete; |
| ReflectionContext &operator=(const ReflectionContext &other) = delete; |
| |
| MemoryReader &getReader() { |
| return *this->Reader; |
| } |
| |
| unsigned getSizeOfHeapObject() { |
| // This must match sizeof(HeapObject) for the target. |
| return sizeof(StoredPointer) * 2; |
| } |
| |
| #if defined(__APPLE__) && defined(__MACH__) |
| template <typename T> bool readMachOSections(RemoteAddress ImageStart) { |
| auto Buf = |
| this->getReader().readBytes(ImageStart, sizeof(typename T::Header)); |
| if (!Buf) |
| return false; |
| auto Header = reinterpret_cast<typename T::Header *>(Buf.get()); |
| assert(Header->magic == T::MagicNumber && "invalid MachO file"); |
| |
| auto NumCommands = Header->sizeofcmds; |
| |
| // The layout of the executable is such that the commands immediately follow |
| // the header. |
| auto CmdStartAddress = |
| RemoteAddress(ImageStart.getAddressData() + sizeof(typename T::Header)); |
| uint32_t SegmentCmdHdrSize = sizeof(typename T::SegmentCmd); |
| uint64_t Offset = 0; |
| |
| // Find the __TEXT segment. |
| typename T::SegmentCmd *Command = nullptr; |
| for (unsigned I = 0; I < NumCommands; ++I) { |
| auto CmdBuf = this->getReader().readBytes( |
| RemoteAddress(CmdStartAddress.getAddressData() + Offset), |
| SegmentCmdHdrSize); |
| auto CmdHdr = reinterpret_cast<typename T::SegmentCmd *>(CmdBuf.get()); |
| if (strncmp(CmdHdr->segname, "__TEXT", sizeof(CmdHdr->segname)) == 0) { |
| Command = CmdHdr; |
| savedBuffers.push_back(std::move(CmdBuf)); |
| break; |
| } |
| Offset += CmdHdr->cmdsize; |
| } |
| |
| // No __TEXT segment, bail out. |
| if (!Command) |
| return false; |
| |
| // Read everything including the __TEXT segment. |
| Buf = this->getReader().readBytes(ImageStart, Command->vmsize); |
| auto Start = reinterpret_cast<const char *>(Buf.get()); |
| |
| auto findMachOSectionByName = [&](std::string Name) |
| -> std::pair<std::pair<const char *, const char *>, uint32_t> { |
| auto cmdOffset = Start + Offset + sizeof(typename T::Header); |
| auto SegCmd = reinterpret_cast<typename T::SegmentCmd *>(cmdOffset); |
| auto SectAddress = reinterpret_cast<const char *>(cmdOffset) + |
| sizeof(typename T::SegmentCmd); |
| for (unsigned I = 0; I < SegCmd->nsects; ++I) { |
| auto S = reinterpret_cast<typename T::Section *>( |
| SectAddress + (I * sizeof(typename T::Section))); |
| if (strncmp(S->sectname, Name.c_str(), strlen(Name.c_str())) != 0) |
| continue; |
| auto Slide = ImageStart.getAddressData() - Command->vmaddr; |
| auto RemoteSecStart = S->addr + Slide; |
| auto LocalSecStart = RemoteSecStart - ImageStart.getAddressData() + Start; |
| auto SecSize = S->size; |
| return {{LocalSecStart, LocalSecStart + SecSize}, 0}; |
| } |
| return {{nullptr, nullptr}, 0}; |
| }; |
| |
| auto FieldMdSec = findMachOSectionByName("__swift5_fieldmd"); |
| auto AssocTySec = findMachOSectionByName("__swift5_assocty"); |
| auto BuiltinTySec = findMachOSectionByName("__swift5_builtin"); |
| auto CaptureSec = findMachOSectionByName("__swift5_capture"); |
| auto TypeRefMdSec = findMachOSectionByName("__swift5_typeref"); |
| auto ReflStrMdSec = findMachOSectionByName("__swift5_reflstr"); |
| |
| if (FieldMdSec.first.first == nullptr && |
| AssocTySec.first.first == nullptr && |
| BuiltinTySec.first.first == nullptr && |
| CaptureSec.first.first == nullptr && |
| TypeRefMdSec.first.first == nullptr && |
| ReflStrMdSec.first.first == nullptr) |
| return false; |
| |
| auto LocalStartAddress = reinterpret_cast<uintptr_t>(Buf.get()); |
| auto RemoteStartAddress = static_cast<uintptr_t>(ImageStart.getAddressData()); |
| |
| ReflectionInfo info = { |
| {{FieldMdSec.first.first, FieldMdSec.first.second}, 0}, |
| {{AssocTySec.first.first, AssocTySec.first.second}, 0}, |
| {{BuiltinTySec.first.first, BuiltinTySec.first.second}, 0}, |
| {{CaptureSec.first.first, CaptureSec.first.second}, 0}, |
| {{TypeRefMdSec.first.first, TypeRefMdSec.first.second}, 0}, |
| {{ReflStrMdSec.first.first, ReflStrMdSec.first.second}, 0}, |
| LocalStartAddress, |
| RemoteStartAddress}; |
| |
| this->addReflectionInfo(info); |
| |
| // Find the __DATA segment. |
| for (unsigned I = 0; I < NumCommands; ++I) { |
| auto CmdBuf = this->getReader().readBytes( |
| RemoteAddress(CmdStartAddress.getAddressData() + Offset), |
| SegmentCmdHdrSize); |
| auto CmdHdr = reinterpret_cast<typename T::SegmentCmd *>(CmdBuf.get()); |
| if (strncmp(CmdHdr->segname, "__DATA", sizeof(CmdHdr->segname)) == 0) { |
| auto DataSegmentEnd = |
| ImageStart.getAddressData() + CmdHdr->vmaddr + CmdHdr->vmsize; |
| assert(DataSegmentEnd > ImageStart.getAddressData() && |
| "invalid range for __DATA"); |
| imageRanges.push_back( |
| std::make_tuple(ImageStart, RemoteAddress(DataSegmentEnd))); |
| break; |
| } |
| Offset += CmdHdr->cmdsize; |
| } |
| |
| savedBuffers.push_back(std::move(Buf)); |
| return true; |
| } |
| |
| bool addImage(RemoteAddress ImageStart) { |
| // We start reading 4 bytes. The first 4 bytes are supposed to be |
| // the magic, so we understand whether this is a 32-bit executable or |
| // a 64-bit one. |
| auto Buf = this->getReader().readBytes(ImageStart, sizeof(uint32_t)); |
| if (!Buf) |
| return false; |
| auto HeaderMagic = reinterpret_cast<const uint32_t *>(Buf.get()); |
| if (*HeaderMagic == llvm::MachO::MH_MAGIC) |
| return readMachOSections<MachOTraits<4>>(ImageStart); |
| if (*HeaderMagic == llvm::MachO::MH_MAGIC_64) |
| return readMachOSections<MachOTraits<8>>(ImageStart); |
| return false; |
| } |
| #else // ELF platforms. |
| bool addImage(RemoteAddress ImageStart) { |
| auto Buf = |
| this->getReader().readBytes(ImageStart, sizeof(llvm::ELF::Elf64_Ehdr)); |
| |
| // Read the header. |
| auto Hdr = reinterpret_cast<const llvm::ELF::Elf64_Ehdr *>(Buf.get()); |
| |
| if (!Hdr->checkMagic()) |
| return false; |
| |
| // From the header, grab informations about the section header table. |
| auto SectionHdrAddress = ImageStart.getAddressData() + Hdr->e_shoff; |
| auto SectionHdrNumEntries = Hdr->e_shnum; |
| auto SectionEntrySize = Hdr->e_shentsize; |
| |
| // Collect all the section headers, we need them to look up the |
| // reflection sections (by name) and the string table. |
| std::vector<const llvm::ELF::Elf64_Shdr *> SecHdrVec; |
| for (unsigned I = 0; I < SectionHdrNumEntries; ++I) { |
| auto SecBuf = this->getReader().readBytes( |
| RemoteAddress(SectionHdrAddress + (I * SectionEntrySize)), |
| SectionEntrySize); |
| auto SecHdr = |
| reinterpret_cast<const llvm::ELF::Elf64_Shdr *>(SecBuf.get()); |
| SecHdrVec.push_back(SecHdr); |
| } |
| |
| // This provides quick access to the section header string table index. |
| // We also here handle the unlikely even where the section index overflows |
| // and it's just a pointer to secondary storage (SHN_XINDEX). |
| uint32_t SecIdx = Hdr->e_shstrndx; |
| if (SecIdx == llvm::ELF::SHN_XINDEX) { |
| assert(!SecHdrVec.empty() && "malformed ELF object"); |
| SecIdx = SecHdrVec[0]->sh_link; |
| } |
| |
| assert(SecIdx < SecHdrVec.size() && "malformed ELF object"); |
| |
| const llvm::ELF::Elf64_Shdr *SecHdrStrTab = SecHdrVec[SecIdx]; |
| llvm::ELF::Elf64_Off StrTabOffset = SecHdrStrTab->sh_offset; |
| llvm::ELF::Elf64_Xword StrTabSize = SecHdrStrTab->sh_size; |
| |
| auto StrTabStart = |
| RemoteAddress(ImageStart.getAddressData() + StrTabOffset); |
| auto StrTabBuf = this->getReader().readBytes(StrTabStart, StrTabSize); |
| auto StrTab = reinterpret_cast<const char *>(StrTabBuf.get()); |
| |
| auto findELFSectionByName = [&](std::string Name) |
| -> std::pair<std::pair<const char *, const char *>, uint32_t> { |
| // Now for all the sections, find their name. |
| for (const llvm::ELF::Elf64_Shdr *Hdr : SecHdrVec) { |
| uint32_t Offset = Hdr->sh_name; |
| auto SecName = std::string(StrTab + Offset); |
| if (SecName != Name) |
| continue; |
| auto SecStart = |
| RemoteAddress(ImageStart.getAddressData() + Hdr->sh_offset); |
| auto SecSize = Hdr->sh_size; |
| auto SecBuf = this->getReader().readBytes(SecStart, SecSize); |
| auto SecContents = reinterpret_cast<const char *>(SecBuf.get()); |
| return {{SecContents, SecContents + SecSize}, |
| Hdr->sh_addr - Hdr->sh_offset}; |
| } |
| return {{nullptr, nullptr}, 0}; |
| }; |
| |
| auto FieldMdSec = findELFSectionByName("swift5_fieldmd"); |
| auto AssocTySec = findELFSectionByName("swift5_assocty"); |
| auto BuiltinTySec = findELFSectionByName("swift5_builtin"); |
| auto CaptureSec = findELFSectionByName("swift5_capture"); |
| auto TypeRefMdSec = findELFSectionByName("swift5_typeref"); |
| auto ReflStrMdSec = findELFSectionByName("swift5_reflstr"); |
| |
| // We succeed if at least one of the sections is present in the |
| // ELF executable. |
| if (FieldMdSec.first.first == nullptr && |
| AssocTySec.first.first == nullptr && |
| BuiltinTySec.first.first == nullptr && |
| CaptureSec.first.first == nullptr && |
| TypeRefMdSec.first.first == nullptr && |
| ReflStrMdSec.first.first == nullptr) |
| return false; |
| |
| auto LocalStartAddress = reinterpret_cast<uintptr_t>(Buf.get()); |
| auto RemoteStartAddress = |
| static_cast<uintptr_t>(ImageStart.getAddressData()); |
| |
| ReflectionInfo info = { |
| {{FieldMdSec.first.first, FieldMdSec.first.second}, FieldMdSec.second}, |
| {{AssocTySec.first.first, AssocTySec.first.second}, AssocTySec.second}, |
| {{BuiltinTySec.first.first, BuiltinTySec.first.second}, |
| BuiltinTySec.second}, |
| {{CaptureSec.first.first, CaptureSec.first.second}, CaptureSec.second}, |
| {{TypeRefMdSec.first.first, TypeRefMdSec.first.second}, |
| TypeRefMdSec.second}, |
| {{ReflStrMdSec.first.first, ReflStrMdSec.first.second}, |
| ReflStrMdSec.second}, |
| LocalStartAddress, |
| RemoteStartAddress}; |
| |
| this->addReflectionInfo(info); |
| |
| savedBuffers.push_back(std::move(Buf)); |
| return true; |
| } |
| #endif |
| |
| void addReflectionInfo(ReflectionInfo I) { |
| getBuilder().addReflectionInfo(I); |
| } |
| |
| bool ownsObject(RemoteAddress ObjectAddress) { |
| auto MetadataAddress = readMetadataFromInstance(ObjectAddress.getAddressData()); |
| if (!MetadataAddress) |
| return true; |
| return ownsAddress(RemoteAddress(*MetadataAddress)); |
| } |
| |
| /// Returns true if the address falls within a registered image. |
| bool ownsAddress(RemoteAddress Address) { |
| for (auto Range : imageRanges) { |
| auto Start = std::get<0>(Range); |
| auto End = std::get<1>(Range); |
| if (Start.getAddressData() <= Address.getAddressData() |
| && Address.getAddressData() < End.getAddressData()) |
| return true; |
| } |
| |
| return false; |
| } |
| |
| /// Return a description of the layout of a class instance with the given |
| /// metadata as its isa pointer. |
| const TypeInfo *getMetadataTypeInfo(StoredPointer MetadataAddress) { |
| // See if we cached the layout already |
| auto found = Cache.find(MetadataAddress); |
| if (found != Cache.end()) |
| return found->second; |
| |
| auto &TC = getBuilder().getTypeConverter(); |
| |
| const TypeInfo *TI = nullptr; |
| |
| auto TR = readTypeFromMetadata(MetadataAddress); |
| auto kind = this->readKindFromMetadata(MetadataAddress); |
| if (TR != nullptr && kind) { |
| switch (*kind) { |
| case MetadataKind::Class: { |
| // Figure out where the stored properties of this class begin |
| // by looking at the size of the superclass |
| auto start = |
| this->readInstanceStartAndAlignmentFromClassMetadata(MetadataAddress); |
| |
| // Perform layout |
| if (start) |
| TI = TC.getClassInstanceTypeInfo(TR, *start); |
| |
| break; |
| } |
| default: |
| break; |
| } |
| } |
| |
| // Cache the result for future lookups |
| Cache[MetadataAddress] = TI; |
| return TI; |
| } |
| |
| /// Return a description of the layout of a class instance with the given |
| /// metadata as its isa pointer. |
| const TypeInfo *getInstanceTypeInfo(StoredPointer ObjectAddress) { |
| auto MetadataAddress = readMetadataFromInstance(ObjectAddress); |
| if (!MetadataAddress) |
| return nullptr; |
| |
| auto kind = this->readKindFromMetadata(*MetadataAddress); |
| if (!kind) |
| return nullptr; |
| |
| switch (*kind) { |
| case MetadataKind::Class: |
| return getMetadataTypeInfo(*MetadataAddress); |
| |
| case MetadataKind::HeapLocalVariable: { |
| auto CDAddr = this->readCaptureDescriptorFromMetadata(*MetadataAddress); |
| if (!CDAddr) |
| return nullptr; |
| |
| // FIXME: Non-generic SIL boxes also use the HeapLocalVariable metadata |
| // kind, but with a null capture descriptor right now (see |
| // FixedBoxTypeInfoBase::allocate). |
| // |
| // Non-generic SIL boxes share metadata among types with compatible |
| // layout, but we need some way to get an outgoing pointer map for them. |
| auto *CD = getBuilder().getCaptureDescriptor(*CDAddr); |
| if (CD == nullptr) |
| return nullptr; |
| |
| auto Info = getBuilder().getClosureContextInfo(*CD, 0); |
| |
| return getClosureContextInfo(ObjectAddress, Info); |
| } |
| |
| case MetadataKind::HeapGenericLocalVariable: { |
| // Generic SIL @box type - there is always an instantiated metadata |
| // pointer for the boxed type. |
| if (auto Meta = readMetadata(*MetadataAddress)) { |
| auto GenericHeapMeta = |
| cast<TargetGenericBoxHeapMetadata<Runtime>>(Meta.getLocalBuffer()); |
| return getMetadataTypeInfo(GenericHeapMeta->BoxedType); |
| } |
| return nullptr; |
| } |
| |
| case MetadataKind::ErrorObject: |
| // Error boxed existential on non-Objective-C runtime target |
| return nullptr; |
| |
| default: |
| return nullptr; |
| } |
| } |
| |
| bool |
| projectExistential(RemoteAddress ExistentialAddress, |
| const TypeRef *ExistentialTR, |
| const TypeRef **OutInstanceTR, |
| RemoteAddress *OutInstanceAddress) { |
| if (ExistentialTR == nullptr) |
| return false; |
| |
| auto ExistentialTI = getTypeInfo(ExistentialTR); |
| if (ExistentialTI == nullptr) |
| return false; |
| |
| auto ExistentialRecordTI = dyn_cast<const RecordTypeInfo>(ExistentialTI); |
| if (ExistentialRecordTI == nullptr) |
| return false; |
| |
| switch (ExistentialRecordTI->getRecordKind()) { |
| // Class existentials have trivial layout. |
| // It is itself the pointer to the instance followed by the witness tables. |
| case RecordKind::ClassExistential: |
| // This is just Builtin.UnknownObject |
| *OutInstanceTR = ExistentialRecordTI->getFields()[0].TR; |
| *OutInstanceAddress = ExistentialAddress; |
| return true; |
| |
| case RecordKind::OpaqueExistential: { |
| auto OptMetaAndValue = |
| readMetadataAndValueOpaqueExistential(ExistentialAddress); |
| if (!OptMetaAndValue) |
| return false; |
| RemoteAddress MetadataAddress = OptMetaAndValue->first; |
| RemoteAddress ValueAddress = OptMetaAndValue->second; |
| |
| auto InstanceTR = readTypeFromMetadata(MetadataAddress.getAddressData()); |
| if (!InstanceTR) |
| return false; |
| |
| *OutInstanceTR = InstanceTR; |
| *OutInstanceAddress = ValueAddress; |
| return true; |
| } |
| case RecordKind::ErrorExistential: { |
| auto OptMetaAndValue = |
| readMetadataAndValueErrorExistential(ExistentialAddress); |
| if (!OptMetaAndValue) |
| return false; |
| |
| RemoteAddress InstanceMetadataAddress = OptMetaAndValue->first; |
| RemoteAddress InstanceAddress = OptMetaAndValue->second; |
| |
| auto InstanceTR = |
| readTypeFromMetadata(InstanceMetadataAddress.getAddressData()); |
| if (!InstanceTR) |
| return false; |
| |
| *OutInstanceTR = InstanceTR; |
| *OutInstanceAddress = RemoteAddress(InstanceAddress); |
| return true; |
| } |
| default: |
| return false; |
| } |
| } |
| |
| /// Return a description of the layout of a value with the given type. |
| const TypeInfo *getTypeInfo(const TypeRef *TR) { |
| return getBuilder().getTypeConverter().getTypeInfo(TR); |
| } |
| |
| private: |
| const TypeInfo *getClosureContextInfo(StoredPointer Context, |
| const ClosureContextInfo &Info) { |
| RecordTypeInfoBuilder Builder(getBuilder().getTypeConverter(), |
| RecordKind::ClosureContext); |
| |
| auto Metadata = readMetadataFromInstance(Context); |
| if (!Metadata) |
| return nullptr; |
| |
| // Calculate the offset of the first capture. |
| // See GenHeap.cpp, buildPrivateMetadata(). |
| auto OffsetToFirstCapture = |
| this->readOffsetToFirstCaptureFromMetadata(*Metadata); |
| if (!OffsetToFirstCapture) |
| return nullptr; |
| |
| // Initialize the builder. |
| Builder.addField(*OffsetToFirstCapture, sizeof(StoredPointer), |
| /*numExtraInhabitants=*/0); |
| |
| // Skip the closure's necessary bindings struct, if it's present. |
| auto SizeOfNecessaryBindings = Info.NumBindings * sizeof(StoredPointer); |
| Builder.addField(SizeOfNecessaryBindings, sizeof(StoredPointer), |
| /*numExtraInhabitants=*/0); |
| |
| // FIXME: should be unordered_set but I'm too lazy to write a hash |
| // functor |
| std::set<std::pair<const TypeRef *, const MetadataSource *>> Done; |
| GenericArgumentMap Subs; |
| |
| ArrayRef<const TypeRef *> CaptureTypes = Info.CaptureTypes; |
| |
| // Closure context element layout depends on the layout of the |
| // captured types, but captured types might depend on element |
| // layout (of previous elements). Use an iterative approach to |
| // solve the problem. |
| while (!CaptureTypes.empty()) { |
| const TypeRef *OrigCaptureTR = CaptureTypes[0]; |
| |
| // If we failed to demangle the capture type, we cannot proceed. |
| if (OrigCaptureTR == nullptr) |
| return nullptr; |
| |
| const TypeRef *SubstCaptureTR = nullptr; |
| |
| // If we have enough substitutions to make this captured value's |
| // type concrete, or we know it's size anyway (because it is a |
| // class reference or metatype, for example), go ahead and add |
| // it to the layout. |
| if (OrigCaptureTR->isConcreteAfterSubstitutions(Subs)) |
| SubstCaptureTR = OrigCaptureTR->subst(getBuilder(), Subs); |
| else if (getBuilder().getTypeConverter().hasFixedSize(OrigCaptureTR)) |
| SubstCaptureTR = OrigCaptureTR; |
| |
| if (SubstCaptureTR != nullptr) { |
| Builder.addField("", SubstCaptureTR); |
| if (Builder.isInvalid()) |
| return nullptr; |
| |
| // Try the next capture type. |
| CaptureTypes = CaptureTypes.slice(1); |
| continue; |
| } |
| |
| // Ok, we do not have enough substitutions yet. Perhaps we have |
| // enough elements figured out that we can pick off some |
| // metadata sources though, and use those to derive some new |
| // substitutions. |
| bool Progress = false; |
| for (auto Source : Info.MetadataSources) { |
| // Don't read a source more than once. |
| if (Done.count(Source)) |
| continue; |
| |
| // If we don't have enough information to read this source |
| // (because it is fulfilled by metadata from a capture at |
| // at unknown offset), keep going. |
| if (!isMetadataSourceReady(Source.second, Builder)) |
| continue; |
| |
| auto Metadata = readMetadataSource(Context, Source.second, Builder); |
| if (!Metadata) |
| return nullptr; |
| |
| auto *SubstTR = readTypeFromMetadata(*Metadata); |
| if (SubstTR == nullptr) |
| return nullptr; |
| |
| if (!TypeRef::deriveSubstitutions(Subs, Source.first, SubstTR)) |
| return nullptr; |
| |
| Done.insert(Source); |
| Progress = true; |
| } |
| |
| // If we failed to make any forward progress above, we're stuck |
| // and cannot close out this layout. |
| if (!Progress) |
| return nullptr; |
| } |
| |
| // Ok, we have a complete picture now. |
| return Builder.build(); |
| } |
| |
| /// Checks if we have enough information to read the given metadata |
| /// source. |
| /// |
| /// \param Builder Used to obtain offsets of elements known so far. |
| bool isMetadataSourceReady(const MetadataSource *MS, |
| const RecordTypeInfoBuilder &Builder) { |
| switch (MS->getKind()) { |
| case MetadataSourceKind::ClosureBinding: |
| return true; |
| case MetadataSourceKind::ReferenceCapture: { |
| unsigned Index = cast<ReferenceCaptureMetadataSource>(MS)->getIndex(); |
| return Index < Builder.getNumFields(); |
| } |
| case MetadataSourceKind::MetadataCapture: { |
| unsigned Index = cast<MetadataCaptureMetadataSource>(MS)->getIndex(); |
| return Index < Builder.getNumFields(); |
| } |
| case MetadataSourceKind::GenericArgument: { |
| auto Base = cast<GenericArgumentMetadataSource>(MS)->getSource(); |
| return isMetadataSourceReady(Base, Builder); |
| } |
| case MetadataSourceKind::Self: |
| case MetadataSourceKind::SelfWitnessTable: |
| return true; |
| } |
| |
| swift_runtime_unreachable("Unhandled MetadataSourceKind in switch."); |
| } |
| |
| /// Read metadata for a captured generic type from a closure context. |
| /// |
| /// \param Context The closure context in the remote process. |
| /// |
| /// \param MS The metadata source, which must be "ready" as per the |
| /// above. |
| /// |
| /// \param Builder Used to obtain offsets of elements known so far. |
| llvm::Optional<StoredPointer> |
| readMetadataSource(StoredPointer Context, |
| const MetadataSource *MS, |
| const RecordTypeInfoBuilder &Builder) { |
| switch (MS->getKind()) { |
| case MetadataSourceKind::ClosureBinding: { |
| unsigned Index = cast<ClosureBindingMetadataSource>(MS)->getIndex(); |
| |
| // Skip the context's HeapObject header |
| // (one word each for isa pointer and reference counts). |
| // |
| // Metadata and conformance tables are stored consecutively after |
| // the heap object header, in the 'necessary bindings' area. |
| // |
| // We should only have the index of a type metadata record here. |
| unsigned Offset = getSizeOfHeapObject() + |
| sizeof(StoredPointer) * Index; |
| |
| StoredPointer MetadataAddress; |
| if (!getReader().readInteger(RemoteAddress(Context + Offset), |
| &MetadataAddress)) |
| break; |
| |
| return MetadataAddress; |
| } |
| case MetadataSourceKind::ReferenceCapture: { |
| unsigned Index = cast<ReferenceCaptureMetadataSource>(MS)->getIndex(); |
| |
| // We should already have enough type information to know the offset |
| // of this capture in the context. |
| unsigned CaptureOffset = Builder.getFieldOffset(Index); |
| |
| StoredPointer CaptureAddress; |
| if (!getReader().readInteger(RemoteAddress(Context + CaptureOffset), |
| &CaptureAddress)) |
| break; |
| |
| // Read the requested capture's isa pointer. |
| return readMetadataFromInstance(CaptureAddress); |
| } |
| case MetadataSourceKind::MetadataCapture: { |
| unsigned Index = cast<MetadataCaptureMetadataSource>(MS)->getIndex(); |
| |
| // We should already have enough type information to know the offset |
| // of this capture in the context. |
| unsigned CaptureOffset = Builder.getFieldOffset(Index); |
| |
| StoredPointer CaptureAddress; |
| if (!getReader().readInteger(RemoteAddress(Context + CaptureOffset), |
| &CaptureAddress)) |
| break; |
| |
| return CaptureAddress; |
| } |
| case MetadataSourceKind::GenericArgument: { |
| auto *GAMS = cast<GenericArgumentMetadataSource>(MS); |
| auto Base = readMetadataSource(Context, GAMS->getSource(), Builder); |
| if (!Base) |
| break; |
| |
| unsigned Index = GAMS->getIndex(); |
| auto Arg = readGenericArgFromMetadata(*Base, Index); |
| if (!Arg) |
| break; |
| |
| return *Arg; |
| } |
| case MetadataSourceKind::Self: |
| case MetadataSourceKind::SelfWitnessTable: |
| break; |
| } |
| |
| return llvm::None; |
| } |
| }; |
| |
| } // end namespace reflection |
| } // end namespace swift |
| |
| #endif // SWIFT_REFLECTION_REFLECTIONCONTEXT_H |