Reflection: Decode imported Objective-C classes and protocols as their own TypeRef

Right now we expect that every class and protocol has a field
descriptor that tells us if the entity is @objc or not.

For imported types, the descriptor will not exist if we did not
directly emit a field whose concrete type contains the imported
type. For example, in lldb, we might have a generic type whose
runtime substituted type includes an imported type.

In this case, TypeLowering would fail to produce a layout because
it did not find a field descriptor for the imported type.

A better approach is to have the TypeDecoder call a different
factory method for imported types, and handle them specially in
TypeLowering, bypassing the field type metadata altogether.
This commit is contained in:
Slava Pestov
2018-11-01 21:55:29 -04:00
parent 5655b35bc3
commit d093fcb4a4
11 changed files with 229 additions and 104 deletions

View File

@@ -22,6 +22,7 @@
#include "swift/Demangling/Demangler.h"
#include "swift/Basic/LLVM.h"
#include "swift/Runtime/Unreachable.h"
#include "swift/Strings.h"
#include <vector>
namespace swift {
@@ -75,6 +76,34 @@ public:
}
};
#if SWIFT_OBJC_INTEROP
/// For a mangled node that refers to an Objective-C class or protocol,
/// return the class or protocol name.
static inline Optional<StringRef> getObjCClassOrProtocolName(
const Demangle::NodePointer &node) {
if (node->getKind() != Demangle::Node::Kind::Class &&
node->getKind() != Demangle::Node::Kind::Protocol)
return None;
if (node->getNumChildren() != 2)
return None;
// Check whether we have the __ObjC module.
auto moduleNode = node->getChild(0);
if (moduleNode->getKind() != Demangle::Node::Kind::Module ||
moduleNode->getText() != MANGLING_MODULE_OBJC)
return None;
// Check whether we have an identifier.
auto nameNode = node->getChild(1);
if (nameNode->getKind() != Demangle::Node::Kind::Identifier)
return None;
return nameNode->getText();
}
#endif
/// Decode a mangled type to construct an abstract type, forming such
/// types by invoking a custom builder.
template <typename BuilderType>
@@ -112,6 +141,13 @@ class TypeDecoder {
return decodeMangledType(Node->getChild(0));
case NodeKind::Class:
{
#if SWIFT_OBJC_INTEROP
if (auto mangledName = getObjCClassOrProtocolName(Node))
return Builder.createObjCClassType(mangledName->str());
#endif
LLVM_FALLTHROUGH;
}
case NodeKind::Enum:
case NodeKind::Structure:
case NodeKind::TypeAlias: // This can show up for imported Clang decls.
@@ -125,6 +161,14 @@ class TypeDecoder {
return Builder.createNominalType(typeDecl, parent);
}
case NodeKind::BoundGenericClass:
{
#if SWIFT_OBJC_INTEROP
if (Node->getNumChildren() == 2)
if (auto mangledName = getObjCClassOrProtocolName(Node->getChild(0)))
return Builder.createObjCClassType(mangledName->str());
#endif
LLVM_FALLTHROUGH;
}
case NodeKind::BoundGenericEnum:
case NodeKind::BoundGenericStructure:
case NodeKind::BoundGenericOtherNominalType: {
@@ -528,6 +572,11 @@ private:
&& node->getKind() != NodeKind::ProtocolSymbolicReference)
return BuiltProtocolDecl();
#if SWIFT_OBJC_INTEROP
if (auto objcProtocolName = getObjCClassOrProtocolName(node))
return Builder.createObjCProtocolDecl(objcProtocolName->str());
#endif
return Builder.createProtocolDecl(node);
}

View File

@@ -386,11 +386,11 @@ public:
};
class ProtocolCompositionTypeRef final : public TypeRef {
std::vector<const NominalTypeRef *> Protocols;
std::vector<const TypeRef *> Protocols;
const TypeRef *Superclass;
bool HasExplicitAnyObject;
static TypeRefID Profile(std::vector<const NominalTypeRef *> Protocols,
static TypeRefID Profile(std::vector<const TypeRef *> Protocols,
const TypeRef *Superclass,
bool HasExplicitAnyObject) {
TypeRefID ID;
@@ -403,7 +403,7 @@ class ProtocolCompositionTypeRef final : public TypeRef {
}
public:
ProtocolCompositionTypeRef(std::vector<const NominalTypeRef *> Protocols,
ProtocolCompositionTypeRef(std::vector<const TypeRef *> Protocols,
const TypeRef *Superclass,
bool HasExplicitAnyObject)
: TypeRef(TypeRefKind::ProtocolComposition),
@@ -412,13 +412,14 @@ public:
template <typename Allocator>
static const ProtocolCompositionTypeRef *
create(Allocator &A, std::vector<const NominalTypeRef *> Protocols,
create(Allocator &A, std::vector<const TypeRef *> Protocols,
const TypeRef *Superclass, bool HasExplicitAnyObject) {
FIND_OR_CREATE_TYPEREF(A, ProtocolCompositionTypeRef, Protocols,
Superclass, HasExplicitAnyObject);
}
const std::vector<const NominalTypeRef *> &getProtocols() const {
// These are either NominalTypeRef or ObjCProtocolTypeRef.
const std::vector<const TypeRef *> &getProtocols() const {
return Protocols;
}
@@ -633,6 +634,36 @@ public:
}
};
class ObjCProtocolTypeRef final : public TypeRef {
std::string Name;
static const ObjCProtocolTypeRef *UnnamedSingleton;
static TypeRefID Profile(const std::string &Name) {
TypeRefID ID;
ID.addString(Name);
return ID;
}
public:
ObjCProtocolTypeRef(const std::string &Name)
: TypeRef(TypeRefKind::ObjCProtocol), Name(Name) {}
static const ObjCProtocolTypeRef *getUnnamed();
template <typename Allocator>
static const ObjCProtocolTypeRef *create(Allocator &A,
const std::string &Name) {
FIND_OR_CREATE_TYPEREF(A, ObjCProtocolTypeRef, Name);
}
const std::string &getName() const {
return Name;
}
static bool classof(const TypeRef *TR) {
return TR->getKind() == TypeRefKind::ObjCProtocol;
}
};
class OpaqueTypeRef final : public TypeRef {
static const OpaqueTypeRef *Singleton;

View File

@@ -160,7 +160,7 @@ class TypeRefBuilder {
public:
using BuiltType = const TypeRef *;
using BuiltNominalTypeDecl = Optional<std::string>;
using BuiltProtocolDecl = Optional<std::string>;
using BuiltProtocolDecl = Optional<std::pair<std::string, bool /*isObjC*/>>;
TypeRefBuilder();
@@ -214,9 +214,14 @@ public:
return Demangle::mangleNode(node);
}
Optional<std::string>
BuiltProtocolDecl
createProtocolDecl(const Demangle::NodePointer &node) {
return Demangle::mangleNode(node);
return std::make_pair(Demangle::mangleNode(node), false);
}
BuiltProtocolDecl
createObjCProtocolDecl(std::string &&name) {
return std::make_pair(name, true);
}
Optional<std::string> createNominalTypeDecl(std::string &&mangledName) {
@@ -265,9 +270,15 @@ public:
createProtocolCompositionType(ArrayRef<BuiltProtocolDecl> protocols,
BuiltType superclass,
bool isClassBound) {
std::vector<const NominalTypeRef *> protocolRefs;
std::vector<const TypeRef *> protocolRefs;
for (const auto &protocol : protocols) {
protocolRefs.push_back(createNominalType(protocol));
if (!protocol)
continue;
if (protocol->second)
protocolRefs.push_back(createObjCProtocolType(protocol->first));
else
protocolRefs.push_back(createNominalType(protocol->first));
}
return ProtocolCompositionTypeRef::create(*this, protocolRefs, superclass,
@@ -292,8 +303,12 @@ public:
const DependentMemberTypeRef *
createDependentMemberType(const std::string &member,
const TypeRef *base,
Optional<std::string> protocol) {
return DependentMemberTypeRef::create(*this, member, base, *protocol);
BuiltProtocolDecl protocol) {
// Objective-C protocols don't have dependent types.
if (protocol->second)
return nullptr;
return DependentMemberTypeRef::create(*this, member, base,
protocol->first);
}
#define REF_STORAGE(Name, ...) \
@@ -306,15 +321,20 @@ public:
return SILBoxTypeRef::create(*this, base);
}
const ObjCClassTypeRef *
createObjCClassType(const std::string &mangledName) {
return ObjCClassTypeRef::create(*this, mangledName);
}
const ObjCClassTypeRef *getUnnamedObjCClassType() {
return createObjCClassType("");
}
const ObjCClassTypeRef *
createObjCClassType(const std::string &name) {
return ObjCClassTypeRef::create(*this, name);
}
const ObjCProtocolTypeRef *
createObjCProtocolType(const std::string &name) {
return ObjCProtocolTypeRef::create(*this, name);
}
const ForeignClassTypeRef *
createForeignClassType(const std::string &mangledName) {
return ForeignClassTypeRef::create(*this, mangledName);

View File

@@ -28,6 +28,7 @@ TYPEREF(GenericTypeParameter, TypeRef)
TYPEREF(DependentMember, TypeRef)
TYPEREF(ForeignClass, TypeRef)
TYPEREF(ObjCClass, TypeRef)
TYPEREF(ObjCProtocol, TypeRef)
TYPEREF(Opaque, TypeRef)
#define REF_STORAGE(Name, ...) \
TYPEREF(Name##Storage, TypeRef)

View File

@@ -509,6 +509,8 @@ public:
if (!Demangled)
return BuiltType();
// FIXME: Imported protocols?
auto Protocol = Builder.createProtocolDecl(Demangled);
if (!Protocol)
return BuiltType();

View File

@@ -438,6 +438,16 @@ public:
return createNominalType(typeDecl, /*parent*/ Type());
}
ProtocolDecl *createObjCProtocolDecl(StringRef name) {
auto typeDecl =
findForeignNominalTypeDecl(name, /*relatedEntityKind*/{},
ForeignModuleKind::Imported,
Demangle::Node::Kind::Protocol);
if (auto *protocolDecl = dyn_cast_or_null<ProtocolDecl>(typeDecl))
return protocolDecl;
return nullptr;
}
Type createForeignClassType(StringRef mangledName) {
auto typeDecl = createNominalTypeDecl(mangledName);
if (!typeDecl) return Type();

View File

@@ -197,7 +197,7 @@ BuiltinTypeInfo::BuiltinTypeInfo(const BuiltinTypeDescriptor *descriptor)
/// Utility class for building values that contain witness tables.
class ExistentialTypeInfoBuilder {
TypeConverter &TC;
std::vector<const NominalTypeRef *> Protocols;
std::vector<const TypeRef *> Protocols;
const TypeRef *Superclass = nullptr;
ExistentialTypeRepresentation Representation;
ReferenceCounting Refcounting;
@@ -218,8 +218,9 @@ class ExistentialTypeInfoBuilder {
return false;
for (auto *P : Protocols) {
if (P->isErrorProtocol())
return true;
if (auto *NTD = dyn_cast<NominalTypeRef>(P))
if (NTD->isErrorProtocol())
return true;
}
return false;
}
@@ -232,6 +233,20 @@ class ExistentialTypeInfoBuilder {
}
for (auto *P : Protocols) {
auto *NTD = dyn_cast<NominalTypeRef>(P);
auto *OP = dyn_cast<ObjCProtocolTypeRef>(P);
if (!NTD && !OP) {
DEBUG_LOG(std::cerr << "Bad protocol: "; P->dump())
Invalid = true;
continue;
}
// Don't look up field info for imported Objective-C protocols.
if (OP) {
ObjC = true;
continue;
}
std::pair<const FieldDescriptor *, const ReflectionInfo *> FD =
TC.getBuilder().getFieldTypeInfo(P);
if (FD.first == nullptr) {
@@ -270,7 +285,7 @@ public:
ObjC(false), WitnessTableCount(0),
Invalid(false) {}
void addProtocol(const NominalTypeRef *P) {
void addProtocol(const TypeRef *P) {
Protocols.push_back(P);
}
@@ -286,11 +301,19 @@ public:
// Anything else should either be a superclass constraint, or
// we have an invalid typeref.
if (!isa<NominalTypeRef>(T) &&
!isa<BoundGenericTypeRef>(T)) {
!isa<BoundGenericTypeRef>(T) &&
!isa<ObjCClassTypeRef>(T)) {
DEBUG_LOG(std::cerr << "Bad existential member: "; T->dump())
Invalid = true;
return;
}
// Don't look up field info for imported Objective-C classes.
if (auto *OC = dyn_cast<ObjCClassTypeRef>(T)) {
addAnyObject();
return;
}
const auto &FD = TC.getBuilder().getFieldTypeInfo(T);
if (FD.first == nullptr) {
DEBUG_LOG(std::cerr << "No field descriptor: "; T->dump())
@@ -721,6 +744,10 @@ public:
return true;
}
bool visitObjCProtocolTypeRef(const ObjCProtocolTypeRef *OP) {
return true;
}
#define REF_STORAGE(Name, ...) \
bool \
visit##Name##StorageTypeRef(const Name##StorageTypeRef *US) { \
@@ -848,6 +875,10 @@ public:
return MetatypeRepresentation::Unknown;
}
MetatypeRepresentation visitObjCProtocolTypeRef(const ObjCProtocolTypeRef *OP) {
return MetatypeRepresentation::Unknown;
}
#define REF_STORAGE(Name, ...) \
MetatypeRepresentation \
visit##Name##StorageTypeRef(const Name##StorageTypeRef *US) { \
@@ -1197,6 +1228,11 @@ public:
ReferenceCounting::Unknown);
}
const TypeInfo *visitObjCProtocolTypeRef(const ObjCProtocolTypeRef *OP) {
return TC.getReferenceTypeInfo(ReferenceKind::Strong,
ReferenceCounting::Unknown);
}
// Apply a storage qualifier, like 'weak', 'unowned' or 'unowned(unsafe)'
// to a type with reference semantics, such as a class reference or
// class-bound existential.

View File

@@ -236,6 +236,13 @@ public:
OS << ')';
}
void visitObjCProtocolTypeRef(const ObjCProtocolTypeRef *OC) {
printHeader("objective_c_protocol");
if (!OC->getName().empty())
printField("name", OC->getName());
OS << ')';
}
#define REF_STORAGE(Name, name, ...) \
void visit##Name##StorageTypeRef(const Name##StorageTypeRef *US) { \
printHeader(#name "_storage"); \
@@ -334,6 +341,10 @@ struct TypeRefIsConcrete
bool visitObjCClassTypeRef(const ObjCClassTypeRef *OC) {
return true;
}
bool visitObjCProtocolTypeRef(const ObjCProtocolTypeRef *OC) {
return true;
}
bool visitOpaqueTypeRef(const OpaqueTypeRef *O) {
return true;
@@ -514,6 +525,10 @@ public:
return OC;
}
const TypeRef *visitObjCProtocolTypeRef(const ObjCProtocolTypeRef *OP) {
return OP;
}
#define REF_STORAGE(Name, name, ...) \
const TypeRef *visit##Name##StorageTypeRef(const Name##StorageTypeRef *US) { \
return US; \
@@ -657,19 +672,21 @@ public:
SubstBase = Superclass;
}
auto Protocol = std::make_pair(DM->getProtocol(), false);
// We didn't find the member type, so return something to let the
// caller know we're dealing with incomplete metadata.
if (TypeWitness == nullptr)
return Builder.createDependentMemberType(DM->getMember(),
SubstBase,
DM->getProtocol());
Protocol);
// Likewise if we can't get the substitution map.
auto SubstMap = SubstBase->getSubstMap();
if (!SubstMap)
return Builder.createDependentMemberType(DM->getMember(),
SubstBase,
DM->getProtocol());
Protocol);
// Apply base type substitutions to get the fully-substituted nested type.
auto *Subst = TypeWitness->subst(Builder, *SubstMap);
@@ -686,6 +703,10 @@ public:
return OC;
}
const TypeRef *visitObjCProtocolTypeRef(const ObjCProtocolTypeRef *OP) {
return OP;
}
#define REF_STORAGE(Name, name, ...) \
const TypeRef *visit##Name##StorageTypeRef(const Name##StorageTypeRef *US) { \
return Name##StorageTypeRef::create(Builder, visit(US->getType())); \

View File

@@ -330,31 +330,6 @@ ParsedTypeIdentity::parse(const TypeContextDescriptor *type) {
}
#if SWIFT_OBJC_INTEROP
/// For a mangled node that refers to an Objective-C class or protocol,
/// return the class or protocol name.
static Optional<StringRef> getObjCClassOrProtocolName(
const Demangle::NodePointer &node) {
if (node->getKind() != Demangle::Node::Kind::Class &&
node->getKind() != Demangle::Node::Kind::Protocol)
return None;
if (node->getNumChildren() != 2)
return None;
// Check whether we have the __ObjC module.
auto moduleNode = node->getChild(0);
if (moduleNode->getKind() != Demangle::Node::Kind::Module ||
moduleNode->getText() != MANGLING_MODULE_OBJC)
return None;
// Check whether we have an identifier.
auto nameNode = node->getChild(1);
if (nameNode->getKind() != Demangle::Node::Kind::Identifier)
return None;
return nameNode->getText();
}
/// Determine whether the two demangle trees both refer to the same
/// Objective-C class or protocol referenced by name.
static bool sameObjCTypeManglings(Demangle::NodePointer node1,
@@ -363,10 +338,10 @@ static bool sameObjCTypeManglings(Demangle::NodePointer node1,
if (node1->getKind() != node2->getKind())
return false;
auto name1 = getObjCClassOrProtocolName(node1);
auto name1 = Demangle::getObjCClassOrProtocolName(node1);
if (!name1) return false;
auto name2 = getObjCClassOrProtocolName(node2);
auto name2 = Demangle::getObjCClassOrProtocolName(node2);
if (!name2) return false;
return *name1 == *name2;
@@ -950,45 +925,19 @@ public:
lookupDependentMember(lookupDependentMember) { }
using BuiltType = const Metadata *;
struct BuiltNominalTypeDecl :
llvm::PointerUnion<const TypeContextDescriptor *, const Metadata *>
{
using PointerUnion::PointerUnion;
explicit operator bool() const { return !isNull(); }
};
using BuiltNominalTypeDecl = const TypeContextDescriptor *;
using BuiltProtocolDecl = ProtocolDescriptorRef;
Demangle::NodeFactory &getNodeFactory() { return demangler; }
BuiltNominalTypeDecl createNominalTypeDecl(
const Demangle::NodePointer &node) const {
#if SWIFT_OBJC_INTEROP
// If we have an Objective-C class name, call into the Objective-C
// runtime to find them.
if (auto objcClassName = getObjCClassOrProtocolName(node)) {
auto objcClass = objc_getClass(objcClassName->str().c_str());
return swift_getObjCClassMetadata((const ClassMetadata *)objcClass);
}
#endif
// Look for a nominal type descriptor based on its mangled name.
return _findNominalTypeDescriptor(node, demangler);
}
BuiltProtocolDecl createProtocolDecl(
const Demangle::NodePointer &node) const {
#if SWIFT_OBJC_INTEROP
// If we have an Objective-C protocol name, call into the Objective-C
// runtime to find them.
if (auto objcProtocolName = getObjCClassOrProtocolName(node)) {
return ProtocolDescriptorRef::forObjC(objc_getProtocol(
objcProtocolName->str().c_str()));
}
#endif
// Look for a protocol descriptor based on its mangled name.
std::string mangledName;
if (auto protocol = _findProtocolDescriptor(node, demangler, mangledName))
@@ -1006,6 +955,25 @@ public:
return ProtocolDescriptorRef();
}
BuiltProtocolDecl createObjCProtocolDecl(
const std::string &mangledName) const {
#if SWIFT_OBJC_INTEROP
return ProtocolDescriptorRef::forObjC(
objc_getProtocol(mangledName.c_str()));
#else
return ProtocolDescriptorRef();
#endif
}
BuiltType createObjCClassType(const std::string &mangledName) const {
#if SWIFT_OBJC_INTEROP
auto objcClass = objc_getClass(mangledName.c_str());
return swift_getObjCClassMetadata((const ClassMetadata *)objcClass);
#else
return BuiltType();
#endif
}
BuiltType createNominalType(BuiltNominalTypeDecl metadataOrTypeDecl,
BuiltType parent) const {
// Treat nominal type creation the same way as generic type creation,
@@ -1013,15 +981,9 @@ public:
return createBoundGenericType(metadataOrTypeDecl, { }, parent);
}
BuiltType createBoundGenericType(BuiltNominalTypeDecl metadataOrTypeDecl,
BuiltType createBoundGenericType(BuiltNominalTypeDecl typeDecl,
const ArrayRef<BuiltType> genericArgs,
const BuiltType parent) const {
// If we already have metadata, return it.
if (auto metadata = metadataOrTypeDecl.dyn_cast<const Metadata *>())
return metadata;
auto typeDecl = metadataOrTypeDecl.get<const TypeContextDescriptor *>();
// Figure out the various levels of generic parameters we have in
// this type.
std::vector<unsigned> genericParamCounts;

View File

@@ -11,10 +11,10 @@
// CHECK: TypesToReflect.OC
// CHECK: -----------------
// CHECK: nsObject: __C.NSObject
// CHECK: (class __C.NSObject)
// CHECK: (objective_c_class name=NSObject)
// CHECK: nsString: __C.NSString
// CHECK: (class __C.NSString)
// CHECK: (objective_c_class name=NSString)
// CHECK: cfString: __C.CFStringRef
// CHECK: (alias __C.CFStringRef)
@@ -25,7 +25,7 @@
// CHECK: ocnss: TypesToReflect.GenericOC<__C.NSString>
// CHECK: (bound_generic_class TypesToReflect.GenericOC
// CHECK: (class __C.NSString))
// CHECK: (objective_c_class name=NSString))
// CHECK: occfs: TypesToReflect.GenericOC<__C.CFStringRef>
// CHECK: (bound_generic_class TypesToReflect.GenericOC
@@ -37,7 +37,7 @@
// CHECK: TypesToReflect.HasObjCClasses
// CHECK: -----------------------------
// CHECK: url: __C.NSURL
// CHECK: (class __C.NSURL)
// CHECK: (objective_c_class name=NSURL)
// CHECK: integer: Swift.Int
// CHECK: (struct Swift.Int)
@@ -48,13 +48,6 @@
// CHECK: TypesToReflect.OP
// CHECK: -----------------
// CHECK: __C.NSBundle
// CHECK: ----------
// CHECK: __C.NSURL
// CHECK: ---------
// CHECK: __C.NSCoding
// CHECK: ------------
// CHECK: ASSOCIATED TYPES:
// CHECK: =================
@@ -77,7 +70,7 @@
// CHECK-NEXT: ====================
// CHECK: - Capture types:
// CHECK-NEXT: (class __C.NSBundle)
// CHECK-NEXT: (objective_c_class name=NSBundle)
// CHECK-NEXT: (protocol_composition
// CHECK-NEXT: (protocol __C.NSCoding))
// CHECK-NEXT: (objective_c_protocol name=NSCoding))
// CHECK-NEXT: - Metadata sources:

View File

@@ -215,10 +215,10 @@ TEST(TypeRefTest, UniqueFunctionTypeRef) {
TEST(TypeRefTest, UniqueProtocolTypeRef) {
TypeRefBuilder Builder;
Optional<std::string> P1 = ABC;
Optional<std::string> P2 = ABC;
Optional<std::string> P3 = ABCD;
Optional<std::string> P4 = XYZ;
TypeRefBuilder::BuiltProtocolDecl P1 = std::make_pair(ABC, false);
TypeRefBuilder::BuiltProtocolDecl P2 = std::make_pair(ABC, false);
TypeRefBuilder::BuiltProtocolDecl P3 = std::make_pair(ABCD, false);
TypeRefBuilder::BuiltProtocolDecl P4 = std::make_pair(XYZ, false);
EXPECT_EQ(P1, P2);
EXPECT_NE(P2, P3);
@@ -280,8 +280,8 @@ TEST(TypeRefTest, UniqueDependentMemberTypeRef) {
auto N1 = Builder.createNominalType(ABC, nullptr);
auto N2 = Builder.createNominalType(XYZ, nullptr);
Optional<std::string> P1 = ABC;
Optional<std::string> P2 = ABCD;
TypeRefBuilder::BuiltProtocolDecl P1 = std::make_pair(ABC, false);
TypeRefBuilder::BuiltProtocolDecl P2 = std::make_pair(ABCD, false);
auto DM1 = Builder.createDependentMemberType("Index", N1, P1);
auto DM2 = Builder.createDependentMemberType("Index", N1, P1);