mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
IRGen: Make type(of:) behavior consistent in ObjC bridged contexts.
When we use type(of: x) on a class in an ObjC bridged context, the optimizer turns this into a SIL `value_metatype @objc` operation, which is supposed to get the dynamic type of the object as an ObjC class. This was previously lowered by IRGen into a `object_getClass` call, which extracts the isa pointer from the object, but is inconsistent with the `-class` method in ObjC or with the Swift-native behavior, which both look through artificial subclasses, proxies, and so on. This inconsistency led to observably different behavior between debug and release builds and between ObjC-bridged and native entry points, so provide an alternative runtime entry point that replicates the behavior of getting a native Swift class. Fixes SR-7258.
This commit is contained in:
@@ -4154,6 +4154,10 @@ swift_getObjCClassMetadata(const ClassMetadata *theClass);
|
||||
SWIFT_RUNTIME_EXPORT
|
||||
const ClassMetadata *
|
||||
swift_getObjCClassFromMetadata(const Metadata *theClass);
|
||||
|
||||
SWIFT_RUNTIME_EXPORT
|
||||
const ClassMetadata *
|
||||
swift_getObjCClassFromObject(HeapObject *object);
|
||||
#endif
|
||||
|
||||
/// \brief Fetch a unique type metadata object for a foreign type.
|
||||
|
||||
@@ -869,6 +869,12 @@ FUNCTION(GetObjCClassFromMetadata, swift_getObjCClassFromMetadata, C_CC,
|
||||
ARGS(TypeMetadataPtrTy),
|
||||
ATTRS(NoUnwind, ReadNone))
|
||||
|
||||
// Metadata *swift_getObjCClassFromObject(id object);
|
||||
FUNCTION(GetObjCClassFromObject, swift_getObjCClassFromObject, C_CC,
|
||||
RETURNS(ObjCClassPtrTy),
|
||||
ARGS(ObjCPtrTy),
|
||||
ATTRS(NoUnwind, ReadNone))
|
||||
|
||||
// MetadataResponse swift_getTupleTypeMetadata(MetadataRequest request,
|
||||
// TupleTypeFlags flags,
|
||||
// Metadata * const *elts,
|
||||
|
||||
@@ -718,7 +718,9 @@ void irgen::emitScalarExistentialDowncast(IRGenFunction &IGF,
|
||||
}
|
||||
} else {
|
||||
// Get the type metadata for the instance.
|
||||
metadataValue = emitDynamicTypeOfHeapObject(IGF, value, srcType);
|
||||
metadataValue = emitDynamicTypeOfHeapObject(IGF, value,
|
||||
MetatypeRepresentation::Thick,
|
||||
srcType);
|
||||
}
|
||||
|
||||
// Look up witness tables for the protocols that need them.
|
||||
|
||||
@@ -1881,15 +1881,8 @@ void irgen::emitMetatypeOfClassExistential(IRGenFunction &IGF, Explosion &value,
|
||||
assert((IGF.IGM.ObjCInterop || repr != MetatypeRepresentation::ObjC) &&
|
||||
"Class metatypes should not have ObjC representation without runtime");
|
||||
|
||||
if (repr == MetatypeRepresentation::Thick) {
|
||||
auto dynamicType = emitDynamicTypeOfOpaqueHeapObject(IGF, instance);
|
||||
auto dynamicType = emitDynamicTypeOfOpaqueHeapObject(IGF, instance, repr);
|
||||
out.add(dynamicType);
|
||||
} else if (repr == MetatypeRepresentation::ObjC) {
|
||||
auto dynamicType = emitHeapMetadataRefForUnknownHeapObject(IGF, instance);
|
||||
out.add(dynamicType);
|
||||
} else {
|
||||
llvm_unreachable("Unknown metatype representation");
|
||||
}
|
||||
|
||||
// Get the witness tables.
|
||||
out.add(tablesAndValue.first);
|
||||
@@ -1926,7 +1919,8 @@ irgen::emitClassExistentialProjection(IRGenFunction &IGF,
|
||||
ArrayRef<llvm::Value*> wtables;
|
||||
llvm::Value *value;
|
||||
std::tie(wtables, value) = baseTI.getWitnessTablesAndValue(base);
|
||||
auto metadata = emitDynamicTypeOfOpaqueHeapObject(IGF, value);
|
||||
auto metadata = emitDynamicTypeOfOpaqueHeapObject(IGF, value,
|
||||
MetatypeRepresentation::Thick);
|
||||
IGF.bindArchetype(openedArchetype, metadata, MetadataState::Complete,
|
||||
wtables);
|
||||
|
||||
@@ -2265,7 +2259,8 @@ Address irgen::emitOpaqueBoxedExistentialProjection(
|
||||
IGF.getTypeInfo(existentialTy).as<ClassExistentialTypeInfo>();
|
||||
auto valueAddr = baseTI.projectValue(IGF, base);
|
||||
auto value = IGF.Builder.CreateLoad(valueAddr);
|
||||
auto metadata = emitDynamicTypeOfOpaqueHeapObject(IGF, value);
|
||||
auto metadata = emitDynamicTypeOfOpaqueHeapObject(IGF, value,
|
||||
MetatypeRepresentation::Thick);
|
||||
|
||||
// If we are projecting into an opened archetype, capture the
|
||||
// witness tables.
|
||||
|
||||
@@ -1796,7 +1796,8 @@ llvm::Value *IRGenFunction::getLocalSelfMetadata() {
|
||||
case ObjCMetatype:
|
||||
return emitObjCMetadataRefForMetadata(*this, LocalSelf);
|
||||
case ObjectReference:
|
||||
return emitDynamicTypeOfOpaqueHeapObject(*this, LocalSelf);
|
||||
return emitDynamicTypeOfOpaqueHeapObject(*this, LocalSelf,
|
||||
MetatypeRepresentation::Thick);
|
||||
}
|
||||
|
||||
llvm_unreachable("Not a valid LocalSelfKind.");
|
||||
@@ -1916,11 +1917,26 @@ llvm::Value *irgen::emitHeapMetadataRefForHeapObject(IRGenFunction &IGF,
|
||||
/// Given an opaque class instance pointer, produce the type metadata reference
|
||||
/// as a %type*.
|
||||
llvm::Value *irgen::emitDynamicTypeOfOpaqueHeapObject(IRGenFunction &IGF,
|
||||
llvm::Value *object) {
|
||||
llvm::Value *object,
|
||||
MetatypeRepresentation repr) {
|
||||
object = IGF.Builder.CreateBitCast(object, IGF.IGM.ObjCPtrTy);
|
||||
auto metadata = IGF.Builder.CreateCall(IGF.IGM.getGetObjectTypeFn(),
|
||||
llvm::CallInst *metadata;
|
||||
|
||||
switch (repr) {
|
||||
case MetatypeRepresentation::ObjC:
|
||||
metadata = IGF.Builder.CreateCall(IGF.IGM.getGetObjCClassFromObjectFn(),
|
||||
object,
|
||||
object->getName() + ".Type");
|
||||
break;
|
||||
case MetatypeRepresentation::Thick:
|
||||
metadata = IGF.Builder.CreateCall(IGF.IGM.getGetObjectTypeFn(),
|
||||
object,
|
||||
object->getName() + ".Type");
|
||||
break;
|
||||
case MetatypeRepresentation::Thin:
|
||||
llvm_unreachable("class metadata can't be thin");
|
||||
}
|
||||
|
||||
metadata->setDoesNotThrow();
|
||||
metadata->setOnlyReadsMemory();
|
||||
return metadata;
|
||||
@@ -1944,18 +1960,18 @@ emitHeapMetadataRefForUnknownHeapObject(IRGenFunction &IGF,
|
||||
/// as a %type*.
|
||||
llvm::Value *irgen::emitDynamicTypeOfHeapObject(IRGenFunction &IGF,
|
||||
llvm::Value *object,
|
||||
MetatypeRepresentation repr,
|
||||
SILType objectType,
|
||||
bool suppressCast) {
|
||||
// If it is known to have swift metadata, just load.
|
||||
// If it is known to have swift metadata, just load. A swift class is both
|
||||
// heap metadata and type metadata.
|
||||
if (hasKnownSwiftMetadata(IGF.IGM, objectType.getSwiftRValueType())) {
|
||||
return emitLoadOfHeapMetadataRef(IGF, object,
|
||||
getIsaEncodingForType(IGF.IGM, objectType.getSwiftRValueType()),
|
||||
suppressCast);
|
||||
}
|
||||
|
||||
// Okay, ask the runtime for the type metadata of this
|
||||
// potentially-ObjC object.
|
||||
return emitDynamicTypeOfOpaqueHeapObject(IGF, object);
|
||||
return emitDynamicTypeOfOpaqueHeapObject(IGF, object, repr);
|
||||
}
|
||||
|
||||
static ClassDecl *getRootClass(ClassDecl *theClass) {
|
||||
|
||||
@@ -143,12 +143,14 @@ emitAllocateExistentialBoxInBuffer(IRGenFunction &IGF, SILType boxedType,
|
||||
/// Given an opaque class instance pointer, produce the type
|
||||
/// metadata reference as a %type*.
|
||||
llvm::Value *emitDynamicTypeOfOpaqueHeapObject(IRGenFunction &IGF,
|
||||
llvm::Value *object);
|
||||
llvm::Value *object,
|
||||
MetatypeRepresentation rep);
|
||||
|
||||
/// Given a heap-object instance, with some heap-object type,
|
||||
/// produce a reference to its type metadata.
|
||||
llvm::Value *emitDynamicTypeOfHeapObject(IRGenFunction &IGF,
|
||||
llvm::Value *object,
|
||||
MetatypeRepresentation rep,
|
||||
SILType objectType,
|
||||
bool suppressCast = false);
|
||||
|
||||
|
||||
@@ -627,7 +627,9 @@ bindParameterSource(SILParameterInfo param, unsigned paramIndex,
|
||||
llvm::Value *instanceRef = getParameter(paramIndex);
|
||||
SILType instanceType = SILType::getPrimitiveObjectType(paramType);
|
||||
llvm::Value *metadata =
|
||||
emitDynamicTypeOfHeapObject(IGF, instanceRef, instanceType);
|
||||
emitDynamicTypeOfHeapObject(IGF, instanceRef,
|
||||
MetatypeRepresentation::Thick,
|
||||
instanceType);
|
||||
IGF.bindLocalTypeDataFromTypeMetadata(paramType, IsInexact, metadata,
|
||||
MetadataState::Complete);
|
||||
return;
|
||||
@@ -686,7 +688,9 @@ void BindPolymorphicParameter::emit(Explosion &nativeParam, unsigned paramIndex)
|
||||
llvm::Value *instanceRef = nativeParam.getAll()[0];
|
||||
SILType instanceType = SILType::getPrimitiveObjectType(paramType);
|
||||
llvm::Value *metadata =
|
||||
emitDynamicTypeOfHeapObject(IGF, instanceRef, instanceType);
|
||||
emitDynamicTypeOfHeapObject(IGF, instanceRef,
|
||||
MetatypeRepresentation::Thick,
|
||||
instanceType);
|
||||
IGF.bindLocalTypeDataFromTypeMetadata(paramType, IsInexact, metadata,
|
||||
MetadataState::Complete);
|
||||
}
|
||||
|
||||
@@ -1914,24 +1914,6 @@ static llvm::Value *getClassBaseValue(IRGenSILFunction &IGF,
|
||||
return e.claimNext();
|
||||
}
|
||||
|
||||
static llvm::Value *getClassMetatype(IRGenFunction &IGF,
|
||||
llvm::Value *baseValue,
|
||||
MetatypeRepresentation repr,
|
||||
SILType instanceType) {
|
||||
switch (repr) {
|
||||
case MetatypeRepresentation::Thin:
|
||||
llvm_unreachable("Class metatypes are never thin");
|
||||
|
||||
case MetatypeRepresentation::Thick:
|
||||
return emitDynamicTypeOfHeapObject(IGF, baseValue, instanceType);
|
||||
|
||||
case MetatypeRepresentation::ObjC:
|
||||
return emitHeapMetadataRefForHeapObject(IGF, baseValue, instanceType);
|
||||
}
|
||||
|
||||
llvm_unreachable("Not a valid MetatypeRepresentation.");
|
||||
}
|
||||
|
||||
void IRGenSILFunction::visitValueMetatypeInst(swift::ValueMetatypeInst *i) {
|
||||
SILType instanceTy = i->getOperand()->getType();
|
||||
auto metaTy = i->getType().castTo<MetatypeType>();
|
||||
@@ -1945,12 +1927,12 @@ void IRGenSILFunction::visitValueMetatypeInst(swift::ValueMetatypeInst *i) {
|
||||
Explosion e;
|
||||
|
||||
if (instanceTy.getClassOrBoundGenericClass()) {
|
||||
e.add(getClassMetatype(*this,
|
||||
e.add(emitDynamicTypeOfHeapObject(*this,
|
||||
getClassBaseValue(*this, i->getOperand()),
|
||||
metaTy->getRepresentation(), instanceTy));
|
||||
} else if (auto arch = instanceTy.getAs<ArchetypeType>()) {
|
||||
if (arch->requiresClass()) {
|
||||
e.add(getClassMetatype(*this,
|
||||
e.add(emitDynamicTypeOfHeapObject(*this,
|
||||
getClassBaseValue(*this, i->getOperand()),
|
||||
metaTy->getRepresentation(), instanceTy));
|
||||
} else {
|
||||
|
||||
@@ -96,6 +96,34 @@ static Class _swift_getObjCClassOfAllocated(const void *object) {
|
||||
return class_const_cast(_swift_getClassOfAllocated(object));
|
||||
}
|
||||
|
||||
/// \brief Fetch the ObjC class object associated with the formal dynamic
|
||||
/// type of the given (possibly Objective-C) object. The formal
|
||||
/// dynamic type ignores dynamic subclasses such as those introduced
|
||||
/// by KVO.
|
||||
///
|
||||
/// The object pointer may be a tagged pointer, but cannot be null.
|
||||
const ClassMetadata *swift::swift_getObjCClassFromObject(HeapObject *object) {
|
||||
auto classAsMetadata = _swift_getClass(object);
|
||||
|
||||
// Walk up the superclass chain skipping over artifical Swift classes.
|
||||
// If we find a non-Swift class use the result of [object class] instead.
|
||||
|
||||
while (classAsMetadata && classAsMetadata->isTypeMetadata()) {
|
||||
if (!classAsMetadata->isArtificialSubclass())
|
||||
return classAsMetadata;
|
||||
classAsMetadata = classAsMetadata->Superclass;
|
||||
}
|
||||
|
||||
id objcObject = reinterpret_cast<id>(object);
|
||||
Class objcClass = [objcObject class];
|
||||
if (objcObjectIsClass(objcObject)) {
|
||||
// Original object is a class. We want a
|
||||
// metaclass but +class doesn't give that to us.
|
||||
objcClass = object_getClass(objcClass);
|
||||
}
|
||||
classAsMetadata = reinterpret_cast<const ClassMetadata *>(objcClass);
|
||||
return classAsMetadata;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// \brief Fetch the type metadata associated with the formal dynamic
|
||||
|
||||
@@ -67,7 +67,7 @@ entry:
|
||||
|
||||
// CHECK-LABEL: define{{( protected)?}} swiftcc %objc_class* @existential_objc_metatype(%objc_object*) {{.*}} {
|
||||
// CHECK: entry:
|
||||
// CHECK-NEXT: [[METATYPE:%.*]] = call %objc_class* @object_getClass(%objc_object* %0) {{#[0-9]+}}
|
||||
// CHECK-NEXT: [[METATYPE:%.*]] = call %objc_class* @swift_getObjCClassFromObject(%objc_object* %0) {{#[0-9]+}}
|
||||
// CHECK-NEXT: ret %objc_class* [[METATYPE]]
|
||||
// CHECK-NEXT: }
|
||||
sil @existential_objc_metatype : $@convention(thin) (AnyObject) -> (@objc_metatype AnyObject.Type) {
|
||||
|
||||
Reference in New Issue
Block a user