Skip to content

Commit 02640fc

Browse files
committed
Runtime: Look through existentials when bridging to id.
1 parent 16f60d8 commit 02640fc

File tree

3 files changed

+154
-48
lines changed

3 files changed

+154
-48
lines changed

stdlib/public/core/BridgeObjectiveC.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -209,16 +209,18 @@ func _bridgeNonVerbatimToObjectiveC<T>(_ x: T) -> AnyObject?
209209
/// the boxed value, but is otherwise opaque.
210210
///
211211
/// TODO: This should subsume `_bridgeToObjectiveC` above.
212-
func _bridgeAnythingToObjectiveC<T>(_ x: T) -> AnyObject {
212+
/// COMPILER_INTRINSIC
213+
public func _bridgeAnythingToObjectiveC<T>(_ x: T) -> AnyObject {
213214
if _fastPath(_isClassOrObjCExistential(T.self)) {
214215
return unsafeBitCast(x, to: AnyObject.self)
215216
}
216217
return _bridgeAnythingNonVerbatimToObjectiveC(x)
217218
}
218219

219220
// TODO: This should subsume `_bridgeNonVerbatimToObjectiveC` above.
221+
/// COMPILER_INTRINSIC
220222
@_silgen_name("_swift_bridgeAnythingNonVerbatimToObjectiveC")
221-
func _bridgeAnythingNonVerbatimToObjectiveC<T>(_ x: T) -> AnyObject
223+
public func _bridgeAnythingNonVerbatimToObjectiveC<T>(_ x: T) -> AnyObject
222224

223225
/// Convert `x` from its Objective-C representation to its Swift
224226
/// representation.

stdlib/public/runtime/Casting.cpp

Lines changed: 99 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1557,6 +1557,50 @@ static bool _dynamicCastToUnknownClassFromExistential(OpaqueValue *dest,
15571557
}
15581558
}
15591559

1560+
static void unwrapExistential(OpaqueValue *src,
1561+
const ExistentialTypeMetadata *srcType,
1562+
OpaqueValue *&srcValue,
1563+
const Metadata *&srcCapturedType,
1564+
bool &isOutOfLine,
1565+
bool &canTake) {
1566+
switch (srcType->getRepresentation()) {
1567+
case ExistentialTypeRepresentation::Class: {
1568+
auto classContainer =
1569+
reinterpret_cast<const ClassExistentialContainer*>(src);
1570+
srcValue = (OpaqueValue*) &classContainer->Value;
1571+
void *obj = classContainer->Value;
1572+
srcCapturedType = swift_getObjectType(reinterpret_cast<HeapObject*>(obj));
1573+
isOutOfLine = false;
1574+
canTake = true;
1575+
break;
1576+
}
1577+
case ExistentialTypeRepresentation::Opaque: {
1578+
auto opaqueContainer = reinterpret_cast<OpaqueExistentialContainer*>(src);
1579+
srcCapturedType = opaqueContainer->Type;
1580+
srcValue = srcCapturedType->vw_projectBuffer(&opaqueContainer->Buffer);
1581+
isOutOfLine = (src != srcValue);
1582+
canTake = true;
1583+
break;
1584+
}
1585+
case ExistentialTypeRepresentation::Error: {
1586+
const SwiftError *errorBox
1587+
= *reinterpret_cast<const SwiftError * const *>(src);
1588+
1589+
srcCapturedType = errorBox->getType();
1590+
// A bridged NSError is itself the value.
1591+
if (errorBox->isPureNSError())
1592+
srcValue = src;
1593+
else
1594+
srcValue = const_cast<OpaqueValue*>(errorBox->getValue());
1595+
1596+
// The value is out-of-line, but we can't take it, since it may be shared.
1597+
isOutOfLine = true;
1598+
canTake = false;
1599+
break;
1600+
}
1601+
}
1602+
}
1603+
15601604
/// Perform a dynamic cast from an existential type to a
15611605
/// non-existential type.
15621606
static bool _dynamicCastFromExistential(OpaqueValue *dest,
@@ -1569,42 +1613,8 @@ static bool _dynamicCastFromExistential(OpaqueValue *dest,
15691613
bool isOutOfLine;
15701614
bool canTake;
15711615

1572-
switch (srcType->getRepresentation()) {
1573-
case ExistentialTypeRepresentation::Class: {
1574-
auto classContainer =
1575-
reinterpret_cast<const ClassExistentialContainer*>(src);
1576-
srcValue = (OpaqueValue*) &classContainer->Value;
1577-
void *obj = classContainer->Value;
1578-
srcCapturedType = swift_getObjectType(reinterpret_cast<HeapObject*>(obj));
1579-
isOutOfLine = false;
1580-
canTake = true;
1581-
break;
1582-
}
1583-
case ExistentialTypeRepresentation::Opaque: {
1584-
auto opaqueContainer = reinterpret_cast<OpaqueExistentialContainer*>(src);
1585-
srcCapturedType = opaqueContainer->Type;
1586-
srcValue = srcCapturedType->vw_projectBuffer(&opaqueContainer->Buffer);
1587-
isOutOfLine = (src != srcValue);
1588-
canTake = true;
1589-
break;
1590-
}
1591-
case ExistentialTypeRepresentation::Error: {
1592-
const SwiftError *errorBox
1593-
= *reinterpret_cast<const SwiftError * const *>(src);
1594-
1595-
srcCapturedType = errorBox->getType();
1596-
// A bridged NSError is itself the value.
1597-
if (errorBox->isPureNSError())
1598-
srcValue = src;
1599-
else
1600-
srcValue = const_cast<OpaqueValue*>(errorBox->getValue());
1601-
1602-
// The value is out-of-line, but we can't take it, since it may be shared.
1603-
isOutOfLine = true;
1604-
canTake = false;
1605-
break;
1606-
}
1607-
}
1616+
unwrapExistential(src, srcType,
1617+
srcValue, srcCapturedType, isOutOfLine, canTake);
16081618

16091619
auto subFlags = flags;
16101620
if (!canTake)
@@ -1622,8 +1632,11 @@ static bool _dynamicCastFromExistential(OpaqueValue *dest,
16221632
} else {
16231633
// swift_dynamicCast took or destroyed the value as per the original request
16241634
// We may still have an opaque existential container to deallocate.
1625-
if (isOutOfLine)
1635+
if (isOutOfLine) {
1636+
assert(srcType->getRepresentation()
1637+
== ExistentialTypeRepresentation::Opaque);
16261638
_maybeDeallocateOpaqueExistential(src, result, flags);
1639+
}
16271640
}
16281641

16291642
return result;
@@ -2515,16 +2528,48 @@ static bool _dynamicCastClassToValueViaObjCBridgeable(
25152528
return success;
25162529
}
25172530

2518-
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
2519-
extern "C"
2520-
id _swift_bridgeAnythingNonVerbatimToObjectiveC(OpaqueValue *src,
2521-
const Metadata *srcType) {
2531+
static id bridgeAnythingNonVerbatimToObjectiveC(OpaqueValue *src,
2532+
const Metadata *srcType,
2533+
bool consume) {
25222534
// We can always bridge objects verbatim.
25232535
if (srcType->isAnyClass()) {
2524-
return *(id*)src;
2536+
id result;
2537+
memcpy(&result, src, sizeof(id));
2538+
if (!consume)
2539+
swift_unknownRetain(result);
2540+
return result;
25252541
}
25262542

2527-
// TODO: Look through existential containers.
2543+
// Dig through existential types.
2544+
if (auto srcExistentialTy = dyn_cast<ExistentialTypeMetadata>(srcType)) {
2545+
OpaqueValue *srcInnerValue;
2546+
const Metadata *srcInnerType;
2547+
bool isOutOfLine;
2548+
bool canTake;
2549+
2550+
unwrapExistential(src, srcExistentialTy,
2551+
srcInnerValue, srcInnerType, isOutOfLine, canTake);
2552+
auto result = bridgeAnythingNonVerbatimToObjectiveC(srcInnerValue,
2553+
srcInnerType,
2554+
consume && canTake);
2555+
// Clean up the existential, or its remains after taking the value from
2556+
// it.
2557+
if (consume) {
2558+
if (canTake) {
2559+
if (isOutOfLine) {
2560+
// Should only be true of opaque existentials.
2561+
assert(srcExistentialTy->getRepresentation()
2562+
== ExistentialTypeRepresentation::Opaque);
2563+
auto container = reinterpret_cast<OpaqueExistentialContainer*>(src);
2564+
srcInnerType->vw_deallocateBuffer(&container->Buffer);
2565+
}
2566+
} else {
2567+
// We didn't take the value, so clean up the existential value.
2568+
srcType->vw_destroy(src);
2569+
}
2570+
}
2571+
return result;
2572+
}
25282573

25292574
if (auto srcBridgeWitness = findBridgeWitness(srcType)) {
25302575
// Check whether the source is bridged to Objective-C.
@@ -2537,14 +2582,22 @@ id _swift_bridgeAnythingNonVerbatimToObjectiveC(OpaqueValue *src,
25372582
auto srcBridgedObject =
25382583
srcBridgeWitness->bridgeToObjectiveC(src, srcType, srcBridgeWitness);
25392584

2540-
// The source object was passed in +1.
2541-
srcType->vw_destroy(src);
2585+
// Consume if the source object was passed in +1.
2586+
if (consume)
2587+
srcType->vw_destroy(src);
25422588

25432589
return (id)srcBridgedObject;
25442590
}
25452591

25462592
// TODO: Fall back to boxing here.
2547-
crash("unimplemented universal bridging conversion");
2593+
crash("unimplemented boxing bridge");
2594+
}
2595+
2596+
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE
2597+
extern "C"
2598+
id _swift_bridgeAnythingNonVerbatimToObjectiveC(OpaqueValue *src,
2599+
const Metadata *srcType) {
2600+
return bridgeAnythingNonVerbatimToObjectiveC(src, srcType, /*consume*/ true);
25482601
}
25492602

25502603
//===--- Bridging helpers for the Swift stdlib ----------------------------===//

test/1_stdlib/BridgeIdAsAny.swift.gyb

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// RUN: rm -rf %t && mkdir %t
2+
//
3+
// RUN: %gyb %s -o %t/BridgeIdAsAny.swift
4+
// RUN: %target-build-swift -module-name a %t/BridgeIdAsAny.swift -o %t.out
5+
// RUN: %target-run %t.out
6+
// REQUIRES: executable_test
7+
//
8+
// REQUIRES: objc_interop
9+
10+
import StdlibUnittest
11+
import Foundation
12+
13+
var BridgeAnything = TestSuite("BridgeAnything")
14+
15+
func wantonlyWrapInAny<T>(_ x: T) -> Any {
16+
return x
17+
}
18+
19+
// Professional runtime testers on a closed course. Do not attempt at home.
20+
extension LifetimeTracked: Error {}
21+
extension String: Error {}
22+
23+
% for testName, valueExpr, testExpr in [("classes", "LifetimeTracked(0)", " === x"), ("strings", '"vitameatavegamin"', '.isEqual(to: "vitameatavegamin")')]:
24+
BridgeAnything.test("${testName}") {
25+
do {
26+
let x = ${valueExpr}
27+
expectTrue(_bridgeAnythingToObjectiveC(x)${testExpr})
28+
expectTrue(_bridgeAnythingNonVerbatimToObjectiveC(x)${testExpr})
29+
30+
let xInAny: Any = x
31+
expectTrue(_bridgeAnythingToObjectiveC(xInAny)${testExpr})
32+
expectTrue(_bridgeAnythingNonVerbatimToObjectiveC(xInAny)${testExpr})
33+
34+
let xInAnyInAny = wantonlyWrapInAny(xInAny)
35+
expectTrue(_bridgeAnythingToObjectiveC(xInAnyInAny)${testExpr})
36+
expectTrue(_bridgeAnythingNonVerbatimToObjectiveC(xInAnyInAny)${testExpr})
37+
38+
let xInError: Error = x
39+
expectTrue(_bridgeAnythingToObjectiveC(xInError)${testExpr})
40+
expectTrue(_bridgeAnythingNonVerbatimToObjectiveC(xInError)${testExpr})
41+
42+
let xInErrorInAny = wantonlyWrapInAny(xInError)
43+
expectTrue(_bridgeAnythingToObjectiveC(xInErrorInAny)${testExpr})
44+
expectTrue(_bridgeAnythingNonVerbatimToObjectiveC(xInErrorInAny)${testExpr})
45+
}
46+
47+
expectEqual(0, LifetimeTracked.instances)
48+
}
49+
% end
50+
51+
runAllTests()

0 commit comments

Comments
 (0)