//===--- 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/COFF.h" #include "llvm/BinaryFormat/MachO.h" #include "llvm/BinaryFormat/ELF.h" #include "llvm/Object/COFF.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 #include #include #include #include namespace { template 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; }; template struct ELFTraits; template <> struct ELFTraits { using Header = const struct llvm::ELF::Elf32_Ehdr; using Section = const struct llvm::ELF::Elf32_Shdr; using Offset = llvm::ELF::Elf32_Off; using Size = llvm::ELF::Elf32_Word; static constexpr unsigned char ELFClass = llvm::ELF::ELFCLASS32; }; template <> struct ELFTraits { using Header = const struct llvm::ELF::Elf64_Ehdr; using Section = const struct llvm::ELF::Elf64_Shdr; using Offset = llvm::ELF::Elf64_Off; using Size = llvm::ELF::Elf64_Xword; static constexpr unsigned char ELFClass = llvm::ELF::ELFCLASS64; }; } // namespace namespace swift { namespace reflection { using swift::remote::MemoryReader; using swift::remote::RemoteAddress; template class ReflectionContext : public remote::MetadataReader { using super = remote::MetadataReader; using super::readMetadata; using super::readObjCClassName; std::unordered_map Cache; /// All buffers we need to keep around long term. This will automatically free them /// when this object is destroyed. std::vector savedBuffers; std::vector> 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 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 bool readMachOSections(RemoteAddress ImageStart) { auto Buf = this->getReader().readBytes(ImageStart, sizeof(typename T::Header)); if (!Buf) return false; auto Header = reinterpret_cast(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(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; // Find the load command offset. auto loadCmdOffset = ImageStart.getAddressData() + Offset + sizeof(typename T::Header); // Read the load command. auto LoadCmdAddress = reinterpret_cast(loadCmdOffset); auto LoadCmdBuf = this->getReader().readBytes( RemoteAddress(LoadCmdAddress), sizeof(typename T::SegmentCmd)); auto LoadCmd = reinterpret_cast(LoadCmdBuf.get()); // The sections start immediately after the load command. unsigned NumSect = LoadCmd->nsects; auto SectAddress = reinterpret_cast(loadCmdOffset) + sizeof(typename T::SegmentCmd); auto Sections = this->getReader().readBytes( RemoteAddress(SectAddress), NumSect * sizeof(typename T::Section)); auto Slide = ImageStart.getAddressData() - Command->vmaddr; std::string Prefix = "__swift5"; uint64_t RangeStart = UINT64_MAX; uint64_t RangeEnd = UINT64_MAX; auto SectionsBuf = reinterpret_cast(Sections.get()); for (unsigned I = 0; I < NumSect; ++I) { auto S = reinterpret_cast( SectionsBuf + (I * sizeof(typename T::Section))); if (strncmp(S->sectname, Prefix.c_str(), strlen(Prefix.c_str())) != 0) continue; if (RangeStart == UINT64_MAX && RangeEnd == UINT64_MAX) { RangeStart = S->addr + Slide; RangeEnd = S->addr + S->size + Slide; continue; } RangeStart = std::min(RangeStart, (uint64_t)S->addr + Slide); RangeEnd = std::max(RangeEnd, (uint64_t)(S->addr + S->size + Slide)); } if (RangeStart == UINT64_MAX && RangeEnd == UINT64_MAX) return false; auto SectBuf = this->getReader().readBytes(RemoteAddress(RangeStart), RangeEnd - RangeStart); auto findMachOSectionByName = [&](std::string Name) -> std::pair, uint64_t> { for (unsigned I = 0; I < NumSect; ++I) { auto S = reinterpret_cast( SectionsBuf + (I * sizeof(typename T::Section))); if (strncmp(S->sectname, Name.c_str(), strlen(Name.c_str())) != 0) continue; auto RemoteSecStart = S->addr + Slide; auto SectBufData = reinterpret_cast(SectBuf.get()); auto LocalSectStart = reinterpret_cast(SectBufData + RemoteSecStart - RangeStart); auto LocalSectEnd = reinterpret_cast(LocalSectStart + S->size); return {{LocalSectStart, LocalSectEnd}, 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(SectBuf.get()); auto RemoteStartAddress = static_cast(RangeStart); 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(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)); savedBuffers.push_back(std::move(SectBuf)); savedBuffers.push_back(std::move(Sections)); 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(Buf.get()); if (*HeaderMagic == llvm::MachO::MH_MAGIC) return readMachOSections>(ImageStart); if (*HeaderMagic == llvm::MachO::MH_MAGIC_64) return readMachOSections>(ImageStart); return false; } #elif defined(_WIN32) bool addImage(RemoteAddress ImageStart) { auto DOSHdrBuf = this->getReader().readBytes( ImageStart, sizeof(llvm::object::dos_header)); auto DOSHdr = reinterpret_cast(DOSHdrBuf.get()); auto COFFFileHdrAddr = ImageStart.getAddressData() + DOSHdr->AddressOfNewExeHeader + sizeof(llvm::COFF::PEMagic); auto COFFFileHdrBuf = this->getReader().readBytes( RemoteAddress(COFFFileHdrAddr), sizeof(llvm::object::coff_file_header)); auto COFFFileHdr = reinterpret_cast( COFFFileHdrBuf.get()); auto SectionTableAddr = COFFFileHdrAddr + sizeof(llvm::object::coff_file_header) + COFFFileHdr->SizeOfOptionalHeader; auto SectionTableBuf = this->getReader().readBytes( RemoteAddress(SectionTableAddr), sizeof(llvm::object::coff_section) * COFFFileHdr->NumberOfSections); auto findCOFFSectionByName = [&](llvm::StringRef Name) -> std::pair, uint32_t> { for (size_t i = 0; i < COFFFileHdr->NumberOfSections; ++i) { const llvm::object::coff_section *COFFSec = reinterpret_cast( SectionTableBuf.get()) + i; llvm::StringRef SectionName = (COFFSec->Name[llvm::COFF::NameSize - 1] == 0) ? COFFSec->Name : llvm::StringRef(COFFSec->Name, llvm::COFF::NameSize); if (SectionName != Name) continue; auto Addr = ImageStart.getAddressData() + COFFSec->PointerToRawData; auto Buf = this->getReader().readBytes(RemoteAddress(Addr), COFFSec->VirtualSize); const char *Begin = reinterpret_cast(Buf.get()); const char *End = Begin + COFFSec->VirtualSize; savedBuffers.push_back(std::move(Buf)); return {{Begin, End}, COFFSec->VirtualAddress - COFFSec->PointerToRawData}; } return {{nullptr, nullptr}, 0}; }; std::pair, uint32_t> CaptureSec = findCOFFSectionByName(".sw5cptr"); std::pair, uint32_t> TypeRefMdSec = findCOFFSectionByName(".sw5tyrf"); // FIXME: Make use of .sw5flmd section (the section content appears to be // incorrect on Windows at the moment). std::pair, uint32_t> FieldMdSec = { {nullptr, nullptr}, 0}; // FIXME: Make use of .sw5asty. std::pair, uint32_t> AssocTySec = { {nullptr, nullptr}, 0}; // FIXME: Make use of .sw5bltn. std::pair, uint32_t> BuiltinTySec = { {nullptr, nullptr}, 0}; // FIXME: Make use of .sw5repl. std::pair, uint32_t> ReflStrMdSec = { {nullptr, nullptr}, 0}; 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(DOSHdrBuf.get()); auto RemoteStartAddress = static_cast(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); return true; } #else // ELF platforms. template bool readELFSections(RemoteAddress ImageStart) { auto Buf = this->getReader().readBytes(ImageStart, sizeof(typename T::Header)); auto Hdr = reinterpret_cast(Buf.get()); assert(Hdr->getFileClass() == T::ELFClass && "invalid ELF file class"); // 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 SecHdrVec; for (unsigned I = 0; I < SectionHdrNumEntries; ++I) { auto SecBuf = this->getReader().readBytes( RemoteAddress(SectionHdrAddress + (I * SectionEntrySize)), SectionEntrySize); auto SecHdr = reinterpret_cast(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 typename T::Section *SecHdrStrTab = SecHdrVec[SecIdx]; typename T::Offset StrTabOffset = SecHdrStrTab->sh_offset; typename T::Size StrTabSize = SecHdrStrTab->sh_size; auto StrTabStart = RemoteAddress(ImageStart.getAddressData() + StrTabOffset); auto StrTabBuf = this->getReader().readBytes(StrTabStart, StrTabSize); auto StrTab = reinterpret_cast(StrTabBuf.get()); auto findELFSectionByName = [&](std::string Name) -> std::pair, uint64_t> { // Now for all the sections, find their name. for (const typename T::Section *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(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(Buf.get()); auto RemoteStartAddress = static_cast(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; } bool addImage(RemoteAddress ImageStart) { auto Buf = this->getReader().readBytes(ImageStart, sizeof(llvm::ELF::Elf64_Ehdr)); // Read the header. auto Hdr = reinterpret_cast(Buf.get()); if (!Hdr->checkMagic()) return false; // Check if we have a ELFCLASS32 or ELFCLASS64 unsigned char FileClass = Hdr->getFileClass(); if (FileClass == llvm::ELF::ELFCLASS64) { return readELFSections>(ImageStart); } else if (FileClass == llvm::ELF::ELFCLASS32) { return readELFSections>(ImageStart); } else { return false; } } #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>(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(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; auto InstanceTR = readTypeFromMetadata( OptMetaAndValue->MetadataAddress.getAddressData()); if (!InstanceTR) return false; *OutInstanceTR = InstanceTR; *OutInstanceAddress = OptMetaAndValue->PayloadAddress; return true; } case RecordKind::ErrorExistential: { auto OptMetaAndValue = readMetadataAndValueErrorExistential(ExistentialAddress); if (!OptMetaAndValue) return false; // FIXME: Check third value, 'IsBridgedError' auto InstanceTR = readTypeFromMetadata( OptMetaAndValue->MetadataAddress.getAddressData()); if (!InstanceTR) return false; *OutInstanceTR = InstanceTR; *OutInstanceAddress = OptMetaAndValue->PayloadAddress; 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, /*alignment=*/sizeof(StoredPointer), /*numExtraInhabitants=*/0, /*bitwiseTakable=*/true); // Skip the closure's necessary bindings struct, if it's present. auto SizeOfNecessaryBindings = Info.NumBindings * sizeof(StoredPointer); Builder.addField(/*size=*/SizeOfNecessaryBindings, /*alignment=*/sizeof(StoredPointer), /*numExtraInhabitants=*/0, /*bitwiseTakable=*/true); // FIXME: should be unordered_set but I'm too lazy to write a hash // functor std::set> Done; GenericArgumentMap Subs; ArrayRef 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(MS)->getIndex(); return Index < Builder.getNumFields(); } case MetadataSourceKind::MetadataCapture: { unsigned Index = cast(MS)->getIndex(); return Index < Builder.getNumFields(); } case MetadataSourceKind::GenericArgument: { auto Base = cast(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 readMetadataSource(StoredPointer Context, const MetadataSource *MS, const RecordTypeInfoBuilder &Builder) { switch (MS->getKind()) { case MetadataSourceKind::ClosureBinding: { unsigned Index = cast(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(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(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(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