Record Objective-C method lookup tables in Swift modules.

Include a mapping from Objective-C selectors to the @objc methods that
produce Objective-c methods with those selectors. Use this to lazily
populate the Objective-C method lookup tables in each class. This makes
@objc override checking work across Swift modules, which is part of
rdar://problem/18391046.

Note that we use a single, unified selector table, both because it is
simpler and because it makes global queries ("is there any method with
the given selector?") easier.

Swift SVN r23214
This commit is contained in:
Doug Gregor
2014-11-11 00:19:03 +00:00
parent 3910c25da1
commit b27e88b70b
16 changed files with 426 additions and 18 deletions

View File

@@ -264,6 +264,65 @@ ModuleFile::readDeclTable(ArrayRef<uint64_t> fields, StringRef blobData) {
base + sizeof(uint32_t), base));
}
/// Used to deserialize entries in the on-disk Objective-C method table.
class ModuleFile::ObjCMethodTableInfo {
public:
using internal_key_type = std::string;
using external_key_type = ObjCSelector;
using data_type = SmallVector<std::tuple<TypeID, bool, DeclID>, 8>;
using hash_value_type = uint32_t;
using offset_type = unsigned;
internal_key_type GetInternalKey(external_key_type ID) {
llvm::SmallString<32> scratch;
return ID.getString(scratch).str();
}
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 std::string(reinterpret_cast<const char *>(data), length);
}
static data_type ReadData(internal_key_type key, const uint8_t *data,
unsigned length) {
data_type result;
while (length > 0) {
TypeID typeID = endian::readNext<uint32_t, little, unaligned>(data);
bool isInstanceMethod = *data++ != 0;
DeclID methodID = endian::readNext<uint32_t, little, unaligned>(data);
result.push_back(std::make_tuple(typeID, isInstanceMethod, methodID));
length -= sizeof(TypeID) + 1 + sizeof(DeclID);
}
return result;
}
};
std::unique_ptr<ModuleFile::SerializedObjCMethodTable>
ModuleFile::readObjCMethodTable(ArrayRef<uint64_t> fields, StringRef blobData) {
uint32_t tableOffset;
index_block::ObjCMethodTableLayout::readRecord(fields, tableOffset);
auto base = reinterpret_cast<const uint8_t *>(blobData.data());
using OwnedTable = std::unique_ptr<SerializedObjCMethodTable>;
return OwnedTable(
SerializedObjCMethodTable::Create(base + tableOffset,
base + sizeof(uint32_t), base));
}
bool ModuleFile::readIndexBlock(llvm::BitstreamCursor &cursor) {
cursor.EnterSubBlock(INDEX_BLOCK_ID);
@@ -317,6 +376,9 @@ bool ModuleFile::readIndexBlock(llvm::BitstreamCursor &cursor) {
case index_block::OPERATOR_METHODS:
OperatorMethodDecls = readDeclTable(scratch, blobData);
break;
case index_block::OBJC_METHODS:
ObjCMethods = readObjCMethodTable(scratch, blobData);
break;
default:
// Unknown index kind, which this version of the compiler won't use.
break;
@@ -1012,6 +1074,40 @@ void ModuleFile::loadExtensions(NominalTypeDecl *nominal) {
}
}
void ModuleFile::loadObjCMethods(
ClassDecl *classDecl,
ObjCSelector selector,
bool isInstanceMethod,
llvm::TinyPtrVector<AbstractFunctionDecl *> &methods) {
// If we don't have an Objective-C method table, there's nothing to do.
if (!ObjCMethods)
return;
// Look for all methods in the module file with this selector.
auto known = ObjCMethods->find(selector);
if (known == ObjCMethods->end()) {
return;
}
auto results = *known;
for (const auto &result : results) {
// If the method is the wrong kind (instance vs. class), skip it.
if (isInstanceMethod != std::get<1>(result))
continue;
// If the method isn't defined in the requested class, skip it.
Type type = getType(std::get<0>(result));
if (type->getClassOrBoundGenericClass() != classDecl)
continue;
// Deserialize the method and add it to the list.
if (auto func = dyn_cast_or_null<AbstractFunctionDecl>(
getDecl(std::get<2>(result)))) {
methods.push_back(func);
}
}
}
void ModuleFile::lookupClassMember(Module::AccessPathTy accessPath,
DeclName name,
SmallVectorImpl<ValueDecl*> &results) {