//===--- SwiftLookupTable.cpp - Swift Lookup Table ------------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This file implements support for Swift name lookup tables stored in Clang // modules. // //===----------------------------------------------------------------------===// #include "SwiftLookupTable.h" #include "swift/Basic/STLExtras.h" #include "swift/Basic/Version.h" #include "clang/AST/DeclObjC.h" #include "clang/Lex/MacroInfo.h" #include "clang/Serialization/ASTBitCodes.h" #include "clang/Serialization/ASTReader.h" #include "clang/Serialization/ASTWriter.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringExtras.h" #include "llvm/Bitcode/BitstreamReader.h" #include "llvm/Bitcode/BitstreamWriter.h" #include "llvm/Bitcode/RecordLayout.h" #include "llvm/Support/OnDiskHashTable.h" using namespace swift; using namespace llvm::support; /// Determine whether the new declarations matches an existing declaration. static bool matchesExistingDecl(clang::Decl *decl, clang::Decl *existingDecl) { // If the canonical declarations are equivalent, we have a match. if (decl->getCanonicalDecl() == existingDecl->getCanonicalDecl()) { return true; } return false; } bool SwiftLookupTable::contextRequiresName(ContextKind kind) { switch (kind) { case ContextKind::ObjCClass: case ContextKind::ObjCProtocol: case ContextKind::Tag: case ContextKind::Typedef: return true; case ContextKind::TranslationUnit: return false; } } /// Try to translate the given Clang declaration into a context. static Optional> translateDeclToContext(clang::NamedDecl *decl) { // Tag declaration. if (auto tag = dyn_cast(decl)) { if (tag->getIdentifier()) return std::make_pair(SwiftLookupTable::ContextKind::Tag, tag->getName()); if (auto typedefDecl = tag->getTypedefNameForAnonDecl()) return std::make_pair(SwiftLookupTable::ContextKind::Tag, typedefDecl->getName()); return None; } // Objective-C class context. if (auto objcClass = dyn_cast(decl)) return std::make_pair(SwiftLookupTable::ContextKind::ObjCClass, objcClass->getName()); // Objective-C protocol context. if (auto objcProtocol = dyn_cast(decl)) return std::make_pair(SwiftLookupTable::ContextKind::ObjCProtocol, objcProtocol->getName()); // Typedefs. if (auto typedefName = dyn_cast(decl)) { // If this typedef is merely a restatement of a tag declaration's type, // return the result for that tag. if (auto tag = typedefName->getUnderlyingType()->getAsTagDecl()) return translateDeclToContext(tag); // Otherwise, this must be a typedef mapped to a strong type. return std::make_pair(SwiftLookupTable::ContextKind::Typedef, typedefName->getName()); } return None; } Optional> SwiftLookupTable::translateContext(EffectiveClangContext context) { switch (context.getKind()) { case EffectiveClangContext::DeclContext: { auto dc = context.getAsDeclContext(); // Translation unit context. if (dc->isTranslationUnit()) return std::make_pair(ContextKind::TranslationUnit, StringRef()); // Tag declaration context. if (auto tag = dyn_cast(dc)) { if (tag->getIdentifier()) return std::make_pair(ContextKind::Tag, tag->getName()); if (auto typedefDecl = tag->getTypedefNameForAnonDecl()) return std::make_pair(ContextKind::Tag, typedefDecl->getName()); return None; } // Objective-C class context. if (auto objcClass = dyn_cast(dc)) return std::make_pair(ContextKind::ObjCClass, objcClass->getName()); // Objective-C protocol context. if (auto objcProtocol = dyn_cast(dc)) return std::make_pair(ContextKind::ObjCProtocol, objcProtocol->getName()); return None; } case EffectiveClangContext::TypedefContext: return std::make_pair(ContextKind::Typedef, context.getTypedefName()->getName()); case EffectiveClangContext::UnresolvedContext: // Resolve the context. if (auto decl = resolveContext(context.getUnresolvedName())) { if (auto context = translateDeclToContext(decl)) return context; } return None; } } /// Lookup an unresolved context name and resolve it to a Clang /// declaration context or typedef name. clang::NamedDecl *SwiftLookupTable::resolveContext(StringRef unresolvedName) { // Look for a context with the given Swift name. for (auto entry : lookup(unresolvedName, std::make_pair(ContextKind::TranslationUnit, StringRef()))) { if (auto decl = entry.dyn_cast()) { if (isa(decl) || isa(decl) || isa(decl) || isa(decl)) return decl; } } // FIXME: Search imported modules to resolve the context. return nullptr; } void SwiftLookupTable::addCategory(clang::ObjCCategoryDecl *category) { assert(!Reader && "Cannot modify a lookup table stored on disk"); // Add the category. Categories.push_back(category); } void SwiftLookupTable::addEntry(DeclName name, SingleEntry newEntry, EffectiveClangContext effectiveContext) { assert(!Reader && "Cannot modify a lookup table stored on disk"); // Translate the context. auto contextOpt = translateContext(effectiveContext); if (!contextOpt) return; auto context = *contextOpt; // Find the list of entries for this base name. auto &entries = LookupTable[name.getBaseName().str()]; auto decl = newEntry.dyn_cast(); auto macro = newEntry.dyn_cast(); for (auto &entry : entries) { if (entry.Context == context) { // We have entries for this context. // Check whether this entry matches any existing entry. for (auto &existingEntry : entry.DeclsOrMacros) { // If it matches an existing declaration, there's nothing to do. if (decl && isDeclEntry(existingEntry) && matchesExistingDecl(decl, mapStoredDecl(existingEntry))) return; // If it matches an existing macro, overwrite the existing entry. if (macro && isMacroEntry(existingEntry)) { existingEntry = encodeEntry(macro); return; } } // Add an entry to this context. if (decl) entry.DeclsOrMacros.push_back(encodeEntry(decl)); else entry.DeclsOrMacros.push_back(encodeEntry(macro)); return; } } // This is a new context for this name. Add it. FullTableEntry entry; entry.Context = context; if (decl) entry.DeclsOrMacros.push_back(encodeEntry(decl)); else entry.DeclsOrMacros.push_back(encodeEntry(macro)); entries.push_back(entry); } auto SwiftLookupTable::findOrCreate(StringRef baseName) -> llvm::DenseMap>::iterator { // If there is no base name, there is nothing to find. if (baseName.empty()) return LookupTable.end(); // Find entries for this base name. auto known = LookupTable.find(baseName); // If we found something, we're done. if (known != LookupTable.end()) return known; // If there's no reader, we've found all there is to find. if (!Reader) return known; // Lookup this base name in the module file. SmallVector results; (void)Reader->lookup(baseName, results); // Add an entry to the table so we don't look again. known = LookupTable.insert({ std::move(baseName), std::move(results) }).first; return known; } SmallVector SwiftLookupTable::lookup( StringRef baseName, llvm::Optional> searchContext) { SmallVector result; // Find the lookup table entry for this base name. auto known = findOrCreate(baseName); if (known == LookupTable.end()) return result; // Walk each of the entries. for (auto &entry : known->second) { // If we're looking in a particular context and it doesn't match the // entry context, we're done. if (searchContext && entry.Context != *searchContext) continue; // Map each of the declarations. for (auto &stored : entry.DeclsOrMacros) result.push_back(mapStored(stored)); } return result; } SmallVector SwiftLookupTable::lookup(StringRef baseName, EffectiveClangContext searchContext) { // Translate context. Optional> context; if (searchContext) { context = translateContext(searchContext); if (!context) return { }; } return lookup(baseName, context); } SmallVector SwiftLookupTable::allBaseNames() { // If we have a reader, enumerate its base names. if (Reader) return Reader->getBaseNames(); // Otherwise, walk the lookup table. SmallVector result; for (const auto &entry : LookupTable) { result.push_back(entry.first); } return result; } SmallVector SwiftLookupTable::lookupObjCMembers(StringRef baseName) { SmallVector result; // Find the lookup table entry for this base name. auto known = findOrCreate(baseName); if (known == LookupTable.end()) return result; // Walk each of the entries. for (auto &entry : known->second) { // If we're looking in a particular context and it doesn't match the // entry context, we're done. switch (entry.Context.first) { case ContextKind::TranslationUnit: case ContextKind::Tag: continue; case ContextKind::ObjCClass: case ContextKind::ObjCProtocol: case ContextKind::Typedef: break; } // Map each of the declarations. for (auto &stored : entry.DeclsOrMacros) { assert(isDeclEntry(stored) && "Not a declaration?"); result.push_back(mapStoredDecl(stored)); } } return result; } ArrayRef SwiftLookupTable::categories() { if (!Categories.empty() || !Reader) return Categories; // Map categories known to the reader. for (auto declID : Reader->categories()) { auto category = cast_or_null( Reader->getASTReader().GetLocalDecl(Reader->getModuleFile(), declID)); if (category) Categories.push_back(category); } return Categories; } static void printName(clang::NamedDecl *named, llvm::raw_ostream &out) { // If there is a name, print it. if (!named->getDeclName().isEmpty()) { // If we have an Objective-C method, print the class name along // with '+'/'-'. if (auto objcMethod = dyn_cast(named)) { out << (objcMethod->isInstanceMethod() ? '-' : '+') << '['; if (auto classDecl = objcMethod->getClassInterface()) { classDecl->printName(out); out << ' '; } else if (auto proto = dyn_cast( objcMethod->getDeclContext())) { proto->printName(out); out << ' '; } named->printName(out); out << ']'; return; } // If we have an Objective-C property, print the class name along // with the property name. if (auto objcProperty = dyn_cast(named)) { auto dc = objcProperty->getDeclContext(); if (auto classDecl = dyn_cast(dc)) { classDecl->printName(out); out << '.'; } else if (auto categoryDecl = dyn_cast(dc)) { categoryDecl->getClassInterface()->printName(out); out << '.'; } else if (auto proto = dyn_cast(dc)) { proto->printName(out); out << '.'; } named->printName(out); return; } named->printName(out); return; } // If this is an anonymous tag declaration with a typedef name, use that. if (auto tag = dyn_cast(named)) { if (auto typedefName = tag->getTypedefNameForAnonDecl()) { printName(typedefName, out); return; } } } void SwiftLookupTable::deserializeAll() { if (!Reader) return; for (auto baseName : Reader->getBaseNames()) { (void)lookup(baseName, None); } (void)categories(); } void SwiftLookupTable::dump() const { // Dump the base name -> full table entry mappings. SmallVector baseNames; for (const auto &entry : LookupTable) { baseNames.push_back(entry.first); } llvm::array_pod_sort(baseNames.begin(), baseNames.end()); llvm::errs() << "Base name -> entry mappings:\n"; for (auto baseName : baseNames) { llvm::errs() << " " << baseName << ":\n"; const auto &entries = LookupTable.find(baseName)->second; for (const auto &entry : entries) { llvm::errs() << " "; switch (entry.Context.first) { case ContextKind::TranslationUnit: llvm::errs() << "TU"; break; case ContextKind::Tag: case ContextKind::ObjCClass: case ContextKind::ObjCProtocol: case ContextKind::Typedef: llvm::errs() << entry.Context.second; } llvm::errs() << ": "; interleave(entry.DeclsOrMacros.begin(), entry.DeclsOrMacros.end(), [this](uintptr_t entry) { if (isSerializationIDEntry(entry)) { llvm::errs() << (isMacroEntry(entry) ? "macro" : "decl") << " ID #" << getSerializationID(entry); } else if (isMacroEntry(entry)) { llvm::errs() << "Macro"; } else { auto decl = const_cast(this) ->mapStoredDecl(entry); printName(decl, llvm::errs()); } }, [] { llvm::errs() << ", "; }); llvm::errs() << "\n"; } } if (!Categories.empty()) { llvm::errs() << "Categories: "; interleave(Categories.begin(), Categories.end(), [](clang::ObjCCategoryDecl *category) { llvm::errs() << category->getClassInterface()->getName() << "(" << category->getName() << ")"; }, [] { llvm::errs() << ", "; }); llvm::errs() << "\n"; } else if (Reader && !Reader->categories().empty()) { llvm::errs() << "Categories: "; interleave(Reader->categories().begin(), Reader->categories().end(), [](clang::serialization::DeclID declID) { llvm::errs() << "decl ID #" << declID; }, [] { llvm::errs() << ", "; }); llvm::errs() << "\n"; } } // --------------------------------------------------------------------------- // Serialization // --------------------------------------------------------------------------- using llvm::BCArray; using llvm::BCBlob; using llvm::BCFixed; using llvm::BCGenericRecordLayout; using llvm::BCRecordLayout; using llvm::BCVBR; namespace { enum RecordTypes { /// Record that contains the mapping from base names to entities with that /// name. BASE_NAME_TO_ENTITIES_RECORD_ID = clang::serialization::FIRST_EXTENSION_RECORD_ID, /// Record that contains the list of Objective-C category/extension IDs. CATEGORIES_RECORD_ID }; using BaseNameToEntitiesTableRecordLayout = BCRecordLayout, BCBlob>; using CategoriesRecordLayout = llvm::BCRecordLayout; /// Trait used to write the on-disk hash table for the base name -> entities /// mapping. class BaseNameToEntitiesTableWriterInfo { SwiftLookupTable &Table; clang::ASTWriter &Writer; public: using key_type = StringRef; using key_type_ref = key_type; using data_type = SmallVector; using data_type_ref = data_type &; using hash_value_type = uint32_t; using offset_type = unsigned; BaseNameToEntitiesTableWriterInfo(SwiftLookupTable &table, clang::ASTWriter &writer) : Table(table), Writer(writer) { } hash_value_type ComputeHash(key_type_ref key) { return llvm::HashString(key); } std::pair EmitKeyDataLength(raw_ostream &out, key_type_ref key, data_type_ref data) { // The length of the key. uint32_t keyLength = key.size(); // # of entries uint32_t dataLength = sizeof(uint16_t); // Storage per entry. for (const auto &entry : data) { // Context info. dataLength += 1; if (SwiftLookupTable::contextRequiresName(entry.Context.first)) { dataLength += sizeof(uint16_t) + entry.Context.second.size(); } // # of entries. dataLength += sizeof(uint16_t); // Actual entries. dataLength += (sizeof(clang::serialization::DeclID) * entry.DeclsOrMacros.size()); } endian::Writer writer(out); writer.write(keyLength); writer.write(dataLength); return { keyLength, dataLength }; } void EmitKey(raw_ostream &out, key_type_ref key, unsigned len) { out << key; } void EmitData(raw_ostream &out, key_type_ref key, data_type_ref data, unsigned len) { endian::Writer writer(out); // # of entries writer.write(data.size()); for (auto &fullEntry : data) { // Context. writer.write(static_cast(fullEntry.Context.first)); if (SwiftLookupTable::contextRequiresName(fullEntry.Context.first)) { writer.write(fullEntry.Context.second.size()); out << fullEntry.Context.second; } // # of entries. writer.write(fullEntry.DeclsOrMacros.size()); // Write the declarations and macros. for (auto &entry : fullEntry.DeclsOrMacros) { uint32_t id; if (SwiftLookupTable::isDeclEntry(entry)) { auto decl = Table.mapStoredDecl(entry); id = (Writer.getDeclID(decl) << 2) | 0x02; } else { auto macro = Table.mapStoredMacro(entry); id = (Writer.getMacroID(macro) << 2) | 0x02 | 0x01; } writer.write(id); } } } }; } void SwiftLookupTableWriter::writeExtensionContents( clang::Sema &sema, llvm::BitstreamWriter &stream) { // Populate the lookup table. SwiftLookupTable table(nullptr); PopulateTable(sema, table); SmallVector ScratchRecord; // First, gather the sorted list of base names. SmallVector baseNames; for (const auto &entry : table.LookupTable) baseNames.push_back(entry.first); llvm::array_pod_sort(baseNames.begin(), baseNames.end()); // Form the mapping from base names to entities with their context. { llvm::SmallString<4096> hashTableBlob; uint32_t tableOffset; { llvm::OnDiskChainedHashTableGenerator generator; BaseNameToEntitiesTableWriterInfo info(table, Writer); for (auto baseName : baseNames) generator.insert(baseName, table.LookupTable[baseName], info); llvm::raw_svector_ostream blobStream(hashTableBlob); // Make sure that no bucket is at offset 0 endian::Writer(blobStream).write(0); tableOffset = generator.Emit(blobStream, info); } BaseNameToEntitiesTableRecordLayout layout(stream); layout.emit(ScratchRecord, tableOffset, hashTableBlob); } // Write the categories, if there are any. if (!table.Categories.empty()) { SmallVector categoryIDs; for (auto category : table.Categories) { categoryIDs.push_back(Writer.getDeclID(category)); } StringRef blob(reinterpret_cast(categoryIDs.data()), categoryIDs.size() * sizeof(clang::serialization::DeclID)); CategoriesRecordLayout layout(stream); layout.emit(ScratchRecord, blob); } } namespace { /// Used to deserialize the on-disk base name -> entities table. class BaseNameToEntitiesTableReaderInfo { public: using internal_key_type = StringRef; using external_key_type = internal_key_type; using data_type = SmallVector; using hash_value_type = uint32_t; using offset_type = unsigned; internal_key_type GetInternalKey(external_key_type key) { return key; } external_key_type GetExternalKey(internal_key_type key) { return key; } hash_value_type ComputeHash(internal_key_type key) { return llvm::HashString(key); } static bool EqualKey(internal_key_type lhs, internal_key_type rhs) { return lhs == rhs; } static std::pair ReadKeyDataLength(const uint8_t *&data) { unsigned keyLength = endian::readNext(data); unsigned dataLength = endian::readNext(data); return { keyLength, dataLength }; } static internal_key_type ReadKey(const uint8_t *data, unsigned length) { return StringRef((const char *)data, length); } static data_type ReadData(internal_key_type key, const uint8_t *data, unsigned length) { data_type result; // # of entries. unsigned numEntries = endian::readNext(data); result.reserve(numEntries); // Read all of the entries. while (numEntries--) { SwiftLookupTable::FullTableEntry entry; // Read the context. entry.Context.first = static_cast( endian::readNext(data)); if (SwiftLookupTable::contextRequiresName(entry.Context.first)) { uint16_t length = endian::readNext(data); entry.Context.second = StringRef((const char *)data, length); data += length; } // Read the declarations and macros. unsigned numDeclsOrMacros = endian::readNext(data); while (numDeclsOrMacros--) { auto id = endian::readNext(data); entry.DeclsOrMacros.push_back(id); } result.push_back(entry); } return result; } }; } namespace swift { using SerializedBaseNameToEntitiesTable = llvm::OnDiskIterableChainedHashTable; } clang::NamedDecl *SwiftLookupTable::mapStoredDecl(uintptr_t &entry) { assert(isDeclEntry(entry) && "Not a declaration entry"); // If we have an AST node here, just cast it. if (isASTNodeEntry(entry)) { return static_cast(getPointerFromEntry(entry)); } // Otherwise, resolve the declaration. assert(Reader && "Cannot resolve the declaration without a reader"); clang::serialization::DeclID declID = getSerializationID(entry); auto decl = cast_or_null( Reader->getASTReader().GetLocalDecl(Reader->getModuleFile(), declID)); // Update the entry now that we've resolved the declaration. entry = encodeEntry(decl); return decl; } clang::MacroInfo *SwiftLookupTable::mapStoredMacro(uintptr_t &entry) { assert(isMacroEntry(entry) && "Not a macro entry"); // If we have an AST node here, just cast it. if (isASTNodeEntry(entry)) { return static_cast(getPointerFromEntry(entry)); } // Otherwise, resolve the macro. assert(Reader && "Cannot resolve the macro without a reader"); clang::serialization::MacroID macroID = getSerializationID(entry); auto macro = cast_or_null( Reader->getASTReader().getMacro( Reader->getASTReader().getGlobalMacroID( Reader->getModuleFile(), macroID))); // Update the entry now that we've resolved the macro. entry = encodeEntry(macro); return macro; } SwiftLookupTable::SingleEntry SwiftLookupTable::mapStored(uintptr_t &entry) { if (isDeclEntry(entry)) return mapStoredDecl(entry); return mapStoredMacro(entry); } SwiftLookupTableReader::~SwiftLookupTableReader() { OnRemove(); delete static_cast(SerializedTable); } std::unique_ptr SwiftLookupTableReader::create(clang::ModuleFileExtension *extension, clang::ASTReader &reader, clang::serialization::ModuleFile &moduleFile, std::function onRemove, const llvm::BitstreamCursor &stream) { // Look for the base name -> entities table record. SmallVector scratch; auto cursor = stream; auto next = cursor.advance(); std::unique_ptr serializedTable; ArrayRef categories; while (next.Kind != llvm::BitstreamEntry::EndBlock) { if (next.Kind == llvm::BitstreamEntry::Error) return nullptr; if (next.Kind == llvm::BitstreamEntry::SubBlock) { // Unknown sub-block, possibly for use by a future version of the // API notes format. if (cursor.SkipBlock()) return nullptr; next = cursor.advance(); continue; } scratch.clear(); StringRef blobData; unsigned kind = cursor.readRecord(next.ID, scratch, &blobData); switch (kind) { case BASE_NAME_TO_ENTITIES_RECORD_ID: { // Already saw base name -> entities table. if (serializedTable) return nullptr; uint32_t tableOffset; BaseNameToEntitiesTableRecordLayout::readRecord(scratch, tableOffset); auto base = reinterpret_cast(blobData.data()); serializedTable.reset( SerializedBaseNameToEntitiesTable::Create(base + tableOffset, base + sizeof(uint32_t), base)); break; } case CATEGORIES_RECORD_ID: { // Already saw categories; input is malformed. if (!categories.empty()) return nullptr; auto start = reinterpret_cast(blobData.data()); unsigned numElements = blobData.size() / sizeof(clang::serialization::DeclID); categories = llvm::makeArrayRef(start, numElements); break; } default: // Unknown record, possibly for use by a future version of the // module format. break; } next = cursor.advance(); } if (!serializedTable) return nullptr; // Create the reader. return std::unique_ptr( new SwiftLookupTableReader(extension, reader, moduleFile, onRemove, serializedTable.release(), categories)); } SmallVector SwiftLookupTableReader::getBaseNames() { auto table = static_cast(SerializedTable); SmallVector results; for (auto key : table->keys()) { results.push_back(key); } return results; } /// Retrieve the set of entries associated with the given base name. /// /// \returns true if we found anything, false otherwise. bool SwiftLookupTableReader::lookup( StringRef baseName, SmallVectorImpl &entries) { auto table = static_cast(SerializedTable); // Look for an entry with this base name. auto known = table->find(baseName); if (known == table->end()) return false; // Grab the results. entries = std::move(*known); return true; }