Skip to content

[IRGen] Correctly emit metadata for ObjC class properties #2011

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

Merged
Merged
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
102 changes: 65 additions & 37 deletions lib/IRGen/GenClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -770,7 +770,8 @@ namespace {
SmallVector<llvm::Constant*, 16> OptInstanceMethods;
SmallVector<llvm::Constant*, 16> OptClassMethods;
SmallVector<llvm::Constant*, 4> Protocols;
SmallVector<llvm::Constant*, 8> Properties;
SmallVector<llvm::Constant*, 8> InstanceProperties;
SmallVector<llvm::Constant*, 8> ClassProperties;
SmallVector<llvm::Constant*, 8> InstanceMethodTypesExt;
SmallVector<llvm::Constant*, 8> ClassMethodTypesExt;
SmallVector<llvm::Constant*, 8> OptInstanceMethodTypesExt;
Expand Down Expand Up @@ -918,74 +919,93 @@ namespace {
public:
llvm::Constant *emitCategory() {
assert(TheExtension && "can't emit category data for a class");
SmallVector<llvm::Constant*, 11> fields;
llvm::Constant *fields[8];
// struct category_t {
// char const *name;
fields.push_back(IGM.getAddrOfGlobalString(CategoryName));
fields[0] = IGM.getAddrOfGlobalString(CategoryName);
// const class_t *theClass;
if (getClass()->hasClangNode())
fields.push_back(IGM.getAddrOfObjCClass(getClass(), NotForDefinition));
fields[1] = IGM.getAddrOfObjCClass(getClass(), NotForDefinition);
else {
auto type = getSelfType(getClass()).getSwiftRValueType();
llvm::Constant *metadata =
tryEmitConstantHeapMetadataRef(IGM, type, /*allowUninit*/ true);
assert(metadata &&
"extended objc class doesn't have constant metadata?");
fields.push_back(metadata);
fields[1] = metadata;
}
// const method_list_t *instanceMethods;
fields.push_back(buildInstanceMethodList());
fields[2] = buildInstanceMethodList();
// const method_list_t *classMethods;
fields.push_back(buildClassMethodList());
fields[3] = buildClassMethodList();
// const protocol_list_t *baseProtocols;
fields.push_back(buildProtocolList());
fields[4] = buildProtocolList();
// const property_list_t *properties;
fields.push_back(buildPropertyList());
fields[5] = buildPropertyList(ForClass);
// const property_list_t *classProperties;
fields[6] = buildPropertyList(ForMetaClass);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We added a uint32_t size = sizeof(category_t) field here (rdar://24804226).

// uint32_t size;
auto sizeofPointer = IGM.getPointerSize().getValue();
unsigned size = sizeofPointer * llvm::array_lengthof(fields);
// Adjust for 'size', which is always 32 bits rather than pointer-sized.
// FIXME: Clang does this by using non-ad-hoc types for ObjC runtime
// structures.
size -= (sizeofPointer - 4) * 1;
fields[7] = llvm::ConstantInt::get(IGM.Int32Ty, size);
// };

return buildGlobalVariable(fields, "_CATEGORY_");
}

llvm::Constant *emitProtocol() {
SmallVector<llvm::Constant*, 11> fields;
llvm::Constant *fields[13];
llvm::SmallString<64> nameBuffer;

assert(isBuildingProtocol() && "not emitting a protocol");

// struct protocol_t {
// Class super;
fields.push_back(null());
fields[0] = null();
// char const *name;
fields.push_back(IGM.getAddrOfGlobalString(getEntityName(nameBuffer)));
fields[1] = IGM.getAddrOfGlobalString(getEntityName(nameBuffer));
// const protocol_list_t *baseProtocols;
fields.push_back(buildProtocolList());
fields[2] = buildProtocolList();
// const method_list_t *requiredInstanceMethods;
fields.push_back(buildInstanceMethodList());
fields[3] = buildInstanceMethodList();
// const method_list_t *requiredClassMethods;
fields.push_back(buildClassMethodList());
fields[4] = buildClassMethodList();
// const method_list_t *optionalInstanceMethods;
fields.push_back(buildOptInstanceMethodList());
fields[5] = buildOptInstanceMethodList();
// const method_list_t *optionalClassMethods;
fields.push_back(buildOptClassMethodList());
fields[6] = buildOptClassMethodList();
// const property_list_t *properties;
fields.push_back(buildPropertyList());
fields[7] = buildPropertyList(ForClass);

// uint32_t size;
unsigned size = IGM.getPointerSize().getValue() * fields.size() +
IGM.getPointerSize().getValue(); // This is for extendedMethodTypes
size += 8; // 'size' and 'flags' fields that haven't been added yet.
fields.push_back(llvm::ConstantInt::get(IGM.Int32Ty, size));
auto sizeofPointer = IGM.getPointerSize().getValue();
unsigned size = sizeofPointer * llvm::array_lengthof(fields);
// Adjust for 'size' and 'flags', which are always 32 bits rather than
// pointer-sized.
// FIXME: Clang does this by using non-ad-hoc types for ObjC runtime
// structures.
size -= (sizeofPointer - 4) * 2;
fields[8] = llvm::ConstantInt::get(IGM.Int32Ty, size);

// uint32_t flags;
auto flags = ProtocolDescriptorFlags()
.withSwift(!getProtocol()->hasClangNode())
.withClassConstraint(ProtocolClassConstraint::Class)
.withDispatchStrategy(ProtocolDispatchStrategy::ObjC)
.withSpecialProtocol(getSpecialProtocolID(getProtocol()));

fields.push_back(llvm::ConstantInt::get(IGM.Int32Ty, flags.getIntValue()));

// const char ** extendedMethodTypes;
fields.push_back(buildOptExtendedMethodTypes());
fields[9] = llvm::ConstantInt::get(IGM.Int32Ty, flags.getIntValue());

// const char ** extendedMethodTypes;
fields[10] = buildOptExtendedMethodTypes();
// const char *demangledName;
fields[11] = null();
// const property_list_t *classProperties;
fields[12] = buildPropertyList(ForMetaClass);
// };

return buildGlobalVariable(fields, "_PROTOCOL_");
Expand Down Expand Up @@ -1062,7 +1082,7 @@ namespace {
fields.push_back(null());

// const property_list_t *baseProperties;
fields.push_back(forMeta ? null() : buildPropertyList());
fields.push_back(buildPropertyList(forMeta));

// };

Expand Down Expand Up @@ -1443,10 +1463,11 @@ namespace {
/// Properties need to be collected in the properties list.
void visitProperty(VarDecl *var) {
if (requiresObjCPropertyDescriptor(IGM, var)) {
// ObjC doesn't support formal class properties.
if (!var->isStatic())
if (llvm::Constant *prop = buildProperty(var))
Properties.push_back(prop);
if (llvm::Constant *prop = buildProperty(var)) {
auto &properties =
(var->isStatic() ? ClassProperties : InstanceProperties);
properties.push_back(prop);
}

// Don't emit getter/setter descriptors for @NSManaged properties.
if (var->getAttrs().hasAttribute<NSManagedAttr>() ||
Expand Down Expand Up @@ -1569,12 +1590,19 @@ namespace {
/// };
///
/// This method does not return a value of a predictable type.
llvm::Constant *buildPropertyList() {
llvm::Constant *buildPropertyList(ForMetaClass_t classOrMeta) {
Size eltSize = 2 * IGM.getPointerSize();
return buildOptionalList(Properties, eltSize,
chooseNamePrefix("_PROPERTIES_",
"_CATEGORY_PROPERTIES_",
"_PROTOCOL_PROPERTIES_"));
StringRef namePrefix;
if (classOrMeta == ForClass) {
return buildOptionalList(InstanceProperties, eltSize,
chooseNamePrefix("_PROPERTIES_",
"_CATEGORY_PROPERTIES_",
"_PROTOCOL_PROPERTIES_"));
}
return buildOptionalList(ClassProperties, eltSize,
chooseNamePrefix("_CLASS_PROPERTIES_",
"_CATEGORY_CLASS_PROPERTIES_",
"_PROTOCOL_CLASS_PROPERTIES_"));
}

/*** General ***********************************************************/
Expand Down
11 changes: 0 additions & 11 deletions lib/IRGen/IRGen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -90,17 +90,6 @@ static void addAddressSanitizerPasses(const PassManagerBuilder &Builder,
PM.add(createAddressSanitizerModulePass());
}

// FIXME: Copied from clang/lib/CodeGen/CGObjCMac.cpp.
// These should be moved to a single definition shared by clang and swift.
enum ImageInfoFlags {
eImageInfo_FixAndContinue = (1 << 0),
eImageInfo_GarbageCollected = (1 << 1),
eImageInfo_GCOnly = (1 << 2),
eImageInfo_OptimizedByDyld = (1 << 3),
eImageInfo_CorrectedSynthesize = (1 << 4),
eImageInfo_ImageIsSimulated = (1 << 5)
};

std::tuple<llvm::TargetOptions, std::string, std::vector<std::string>>
swift::getIRTargetOptions(IRGenOptions &Opts, ASTContext &Ctx) {
// Things that maybe we should collect from the command line:
Expand Down
107 changes: 106 additions & 1 deletion test/IRGen/objc_properties.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@
bareIvar = SomeObject()
wibble = SomeObject()
}

static var sharedProp: Int64 = 0
}

extension SomeObject {
Expand All @@ -38,6 +40,10 @@ extension SomeObject {
bareIvar = self
}
}

class var extensionClassProp : SomeObject.Type {
return self
}
}

// <rdar://problem/16952186> Crash with @lazy in @objc class
Expand All @@ -62,7 +68,10 @@ class Class17127126 {
lazy var x = 1
}


@objc protocol Proto {
var value: Int { get }
static var sharedInstance: AnyObject { get set }
}



Expand All @@ -82,6 +91,27 @@ class Class17127126 {
// CHECK: [[WIBBLE_NAME:@.*]] = private unnamed_addr constant [7 x i8] c"wobble\00"
// CHECK: [[WIBBLE_ATTRS:@.*]] = private unnamed_addr constant [50 x i8] c"T@\22_TtC15objc_properties10SomeObject\22,N,&,Vwibble\00"

// CHECK: [[SHARED_NAME:@.*]] = private unnamed_addr constant [11 x i8] c"sharedProp\00"
// CHECK: [[SHARED_ATTRS:@.*]] = private unnamed_addr constant [17 x i8] c"Tq,N,VsharedProp\00"

// CHECK: @_CLASS_PROPERTIES__TtC15objc_properties10SomeObject = private constant { {{.*}}] } {
// CHECK: i32 16,
// CHECK: i32 1,
// CHECK: [1 x { i8*, i8* }] [{
// CHECK: i8* getelementptr inbounds ([11 x i8], [11 x i8]* [[SHARED_NAME]], i64 0, i64 0),
// CHECK: i8* getelementptr inbounds ([17 x i8], [17 x i8]* [[SHARED_ATTRS]], i64 0, i64 0)
// CHECK: }]
// CHECK: }, section "__DATA, __objc_const", align 8

// CHECK: @_METACLASS_DATA__TtC15objc_properties10SomeObject = private constant { {{.*}} } {
// CHECK-SAME: i32 {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}},
// CHECK-SAME: i8* null,
// CHECK-SAME: i8* getelementptr inbounds ([{{.+}} x i8], [{{.+}} x i8]* {{@.+}}, i64 0, i64 0),
// CHECK-SAME: { {{.+}} }* @_CLASS_METHODS__TtC15objc_properties10SomeObject,
// CHECK-SAME: i8* null, i8* null, i8* null,
// CHECK-SAME: { {{.+}} }* @_CLASS_PROPERTIES__TtC15objc_properties10SomeObject
// CHECK-SAME: }, section "__DATA, __objc_const", align 8

// CHECK: @_INSTANCE_METHODS__TtC15objc_properties10SomeObject = private constant { {{.*}}] } {
// CHECK: i32 24,
// CHECK: i32 8,
Expand Down Expand Up @@ -138,8 +168,22 @@ class Class17127126 {
// CHECK: }]
// CHECK: }, section "__DATA, __objc_const", align 8

// CHECK: @_DATA__TtC15objc_properties10SomeObject = private constant { {{.+}} } {
// CHECK: i32 {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}}, i32 {{[0-9]+}},
// CHECK: i8* null,
// CHECK: i8* getelementptr inbounds ([{{.+}} x i8], [{{.+}} x i8]* {{@.+}}, i64 0, i64 0),
// CHECK: { {{.+}} }* @_INSTANCE_METHODS__TtC15objc_properties10SomeObject,
// CHECK: i8* null,
// CHECK: { {{.+}} }* @_IVARS__TtC15objc_properties10SomeObject,
// CHECK: i8* null,
// CHECK: { {{.+}} }* @_PROPERTIES__TtC15objc_properties10SomeObject
// CHECK: }, section "__DATA, __objc_const", align 8

// CHECK: [[EXTENSIONPROPERTY_NAME:@.*]] = private unnamed_addr constant [18 x i8] c"extensionProperty\00"

// CHECK: [[EXTENSIONCLASSPROPERTY_NAME:@.*]] = private unnamed_addr constant [19 x i8] c"extensionClassProp\00"
// CHECK: [[EXTENSIONCLASSPROPERTY_ATTRS:@.*]] = private unnamed_addr constant [7 x i8] c"T#,N,R\00"

// CHECK: @"_CATEGORY_INSTANCE_METHODS__TtC15objc_properties10SomeObject_$_objc_properties" = private constant { {{.*}}] } {
// CHECK: i32 24,
// CHECK: i32 2,
Expand All @@ -163,10 +207,71 @@ class Class17127126 {
// CHECK: }]
// CHECK: }, section "__DATA, __objc_const", align 8

// CHECK: @"_CATEGORY_CLASS_PROPERTIES__TtC15objc_properties10SomeObject_$_objc_properties" = private constant { {{.*}}] } {
// CHECK: i32 16,
// CHECK: i32 1,
// CHECK: [1 x { i8*, i8* }] [{
// CHECK: i8* getelementptr inbounds ([19 x i8], [19 x i8]* [[EXTENSIONCLASSPROPERTY_NAME]], i64 0, i64 0),
// CHECK: i8* getelementptr inbounds ([7 x i8], [7 x i8]* [[EXTENSIONCLASSPROPERTY_ATTRS]], i64 0, i64 0)
// CHECK: }]
// CHECK: }, section "__DATA, __objc_const", align 8

// CHECK: @"_CATEGORY__TtC15objc_properties10SomeObject_$_objc_properties" = private constant { {{.+}} } {
// CHECK: i8* getelementptr inbounds ([{{.+}} x i8], [{{.+}} x i8]* {{@.+}}, i64 0, i64 0),
// CHECK: %swift.type* bitcast (i64* getelementptr inbounds (<{ {{.+}} }>* @_TMfC15objc_properties10SomeObject, i32 0, i32 2) to %swift.type*),
// CHECK: { {{.+}} }* @"_CATEGORY_INSTANCE_METHODS__TtC15objc_properties10SomeObject_$_objc_properties",
// CHECK: { {{.+}} }* @"_CATEGORY_CLASS_METHODS__TtC15objc_properties10SomeObject_$_objc_properties",
// CHECK: i8* null,
// CHECK: { {{.+}} }* @"_CATEGORY_PROPERTIES__TtC15objc_properties10SomeObject_$_objc_properties",
// CHECK: { {{.+}} }* @"_CATEGORY_CLASS_PROPERTIES__TtC15objc_properties10SomeObject_$_objc_properties",
// CHECK: i32 60
// CHECK: }, section "__DATA, __objc_const", align 8


// CHECK: @_INSTANCE_METHODS__TtC15objc_properties4Tree =
// CHECK: i8* getelementptr inbounds ([7 x i8], [7 x i8]* @"\01L_selector_data(parent)", i64 0, i64 0),
// CHECK: i8* getelementptr inbounds ([8 x i8], [8 x i8]* [[GETTER_SIGNATURE]], i64 0, i64 0),
// CHECK: i8* bitcast (%2* (%2*, i8*)* @_TToFC15objc_properties4Treeg6parentXwGSqS0__ to i8*)
// CHECK: i8* getelementptr inbounds ([11 x i8], [11 x i8]* @"\01L_selector_data(setParent:)", i64 0, i64 0),
// CHECK: i8* getelementptr inbounds ([11 x i8], [11 x i8]* [[SETTER_SIGNATURE]], i64 0, i64 0),
// CHECK: i8* bitcast (void (%2*, i8*, %2*)* @_TToFC15objc_properties4Trees6parentXwGSqS0__ to i8*)

// CHECK: @_PROTOCOL__TtP15objc_properties5Proto_ = private constant { {{.+}} } {
// CHECK: i8* null,
// CHECK: i8* getelementptr inbounds ([{{.+}} x i8], [{{.+}} x i8]* {{@.+}}, i64 0, i64 0),
// CHECK: i8* null,
// CHECK: { {{.+}} }* @_PROTOCOL_INSTANCE_METHODS__TtP15objc_properties5Proto_,
// CHECK: { {{.+}} }* @_PROTOCOL_CLASS_METHODS__TtP15objc_properties5Proto_,
// CHECK: i8* null,
// CHECK: i8* null,
// CHECK: { {{.+}} }* @_PROTOCOL_PROPERTIES__TtP15objc_properties5Proto_,
// CHECK: i32 96, i32 1,
// CHECK: { {{.+}} }* @_PROTOCOL_METHOD_TYPES__TtP15objc_properties5Proto_,
// CHECK: i8* null,
// CHECK: { {{.+}} }* @_PROTOCOL_CLASS_PROPERTIES__TtP15objc_properties5Proto_
// CHECK: }, section "__DATA, __objc_const", align 8


// CHECK: [[PROTOCOLPROPERTY_NAME:@.+]] = private unnamed_addr constant [6 x i8] c"value\00"
// CHECK: [[PROTOCOLPROPERTY_ATTRS:@.+]] = private unnamed_addr constant [7 x i8] c"Tq,N,R\00"

// CHECK: [[PROTOCOLCLASSPROPERTY_NAME:@.+]] = private unnamed_addr constant [15 x i8] c"sharedInstance\00"
// CHECK: [[PROTOCOLCLASSPROPERTY_ATTRS:@.+]] = private unnamed_addr constant [7 x i8] c"T@,N,&\00"

// CHECK: @_PROTOCOL_PROPERTIES__TtP15objc_properties5Proto_ = private constant { {{.*}}] } {
// CHECK: i32 16,
// CHECK: i32 1,
// CHECK: [1 x { i8*, i8* }] [{
// CHECK: i8* getelementptr inbounds ([6 x i8], [6 x i8]* [[PROTOCOLPROPERTY_NAME]], i64 0, i64 0),
// CHECK: i8* getelementptr inbounds ([7 x i8], [7 x i8]* [[PROTOCOLPROPERTY_ATTRS]], i64 0, i64 0)
// CHECK: }]
// CHECK: }, section "__DATA, __objc_const", align 8

// CHECK: @_PROTOCOL_CLASS_PROPERTIES__TtP15objc_properties5Proto_ = private constant { {{.*}}] } {
// CHECK: i32 16,
// CHECK: i32 1,
// CHECK: [1 x { i8*, i8* }] [{
// CHECK: i8* getelementptr inbounds ([15 x i8], [15 x i8]* [[PROTOCOLCLASSPROPERTY_NAME]], i64 0, i64 0),
// CHECK: i8* getelementptr inbounds ([7 x i8], [7 x i8]* [[PROTOCOLCLASSPROPERTY_ATTRS]], i64 0, i64 0)
// CHECK: }]
// CHECK: }, section "__DATA, __objc_const", align 8
12 changes: 6 additions & 6 deletions test/IRGen/protocol_metadata.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,20 +37,20 @@ protocol ABO : A, B, O { func abo() }
// CHECK: }

// -- @objc protocol O uses ObjC symbol mangling and layout
// CHECK: @_PROTOCOL__TtP17protocol_metadata1O_ = private constant { {{.*}} i32, { [1 x i8*] }* } {
// CHECK: @_PROTOCOL__TtP17protocol_metadata1O_ = private constant { {{.*}} i32, { [1 x i8*] }*, i8*, i8* } {
// CHECK: @_PROTOCOL_INSTANCE_METHODS__TtP17protocol_metadata1O_,
// -- flags: 1 = Swift
// CHECK: i32 80, i32 1
// -- size, flags: 1 = Swift
// CHECK: i32 96, i32 1
// CHECK: }
// CHECK: @_PROTOCOL_METHOD_TYPES__TtP17protocol_metadata1O_
// CHECK: }

// -- @objc protocol OPT uses ObjC symbol mangling and layout
// CHECK: @_PROTOCOL__TtP17protocol_metadata3OPT_ = private constant { {{.*}} i32, { [4 x i8*] }* } {
// CHECK: @_PROTOCOL__TtP17protocol_metadata3OPT_ = private constant { {{.*}} i32, { [4 x i8*] }*, i8*, i8* } {
// CHECK: @_PROTOCOL_INSTANCE_METHODS_OPT__TtP17protocol_metadata3OPT_,
// CHECK: @_PROTOCOL_CLASS_METHODS_OPT__TtP17protocol_metadata3OPT_,
// -- flags: 1 = Swift
// CHECK: i32 80, i32 1
// -- size, flags: 1 = Swift
// CHECK: i32 96, i32 1
// CHECK: }
// CHECK: @_PROTOCOL_METHOD_TYPES__TtP17protocol_metadata3OPT_
// CHECK: }
Expand Down
10 changes: 10 additions & 0 deletions test/Interpreter/objc_class_properties.swift
Original file line number Diff line number Diff line change
Expand Up @@ -208,5 +208,15 @@ ClassProperties.test("namingConflict/protocol") {
expectEmpty(type.protoProp)
}

ClassProperties.test("runtime") {
let theClass: AnyObject = SwiftClass.self
let prop = class_getProperty(object_getClass(theClass), "value")
expectTrue(prop != nil)

let nameAsCString = property_getName(prop)
expectTrue(nameAsCString != nil)
expectEqual("value", String(cString: nameAsCString))
}

runAllTests()