//===--- MetadataLookup.cpp - Swift Language Type Name Lookup -------------===// // // 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 // //===----------------------------------------------------------------------===// // // Implementations of runtime functions for looking up a type by name. // //===----------------------------------------------------------------------===// #include "swift/Basic/LLVM.h" #include "swift/Basic/Lazy.h" #include "swift/Demangling/Demangler.h" #include "swift/Runtime/Concurrent.h" #include "swift/Runtime/HeapObject.h" #include "swift/Runtime/Metadata.h" #include "swift/Runtime/Mutex.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/PointerIntPair.h" #include "llvm/ADT/StringExtras.h" #include "Private.h" #include "ImageInspection.h" using namespace swift; using namespace Demangle; #if SWIFT_OBJC_INTEROP #include #include #include #endif // Type Metadata Cache. namespace { struct TypeMetadataSection { const TypeMetadataRecord *Begin, *End; const TypeMetadataRecord *begin() const { return Begin; } const TypeMetadataRecord *end() const { return End; } }; struct TypeMetadataCacheEntry { private: std::string Name; const Metadata *Metadata; public: TypeMetadataCacheEntry(const llvm::StringRef name, const ::Metadata *metadata) : Name(name.str()), Metadata(metadata) {} const ::Metadata *getMetadata(void) { return Metadata; } int compareWithKey(llvm::StringRef aName) const { return aName.compare(Name); } template static size_t getExtraAllocationSize(T &&... ignored) { return 0; } }; } // end anonymous namespace struct TypeMetadataState { ConcurrentMap Cache; std::vector SectionsToScan; Mutex SectionsToScanLock; TypeMetadataState() { SectionsToScan.reserve(16); initializeTypeMetadataRecordLookup(); } }; static Lazy TypeMetadataRecords; static void _registerTypeMetadataRecords(TypeMetadataState &T, const TypeMetadataRecord *begin, const TypeMetadataRecord *end) { ScopedLock guard(T.SectionsToScanLock); T.SectionsToScan.push_back(TypeMetadataSection{begin, end}); } void swift::addImageTypeMetadataRecordBlockCallback(const void *records, uintptr_t recordsSize) { assert(recordsSize % sizeof(TypeMetadataRecord) == 0 && "weird-sized type metadata section?!"); // If we have a section, enqueue the type metadata for lookup. auto recordBytes = reinterpret_cast(records); auto recordsBegin = reinterpret_cast(records); auto recordsEnd = reinterpret_cast(recordBytes + recordsSize); // Type metadata cache should always be sufficiently initialized by this // point. Attempting to go through get() may also lead to an infinite loop, // since we register records during the initialization of // TypeMetadataRecords. _registerTypeMetadataRecords(TypeMetadataRecords.unsafeGetAlreadyInitialized(), recordsBegin, recordsEnd); } void swift::swift_registerTypeMetadataRecords(const TypeMetadataRecord *begin, const TypeMetadataRecord *end) { auto &T = TypeMetadataRecords.get(); _registerTypeMetadataRecords(T, begin, end); } // copied from ProtocolConformanceRecord::getCanonicalTypeMetadata() template<> const Metadata *TypeMetadataRecord::getCanonicalTypeMetadata() const { switch (getTypeKind()) { case TypeMetadataRecordKind::UniqueDirectType: return getDirectType(); case TypeMetadataRecordKind::NonuniqueDirectType: return swift_getForeignTypeMetadata((ForeignTypeMetadata *)getDirectType()); case TypeMetadataRecordKind::UniqueDirectClass: if (auto *ClassMetadata = static_cast(getDirectType())) return swift_getObjCClassMetadata(ClassMetadata); else return nullptr; default: return nullptr; } } // returns the type metadata for the type named by typeNode const Metadata * swift::_matchMetadataByMangledTypeName(const llvm::StringRef typeName, const Metadata *metadata, const NominalTypeDescriptor *ntd) { if (metadata != nullptr) { assert(ntd == nullptr); ntd = metadata->getNominalTypeDescriptor(); } if (ntd == nullptr || ntd->Name.get() != typeName) return nullptr; // Call the accessor if there is one. if (metadata == nullptr && !ntd->GenericParams.isGeneric()) { if (auto accessFn = ntd->getAccessFunction()) metadata = accessFn(); } return metadata; } // returns the type metadata for the type named by typeName static const Metadata * _searchTypeMetadataRecords(const TypeMetadataState &T, const llvm::StringRef typeName) { unsigned sectionIdx = 0; unsigned endSectionIdx = T.SectionsToScan.size(); const Metadata *foundMetadata = nullptr; for (; sectionIdx < endSectionIdx; ++sectionIdx) { auto §ion = T.SectionsToScan[sectionIdx]; for (const auto &record : section) { if (auto metadata = record.getCanonicalTypeMetadata()) foundMetadata = _matchMetadataByMangledTypeName(typeName, metadata, nullptr); else if (auto ntd = record.getNominalTypeDescriptor()) foundMetadata = _matchMetadataByMangledTypeName(typeName, nullptr, ntd); if (foundMetadata != nullptr) return foundMetadata; } } return nullptr; } static const Metadata * _classByName(const llvm::StringRef typeName) { size_t DotPos = typeName.find('.'); if (DotPos == llvm::StringRef::npos) return nullptr; if (typeName.find('.', DotPos + 1) != llvm::StringRef::npos) return nullptr; Demangle::NodeFactory Factory; NodePointer ClassNd = Factory.createNode(Node::Kind::Class); NodePointer ModuleNd = Factory.createNode(Node::Kind::Module, typeName.substr(0, DotPos)); NodePointer NameNd = Factory.createNode(Node::Kind::Identifier, typeName.substr(DotPos + 1)); ClassNd->addChild(ModuleNd, Factory); ClassNd->addChild(NameNd, Factory); std::string Mangled = mangleNode(ClassNd); StringRef MangledName = Mangled; const Metadata *foundMetadata = nullptr; auto &T = TypeMetadataRecords.get(); // Look for an existing entry. // Find the bucket for the metadata entry. if (auto Value = T.Cache.find(MangledName)) return Value->getMetadata(); // Check type metadata records T.SectionsToScanLock.withLock([&] { foundMetadata = _searchTypeMetadataRecords(T, MangledName); }); // Check protocol conformances table. Note that this has no support for // resolving generic types yet. if (!foundMetadata) foundMetadata = _searchConformancesByMangledTypeName(MangledName); if (foundMetadata) { T.Cache.getOrInsert(MangledName, foundMetadata); } #if SWIFT_OBJC_INTEROP // Check for ObjC class // FIXME does this have any value? any ObjC class with a Swift name // should already be registered as a Swift type. if (foundMetadata == nullptr) { std::string prefixedName("_Tt" + typeName.str()); foundMetadata = reinterpret_cast (objc_lookUpClass(prefixedName.c_str())); } #endif return foundMetadata; } /// Return the type metadata for a given name, used in the /// implementation of _typeByName(). /// /// Currently only top-level classes are supported. /// \param typeName The name of a class in the form: . /// \return Returns the metadata of the type, if found. SWIFT_CC(swift) SWIFT_RUNTIME_EXPORT const Metadata * swift_getTypeByName(const char *typeName, size_t typeNameLength) { llvm::StringRef name(typeName, typeNameLength); return _classByName(name); }