Skip to content

Commit 9ceb3fa

Browse files
committed
Runtime: Add support for Objective-C resilient class stubs
This adds a new tail-allocated field to class context descriptors storing a pointer to an Objective-C class stub. When the stub is present, we use the new _objc_realizeClassFromSwift() entry point to realize the class instead of calling objc_readClassPair(). This should attach categories to the realized class, if they were emitted to reference the stub.
1 parent 1864f6f commit 9ceb3fa

File tree

3 files changed

+95
-8
lines changed

3 files changed

+95
-8
lines changed

include/swift/ABI/Metadata.h

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3683,6 +3683,20 @@ struct TargetResilientSuperclass {
36833683
TargetRelativeDirectPointer<Runtime, const void, /*nullable*/true> Superclass;
36843684
};
36853685

3686+
template <typename Runtime>
3687+
struct TargetObjCResilientClassStub {
3688+
/// A relative pointer to an Objective-C resilient class stub:
3689+
/// - isa pointer, always 1
3690+
/// - an update callback, of type 'Class (*)(Class *, objc_class_stub *)'
3691+
///
3692+
/// Class stubs are used for two purposes:
3693+
///
3694+
/// - Objective-C can reference class stubs when calling static methods.
3695+
/// - Objective-C and Swift can reference class stubs when emitting
3696+
/// categories (in Swift, extensions with @objc members).
3697+
TargetRelativeDirectPointer<Runtime, const void> Stub;
3698+
};
3699+
36863700
template <typename Runtime>
36873701
class TargetClassDescriptor final
36883702
: public TargetTypeContextDescriptor<Runtime>,
@@ -3695,7 +3709,8 @@ class TargetClassDescriptor final
36953709
TargetVTableDescriptorHeader<Runtime>,
36963710
TargetMethodDescriptor<Runtime>,
36973711
TargetOverrideTableHeader<Runtime>,
3698-
TargetMethodOverrideDescriptor<Runtime>> {
3712+
TargetMethodOverrideDescriptor<Runtime>,
3713+
TargetObjCResilientClassStub<Runtime>> {
36993714
private:
37003715
using TrailingGenericContextObjects =
37013716
TrailingGenericContextObjects<TargetClassDescriptor<Runtime>,
@@ -3706,7 +3721,8 @@ class TargetClassDescriptor final
37063721
TargetVTableDescriptorHeader<Runtime>,
37073722
TargetMethodDescriptor<Runtime>,
37083723
TargetOverrideTableHeader<Runtime>,
3709-
TargetMethodOverrideDescriptor<Runtime>>;
3724+
TargetMethodOverrideDescriptor<Runtime>,
3725+
TargetObjCResilientClassStub<Runtime>>;
37103726

37113727
using TrailingObjects =
37123728
typename TrailingGenericContextObjects::TrailingObjects;
@@ -3722,6 +3738,8 @@ class TargetClassDescriptor final
37223738
TargetForeignMetadataInitialization<Runtime>;
37233739
using SingletonMetadataInitialization =
37243740
TargetSingletonMetadataInitialization<Runtime>;
3741+
using ObjCResilientClassStub =
3742+
TargetObjCResilientClassStub<Runtime>;
37253743

37263744
using StoredPointer = typename Runtime::StoredPointer;
37273745
using StoredPointerDifference = typename Runtime::StoredPointerDifference;
@@ -3759,7 +3777,9 @@ class TargetClassDescriptor final
37593777
/// positive size of metadata objects of this class (in words).
37603778
uint32_t MetadataPositiveSizeInWords;
37613779

3762-
// Maybe add something here that's useful only for resilient types?
3780+
/// Classes with resilient ancestry instead use this flags word to
3781+
/// indicate the presence of an Objective-C resilient class stub.
3782+
ExtraClassDescriptorFlags ExtraClassFlags;
37633783
};
37643784

37653785
/// The number of additional members added by this class to the class
@@ -3835,6 +3855,10 @@ class TargetClassDescriptor final
38353855
return getOverrideTable()->NumEntries;
38363856
}
38373857

3858+
size_t numTrailingObjects(OverloadToken<ObjCResilientClassStub>) const {
3859+
return hasObjCResilientClassStub() ? 1 : 0;
3860+
}
3861+
38383862
public:
38393863
const TargetRelativeDirectPointer<Runtime, const void, /*nullable*/true> &
38403864
getResilientSuperclass() const {
@@ -3954,7 +3978,24 @@ class TargetClassDescriptor final
39543978
&& i < numTrailingObjects(OverloadToken<MethodDescriptor>{}));
39553979
return getMethodDescriptors()[i].Impl.get();
39563980
}
3957-
3981+
3982+
/// Whether this context descriptor references an Objective-C resilient
3983+
/// class stub. See the description of TargetObjCResilientClassStub above
3984+
/// for details.
3985+
bool hasObjCResilientClassStub() const {
3986+
if (!hasResilientSuperclass())
3987+
return false;
3988+
return ExtraClassFlags.hasObjCResilientClassStub();
3989+
}
3990+
3991+
const void *getObjCResilientClassStub() const {
3992+
if (!hasObjCResilientClassStub())
3993+
return nullptr;
3994+
3995+
return this->template getTrailingObjects<ObjCResilientClassStub>()
3996+
->Stub.get();
3997+
}
3998+
39583999
static bool classof(const TargetContextDescriptor<Runtime> *cd) {
39594000
return cd->getKind() == ContextDescriptorKind::Class;
39604001
}

include/swift/ABI/MetadataValues.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1309,6 +1309,28 @@ class TypeContextDescriptorFlags : public FlagSet<uint16_t> {
13091309
class_setResilientSuperclassReferenceKind)
13101310
};
13111311

1312+
/// Extra flags for resilient classes, since we need more than 16 bits of
1313+
/// flags there.
1314+
class ExtraClassDescriptorFlags : public FlagSet<uint32_t> {
1315+
enum {
1316+
/// Set if the context descriptor includes a pointer to an Objective-C
1317+
/// resilient class stub structure. See the description of
1318+
/// TargetObjCResilientClassStub in Metadata.h for details.
1319+
///
1320+
/// Only meaningful for class descriptors when Objective-C interop is
1321+
/// enabled.
1322+
HasObjCResilientClassStub = 0,
1323+
};
1324+
1325+
public:
1326+
explicit ExtraClassDescriptorFlags(uint32_t bits) : FlagSet(bits) {}
1327+
constexpr ExtraClassDescriptorFlags() {}
1328+
1329+
FLAGSET_DEFINE_FLAG_ACCESSORS(HasObjCResilientClassStub,
1330+
hasObjCResilientClassStub,
1331+
setObjCResilientClassStub)
1332+
};
1333+
13121334
/// Flags for protocol context descriptors. These values are used as the
13131335
/// kindSpecificFlags of the ContextDescriptorFlags for the protocol.
13141336
class ProtocolContextDescriptorFlags : public FlagSet<uint16_t> {

stdlib/public/runtime/Metadata.cpp

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2641,6 +2641,11 @@ getSuperclassMetadata(ClassMetadata *self, bool allowDependency) {
26412641
return {MetadataDependency(), super};
26422642
}
26432643

2644+
// Suppress diagnostic about the availability of _objc_realizeClassFromSwift.
2645+
// We test availability with a nullptr check, but the compiler doesn't see that.
2646+
#pragma clang diagnostic push
2647+
#pragma clang diagnostic ignored "-Wunguarded-availability-new"
2648+
26442649
static SWIFT_CC(swift) MetadataDependency
26452650
_swift_initClassMetadataImpl(ClassMetadata *self,
26462651
ClassLayoutFlags layoutFlags,
@@ -2667,6 +2672,11 @@ _swift_initClassMetadataImpl(ClassMetadata *self,
26672672
(void)unused;
26682673
setUpObjCRuntimeGetImageNameFromClass();
26692674
}, nullptr);
2675+
2676+
// Temporary workaround until _objc_realizeClassFromSwift is in the SDK.
2677+
static auto _objc_realizeClassFromSwift =
2678+
(Class (*)(Class _Nullable, const void *_Nullable))
2679+
dlsym(RTLD_NEXT, "_objc_realizeClassFromSwift");
26702680
#endif
26712681

26722682
// Copy field offsets, generic arguments and (if necessary) vtable entries
@@ -2681,23 +2691,37 @@ _swift_initClassMetadataImpl(ClassMetadata *self,
26812691
initClassFieldOffsetVector(self, numFields, fieldTypes, fieldOffsets);
26822692

26832693
#if SWIFT_OBJC_INTEROP
2684-
if (self->getDescription()->isGeneric())
2694+
auto *description = self->getDescription();
2695+
if (description->isGeneric()) {
2696+
assert(!description->hasObjCResilientClassStub());
26852697
initGenericObjCClass(self, numFields, fieldTypes, fieldOffsets);
2686-
else {
2698+
} else {
26872699
initObjCClass(self, numFields, fieldTypes, fieldOffsets);
26882700

26892701
// Register this class with the runtime. This will also cause the
26902702
// runtime to slide the field offsets stored in the field offset
26912703
// globals. Note that the field offset vector is *not* updated;
26922704
// however we should not be using it for anything in a non-generic
26932705
// class.
2694-
swift_instantiateObjCClass(self);
2706+
if (auto *stub = description->getObjCResilientClassStub()) {
2707+
if (_objc_realizeClassFromSwift == nullptr) {
2708+
fatalError(0, "class %s requires missing Objective-C runtime feature; "
2709+
"the deployment target was newer than this OS\n",
2710+
self->getDescription()->Name.get());
2711+
}
2712+
_objc_realizeClassFromSwift((Class) self, stub);
2713+
} else
2714+
swift_instantiateObjCClass(self);
26952715
}
2716+
#else
2717+
assert(!description->hasObjCResilientClassStub());
26962718
#endif
26972719

26982720
return MetadataDependency();
26992721
}
27002722

2723+
#pragma clang diagnostic pop
2724+
27012725
void swift::swift_initClassMetadata(ClassMetadata *self,
27022726
ClassLayoutFlags layoutFlags,
27032727
size_t numFields,
@@ -2737,7 +2761,7 @@ _swift_updateClassMetadataImpl(ClassMetadata *self,
27372761
#ifndef OBJC_REALIZECLASSFROMSWIFT_DEFINED
27382762
// Temporary workaround until _objc_realizeClassFromSwift is in the SDK.
27392763
static auto _objc_realizeClassFromSwift =
2740-
(Class (*)(Class _Nullable, void* _Nullable))
2764+
(Class (*)(Class _Nullable, const void *_Nullable))
27412765
dlsym(RTLD_NEXT, "_objc_realizeClassFromSwift");
27422766
#endif
27432767

0 commit comments

Comments
 (0)