Skip to content

Commit 9ce9784

Browse files
committed
Runtime: Tweak Mirrors for resilience
Make _MagicMirrorData @_fixed_layout, but not the concrete mirror implementations. To make the calling convention work in resilient builds, make the runtime entry points into top-level functions that take a _MagicMirrorData, instead of adding @_silgen_name attributes on methods. This involves changing the convention on the 'owner' parameter from +0 to +1. Also, there was a memory leak in the old enum code that I noticed by inspection. We would copy the enum value into a box, strip the enum tag bits, take the box contents but never free the box itself. The fix isn't very satisfying either -- we have to modify the source enum in-place to strip tag bits, then we copy it into a box having the right payload type, and add the tag bits back. At least this way, we can free the box after.
1 parent 3e12ecc commit 9ce9784

File tree

3 files changed

+91
-53
lines changed

3 files changed

+91
-53
lines changed

stdlib/public/core/ObjCMirrors.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ struct _ObjCMirror : _Mirror {
4242
return _getObjCSummary(data)
4343
}
4444
public var quickLookObject: PlaygroundQuickLook? {
45-
return _getClassPlaygroundQuickLook(data)
45+
var result: PlaygroundQuickLook? = nil
46+
_getClassPlaygroundQuickLook(&result, data)
47+
return result
4648
}
4749
public var disposition: _MirrorDisposition { return .objCObject }
4850
}
@@ -68,7 +70,9 @@ struct _ObjCSuperMirror : _Mirror {
6870
return _getObjCSummary(data)
6971
}
7072
public var quickLookObject: PlaygroundQuickLook? {
71-
return _getClassPlaygroundQuickLook(data)
73+
var result: PlaygroundQuickLook? = nil
74+
_getClassPlaygroundQuickLook(&result, data)
75+
return result
7276
}
7377
public var disposition: _MirrorDisposition { return .objCObject }
7478
}

stdlib/public/core/Reflection.swift

Lines changed: 51 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,7 @@ func _swift_MagicMirrorData_summaryImpl(
361361
metadata: Any.Type, _ result: UnsafeMutablePointer<String>
362362
)
363363

364+
@_fixed_layout
364365
public struct _MagicMirrorData {
365366
let owner: Builtin.NativeObject
366367
let ptr: Builtin.RawPointer
@@ -407,40 +408,59 @@ struct _OpaqueMirror : _Mirror {
407408
var disposition: _MirrorDisposition { return .aggregate }
408409
}
409410

411+
@warn_unused_result
412+
@_silgen_name("swift_TupleMirror_count")
413+
func _getTupleCount(_: _MagicMirrorData) -> Int
414+
415+
// Like the other swift_*Mirror_subscript functions declared here and
416+
// elsewhere, this is implemented in the runtime. The Swift CC would
417+
// normally require the String to be returned directly and the _Mirror
418+
// indirectly. However, Clang isn't currently capable of doing that
419+
// reliably because the size of String exceeds the normal direct-return
420+
// ABI rules on most platforms. Therefore, we make this function generic,
421+
// which has the disadvantage of passing the String type metadata as an
422+
// extra argument, but does force the string to be returned indirectly.
423+
@warn_unused_result
424+
@_silgen_name("swift_TupleMirror_subscript")
425+
func _getTupleChild<T>(_: Int, _: _MagicMirrorData) -> (T, _Mirror)
426+
410427
internal struct _TupleMirror : _Mirror {
411428
let data: _MagicMirrorData
412429

413430
var value: Any { return data.value }
414431
var valueType: Any.Type { return data.valueType }
415432
var objectIdentifier: ObjectIdentifier? { return nil }
416433
var count: Int {
417-
@_silgen_name("swift_TupleMirror_count")get
434+
return _getTupleCount(data)
418435
}
419436
subscript(i: Int) -> (String, _Mirror) {
420-
return _subscript_get(i)
437+
return _getTupleChild(i, data)
421438
}
422-
@_silgen_name("swift_TupleMirror_subscript")
423-
func _subscript_get<T>(i: Int) -> (T, _Mirror)
424-
425439
var summary: String { return "(\(count) elements)" }
426440
var quickLookObject: PlaygroundQuickLook? { return nil }
427441
var disposition: _MirrorDisposition { return .tuple }
428442
}
429443

444+
@warn_unused_result
445+
@_silgen_name("swift_StructMirror_count")
446+
func _getStructCount(_: _MagicMirrorData) -> Int
447+
448+
@warn_unused_result
449+
@_silgen_name("swift_StructMirror_subscript")
450+
func _getStructChild<T>(_: Int, _: _MagicMirrorData) -> (T, _Mirror)
451+
430452
struct _StructMirror : _Mirror {
431453
let data: _MagicMirrorData
432454

433455
var value: Any { return data.value }
434456
var valueType: Any.Type { return data.valueType }
435457
var objectIdentifier: ObjectIdentifier? { return nil }
436458
var count: Int {
437-
@_silgen_name("swift_StructMirror_count")get
459+
return _getStructCount(data)
438460
}
439461
subscript(i: Int) -> (String, _Mirror) {
440-
return _subscript_get(i)
462+
return _getStructChild(i, data)
441463
}
442-
@_silgen_name("swift_StructMirror_subscript")
443-
func _subscript_get<T>(i: Int) -> (T, _Mirror)
444464

445465
var summary: String {
446466
return _typeName(valueType)
@@ -449,23 +469,34 @@ struct _StructMirror : _Mirror {
449469
var disposition: _MirrorDisposition { return .`struct` }
450470
}
451471

472+
@warn_unused_result
473+
@_silgen_name("swift_EnumMirror_count")
474+
func _getEnumCount(_: _MagicMirrorData) -> Int
475+
476+
@warn_unused_result
477+
@_silgen_name("swift_EnumMirror_subscript")
478+
func _getEnumChild<T>(_: Int, _: _MagicMirrorData) -> (T, _Mirror)
479+
480+
@warn_unused_result
481+
@_silgen_name("swift_EnumMirror_caseName")
482+
func _swift_EnumMirror_caseName(
483+
data: _MagicMirrorData) -> UnsafePointer<CChar>
484+
452485
struct _EnumMirror : _Mirror {
453486
let data: _MagicMirrorData
454487

455488
var value: Any { return data.value }
456489
var valueType: Any.Type { return data.valueType }
457490
var objectIdentifier: ObjectIdentifier? { return nil }
458491
var count: Int {
459-
@_silgen_name("swift_EnumMirror_count")get
492+
return _getEnumCount(data)
460493
}
461494
var caseName: UnsafePointer<CChar> {
462-
@_silgen_name("swift_EnumMirror_caseName")get
495+
return _swift_EnumMirror_caseName(data)
463496
}
464497
subscript(i: Int) -> (String, _Mirror) {
465-
return _subscript_get(i)
498+
return _getEnumChild(i, data)
466499
}
467-
@_silgen_name("swift_EnumMirror_subscript")
468-
func _subscript_get<T>(i: Int) -> (T, _Mirror)
469500

470501
var summary: String {
471502
let maybeCaseName = String(validatingUTF8: self.caseName)
@@ -483,24 +514,16 @@ struct _EnumMirror : _Mirror {
483514
@_silgen_name("swift_ClassMirror_count")
484515
func _getClassCount(_: _MagicMirrorData) -> Int
485516

486-
// Like the other swift_*Mirror_subscript functions declared here and
487-
// elsewhere, this is implemented in the runtime. The Swift CC would
488-
// normally require the String to be returned directly and the _Mirror
489-
// indirectly. However, Clang isn't currently capable of doing that
490-
// reliably because the size of String exceeds the normal direct-return
491-
// ABI rules on most platforms. Therefore, we make this function generic,
492-
// which has the disadvantage of passing the String type metadata as an
493-
// extra argument, but does force the string to be returned indirectly.
494517
@warn_unused_result
495518
@_silgen_name("swift_ClassMirror_subscript")
496519
func _getClassChild<T>(_: Int, _: _MagicMirrorData) -> (T, _Mirror)
497520

498521
#if _runtime(_ObjC)
499-
@warn_unused_result
500522
@_silgen_name("swift_ClassMirror_quickLookObject")
501523
public func _getClassPlaygroundQuickLook(
502-
data: _MagicMirrorData
503-
) -> PlaygroundQuickLook?
524+
_: inout PlaygroundQuickLook?,
525+
_: _MagicMirrorData
526+
)
504527
#endif
505528

506529
struct _ClassMirror : _Mirror {
@@ -522,7 +545,9 @@ struct _ClassMirror : _Mirror {
522545
}
523546
var quickLookObject: PlaygroundQuickLook? {
524547
#if _runtime(_ObjC)
525-
return _getClassPlaygroundQuickLook(data)
548+
var result: PlaygroundQuickLook? = nil
549+
_getClassPlaygroundQuickLook(&result, data)
550+
return result
526551
#else
527552
return nil
528553
#endif

stdlib/public/runtime/Reflection.mm

Lines changed: 34 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,7 @@ intptr_t swift_TupleMirror_count(HeapObject *owner,
495495
const OpaqueValue *value,
496496
const Metadata *type) {
497497
auto Tuple = static_cast<const TupleTypeMetadata *>(type);
498+
swift_release(owner);
498499
return Tuple->NumElements;
499500
}
500501

@@ -524,9 +525,6 @@ void swift_TupleMirror_subscript(String *outString,
524525
auto bytes = reinterpret_cast<const char*>(value);
525526
auto eltData = reinterpret_cast<const OpaqueValue *>(bytes + elt.Offset);
526527

527-
// This retain matches the -1 in reflect.
528-
swift_retain(owner);
529-
530528
// 'owner' is consumed by this call.
531529
new (outMirror) Mirror(reflect(owner, eltData, elt.Type));
532530
}
@@ -551,6 +549,7 @@ intptr_t swift_StructMirror_count(HeapObject *owner,
551549
const OpaqueValue *value,
552550
const Metadata *type) {
553551
auto Struct = static_cast<const StructMetadata *>(type);
552+
swift_release(owner);
554553
return Struct->Description->Struct.NumFields;
555554
}
556555

@@ -576,9 +575,6 @@ void swift_StructMirror_subscript(String *outString,
576575

577576
new (outString) String(getFieldName(Struct->Description->Struct.FieldNames, i));
578577

579-
// This matches the -1 in reflect.
580-
swift_retain(owner);
581-
582578
// 'owner' is consumed by this call.
583579
assert(!fieldType.isIndirect() && "indirect struct fields not implemented");
584580
new (outMirror) Mirror(reflect(owner, fieldData, fieldType.getType()));
@@ -635,14 +631,19 @@ static void getEnumMirrorInfo(const OpaqueValue *value,
635631
const char *swift_EnumMirror_caseName(HeapObject *owner,
636632
const OpaqueValue *value,
637633
const Metadata *type) {
638-
if (!isEnumReflectable(type))
634+
if (!isEnumReflectable(type)) {
635+
swift_release(owner);
639636
return nullptr;
637+
}
640638

641639
const auto Enum = static_cast<const EnumMetadata *>(type);
642640
const auto &Description = Enum->Description->Enum;
643641

644642
unsigned tag;
645643
getEnumMirrorInfo(value, type, &tag, nullptr, nullptr);
644+
645+
swift_release(owner);
646+
646647
return getFieldName(Description.CaseNames, tag);
647648
}
648649

@@ -667,6 +668,7 @@ static void getEnumMirrorInfo(const OpaqueValue *value,
667668

668669
MagicMirror *theMirror = reinterpret_cast<MagicMirror *>(&mirror);
669670
MagicMirrorData data = theMirror->Data;
671+
swift_retain(data.Owner);
670672
const char *result = swift_EnumMirror_caseName(data.Owner, data.Value, data.Type);
671673
type->vw_destroy(value);
672674
return result;
@@ -677,11 +679,14 @@ static void getEnumMirrorInfo(const OpaqueValue *value,
677679
intptr_t swift_EnumMirror_count(HeapObject *owner,
678680
const OpaqueValue *value,
679681
const Metadata *type) {
680-
if (!isEnumReflectable(type))
682+
if (!isEnumReflectable(type)) {
683+
swift_release(owner);
681684
return 0;
685+
}
682686

683687
const Metadata *payloadType;
684688
getEnumMirrorInfo(value, type, nullptr, &payloadType, nullptr);
689+
swift_release(owner);
685690
return (payloadType != nullptr) ? 1 : 0;
686691
}
687692

@@ -702,23 +707,28 @@ void swift_EnumMirror_subscript(String *outString,
702707

703708
getEnumMirrorInfo(value, type, &tag, &payloadType, &indirect);
704709

705-
// Copy the payload since the projection is destructive.
706-
BoxPair pair = swift_allocBox(type);
710+
// Copy the enum payload into a box
711+
const Metadata *boxType = (indirect ? &_TMBo.base : payloadType);
712+
BoxPair pair = swift_allocBox(boxType);
713+
714+
type->vw_destructiveProjectEnumData(const_cast<OpaqueValue *>(value));
715+
boxType->vw_initializeWithCopy(pair.second, const_cast<OpaqueValue *>(value));
716+
type->vw_destructiveInjectEnumTag(const_cast<OpaqueValue *>(value),
717+
(int) (tag - Description.getNumPayloadCases()));
718+
719+
swift_release(owner);
707720

708721
owner = pair.first;
709-
type->vw_initializeWithTake(pair.second, const_cast<OpaqueValue *>(value));
710-
type->vw_destructiveProjectEnumData(pair.second);
711722
value = pair.second;
712723

713724
// If the payload is indirect, we need to jump through the box to get it.
714725
if (indirect) {
715726
owner = *reinterpret_cast<HeapObject * const *>(value);
716727
value = swift_projectBox(const_cast<HeapObject *>(owner));
728+
swift_retain(owner);
729+
swift_release(pair.first);
717730
}
718731

719-
// This matches the -1 in reflect.
720-
swift_retain(owner);
721-
722732
new (outString) String(getFieldName(Description.CaseNames, tag));
723733
new (outMirror) Mirror(reflect(owner, value, payloadType));
724734
}
@@ -970,10 +980,10 @@ void swift_ObjCMirror_subscript(String *outString,
970980
}
971981

972982
SWIFT_RUNTIME_STDLIB_INTERFACE
973-
extern "C" OptionalPlaygroundQuickLook
974-
swift_ClassMirror_quickLookObject(HeapObject *owner, const OpaqueValue *value,
983+
extern "C" void
984+
swift_ClassMirror_quickLookObject(OptionalPlaygroundQuickLook &result,
985+
HeapObject *owner, const OpaqueValue *value,
975986
const Metadata *type) {
976-
OptionalPlaygroundQuickLook result;
977987
memset(&result, 0, sizeof(result));
978988

979989
id object = [*reinterpret_cast<const id *>(value) retain];
@@ -1013,7 +1023,7 @@ void swift_ObjCMirror_subscript(String *outString,
10131023

10141024
[object release];
10151025
result.optional.isNone = false;
1016-
return result;
1026+
return;
10171027
}
10181028

10191029
// Various other framework types are used for rich representations.
@@ -1028,7 +1038,7 @@ void swift_ObjCMirror_subscript(String *outString,
10281038
initializeAnyWithTakeOfObject(result.payload.Any, object);
10291039
result.payload.Kind = PlaygroundQuickLook::Tag::AttributedString;
10301040
result.optional.isNone = false;
1031-
return result;
1041+
return;
10321042
} else if ([object isKindOfClass:NSClassFromString(@"NSImage")]
10331043
|| [object isKindOfClass:NSClassFromString(@"UIImage")]
10341044
|| [object isKindOfClass:NSClassFromString(@"NSImageView")]
@@ -1038,31 +1048,30 @@ void swift_ObjCMirror_subscript(String *outString,
10381048
initializeAnyWithTakeOfObject(result.payload.Any, object);
10391049
result.payload.Kind = PlaygroundQuickLook::Tag::Image;
10401050
result.optional.isNone = false;
1041-
return result;
1051+
return;
10421052
} else if ([object isKindOfClass:NSClassFromString(@"NSColor")]
10431053
|| [object isKindOfClass:NSClassFromString(@"UIColor")]) {
10441054
initializeAnyWithTakeOfObject(result.payload.Any, object);
10451055
result.payload.Kind = PlaygroundQuickLook::Tag::Color;
10461056
result.optional.isNone = false;
1047-
return result;
1057+
return;
10481058
} else if ([object isKindOfClass:NSClassFromString(@"NSBezierPath")]
10491059
|| [object isKindOfClass:NSClassFromString(@"UIBezierPath")]) {
10501060
initializeAnyWithTakeOfObject(result.payload.Any, object);
10511061
result.payload.Kind = PlaygroundQuickLook::Tag::BezierPath;
10521062
result.optional.isNone = false;
1053-
return result;
1063+
return;
10541064
} else if ([object isKindOfClass:[NSString class]]) {
10551065
result.payload.TextOrURL = String((NSString*)object);
10561066
[object release];
10571067
result.payload.Kind = PlaygroundQuickLook::Tag::Text;
10581068
result.optional.isNone = false;
1059-
return result;
1069+
return;
10601070
}
10611071

10621072
// Return none if we didn't get a suitable object.
10631073
[object release];
10641074
result.optional.isNone = true;
1065-
return result;
10661075
}
10671076
#endif
10681077

0 commit comments

Comments
 (0)