Skip to content

Commit e25c751

Browse files
authored
Merge pull request #3538 from jckarter/id-as-any-swiftvalue-box
Runtime: Implement an opaque 'SwiftValue' ObjC class to hold bridged values.
2 parents 103b6b8 + bc8433f commit e25c751

File tree

9 files changed

+516
-75
lines changed

9 files changed

+516
-75
lines changed

stdlib/public/runtime/CMakeLists.txt

+1
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

+78-5
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>
@@ -2086,6 +2090,65 @@ checkDynamicCastFromOptional(OpaqueValue *dest,
20862090
return {false, payloadType};
20872091
}
20882092

2093+
#if SWIFT_OBJC_INTEROP
2094+
/// Try to unbox a SwiftValue box to perform a dynamic cast.
2095+
static bool tryDynamicCastBoxedSwiftValue(OpaqueValue *dest,
2096+
OpaqueValue *src,
2097+
const Metadata *srcType,
2098+
const Metadata *targetType,
2099+
DynamicCastFlags flags) {
2100+
// Swift type should be AnyObject or a class type.
2101+
if (!srcType->isAnyClass()) {
2102+
auto existential = dyn_cast<ExistentialTypeMetadata>(srcType);
2103+
if (!existential ||
2104+
existential->Flags.getSpecialProtocol()
2105+
!= SpecialProtocol::AnyObject)
2106+
return false;
2107+
}
2108+
2109+
id srcObject;
2110+
memcpy(&srcObject, src, sizeof(id));
2111+
2112+
// Do we have a SwiftValue?
2113+
SwiftValue *srcSwiftValue = getAsSwiftValue(srcObject);
2114+
if (!srcSwiftValue)
2115+
return false;
2116+
2117+
// If so, extract the boxed value and try to cast it.
2118+
const Metadata *boxedType;
2119+
const OpaqueValue *boxedValue;
2120+
std::tie(boxedType, boxedValue)
2121+
= getValueFromSwiftValue(srcSwiftValue);
2122+
2123+
// We can't touch the value from the box because it may be
2124+
// multiply-referenced.
2125+
// TODO: Check for uniqueness and consume if box is unique?
2126+
2127+
// Does the boxed type exactly match the target type we're looking for?
2128+
if (boxedType == targetType) {
2129+
targetType->vw_initializeWithCopy(dest,
2130+
const_cast<OpaqueValue*>(boxedValue));
2131+
// Release the box if we need to.
2132+
if (flags & DynamicCastFlags::TakeOnSuccess)
2133+
objc_release((id)srcSwiftValue);
2134+
return true;
2135+
}
2136+
2137+
// Maybe we can cast the boxed value to our destination type somehow.
2138+
auto innerFlags = flags - DynamicCastFlags::TakeOnSuccess
2139+
- DynamicCastFlags::DestroyOnFailure;
2140+
if (swift_dynamicCast(dest, const_cast<OpaqueValue*>(boxedValue),
2141+
boxedType, targetType, innerFlags)) {
2142+
// Release the box if we need to.
2143+
if (flags & DynamicCastFlags::TakeOnSuccess)
2144+
objc_release((id)srcSwiftValue);
2145+
return true;
2146+
}
2147+
2148+
return false;
2149+
}
2150+
#endif
2151+
20892152
/// Perform a dynamic cast to an arbitrary type.
20902153
SWIFT_RT_ENTRY_VISIBILITY
20912154
bool swift::swift_dynamicCast(OpaqueValue *dest,
@@ -2230,14 +2293,24 @@ bool swift::swift_dynamicCast(OpaqueValue *dest,
22302293
if (tryDynamicCastNSErrorToValue(dest, src, srcType, targetType, flags)) {
22312294
return true;
22322295
}
2296+
#endif
2297+
SWIFT_FALLTHROUGH;
2298+
}
2299+
2300+
case MetadataKind::Existential: {
2301+
#if SWIFT_OBJC_INTEROP
2302+
// A class or AnyObject reference may point at a boxed SwiftValue.
2303+
if (tryDynamicCastBoxedSwiftValue(dest, src, srcType,
2304+
targetType, flags)) {
2305+
return true;
2306+
}
22332307
#endif
22342308
break;
22352309
}
22362310

2311+
case MetadataKind::ExistentialMetatype:
22372312
case MetadataKind::Enum:
22382313
case MetadataKind::Optional:
2239-
case MetadataKind::Existential:
2240-
case MetadataKind::ExistentialMetatype:
22412314
case MetadataKind::Function:
22422315
case MetadataKind::HeapLocalVariable:
22432316
case MetadataKind::HeapGenericLocalVariable:
@@ -2549,7 +2622,7 @@ static id bridgeAnythingNonVerbatimToObjectiveC(OpaqueValue *src,
25492622
if (consume) {
25502623
if (canTake) {
25512624
if (isOutOfLine) {
2552-
// Should only be true of opaque existentials.
2625+
// Should only be true of opaque existentials right now.
25532626
assert(srcExistentialTy->getRepresentation()
25542627
== ExistentialTypeRepresentation::Opaque);
25552628
auto container = reinterpret_cast<OpaqueExistentialContainer*>(src);
@@ -2581,8 +2654,8 @@ static id bridgeAnythingNonVerbatimToObjectiveC(OpaqueValue *src,
25812654
return (id)srcBridgedObject;
25822655
}
25832656

2584-
// TODO: Fall back to boxing here.
2585-
crash("unimplemented boxing bridge");
2657+
// Fall back to boxing.
2658+
return (id)bridgeAnythingToSwiftValueObject(src, srcType, consume);
25862659
}
25872660

25882661
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_INTERFACE

stdlib/public/runtime/ErrorObject.mm

+2-1
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

+5-5
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

+90
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

+3-52
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)