Skip to content

[Runtime] Remove protocol descriptor overlap #18146

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 7 additions & 21 deletions include/swift/ABI/Metadata.h
Original file line number Diff line number Diff line change
Expand Up @@ -1600,14 +1600,6 @@ class TargetProtocolDescriptorRef {
/// is clear).
StoredPointer storage;

public:
/// Retrieve the protocol descriptor without checking whether we have an
/// Objective-C or Swift protocol.
/// FIXME: Temporarily public while we roll out TargetProtocolDescriptorRef.
ProtocolDescriptorPointer getProtocolDescriptorUnchecked() const {
return reinterpret_cast<ProtocolDescriptorPointer>(storage & ~IsObjCBit);
}

constexpr TargetProtocolDescriptorRef(StoredPointer storage)
: storage(storage) { }

Expand Down Expand Up @@ -1645,11 +1637,6 @@ class TargetProtocolDescriptorRef {
return storage != 0;
}

/// The name of the protocol.
TargetPointer<Runtime, const char> getName() const {
return getProtocolDescriptorUnchecked()->Name;
}

/// Determine what kind of protocol this is, Swift or Objective-C.
ProtocolDispatchStrategy getDispatchStrategy() const {
#if SWIFT_OBJC_INTEROP
Expand All @@ -1669,7 +1656,7 @@ class TargetProtocolDescriptorRef {
}
#endif

return getProtocolDescriptorUnchecked()->Flags.getClassConstraint();
return getSwiftProtocol()->Flags.getClassConstraint();
}

/// Determine whether this protocol needs a witness table.
Expand All @@ -1680,7 +1667,7 @@ class TargetProtocolDescriptorRef {
}
#endif

return getProtocolDescriptorUnchecked()->Flags.needsWitnessTable();
return true;
}

SpecialProtocol getSpecialProtocol() const {
Expand All @@ -1690,7 +1677,7 @@ class TargetProtocolDescriptorRef {
}
#endif

return getProtocolDescriptorUnchecked()->Flags.getSpecialProtocol();
return getSwiftProtocol()->Flags.getSpecialProtocol();
}

/// Retrieve the Swift protocol descriptor.
Expand All @@ -1699,7 +1686,7 @@ class TargetProtocolDescriptorRef {
assert(!isObjC());
#endif

return getProtocolDescriptorUnchecked();
return reinterpret_cast<ProtocolDescriptorPointer>(storage & ~IsObjCBit);
}

/// Retrieve the raw stored pointer and discriminator bit.
Expand All @@ -1710,15 +1697,14 @@ class TargetProtocolDescriptorRef {
#if SWIFT_OBJC_INTEROP
/// Whether this references an Objective-C protocol.
bool isObjC() const {
assert(static_cast<bool>(storage & IsObjCBit) !=
getProtocolDescriptorUnchecked()->Flags.needsWitnessTable());
return (storage & IsObjCBit) != 0;
}

/// Retrieve the Objective-C protocol.
Protocol *getObjCProtocol() const {
TargetPointer<Runtime, Protocol> getObjCProtocol() const {
assert(isObjC());
return reinterpret_cast<Protocol *>(storage & ~IsObjCBit);
return reinterpret_cast<TargetPointer<Runtime, Protocol> >(
storage & ~IsObjCBit);
}
#endif
};
Expand Down
64 changes: 61 additions & 3 deletions include/swift/Remote/MetadataReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include "swift/Remote/MemoryReader.h"
#include "swift/Demangling/Demangler.h"
#include "swift/Demangling/TypeDecoder.h"
#include "swift/Basic/Defer.h"
#include "swift/Basic/Range.h"
#include "swift/Basic/LLVM.h"
#include "swift/Runtime/Unreachable.h"
Expand Down Expand Up @@ -82,6 +83,19 @@ struct delete_with_free {
}
};

#if SWIFT_OBJC_INTEROP
/// Layout of a small prefix of an Objective-C protocol, used only to
/// directly extract the name of the protocol.
template <typename Runtime>
struct TargetObjCProtocolPrefix {
/// Unused by the Swift runtime.
TargetPointer<Runtime, const void> _ObjC_Isa;

/// The mangled name of the protocol.
TargetPointer<Runtime, const char> Name;
};
#endif

/// A generic reader of metadata.
///
/// BuilderType must implement a particular interface which is currently
Expand Down Expand Up @@ -359,8 +373,31 @@ class MetadataReader {

std::vector<BuiltProtocolDecl> Protocols;
for (auto ProtocolAddress : Exist->getProtocols()) {
auto ProtocolDescriptor = readProtocolDescriptor(
ProtocolAddress.getProtocolDescriptorUnchecked());
#if SWIFT_OBJC_INTEROP
// Check whether we have an Objective-C protocol.
if (ProtocolAddress.isObjC()) {
auto MangledNameStr =
readObjCProtocolName(ProtocolAddress.getObjCProtocol());

StringRef MangledName =
Demangle::dropSwiftManglingPrefix(MangledNameStr);

Demangle::Context DCtx;
auto Demangled = DCtx.demangleTypeAsNode(MangledName);
if (!Demangled)
return BuiltType();

auto Protocol = Builder.createProtocolDecl(Demangled);
if (!Protocol)
return BuiltType();

Protocols.push_back(Protocol);
continue;
}
#endif

auto ProtocolDescriptor = readSwiftProtocolDescriptor(
ProtocolAddress.getSwiftProtocol());
if (!ProtocolDescriptor)
return BuiltType();

Expand Down Expand Up @@ -1270,7 +1307,7 @@ class MetadataReader {
}

OwnedProtocolDescriptorRef
readProtocolDescriptor(StoredPointer Address) {
readSwiftProtocolDescriptor(StoredPointer Address) {
auto Size = sizeof(TargetProtocolDescriptor<Runtime>);
auto Buffer = (uint8_t *)malloc(Size);
if (!Reader->readBytes(RemoteAddress(Address), Buffer, Size)) {
Expand All @@ -1282,6 +1319,27 @@ class MetadataReader {
return OwnedProtocolDescriptorRef(Casted);
}

#if SWIFT_OBJC_INTEROP
std::string readObjCProtocolName(StoredPointer Address) {
auto Size = sizeof(TargetObjCProtocolPrefix<Runtime>);
auto Buffer = (uint8_t *)malloc(Size);
SWIFT_DEFER {
free(Buffer);
};

if (!Reader->readBytes(RemoteAddress(Address), Buffer, Size))
return std::string();

auto ProtocolDescriptor
= reinterpret_cast<TargetObjCProtocolPrefix<Runtime> *>(Buffer);
std::string Name;
if (!Reader->readString(RemoteAddress(ProtocolDescriptor->Name), Name))
return std::string();

return Name;
}
#endif

// TODO: We need to be able to produce protocol conformances for each
// substitution type as well in order to accurately rebuild bound generic
// types or types in protocol-constrained inner contexts.
Expand Down
15 changes: 14 additions & 1 deletion stdlib/public/runtime/Demangle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,20 @@ swift::_swift_buildDemanglingForMetadata(const Metadata *type,

for (auto protocol : protocols) {
// The protocol name is mangled as a type symbol, with the _Tt prefix.
StringRef ProtoName(protocol.getName());
StringRef ProtoName;
std::string ProtoNameScratch;
#if SWIFT_OBJC_INTEROP
// For Objective-C protocols, retrieve the protocol name from the
// runtime.
if (protocol.isObjC()) {
ProtoNameScratch = protocol_getName(protocol.getObjCProtocol());
ProtoName = ProtoNameScratch;
} else {
ProtoName = protocol.getSwiftProtocol()->Name;
}
#else
ProtoName = protocol.getSwiftProtocol()->Name;
#endif
NodePointer protocolNode = Dem.demangleSymbol(ProtoName);

// ObjC protocol names aren't mangled.
Expand Down
42 changes: 17 additions & 25 deletions unittests/runtime/Metadata.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
#include <thread>
#include <condition_variable>

#if SWIFT_OBJC_INTEROP
#include <objc/runtime.h>
#endif

using namespace swift;

// Race testing.
Expand Down Expand Up @@ -287,17 +291,6 @@ ProtocolDescriptor ProtocolClassConstrained{
.withDispatchStrategy(ProtocolDispatchStrategy::Swift)
};

#if SWIFT_OBJC_INTEROP
ProtocolDescriptor ProtocolNoWitnessTable{
"_TMp8Metadata22ProtocolNoWitnessTable",
nullptr,
ProtocolDescriptorFlags()
.withSwift(true)
.withClassConstraint(ProtocolClassConstraint::Class)
.withDispatchStrategy(ProtocolDispatchStrategy::ObjC)
};
#endif

TEST(MetadataTest, getExistentialMetadata) {
RaceTest_ExpectEqual<const ExistentialTypeMetadata *>(
[&]() -> const ExistentialTypeMetadata * {
Expand Down Expand Up @@ -327,8 +320,7 @@ TEST(MetadataTest, getExistentialMetadata) {
EXPECT_EQ(1U, a->Flags.getNumWitnessTables());
EXPECT_EQ(ProtocolClassConstraint::Any, a->Flags.getClassConstraint());
EXPECT_EQ(1U, a->NumProtocols);
EXPECT_EQ(&ProtocolA,
a->getProtocols()[0].getProtocolDescriptorUnchecked());
EXPECT_EQ(&ProtocolA, a->getProtocols()[0].getSwiftProtocol());
EXPECT_EQ(SpecialProtocol::None,
a->Flags.getSpecialProtocol());
EXPECT_EQ(nullptr,
Expand All @@ -349,8 +341,7 @@ TEST(MetadataTest, getExistentialMetadata) {
EXPECT_EQ(1U, b->Flags.getNumWitnessTables());
EXPECT_EQ(ProtocolClassConstraint::Any, b->Flags.getClassConstraint());
EXPECT_EQ(1U, b->NumProtocols);
EXPECT_EQ(&ProtocolB,
b->getProtocols()[0].getProtocolDescriptorUnchecked());
EXPECT_EQ(&ProtocolB, b->getProtocols()[0].getSwiftProtocol());
EXPECT_EQ(SpecialProtocol::None,
b->Flags.getSpecialProtocol());
EXPECT_EQ(nullptr,
Expand All @@ -375,15 +366,17 @@ TEST(MetadataTest, getExistentialMetadata) {
EXPECT_EQ(SpecialProtocol::None,
classConstrained->Flags.getSpecialProtocol());
EXPECT_EQ(&ProtocolClassConstrained,
classConstrained->getProtocols()[0].getProtocolDescriptorUnchecked());
classConstrained->getProtocols()[0].getSwiftProtocol());
EXPECT_EQ(nullptr,
classConstrained->getSuperclassConstraint());
return classConstrained;
});

#if SWIFT_OBJC_INTEROP
#define EXAMPLE_OBJC_PROTOCOL_NAME "NSCopying"

ProtocolDescriptorRef protoList7[] = {
ProtocolDescriptorRef::forObjC((Protocol *)&ProtocolNoWitnessTable)
ProtocolDescriptorRef::forObjC(objc_getProtocol(EXAMPLE_OBJC_PROTOCOL_NAME))
};
RaceTest_ExpectEqual<const ExistentialTypeMetadata *>(
[&]() -> const ExistentialTypeMetadata * {
Expand All @@ -398,8 +391,8 @@ TEST(MetadataTest, getExistentialMetadata) {
EXPECT_EQ(1U, noWitnessTable->NumProtocols);
EXPECT_EQ(SpecialProtocol::None,
noWitnessTable->Flags.getSpecialProtocol());
EXPECT_EQ(&ProtocolNoWitnessTable,
noWitnessTable->getProtocols()[0].getProtocolDescriptorUnchecked());
EXPECT_EQ(objc_getProtocol(EXAMPLE_OBJC_PROTOCOL_NAME),
noWitnessTable->getProtocols()[0].getObjCProtocol());
EXPECT_EQ(nullptr,
noWitnessTable->getSuperclassConstraint());
return noWitnessTable;
Expand All @@ -408,7 +401,8 @@ TEST(MetadataTest, getExistentialMetadata) {
RaceTest_ExpectEqual<const ExistentialTypeMetadata *>(
[&]() -> const ExistentialTypeMetadata * {
ProtocolDescriptorRef protoList8[] = {
ProtocolDescriptorRef::forObjC((Protocol*)&ProtocolNoWitnessTable),
ProtocolDescriptorRef::forObjC(
objc_getProtocol(EXAMPLE_OBJC_PROTOCOL_NAME)),
ProtocolDescriptorRef::forSwift(&ProtocolA),
ProtocolDescriptorRef::forSwift(&ProtocolB)
};
Expand Down Expand Up @@ -637,7 +631,7 @@ TEST(MetadataTest, getExistentialTypeMetadata_subclass) {
EXPECT_EQ(ProtocolClassConstraint::Class,
ex1->Flags.getClassConstraint());
EXPECT_EQ(1U, ex1->NumProtocols);
EXPECT_EQ(&OpaqueProto1, ex1->getProtocols()[0].getProtocolDescriptorUnchecked());
EXPECT_EQ(&OpaqueProto1, ex1->getProtocols()[0].getSwiftProtocol());
EXPECT_EQ(&MetadataTest2, ex1->getSuperclassConstraint());
return ex1;
});
Expand All @@ -660,10 +654,8 @@ TEST(MetadataTest, getExistentialTypeMetadata_subclass) {
EXPECT_EQ(ProtocolClassConstraint::Class,
ex2->Flags.getClassConstraint());
EXPECT_EQ(2U, ex2->NumProtocols);
EXPECT_TRUE(ex2->getProtocols()[0].getProtocolDescriptorUnchecked()
== &OpaqueProto1 &&
ex2->getProtocols()[1].getProtocolDescriptorUnchecked()
== &ClassProto1);
EXPECT_TRUE(ex2->getProtocols()[0].getSwiftProtocol() == &OpaqueProto1 &&
ex2->getProtocols()[1].getSwiftProtocol() == &ClassProto1);
EXPECT_EQ(&MetadataTest2, ex2->getSuperclassConstraint());
return ex2;
});
Expand Down