blob: d2369596ce0cbbc1fb0acdb903b65e31775c91dc [file] [log] [blame]
//===--- SwiftLookupTable.cpp - Swift Lookup Table ------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 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/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:
return true;
case ContextKind::TranslationUnit:
return false;
}
}
Optional<std::pair<SwiftLookupTable::ContextKind, StringRef>>
SwiftLookupTable::translateContext(clang::DeclContext *context) {
// Translation unit context.
if (context->isTranslationUnit())
return std::make_pair(ContextKind::TranslationUnit, StringRef());
// Tag declaration context.
if (auto tag = dyn_cast<clang::TagDecl>(context)) {
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<clang::ObjCInterfaceDecl>(context))
return std::make_pair(ContextKind::ObjCClass, objcClass->getName());
// Objective-C protocol context.
if (auto objcProtocol = dyn_cast<clang::ObjCProtocolDecl>(context))
return std::make_pair(ContextKind::ObjCProtocol, objcProtocol->getName());
return None;
}
void SwiftLookupTable::addEntry(DeclName name, SingleEntry newEntry,
clang::DeclContext *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<clang::NamedDecl *>();
auto macro = newEntry.dyn_cast<clang::MacroInfo *>();
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 (decl && isDeclEntry(existingEntry) &&
matchesExistingDecl(decl, mapStoredDecl(existingEntry)))
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<StringRef, SmallVector<FullTableEntry, 2>>::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 somthing, 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;
// Add an entry to the table so we don't look again.
known = LookupTable.insert({ baseName, { } }).first;
// Lookup this base name in the module file.
(void)Reader->lookup(baseName, known->second);
return known;
}
SmallVector<SwiftLookupTable::SingleEntry, 4>
SwiftLookupTable::lookup(StringRef baseName,
clang::DeclContext *searchContext) {
SmallVector<SwiftLookupTable::SingleEntry, 4> result;
// Find the lookup table entry for this base name.
auto known = findOrCreate(baseName);
if (known == LookupTable.end()) return result;
// Translate context.
Optional<std::pair<SwiftLookupTable::ContextKind, StringRef>> context;
if (searchContext) {
context = translateContext(searchContext);
if (!context) 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 (context && *context != entry.Context) continue;
// Map each of the declarations.
for (auto &stored : entry.DeclsOrMacros)
result.push_back(mapStored(stored));
}
return result;
}
SmallVector<StringRef, 4> SwiftLookupTable::allBaseNames() {
// If we have a reader, enumerate its base names.
if (Reader) return Reader->getBaseNames();
// Otherwise, walk the lookup table.
SmallVector<StringRef, 4> result;
for (const auto &entry : LookupTable) {
result.push_back(entry.first);
}
return result;
}
SmallVector<clang::NamedDecl *, 4>
SwiftLookupTable::lookupObjCMembers(StringRef baseName) {
SmallVector<clang::NamedDecl *, 4> 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:
break;
}
// Map each of the declarations.
for (auto &stored : entry.DeclsOrMacros) {
assert(isDeclEntry(stored) && "Not a declaration?");
result.push_back(mapStoredDecl(stored));
}
}
return result;
}
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<clang::ObjCMethodDecl>(named)) {
out << (objcMethod->isInstanceMethod() ? '-' : '+') << '[';
if (auto classDecl = objcMethod->getClassInterface()) {
classDecl->printName(out);
out << ' ';
} else if (auto proto = dyn_cast<clang::ObjCProtocolDecl>(
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<clang::ObjCPropertyDecl>(named)) {
auto dc = objcProperty->getDeclContext();
if (auto classDecl = dyn_cast<clang::ObjCInterfaceDecl>(dc)) {
classDecl->printName(out);
out << '.';
} else if (auto categoryDecl = dyn_cast<clang::ObjCCategoryDecl>(dc)) {
categoryDecl->getClassInterface()->printName(out);
out << '.';
} else if (auto proto = dyn_cast<clang::ObjCProtocolDecl>(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<clang::TagDecl>(named)) {
if (auto typedefName = tag->getTypedefNameForAnonDecl()) {
printName(typedefName, out);
return;
}
}
}
void SwiftLookupTable::deserializeAll() {
if (!Reader) return;
for (auto baseName : Reader->getBaseNames()) {
(void)lookup(baseName, nullptr);
}
}
void SwiftLookupTable::dump() const {
// Dump the base name -> full table entry mappings.
SmallVector<StringRef, 4> 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:
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<SwiftLookupTable *>(this)
->mapStoredDecl(entry);
printName(decl, llvm::errs());
}
},
[] {
llvm::errs() << ", ";
});
llvm::errs() << "\n";
}
}
}
// ---------------------------------------------------------------------------
// Serialization
// ---------------------------------------------------------------------------
using llvm::Fixnum;
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,
};
using BaseNameToEntitiesTableRecordLayout
= BCRecordLayout<BASE_NAME_TO_ENTITIES_RECORD_ID, BCVBR<16>, BCBlob>;
/// 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<SwiftLookupTable::FullTableEntry, 2>;
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<unsigned, unsigned> 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<little> writer(out);
writer.write<uint16_t>(keyLength);
writer.write<uint16_t>(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<little> writer(out);
// # of entries
writer.write<uint16_t>(data.size());
for (auto &fullEntry : data) {
// Context.
writer.write<uint8_t>(static_cast<uint8_t>(fullEntry.Context.first));
if (SwiftLookupTable::contextRequiresName(fullEntry.Context.first)) {
writer.write<uint16_t>(fullEntry.Context.second.size());
out << fullEntry.Context.second;
}
// # of entries.
writer.write<uint16_t>(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<uint32_t>(id);
}
}
}
};
}
void SwiftLookupTableWriter::writeExtensionContents(
clang::Sema &sema,
llvm::BitstreamWriter &stream) {
// Populate the lookup table.
SwiftLookupTable table(nullptr);
PopulateTable(sema, table);
SmallVector<uint64_t, 64> ScratchRecord;
// First, gather the sorted list of base names.
SmallVector<StringRef, 2> 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<BaseNameToEntitiesTableWriterInfo>
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<little>(blobStream).write<uint32_t>(0);
tableOffset = generator.Emit(blobStream, info);
}
BaseNameToEntitiesTableRecordLayout layout(stream);
layout.emit(ScratchRecord, tableOffset, hashTableBlob);
}
}
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<SwiftLookupTable::FullTableEntry, 2>;
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<unsigned, unsigned>
ReadKeyDataLength(const uint8_t *&data) {
unsigned keyLength = endian::readNext<uint16_t, little, unaligned>(data);
unsigned dataLength = endian::readNext<uint16_t, little, unaligned>(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<uint16_t, little, unaligned>(data);
result.reserve(numEntries);
// Read all of the entries.
while (numEntries--) {
SwiftLookupTable::FullTableEntry entry;
// Read the context.
entry.Context.first =
static_cast<SwiftLookupTable::ContextKind>(
endian::readNext<uint8_t, little, unaligned>(data));
if (SwiftLookupTable::contextRequiresName(entry.Context.first)) {
uint16_t length = endian::readNext<uint16_t, little, unaligned>(data);
entry.Context.second = StringRef((const char *)data, length);
data += length;
}
// Read the declarations and macros.
unsigned numDeclsOrMacros =
endian::readNext<uint16_t, little, unaligned>(data);
while (numDeclsOrMacros--) {
auto id = endian::readNext<uint32_t, little, unaligned>(data);
entry.DeclsOrMacros.push_back(id);
}
result.push_back(entry);
}
return result;
}
};
}
namespace swift {
using SerializedBaseNameToEntitiesTable =
llvm::OnDiskIterableChainedHashTable<BaseNameToEntitiesTableReaderInfo>;
}
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<clang::NamedDecl *>(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<clang::NamedDecl>(
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<clang::MacroInfo *>(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<clang::MacroInfo>(
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<SerializedBaseNameToEntitiesTable *>(SerializedTable);
}
std::unique_ptr<SwiftLookupTableReader>
SwiftLookupTableReader::create(clang::ModuleFileExtension *extension,
clang::ASTReader &reader,
clang::serialization::ModuleFile &moduleFile,
std::function<void()> onRemove,
const llvm::BitstreamCursor &stream)
{
// Look for the base name -> entities table record.
SmallVector<uint64_t, 64> scratch;
auto cursor = stream;
auto next = cursor.advance();
std::unique_ptr<SerializedBaseNameToEntitiesTable> serializedTable;
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<const uint8_t *>(blobData.data());
serializedTable.reset(
SerializedBaseNameToEntitiesTable::Create(base + tableOffset,
base + sizeof(uint32_t),
base));
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<SwiftLookupTableReader>(
new SwiftLookupTableReader(extension, reader, moduleFile, onRemove,
serializedTable.release()));
}
SmallVector<StringRef, 4> SwiftLookupTableReader::getBaseNames() {
auto table = static_cast<SerializedBaseNameToEntitiesTable*>(SerializedTable);
SmallVector<StringRef, 4> 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<SwiftLookupTable::FullTableEntry> &entries) {
auto table = static_cast<SerializedBaseNameToEntitiesTable*>(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;
}