Skip to content

Commit 618f111

Browse files
authored
Merge pull request #24189 from slavapestov/class-stubs-fix-and-tests-5.1
Class stubs fix and tests [5.1]
2 parents 81c966e + 6948dfe commit 618f111

File tree

14 files changed

+240
-46
lines changed

14 files changed

+240
-46
lines changed

lib/IRGen/GenCast.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,7 @@ FailableCastResult irgen::emitClassIdenticalCast(IRGenFunction &IGF,
107107
llvm::Value *targetMetadata;
108108
if ((targetMetadata =
109109
tryEmitConstantHeapMetadataRef(IGF.IGM, toType.getASTType(),
110-
/*allowUninitialized*/ false,
111-
/*allowStub*/ false))) {
110+
/*allowUninitialized*/ false))) {
112111
// ok
113112
} else {
114113
targetMetadata

lib/IRGen/GenClass.cpp

Lines changed: 27 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1194,7 +1194,32 @@ namespace {
11941194
if (categoryCount > 0)
11951195
os << categoryCount;
11961196
}
1197-
1197+
1198+
llvm::Constant *getClassMetadataRef() {
1199+
auto *theClass = getClass();
1200+
1201+
if (theClass->hasClangNode())
1202+
return IGM.getAddrOfObjCClass(theClass, NotForDefinition);
1203+
1204+
// Note that getClassMetadataStrategy() will return
1205+
// ClassMetadataStrategy::Resilient if the class is
1206+
// from another resilience domain, even if inside that
1207+
// resilience domain the class has fixed metadata
1208+
// layout.
1209+
//
1210+
// Since a class only has a class stub if its class
1211+
// hierarchy crosses resilience domains, we use a
1212+
// slightly different query here.
1213+
if (theClass->checkAncestry(AncestryFlags::ResilientOther)) {
1214+
assert(IGM.Context.LangOpts.EnableObjCResilientClassStubs);
1215+
return IGM.getAddrOfObjCResilientClassStub(theClass, NotForDefinition,
1216+
TypeMetadataAddress::AddressPoint);
1217+
}
1218+
1219+
auto type = getSelfType(theClass).getASTType();
1220+
return tryEmitConstantHeapMetadataRef(IGM, type, /*allowUninit*/ true);
1221+
}
1222+
11981223
public:
11991224
llvm::Constant *emitCategory() {
12001225
assert(TheExtension && "can't emit category data for a class");
@@ -1205,18 +1230,7 @@ namespace {
12051230
// char const *name;
12061231
fields.add(IGM.getAddrOfGlobalString(CategoryName));
12071232
// const class_t *theClass;
1208-
if (getClass()->hasClangNode())
1209-
fields.add(IGM.getAddrOfObjCClass(getClass(), NotForDefinition));
1210-
else {
1211-
auto type = getSelfType(getClass()).getASTType();
1212-
llvm::Constant *metadata =
1213-
tryEmitConstantHeapMetadataRef(IGM, type,
1214-
/*allowUninit*/ true,
1215-
/*allowStub*/ true);
1216-
assert(metadata &&
1217-
"extended objc class doesn't have constant metadata?");
1218-
fields.add(metadata);
1219-
}
1233+
fields.add(getClassMetadataRef());
12201234
// const method_list_t *instanceMethods;
12211235
fields.add(buildInstanceMethodList());
12221236
// const method_list_t *classMethods;

lib/IRGen/GenDecl.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -911,6 +911,12 @@ void IRGenModule::emitGlobalLists() {
911911
"regular,no_dead_strip"),
912912
llvm::GlobalValue::InternalLinkage, Int8PtrTy, false);
913913

914+
// And categories on class stubs.
915+
emitGlobalList(*this, ObjCCategoriesOnStubs, "objc_categories_stubs",
916+
GetObjCSectionName("__objc_catlist2",
917+
"regular,no_dead_strip"),
918+
llvm::GlobalValue::InternalLinkage, Int8PtrTy, false);
919+
914920
// Emit nonlazily realized class references in a second magic section to make
915921
// sure they are realized by the Objective-C runtime before any instances
916922
// are allocated.
@@ -3740,7 +3746,15 @@ void IRGenModule::emitExtension(ExtensionDecl *ext) {
37403746
"foreign types cannot have categories emitted");
37413747
llvm::Constant *category = emitCategoryData(*this, ext);
37423748
category = llvm::ConstantExpr::getBitCast(category, Int8PtrTy);
3743-
ObjCCategories.push_back(category);
3749+
3750+
auto *theClass = ext->getSelfClassDecl();
3751+
3752+
// Categories on class stubs are added to a separate list.
3753+
if (theClass->checkAncestry(AncestryFlags::ResilientOther))
3754+
ObjCCategoriesOnStubs.push_back(category);
3755+
else
3756+
ObjCCategories.push_back(category);
3757+
37443758
ObjCCategoryDecls.push_back(ext);
37453759
}
37463760
}

lib/IRGen/GenMeta.cpp

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1420,6 +1420,7 @@ namespace {
14201420
super::layout();
14211421
addVTable();
14221422
addOverrideTable();
1423+
addObjCResilientClassStubInfo();
14231424
}
14241425

14251426
void addIncompleteMetadataOrRelocationFunction() {
@@ -1653,6 +1654,20 @@ namespace {
16531654
// uint32_t FieldOffsetVectorOffset;
16541655
B.addInt32(getFieldVectorOffset() / IGM.getPointerSize());
16551656
}
1657+
1658+
void addObjCResilientClassStubInfo() {
1659+
if (IGM.getClassMetadataStrategy(getType()) !=
1660+
ClassMetadataStrategy::Resilient)
1661+
return;
1662+
1663+
if (!hasObjCResilientClassStub(IGM, getType()))
1664+
return;
1665+
1666+
B.addRelativeAddress(
1667+
IGM.getAddrOfObjCResilientClassStub(
1668+
getType(), NotForDefinition,
1669+
TypeMetadataAddress::AddressPoint));
1670+
}
16561671
};
16571672
} // end anonymous namespace
16581673

@@ -2546,8 +2561,7 @@ namespace {
25462561
Type type = Target->mapTypeIntoContext(Target->getSuperclass());
25472562
auto *metadata = tryEmitConstantHeapMetadataRef(
25482563
IGM, type->getCanonicalType(),
2549-
/*allowUninit*/ false,
2550-
/*allowStub*/ false);
2564+
/*allowUninit*/ false);
25512565
assert(metadata != nullptr);
25522566
B.add(metadata);
25532567
}
@@ -3143,12 +3157,6 @@ void irgen::emitClassMetadata(IRGenModule &IGM, ClassDecl *classDecl,
31433157
classDecl, NotForDefinition,
31443158
TypeMetadataAddress::AddressPoint);
31453159
emitObjCClassSymbol(IGM, classDecl, stub);
3146-
3147-
// @_objc_non_lazy_realization is only for use by the standard
3148-
// library, and we cannot support it with Objective-C class
3149-
// stubs (which there are none of in the standard library).
3150-
assert(!classDecl->getAttrs().hasAttribute<ObjCNonLazyRealizationAttr>());
3151-
IGM.addObjCClass(stub, /*eagerInitialization=*/false);
31523160
}
31533161
}
31543162
break;

lib/IRGen/IRGenModule.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -972,6 +972,8 @@ class IRGenModule {
972972
SmallVector<llvm::WeakTrackingVH, 4> ObjCNonLazyClasses;
973973
/// List of Objective-C categories, bitcast to i8*.
974974
SmallVector<llvm::WeakTrackingVH, 4> ObjCCategories;
975+
/// List of Objective-C categories on class stubs, bitcast to i8*.
976+
SmallVector<llvm::WeakTrackingVH, 4> ObjCCategoriesOnStubs;
975977
/// List of non-ObjC protocols described by this module.
976978
SmallVector<ProtocolDecl *, 4> SwiftProtocols;
977979
/// List of protocol conformances to generate descriptors for.

lib/IRGen/MetadataRequest.cpp

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -511,19 +511,12 @@ irgen::getRuntimeReifiedType(IRGenModule &IGM, CanType type) {
511511
llvm::Constant *
512512
irgen::tryEmitConstantHeapMetadataRef(IRGenModule &IGM,
513513
CanType type,
514-
bool allowDynamicUninitialized,
515-
bool allowStub) {
514+
bool allowDynamicUninitialized) {
516515
auto theDecl = type->getClassOrBoundGenericClass();
517516
assert(theDecl && "emitting constant heap metadata ref for non-class type?");
518517

519518
switch (IGM.getClassMetadataStrategy(theDecl)) {
520519
case ClassMetadataStrategy::Resilient:
521-
if (allowStub && IGM.Context.LangOpts.EnableObjCResilientClassStubs) {
522-
return IGM.getAddrOfObjCResilientClassStub(theDecl, NotForDefinition,
523-
TypeMetadataAddress::AddressPoint);
524-
}
525-
return nullptr;
526-
527520
case ClassMetadataStrategy::Singleton:
528521
if (!allowDynamicUninitialized)
529522
return nullptr;

lib/IRGen/MetadataRequest.h

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -592,8 +592,7 @@ CanType getRuntimeReifiedType(IRGenModule &IGM, CanType type);
592592
/// by a constant.
593593
llvm::Constant *tryEmitConstantHeapMetadataRef(IRGenModule &IGM,
594594
CanType type,
595-
bool allowUninitialized,
596-
bool allowStub);
595+
bool allowUninitialized);
597596

598597
enum class MetadataValueType { ObjCClass, TypeMetadata };
599598

stdlib/public/runtime/Metadata.cpp

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2673,12 +2673,19 @@ _swift_initClassMetadataImpl(ClassMetadata *self,
26732673
setUpObjCRuntimeGetImageNameFromClass();
26742674
}, nullptr);
26752675

2676+
#ifndef OBJC_REALIZECLASSFROMSWIFT_DEFINED
26762677
// Temporary workaround until _objc_realizeClassFromSwift is in the SDK.
26772678
static auto _objc_realizeClassFromSwift =
2678-
(Class (*)(Class _Nullable, const void *_Nullable))
2679+
(Class (*)(Class _Nullable, void *_Nullable))
26792680
dlsym(RTLD_NEXT, "_objc_realizeClassFromSwift");
26802681
#endif
26812682

2683+
// Temporary workaround until objc_loadClassref is in the SDK.
2684+
static auto objc_loadClassref =
2685+
(Class (*)(void *))
2686+
dlsym(RTLD_NEXT, "objc_loadClassref");
2687+
#endif
2688+
26822689
// Copy field offsets, generic arguments and (if necessary) vtable entries
26832690
// from our superclass.
26842691
copySuperclassMetadataToSubclass(self, layoutFlags);
@@ -2705,12 +2712,13 @@ _swift_initClassMetadataImpl(ClassMetadata *self,
27052712
// however we should not be using it for anything in a non-generic
27062713
// class.
27072714
if (auto *stub = description->getObjCResilientClassStub()) {
2708-
if (_objc_realizeClassFromSwift == nullptr) {
2715+
if (_objc_realizeClassFromSwift == nullptr ||
2716+
objc_loadClassref == nullptr) {
27092717
fatalError(0, "class %s requires missing Objective-C runtime feature; "
27102718
"the deployment target was newer than this OS\n",
27112719
self->getDescription()->Name.get());
27122720
}
2713-
_objc_realizeClassFromSwift((Class) self, stub);
2721+
_objc_realizeClassFromSwift((Class) self, const_cast<void *>(stub));
27142722
} else
27152723
swift_instantiateObjCClass(self);
27162724
}
@@ -2762,7 +2770,7 @@ _swift_updateClassMetadataImpl(ClassMetadata *self,
27622770
#ifndef OBJC_REALIZECLASSFROMSWIFT_DEFINED
27632771
// Temporary workaround until _objc_realizeClassFromSwift is in the SDK.
27642772
static auto _objc_realizeClassFromSwift =
2765-
(Class (*)(Class _Nullable, const void *_Nullable))
2773+
(Class (*)(Class _Nullable, void *_Nullable))
27662774
dlsym(RTLD_NEXT, "_objc_realizeClassFromSwift");
27672775
#endif
27682776

test/IRGen/class_update_callback_with_stub.swift

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
// RUN: %target-swift-frontend -emit-module -enable-library-evolution -emit-module-path=%t/resilient_struct.swiftmodule -I %t %S/../Inputs/resilient_struct.swift
44
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/Inputs -I %t) -emit-module-path %t/resilient_class.swiftmodule -enable-library-evolution %S/../Inputs/resilient_class.swift
55
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/Inputs -I %t) -emit-module-path %t/resilient_objc_class.swiftmodule -enable-library-evolution %S/../Inputs/resilient_objc_class.swift
6-
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/Inputs -I %t) -I %t -emit-ir -enable-library-evolution -enable-resilient-objc-class-stubs %s | %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize --check-prefix=CHECK-%target-runtime -DINT=i%target-ptrsize
6+
// RUN: %target-swift-frontend(mock-sdk: -sdk %S/Inputs -I %t) -I %t -emit-ir -enable-library-evolution -enable-resilient-objc-class-stubs %s > %t/out
7+
// RUN: %FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-%target-ptrsize --check-prefix=CHECK-%target-runtime -DINT=i%target-ptrsize < %t/out
8+
// RUN: %FileCheck %s --check-prefix=NEGATIVE < %t/out
79

810
import Foundation
911
import resilient_class
@@ -49,6 +51,8 @@ import resilient_objc_class
4951
// CHECK-SAME: @"got.$s15resilient_class22ResilientOutsideParentCMn"
5052
// CHECK-SAME: @"got.$s15resilient_class22ResilientOutsideParentCACycfCTq"
5153
// CHECK-SAME: @"$s31class_update_callback_with_stub17ResilientSubclassCACycfC"
54+
// -- class stub
55+
// CHECK-SAME: @"$s31class_update_callback_with_stub17ResilientSubclassCMt"
5256
// CHECK-SAME: }>, section "__TEXT,__const", align 4
5357

5458

@@ -87,22 +91,27 @@ import resilient_objc_class
8791
// CHECK-LABEL: @"_CATEGORY__TtC31class_update_callback_with_stub27FixedLayoutNSObjectSubclass_$_class_update_callback_with_stub" = private constant
8892
// CHECK-SAME: @"$s31class_update_callback_with_stub27FixedLayoutNSObjectSubclassCMs"
8993

94+
// -- But not if the entire inheritance chain is in a single module
9095

91-
// -- The NSObject-derived class appears on the class list
96+
// CHECK-LABEL: @"_CATEGORY__TtC15resilient_class22ResilientOutsideParent_$_class_update_callback_with_stub" = private constant
97+
// CHECK-SAME: @"$s15resilient_class22ResilientOutsideParentCN"
9298

93-
// CHECK-LABEL: @objc_classes = internal global
94-
// CHECK-SAME: @"$s31class_update_callback_with_stub25ResilientNSObjectSubclassCMs"
95-
// CHECK-SAME: @"$s31class_update_callback_with_stub27FixedLayoutNSObjectSubclassCMs"
96-
// CHECK-SAME: , section "__DATA,__objc_classlist,regular,no_dead_strip"
9799

100+
// -- Class stubs do not appear in the class list
101+
102+
// NEGATIVE-NOT: @objc_classes =
98103

99104
// -- The category list
100105

101106
// CHECK-LABEL: @objc_categories = internal global
107+
// CHECK-SAME: @"_CATEGORY__TtC15resilient_class22ResilientOutsideParent_$_class_update_callback_with_stub"
108+
// CHECK-SAME: , section "__DATA,__objc_catlist,regular,no_dead_strip"
109+
110+
// CHECK-LABEL: @objc_categories_stubs = internal global
102111
// CHECK-SAME: @"_CATEGORY__TtC31class_update_callback_with_stub17ResilientSubclass_$_class_update_callback_with_stub"
103112
// CHECK-SAME: @"_CATEGORY__TtC31class_update_callback_with_stub25ResilientNSObjectSubclass_$_class_update_callback_with_stub"
104113
// CHECK-SAME: @"_CATEGORY__TtC31class_update_callback_with_stub27FixedLayoutNSObjectSubclass_$_class_update_callback_with_stub"
105-
// CHECK-SAME: , section "__DATA,__objc_catlist,regular,no_dead_strip"
114+
// CHECK-SAME: , section "__DATA,__objc_catlist2,regular,no_dead_strip"
106115

107116

108117
// -- Address point for class stubs
@@ -152,4 +161,8 @@ extension ResilientNSObjectSubclass {
152161

153162
extension FixedLayoutNSObjectSubclass {
154163
@objc public func objcMethod() {}
155-
}
164+
}
165+
166+
extension ResilientOutsideParent {
167+
@objc public func anObjcMethod() {}
168+
}
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
// RUN: %empty-directory(%t)
2+
3+
// RUN: %target-build-swift-dylib(%t/%target-library-name(resilient_struct)) -enable-library-evolution %S/../Inputs/resilient_struct.swift -emit-module -emit-module-path %t/resilient_struct.swiftmodule
4+
// RUN: %target-codesign %t/%target-library-name(resilient_struct)
5+
6+
// RUN: %target-build-swift-dylib(%t/%target-library-name(resilient_objc_class)) -I %t -L %t -lresilient_struct -enable-library-evolution %S/../Inputs/resilient_objc_class.swift -emit-module -emit-module-path %t/resilient_objc_class.swiftmodule -Xfrontend -enable-resilient-objc-class-stubs
7+
// RUN: %target-codesign %t/%target-library-name(resilient_objc_class)
8+
9+
// RUN: %target-build-swift %s -L %t -I %t -lresilient_struct -lresilient_objc_class -o %t/main %target-rpath(%t) -Xfrontend -enable-resilient-objc-class-stubs
10+
// RUN: %target-codesign %t/main
11+
12+
// RUN: %target-run %t/main %t/%target-library-name(resilient_struct) %t/%target-library-name(resilient_objc_class)
13+
14+
// REQUIRES: executable_test
15+
// REQUIRES: objc_interop
16+
17+
import StdlibUnittest
18+
import Foundation
19+
import resilient_objc_class
20+
21+
// Old OS versions do not have this hook.
22+
let loadClassrefMissing = {
23+
nil == dlsym(UnsafeMutableRawPointer(bitPattern: -2),
24+
"objc_loadClassref")
25+
}()
26+
27+
var ResilientClassTestSuite = TestSuite("ResilientClass")
28+
29+
class ResilientNSObjectSubclass : ResilientNSObjectOutsideParent {}
30+
31+
@objc protocol MyProtocol {
32+
func myMethod() -> Int
33+
}
34+
35+
extension ResilientNSObjectSubclass : MyProtocol {
36+
@objc func myMethod() -> Int { return 42 }
37+
}
38+
39+
ResilientClassTestSuite.test("category on my class")
40+
.skip(.custom({ loadClassrefMissing },
41+
reason: "class stubs support not present"))
42+
.code {
43+
print(ResilientNSObjectSubclass.self)
44+
let o = ResilientNSObjectSubclass()
45+
expectEqual(42, (o as MyProtocol).myMethod())
46+
}
47+
48+
@objc protocol AnotherProtocol {
49+
func anotherMethod() -> Int
50+
}
51+
52+
extension ResilientNSObjectOutsideParent : AnotherProtocol {
53+
@objc func anotherMethod() -> Int { return 69 }
54+
}
55+
56+
ResilientClassTestSuite.test("category on other class")
57+
.skip(.custom({ loadClassrefMissing },
58+
reason: "class stubs support not present"))
59+
.code {
60+
let o = ResilientNSObjectOutsideParent()
61+
expectEqual(69, (o as AnotherProtocol).anotherMethod())
62+
}
63+
64+
@_optimize(none) func blackHole<T>(_: T) {}
65+
66+
@_optimize(none) func forceMetadata() {
67+
blackHole(ResilientNSObjectSubclass())
68+
}
69+
70+
if loadClassrefMissing {
71+
ResilientClassTestSuite.test("RealizeResilientClass")
72+
.crashOutputMatches("class ResilientNSObjectSubclass requires missing Objective-C runtime feature")
73+
.code {
74+
expectCrashLater()
75+
print("About to crash...")
76+
forceMetadata()
77+
}
78+
}
79+
80+
runAllTests()
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import Foundation
2+
3+
open class BaseClass : NSObject {
4+
@objc dynamic open func instanceMethod() -> Int {
5+
return 42
6+
}
7+
8+
@objc dynamic open class func classMethod() -> Int {
9+
return 31337
10+
}
11+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module first {
2+
header "first.h"
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import first
2+
3+
public class DerivedClass : BaseClass {}

0 commit comments

Comments
 (0)