Skip to content

Commit 03cdc17

Browse files
committed
stdlib: Rewrite _getClassPlaygroundQuickLook() in Swift
The C++ code was very fragile in terms of ABI dependencies and broke when the standard library was built with -enable-resilience. The actual reason it broke is that case numbering changes when resilience is enabled, but instead of messing with that, it seemed more logical to rewrite this routine in Swift instead, to avoid ABI dependencies altogether. This requires using the "shadow protocol" trick to call NSNumber methods, since we cannot import NSNumber from the stdlib.
1 parent 11cff08 commit 03cdc17

File tree

5 files changed

+75
-184
lines changed

5 files changed

+75
-184
lines changed

stdlib/public/core/Mirror.swift

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -573,15 +573,6 @@ internal extension Mirror {
573573

574574
/// The sum of types that can be used as a quick look representation.
575575
public enum PlaygroundQuickLook {
576-
//
577-
// This type must be binary-compatible with the 'PlaygroundQuickLook' struct
578-
// in stdlib/public/runtime/Reflection.mm, and 'PlaygroundQuickLook?' must be
579-
// binary compatible with 'OptionalPlaygroundQuickLook' from the same.
580-
//
581-
// NB: This type is somewhat carefully laid out to *suppress* enum layout
582-
// optimization so that it is easier to manufacture in the C++ runtime
583-
// implementation.
584-
585576
/// Plain text.
586577
case text(String)
587578

stdlib/public/core/ObjCMirrors.swift

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,8 @@ struct _ObjCMirror : _Mirror {
4242
return _getObjCSummary(data)
4343
}
4444
public var quickLookObject: PlaygroundQuickLook? {
45-
var result: PlaygroundQuickLook? = nil
46-
_getClassPlaygroundQuickLook(&result, data)
47-
return result
45+
let object = _swift_ClassMirror_quickLookObject(data)
46+
return _getClassPlaygroundQuickLook(object)
4847
}
4948
public var disposition: _MirrorDisposition { return .objCObject }
5049
}
@@ -70,9 +69,8 @@ struct _ObjCSuperMirror : _Mirror {
7069
return _getObjCSummary(data)
7170
}
7271
public var quickLookObject: PlaygroundQuickLook? {
73-
var result: PlaygroundQuickLook? = nil
74-
_getClassPlaygroundQuickLook(&result, data)
75-
return result
72+
let object = _swift_ClassMirror_quickLookObject(data)
73+
return _getClassPlaygroundQuickLook(object)
7674
}
7775
public var disposition: _MirrorDisposition { return .objCObject }
7876
}

stdlib/public/core/Reflection.swift

Lines changed: 46 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -520,10 +520,50 @@ func _getClassChild<T>(_: Int, _: _MagicMirrorData) -> (T, _Mirror)
520520

521521
#if _runtime(_ObjC)
522522
@_silgen_name("swift_ClassMirror_quickLookObject")
523-
public func _getClassPlaygroundQuickLook(
524-
_: inout PlaygroundQuickLook?,
525-
_: _MagicMirrorData
526-
)
523+
public func _swift_ClassMirror_quickLookObject(_: _MagicMirrorData) -> AnyObject
524+
525+
@_silgen_name("swift_isKind")
526+
func _swift_isKind(object: AnyObject, of: AnyObject) -> Bool
527+
528+
func _isKind(object: AnyObject, of: String) -> Bool {
529+
return _swift_isKind(object, of: _bridgeToObjectiveC(of)!)
530+
}
531+
532+
func _getClassPlaygroundQuickLook(object: AnyObject) -> PlaygroundQuickLook? {
533+
if _isKind(object, of: "NSNumber") {
534+
let number: _NSNumber = unsafeBitCast(object, to: _NSNumber.self)
535+
switch UInt8(number.objCType[0]) {
536+
case UInt8(ascii: "d"):
537+
return .double(number.doubleValue)
538+
case UInt8(ascii: "f"):
539+
return .float(number.floatValue)
540+
case UInt8(ascii: "Q"):
541+
return .uInt(number.unsignedLongLongValue)
542+
default:
543+
return .int(number.longLongValue)
544+
}
545+
} else if _isKind(object, of: "NSAttributedString") {
546+
return .attributedString(object)
547+
} else if _isKind(object, of: "NSImage") ||
548+
_isKind(object, of: "UIImage") ||
549+
_isKind(object, of: "NSImageView") ||
550+
_isKind(object, of: "UIImageView") ||
551+
_isKind(object, of: "CIImage") ||
552+
_isKind(object, of: "NSBitmapImageRep") {
553+
return .image(object)
554+
} else if _isKind(object, of: "NSColor") ||
555+
_isKind(object, of: "UIColor") {
556+
return .color(object)
557+
} else if _isKind(object, of: "NSBezierPath") ||
558+
_isKind(object, of: "UIBezierPath") {
559+
return .bezierPath(object)
560+
} else if _isKind(object, of: "NSString") {
561+
return .text(_forceBridgeFromObjectiveC(object, String.self))
562+
}
563+
564+
return .none
565+
}
566+
527567
#endif
528568

529569
struct _ClassMirror : _Mirror {
@@ -545,9 +585,8 @@ struct _ClassMirror : _Mirror {
545585
}
546586
var quickLookObject: PlaygroundQuickLook? {
547587
#if _runtime(_ObjC)
548-
var result: PlaygroundQuickLook? = nil
549-
_getClassPlaygroundQuickLook(&result, data)
550-
return result
588+
let object = _swift_ClassMirror_quickLookObject(data)
589+
return _getClassPlaygroundQuickLook(object)
551590
#else
552591
return nil
553592
#endif

stdlib/public/core/ShadowProtocols.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,17 @@ public protocol _NSSetCore :
171171
public protocol _NSSet : _NSSetCore {
172172
}
173173

174+
/// A shadow for the API of NSNumber we will use in the core
175+
/// stdlib.
176+
@objc
177+
public protocol _NSNumber {
178+
var doubleValue: Double { get }
179+
var floatValue: Float { get }
180+
var unsignedLongLongValue: UInt64 { get }
181+
var longLongValue: Int64 { get }
182+
var objCType: UnsafePointer<Int8> { get }
183+
}
184+
174185
#else
175186

176187
public protocol _NSArrayCore {}

stdlib/public/runtime/Reflection.mm

Lines changed: 14 additions & 162 deletions
Original file line numberDiff line numberDiff line change
@@ -112,86 +112,7 @@ explicit String(NSString *s)
112112
{}
113113
#endif
114114
};
115-
116-
struct Array {
117-
// Keep the details of Array's implementation opaque to the runtime.
118-
const void *x;
119-
};
120-
121-
struct PlaygroundQuickLook {
122-
struct RawData {
123-
Array Data;
124-
String Type;
125-
};
126-
struct Rectangle {
127-
double x, y, w, h;
128-
};
129-
struct Point {
130-
double x, y;
131-
};
132-
struct Interval {
133-
int64_t loc,len;
134-
};
135-
136-
union {
137-
String TextOrURL;
138-
int64_t Int;
139-
uint64_t UInt;
140-
float Float;
141-
double Double;
142-
Any Any;
143-
RawData Raw;
144-
Rectangle Rect;
145-
Point PointOrSize;
146-
bool Logical;
147-
Interval Range;
148-
};
149-
enum class Tag : uint8_t {
150-
Text,
151-
Int,
152-
UInt,
153-
Float,
154-
Double,
155-
Image,
156-
Sound,
157-
Color,
158-
BezierPath,
159-
AttributedString,
160-
Rectangle,
161-
Point,
162-
Size,
163-
Logical,
164-
Range,
165-
View,
166-
Sprite,
167-
URL,
168-
Raw,
169-
} Kind;
170-
};
171-
172-
struct OptionalPlaygroundQuickLook {
173-
union {
174-
struct {
175-
union {
176-
String TextOrURL;
177-
int64_t Int;
178-
uint64_t UInt;
179-
float Float;
180-
double Double;
181-
Any Any;
182-
PlaygroundQuickLook::RawData Raw;
183-
PlaygroundQuickLook::Rectangle Rect;
184-
PlaygroundQuickLook::Point PointOrSize;
185-
bool Logical;
186-
PlaygroundQuickLook::Interval Range;
187-
};
188-
PlaygroundQuickLook::Tag Kind;
189-
bool isNone;
190-
} optional;
191-
PlaygroundQuickLook payload;
192-
};
193-
};
194-
115+
195116
/// A Mirror witness table for use by MagicMirror.
196117
struct MirrorWitnessTable;
197118

@@ -980,99 +901,30 @@ void swift_ObjCMirror_subscript(String *outString,
980901
}
981902

982903
SWIFT_RUNTIME_STDLIB_INTERFACE
983-
extern "C" void
984-
swift_ClassMirror_quickLookObject(OptionalPlaygroundQuickLook &result,
985-
HeapObject *owner, const OpaqueValue *value,
904+
extern "C" id
905+
swift_ClassMirror_quickLookObject(HeapObject *owner, const OpaqueValue *value,
986906
const Metadata *type) {
987-
memset(&result, 0, sizeof(result));
988-
989907
id object = [*reinterpret_cast<const id *>(value) retain];
990908
swift_release(owner);
991909
if ([object respondsToSelector:@selector(debugQuickLookObject)]) {
992910
id quickLookObject = [object debugQuickLookObject];
993911
[quickLookObject retain];
994912
[object release];
995-
object = quickLookObject;
913+
return quickLookObject;
996914
}
997-
998-
// NSNumbers quick-look as integers or doubles, depending on type.
999-
if ([object isKindOfClass:[NSNumber class]]) {
1000-
NSNumber *n = object;
1001-
1002-
switch ([n objCType][0]) {
1003-
case 'd': // double
1004-
result.payload.Double = [n doubleValue];
1005-
result.payload.Kind = PlaygroundQuickLook::Tag::Double;
1006-
break;
1007-
case 'f': // float
1008-
result.payload.Float = [n floatValue];
1009-
result.payload.Kind = PlaygroundQuickLook::Tag::Float;
1010-
break;
1011-
1012-
case 'Q': // unsigned long long
1013-
result.payload.UInt = [n unsignedLongLongValue];
1014-
result.payload.Kind = PlaygroundQuickLook::Tag::UInt;
1015-
break;
1016915

1017-
// FIXME: decimals?
1018-
default:
1019-
result.payload.Int = [n longLongValue];
1020-
result.payload.Kind = PlaygroundQuickLook::Tag::Int;
1021-
break;
1022-
}
1023-
1024-
[object release];
1025-
result.optional.isNone = false;
1026-
return;
1027-
}
1028-
1029-
// Various other framework types are used for rich representations.
1030-
1031-
/// Store an ObjC reference into an Any.
1032-
auto initializeAnyWithTakeOfObject = [](Any &any, id obj) {
1033-
any.Type = swift_getObjCClassMetadata(_swift_getClass((const void*) obj));
1034-
*reinterpret_cast<id *>(&any.Buffer) = obj;
1035-
};
1036-
1037-
if ([object isKindOfClass:NSClassFromString(@"NSAttributedString")]) {
1038-
initializeAnyWithTakeOfObject(result.payload.Any, object);
1039-
result.payload.Kind = PlaygroundQuickLook::Tag::AttributedString;
1040-
result.optional.isNone = false;
1041-
return;
1042-
} else if ([object isKindOfClass:NSClassFromString(@"NSImage")]
1043-
|| [object isKindOfClass:NSClassFromString(@"UIImage")]
1044-
|| [object isKindOfClass:NSClassFromString(@"NSImageView")]
1045-
|| [object isKindOfClass:NSClassFromString(@"UIImageView")]
1046-
|| [object isKindOfClass:NSClassFromString(@"CIImage")]
1047-
|| [object isKindOfClass:NSClassFromString(@"NSBitmapImageRep")]) {
1048-
initializeAnyWithTakeOfObject(result.payload.Any, object);
1049-
result.payload.Kind = PlaygroundQuickLook::Tag::Image;
1050-
result.optional.isNone = false;
1051-
return;
1052-
} else if ([object isKindOfClass:NSClassFromString(@"NSColor")]
1053-
|| [object isKindOfClass:NSClassFromString(@"UIColor")]) {
1054-
initializeAnyWithTakeOfObject(result.payload.Any, object);
1055-
result.payload.Kind = PlaygroundQuickLook::Tag::Color;
1056-
result.optional.isNone = false;
1057-
return;
1058-
} else if ([object isKindOfClass:NSClassFromString(@"NSBezierPath")]
1059-
|| [object isKindOfClass:NSClassFromString(@"UIBezierPath")]) {
1060-
initializeAnyWithTakeOfObject(result.payload.Any, object);
1061-
result.payload.Kind = PlaygroundQuickLook::Tag::BezierPath;
1062-
result.optional.isNone = false;
1063-
return;
1064-
} else if ([object isKindOfClass:[NSString class]]) {
1065-
result.payload.TextOrURL = String((NSString*)object);
1066-
[object release];
1067-
result.payload.Kind = PlaygroundQuickLook::Tag::Text;
1068-
result.optional.isNone = false;
1069-
return;
1070-
}
1071-
1072-
// Return none if we didn't get a suitable object.
916+
return object;
917+
}
918+
919+
SWIFT_RUNTIME_STDLIB_INTERFACE
920+
extern "C" bool swift_isKind(id object, NSString *className) {
921+
bool result = [object isKindOfClass:NSClassFromString(className)];
1073922
[object release];
1074-
result.optional.isNone = true;
923+
[className release];
924+
925+
return result;
1075926
}
927+
1076928
#endif
1077929

1078930
// -- MagicMirror implementation.

0 commit comments

Comments
 (0)