Skip to content

Commit bc8433f

Browse files
committed
Runtime: Implement an opaque 'SwiftValue' ObjC class to hold bridged values.
If there's no better mapping for a Swift value into an Objective-C object for bridging purposes, we can fall back to boxing the value in a class. This class doesn't have any public interface beyond being `NSObject`-conforming in Objective-C, but is recognized by the Swift runtime so that it can be dynamically cast back to the boxed type.
1 parent 1840690 commit bc8433f

File tree

9 files changed

+516
-75
lines changed

9 files changed

+516
-75
lines changed

stdlib/public/runtime/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ if(SWIFT_HOST_VARIANT MATCHES "${SWIFT_DARWIN_VARIANTS}")
2626
set(swift_runtime_objc_sources
2727
ErrorObject.mm
2828
SwiftObject.mm
29+
SwiftValue.mm
2930
Remangle.cpp
3031
Reflection.mm)
3132
else()

stdlib/public/runtime/Casting.cpp

Lines changed: 78 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,10 @@
3131
#include "Private.h"
3232
#include "../SwiftShims/RuntimeShims.h"
3333
#include "stddef.h"
34+
#if SWIFT_OBJC_INTEROP
35+
#include "swift/Runtime/ObjCBridge.h"
36+
#include "SwiftValue.h"
37+
#endif
3438

3539
#include <cstring>
3640
#include <type_traits>
@@ -2094,6 +2098,65 @@ checkDynamicCastFromOptional(OpaqueValue *dest,
20942098
return {false, payloadType};
20952099
}
20962100

2101+
#if SWIFT_OBJC_INTEROP
2102+
/// Try to unbox a SwiftValue box to perform a dynamic cast.
2103+
static bool tryDynamicCastBoxedSwiftValue(OpaqueValue *dest,
2104+
OpaqueValue *src,
2105+
const Metadata *srcType,
2106+
const Metadata *targetType,
2107+
DynamicCastFlags flags) {
2108+
// Swift type should be AnyObject or a class type.
2109+
if (!srcType->isAnyClass()) {
2110+
auto existential = dyn_cast<ExistentialTypeMetadata>(srcType);
2111+
if (!existential ||
2112+
existential->Flags.getSpecialProtocol()
2113+
!= SpecialProtocol::AnyObject)
2114+
return false;
2115+
}
2116+
2117+
id srcObject;
2118+
memcpy(&srcObject, src, sizeof(id));
2119+
2120+
// Do we have a SwiftValue?
2121+
SwiftValue *srcSwiftValue = getAsSwiftValue(srcObject);
2122+
if (!srcSwiftValue)
2123+
return false;
2124+
2125+
// If so, extract the boxed value and try to cast it.
2126+
const Metadata *boxedType;
2127+
const OpaqueValue *boxedValue;
2128+
std::tie(boxedType, boxedValue)
2129+
= getValueFromSwiftValue(srcSwiftValue);
2130+
2131+
// We can't touch the value from the box because it may be
2132+
// multiply-referenced.
2133+
// TODO: Check for uniqueness and consume if box is unique?
2134+
2135+
// Does the boxed type exactly match the target type we're looking for?
2136+
if (boxedType == targetType) {
2137+
targetType->vw_initializeWithCopy(dest,
2138+
const_cast<OpaqueValue*>(boxedValue));
2139+
// Release the box if we need to.
2140+
if (flags & DynamicCastFlags::TakeOnSuccess)
2141+
objc_release((id)srcSwiftValue);
2142+
return true;
2143+
}
2144+
2145+
// Maybe we can cast the boxed value to our destination type somehow.
2146+
auto innerFlags = flags - DynamicCastFlags::TakeOnSuccess
2147+
- DynamicCastFlags::DestroyOnFailure;
2148+
if (swift_dynamicCast(dest, const_cast<OpaqueValue*>(boxedValue),
2149+
boxedType, targetType, innerFlags)) {
2150+
// Release the box if we need to.
2151+
if (flags & DynamicCastFlags::TakeOnSuccess)
2152+
objc_release((id)srcSwiftValue);
2153+
return true;
2154+
}
2155+
2156+
return false;
2157+
}
2158+
#endif
2159+
20972160
/// Perform a dynamic cast to an arbitrary type.
20982161
SWIFT_RT_ENTRY_VISIBILITY
20992162
bool swift::swift_dynamicCast(OpaqueValue *dest,
@@ -2238,14 +2301,24 @@ bool swift::swift_dynamicCast(OpaqueValue *dest,
22382301
if (tryDynamicCastNSErrorToValue(dest, src, srcType, targetType, flags)) {
22392302
return true;
22402303
}
2304+
#endif
2305+
SWIFT_FALLTHROUGH;
2306+
}
2307+
2308+
case MetadataKind::Existential: {
2309+
#if SWIFT_OBJC_INTEROP
2310+
// A class or AnyObject reference may point at a boxed SwiftValue.
2311+
if (tryDynamicCastBoxedSwiftValue(dest, src, srcType,
2312+
targetType, flags)) {
2313+
return true;
2314+
}
22412315
#endif
22422316
break;
22432317
}
22442318

2319+
case MetadataKind::ExistentialMetatype:
22452320
case MetadataKind::Enum:
22462321
case MetadataKind::Optional:
2247-
case MetadataKind::Existential:
2248-
case MetadataKind::ExistentialMetatype:
22492322
case MetadataKind::Function:
22502323
case MetadataKind::HeapLocalVariable:
22512324
case MetadataKind::HeapGenericLocalVariable:
@@ -2557,7 +2630,7 @@ static id bridgeAnythingNonVerbatimToObjectiveC(OpaqueValue *src,
25572630
if (consume) {
25582631
if (canTake) {
25592632
if (isOutOfLine) {
2560-
// Should only be true of opaque existentials.
2633+
// Should only be true of opaque existentials right now.
25612634
assert(srcExistentialTy->getRepresentation()
25622635
== ExistentialTypeRepresentation::Opaque);
25632636
auto container = reinterpret_cast<OpaqueExistentialContainer*>(src);
@@ -2589,8 +2662,8 @@ static id bridgeAnythingNonVerbatimToObjectiveC(OpaqueValue *src,
25892662
return (id)srcBridgedObject;
25902663
}
25912664

2592-
// TODO: Fall back to boxing here.
2593-
crash("unimplemented boxing bridge");
2665+
// Fall back to boxing.
2666+
return (id)bridgeAnythingToSwiftValueObject(src, srcType, consume);
25942667
}
25952668

25962669
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE

stdlib/public/runtime/ErrorObject.mm

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,8 @@ @interface _SwiftNativeNSError : NSError
4343

4444
@implementation _SwiftNativeNSError
4545

46-
+ (instancetype)alloc {
46+
+ (instancetype)allocWithZone:(NSZone *)zone {
47+
(void)zone;
4748
swift::crash("_SwiftNativeNSError cannot be instantiated");
4849
}
4950

stdlib/public/runtime/MetadataImpl.h

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@
4444
#include "swift/Runtime/Config.h"
4545
#include "swift/Runtime/Metadata.h"
4646
#include "swift/Runtime/HeapObject.h"
47+
#if SWIFT_OBJC_INTEROP
48+
#include "swift/Runtime/ObjCBridge.h"
49+
#endif
4750
#include <cstring>
4851
#include <type_traits>
4952

@@ -373,20 +376,17 @@ struct SwiftWeakRetainableBox :
373376
};
374377

375378
#if SWIFT_OBJC_INTEROP
376-
extern "C" void *objc_retain(void *obj);
377-
extern "C" void objc_release(void *obj);
378-
379379
/// A box implementation class for Objective-C object pointers.
380380
struct ObjCRetainableBox : RetainableBoxBase<ObjCRetainableBox, void*> {
381381
static constexpr unsigned numExtraInhabitants =
382382
swift_getHeapObjectExtraInhabitantCount();
383383

384384
static void *retain(void *obj) {
385-
return objc_retain(obj);
385+
return objc_retain((id)obj);
386386
}
387387

388388
static void release(void *obj) {
389-
objc_release(obj);
389+
objc_release((id)obj);
390390
}
391391
};
392392

stdlib/public/runtime/SwiftObject.h

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
//===--- SwiftObject.h - Native Swift Object root class -------------------===//
2+
//
3+
// This source file is part of the Swift.org open source project
4+
//
5+
// Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors
6+
// Licensed under Apache License v2.0 with Runtime Library Exception
7+
//
8+
// See http://swift.org/LICENSE.txt for license information
9+
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
10+
//
11+
//===----------------------------------------------------------------------===//
12+
//
13+
// This implements the Objective-C root class that provides basic `id`-
14+
// compatibility and `NSObject` protocol conformance for pure Swift classes.
15+
//
16+
//===----------------------------------------------------------------------===//
17+
18+
#ifndef SWIFT_RUNTIME_SWIFTOBJECT_H
19+
#define SWIFT_RUNTIME_SWIFTOBJECT_H
20+
21+
#include "swift/Runtime/Config.h"
22+
#include <cstdint>
23+
#include <utility>
24+
#include "swift/Runtime/HeapObject.h"
25+
#if SWIFT_OBJC_INTEROP
26+
#include <objc/NSObject.h>
27+
#endif
28+
29+
namespace swift {
30+
31+
#if SWIFT_OBJC_INTEROP
32+
struct SwiftObject_s {
33+
void *isa __attribute__((__unavailable__));
34+
uint32_t strongRefCount __attribute__((__unavailable__));
35+
uint32_t weakRefCount __attribute__((__unavailable__));
36+
};
37+
38+
static_assert(sizeof(SwiftObject_s) == sizeof(HeapObject),
39+
"SwiftObject and HeapObject must have the same header");
40+
static_assert(std::is_trivially_constructible<SwiftObject_s>::value,
41+
"SwiftObject must be trivially constructible");
42+
static_assert(std::is_trivially_destructible<SwiftObject_s>::value,
43+
"SwiftObject must be trivially destructible");
44+
45+
} // namespace swift
46+
47+
#if __has_attribute(objc_root_class)
48+
__attribute__((__objc_root_class__))
49+
#endif
50+
SWIFT_RUNTIME_EXPORT @interface SwiftObject<NSObject> {
51+
swift::SwiftObject_s header;
52+
}
53+
54+
- (BOOL)isEqual:(id)object;
55+
- (NSUInteger)hash;
56+
57+
- (Class)superclass;
58+
- (Class)class;
59+
- (instancetype)self;
60+
- (struct _NSZone *)zone;
61+
62+
- (id)performSelector:(SEL)aSelector;
63+
- (id)performSelector:(SEL)aSelector withObject:(id)object;
64+
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
65+
66+
- (BOOL)isProxy;
67+
68+
+ (BOOL)isSubclassOfClass:(Class)aClass;
69+
- (BOOL)isKindOfClass:(Class)aClass;
70+
- (BOOL)isMemberOfClass:(Class)aClass;
71+
- (BOOL)conformsToProtocol:(Protocol *)aProtocol;
72+
73+
- (BOOL)respondsToSelector:(SEL)aSelector;
74+
75+
- (instancetype)retain;
76+
- (oneway void)release;
77+
- (instancetype)autorelease;
78+
- (NSUInteger)retainCount;
79+
80+
- (NSString *)description;
81+
- (NSString *)debugDescription;
82+
@end
83+
84+
namespace swift {
85+
86+
#endif
87+
88+
}
89+
90+
#endif

stdlib/public/runtime/SwiftObject.mm

Lines changed: 3 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010
//
1111
//===----------------------------------------------------------------------===//
1212
//
13-
// This implements runtime support for bridging between Swift and Objective-C
14-
// types in cases where they aren't trivial.
13+
// This implements the Objective-C root class that provides basic `id`-
14+
// compatibility and `NSObject` protocol conformance for pure Swift classes.
1515
//
1616
//===----------------------------------------------------------------------===//
1717

@@ -33,6 +33,7 @@
3333
#include "swift/Strings.h"
3434
#include "../SwiftShims/RuntimeShims.h"
3535
#include "Private.h"
36+
#include "SwiftObject.h"
3637
#include "swift/Runtime/Debug.h"
3738
#if SWIFT_OBJC_INTEROP
3839
#include <dlfcn.h>
@@ -76,56 +77,6 @@ static uintptr_t computeISAMask() {
7677
}
7778

7879
#if SWIFT_OBJC_INTEROP
79-
struct SwiftObject_s {
80-
void *isa __attribute__((__unavailable__));
81-
uint32_t strongRefCount __attribute__((__unavailable__));
82-
uint32_t weakRefCount __attribute__((__unavailable__));
83-
};
84-
85-
static_assert(sizeof(SwiftObject_s) == sizeof(HeapObject),
86-
"SwiftObject and HeapObject must have the same header");
87-
static_assert(std::is_trivially_constructible<SwiftObject_s>::value,
88-
"SwiftObject must be trivially constructible");
89-
static_assert(std::is_trivially_destructible<SwiftObject_s>::value,
90-
"SwiftObject must be trivially destructible");
91-
92-
#if __has_attribute(objc_root_class)
93-
__attribute__((__objc_root_class__))
94-
#endif
95-
SWIFT_RUNTIME_EXPORT @interface SwiftObject<NSObject> {
96-
SwiftObject_s header;
97-
}
98-
99-
- (BOOL)isEqual:(id)object;
100-
- (NSUInteger)hash;
101-
102-
- (Class)superclass;
103-
- (Class)class;
104-
- (instancetype)self;
105-
- (struct _NSZone *)zone;
106-
107-
- (id)performSelector:(SEL)aSelector;
108-
- (id)performSelector:(SEL)aSelector withObject:(id)object;
109-
- (id)performSelector:(SEL)aSelector withObject:(id)object1 withObject:(id)object2;
110-
111-
- (BOOL)isProxy;
112-
113-
+ (BOOL)isSubclassOfClass:(Class)aClass;
114-
- (BOOL)isKindOfClass:(Class)aClass;
115-
- (BOOL)isMemberOfClass:(Class)aClass;
116-
- (BOOL)conformsToProtocol:(Protocol *)aProtocol;
117-
118-
- (BOOL)respondsToSelector:(SEL)aSelector;
119-
120-
- (instancetype)retain;
121-
- (oneway void)release;
122-
- (instancetype)autorelease;
123-
- (NSUInteger)retainCount;
124-
125-
- (NSString *)description;
126-
- (NSString *)debugDescription;
127-
@end
128-
12980
static SwiftObject *_allocHelper(Class cls) {
13081
// XXX FIXME
13182
// When we have layout information, do precise alignment rounding

0 commit comments

Comments
 (0)