Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions stdlib/public/SwiftShims/swift/shims/CoreFoundationShims.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,11 @@ typedef struct __swift_shims_builtin_CFString {
unsigned long length;
} _swift_shims_builtin_CFString;

SWIFT_RUNTIME_STDLIB_API
void * _Nullable
_swift_stdlib_createTaggedPointerString(const _swift_shims_UInt8 * _Nonnull bytes,
_swift_shims_CFIndex length);

SWIFT_RUNTIME_STDLIB_API
__swift_uint8_t _swift_stdlib_isNSString(id _Nonnull obj);

Expand Down
187 changes: 85 additions & 102 deletions stdlib/public/core/StringBridge.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,6 @@ internal typealias _CocoaString = AnyObject
options: UInt,
range: _SwiftNSRange,
locale: AnyObject?) -> Int

@objc(newTaggedNSStringWithASCIIBytes_:length_:)
func createTaggedString(bytes: UnsafePointer<UInt8>,
count: Int) -> AnyObject?
}

/*
Expand Down Expand Up @@ -316,24 +312,26 @@ internal enum _KnownCocoaString {
#if !(arch(i386) || arch(arm) || arch(arm64_32) || arch(wasm32))
case tagged
#endif
#if arch(arm64)
case constantTagged
#endif
//Constant tagged strings are disabled for now
//#if arch(arm64)
// case constantTagged
//#endif

@inline(__always)
init(_ str: _CocoaString) {

#if !(arch(i386) || arch(arm) || arch(arm64_32))
if _isObjCTaggedPointer(str) {
#if arch(arm64)
if let _ = getConstantTaggedCocoaContents(str) {
self = .constantTagged
} else {
self = .tagged
}
#else
//Constant tagged strings are disabled for now
//#if arch(arm64)
// if let _ = getConstantTaggedCocoaContents(str) {
// self = .constantTagged
// } else {
// self = .tagged
// }
//#else
self = .tagged
#endif
//#endif
return
}
#endif
Expand Down Expand Up @@ -504,67 +502,69 @@ private var expectedConstantTagValue:UInt {
private func formConstantTaggedCocoaString(
untaggedCocoa: _CocoaString
) -> AnyObject? {
#if !arch(arm64)
//constant tagged strings are currently disabled
//#if !arch(arm64)
return nil
#else

let constantPtr:UnsafeRawPointer = Builtin.reinterpretCast(untaggedCocoa)

// Check if what we're pointing to is actually a valid tagged constant
guard _swift_stdlib_dyld_is_objc_constant_string(constantPtr) == 1 else {
return nil
}

let retaggedPointer = UInt(bitPattern: constantPtr) | expectedConstantTagValue

return unsafeBitCast(retaggedPointer, to: AnyObject.self)
#endif
//#else
//
// let constantPtr:UnsafeRawPointer = Builtin.reinterpretCast(untaggedCocoa)
//
// // Check if what we're pointing to is actually a valid tagged constant
// guard _swift_stdlib_dyld_is_objc_constant_string(constantPtr) == 1 else {
// return nil
// }
//
// let retaggedPointer = UInt(bitPattern: constantPtr) | expectedConstantTagValue
//
// return unsafeBitCast(retaggedPointer, to: AnyObject.self)
//#endif
}

@inline(__always)
private func getConstantTaggedCocoaContents(_ cocoaString: _CocoaString) ->
(utf16Length: Int,
asciiContentsPointer: UnsafePointer<UInt8>,
untaggedCocoa: _CocoaString)? {
#if !arch(arm64)
//constant tagged strings are currently disabled
//#if !arch(arm64)
return nil
#else

guard _isObjCTaggedPointer(cocoaString) else {
return nil
}

let taggedValue = unsafeBitCast(cocoaString, to: UInt.self)



guard taggedValue & constantTagMask == expectedConstantTagValue else {
return nil
}

let payloadMask = ~constantTagMask
let payload = taggedValue & payloadMask
let ivarPointer = UnsafePointer<_swift_shims_builtin_CFString>(
bitPattern: payload
)!
//#else

guard _swift_stdlib_dyld_is_objc_constant_string(
UnsafeRawPointer(ivarPointer)
) == 1 else {
return nil
}

let length = ivarPointer.pointee.length
let isUTF16Mask:UInt = 0x0000_0000_0000_0004 //CFStringFlags bit 4: isUnicode
let isASCII = ivarPointer.pointee.flags & isUTF16Mask == 0
_precondition(isASCII) // we don't currently support non-ASCII here
let contentsPtr = ivarPointer.pointee.str
return (
utf16Length: Int(length),
asciiContentsPointer: contentsPtr,
untaggedCocoa: Builtin.reinterpretCast(ivarPointer)
)
#endif
// guard _isObjCTaggedPointer(cocoaString) else {
// return nil
// }
//
// let taggedValue = unsafeBitCast(cocoaString, to: UInt.self)
//
//
//
// guard taggedValue & constantTagMask == expectedConstantTagValue else {
// return nil
// }
//
// let payloadMask = ~constantTagMask
// let payload = taggedValue & payloadMask
// let ivarPointer = UnsafePointer<_swift_shims_builtin_CFString>(
// bitPattern: payload
// )!
//
// guard _swift_stdlib_dyld_is_objc_constant_string(
// UnsafeRawPointer(ivarPointer)
// ) == 1 else {
// return nil
// }
//
// let length = ivarPointer.pointee.length
// let isUTF16Mask:UInt = 0x0000_0000_0000_0004 //CFStringFlags bit 4: isUnicode
// let isASCII = ivarPointer.pointee.flags & isUTF16Mask == 0
// _precondition(isASCII) // we don't currently support non-ASCII here
// let contentsPtr = ivarPointer.pointee.str
// return (
// utf16Length: Int(length),
// asciiContentsPointer: contentsPtr,
// untaggedCocoa: Builtin.reinterpretCast(ivarPointer)
// )
//#endif
}

@usableFromInline
Expand All @@ -579,21 +579,18 @@ internal func _bridgeCocoaString(_ cocoaString: _CocoaString) -> _StringGuts {
cocoaString, to: __SharedStringStorage.self).asString._guts
#if !(arch(i386) || arch(arm) || arch(arm64_32))
case .tagged:
// Foundation should be taking care of tagged pointer strings before they
// reach here, so the only ones reaching this point should be back deployed,
// which will never have tagged pointer strings that aren't small, hence
// the force unwrap here.
return _StringGuts(_SmallString(taggedCocoa: cocoaString)!)
#if arch(arm64)
case .constantTagged:
let taggedContents = getConstantTaggedCocoaContents(cocoaString)!
return _StringGuts(
cocoa: taggedContents.untaggedCocoa,
providesFastUTF8: false, //TODO: if contentsPtr is UTF8 compatible, use it
isASCII: true,
length: taggedContents.utf16Length
)
#endif
//Constant tagged strings are disabled for now
//#if arch(arm64)
// case .constantTagged:
// let taggedContents = getConstantTaggedCocoaContents(cocoaString)!
// return _StringGuts(
// cocoa: taggedContents.untaggedCocoa,
// providesFastUTF8: false, //TODO: if contentsPtr is UTF8 compatible, use it
// isASCII: true,
// length: taggedContents.utf16Length
// )
//#endif
#endif
case .cocoa:
// "Copy" it into a value to be sure nobody will modify behind
Expand Down Expand Up @@ -642,27 +639,14 @@ extension String {
}

@_effects(releasenone)
private func _createNSString(
_ receiver: _StringSelectorHolder,
private func _createTaggedNSString(
_ ptr: UnsafePointer<UInt8>,
_ count: Int,
_ encoding: UInt32
_ count: Int
) -> AnyObject? {
return receiver.createTaggedString(bytes: ptr, count: count)
}

@_effects(releasenone)
private func _createCFString(
_ ptr: UnsafePointer<UInt8>,
_ count: Int,
_ encoding: UInt32
) -> AnyObject? {
return _createNSString(
unsafeBitCast(__StringStorage.self as AnyClass, to: _StringSelectorHolder.self),
ptr,
count,
encoding
)
if let str = _swift_stdlib_createTaggedPointerString(ptr, count) {
return Unmanaged.fromOpaque(str).takeRetainedValue()
}
return nil
}

extension String {
Expand All @@ -675,10 +659,9 @@ extension String {
// Smol ASCII a) may bridge to tagged pointers, b) can't contain a BOM
if _guts.isSmallASCII {
let maybeTagged = _guts.asSmall.withUTF8 { bufPtr in
return _createCFString(
return _createTaggedNSString(
bufPtr.baseAddress._unsafelyUnwrappedUnchecked,
bufPtr.count,
kCFStringEncodingUTF8
bufPtr.count
)
}
if let tagged = maybeTagged { return tagged }
Expand Down
59 changes: 35 additions & 24 deletions stdlib/public/stubs/FoundationHelpers.mm
Original file line number Diff line number Diff line change
Expand Up @@ -33,38 +33,44 @@

static CFHashCode(*_CFStringHashCString)(const uint8_t *bytes, CFIndex len);
static CFHashCode(*_CFStringHashNSString)(id str);
static CFTypeID(*_CFGetTypeID)(CFTypeRef obj);
static CFTypeID _CFStringTypeID = 0;
static swift_once_t initializeBridgingFuncsOnce;

extern "C" bool _dyld_is_objc_constant(DyldObjCConstantKind kind,
const void *addr) SWIFT_RUNTIME_WEAK_IMPORT;
static id(*_CFStringCreateTaggedPointerString)(const uint8_t *bytes, CFIndex numBytes);
static __swift_uint8_t(*_NSIsNSString)(id arg);
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using this instead of _CFGetTypeID + _CFStringTypeID lets us keep our dirty data footprint even despite looking up _CFStringCreateTaggedPointerString

static once_t initializeBridgingFuncsOnce;

static void _initializeBridgingFunctionsImpl(void *ctxt) {
auto getStringTypeID =
(CFTypeID(*)(void))
dlsym(RTLD_DEFAULT, "CFStringGetTypeID");
assert(getStringTypeID);
_CFStringTypeID = getStringTypeID();

_CFGetTypeID = (CFTypeID(*)(CFTypeRef obj))dlsym(RTLD_DEFAULT, "CFGetTypeID");
_CFStringHashNSString = (CFHashCode(*)(id))dlsym(RTLD_DEFAULT,
"CFStringHashNSString");
_CFStringHashCString = (CFHashCode(*)(const uint8_t *, CFIndex))dlsym(
RTLD_DEFAULT,
"CFStringHashCString");
void *cf = dlopen("/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation", RTLD_LAZY | RTLD_NOLOAD | RTLD_LOCAL | RTLD_FIRST);
_NSIsNSString = (__swift_uint8_t(*)(id))dlsym(cf ? cf : RTLD_DEFAULT, "_NSIsNSString");
_CFStringHashNSString = (CFHashCode(*)(id))dlsym(cf ? cf : RTLD_DEFAULT, "CFStringHashNSString");
_CFStringHashCString = (CFHashCode(*)(const uint8_t *, CFIndex))dlsym(cf ? cf : RTLD_DEFAULT, "CFStringHashCString");
_CFStringCreateTaggedPointerString = (id(*)(const uint8_t *, CFIndex))dlsym(cf ? cf : RTLD_DEFAULT, "_CFStringCreateTaggedPointerString");
if (cf) {
dlclose(cf);
}
}

static inline void initializeBridgingFunctions() {
swift_once(&initializeBridgingFuncsOnce,
_initializeBridgingFunctionsImpl,
nullptr);
once(initializeBridgingFuncsOnce,
_initializeBridgingFunctionsImpl,
nullptr);
}

void *
_swift_stdlib_createTaggedPointerString(const _swift_shims_UInt8 * _Nonnull bytes,
_swift_shims_CFIndex length) {
initializeBridgingFunctions();
// if (_CFStringCreateTaggedPointerString != NULL) {
return (void *)_CFStringCreateTaggedPointerString(bytes, length);
// }
// return nil;
}

__swift_uint8_t
_swift_stdlib_isNSString(id obj) {
initializeBridgingFunctions();
return _CFGetTypeID((CFTypeRef)obj) == _CFStringTypeID ? 1 : 0;
if (_NSIsNSString != NULL) {
return _NSIsNSString(obj);
}
return [obj isKindOfClass: objc_lookUpClass("NSString")];
}

_swift_shims_CFHashCode
Expand Down Expand Up @@ -106,10 +112,15 @@ typedef __swift_uint8_t (*getCStringImplPtr)(id,

}

//extern "C" bool _dyld_is_objc_constant(DyldObjCConstantKind kind,
// const void *addr) SWIFT_RUNTIME_WEAK_IMPORT;

__swift_uint8_t
_swift_stdlib_dyld_is_objc_constant_string(const void *addr) {
return (SWIFT_RUNTIME_WEAK_CHECK(_dyld_is_objc_constant)
&& SWIFT_RUNTIME_WEAK_USE(_dyld_is_objc_constant(dyld_objc_string_kind, addr))) ? 1 : 0;
return false;
//This currently always return false
// return (SWIFT_RUNTIME_WEAK_CHECK(_dyld_is_objc_constant)
// && SWIFT_RUNTIME_WEAK_USE(_dyld_is_objc_constant(dyld_objc_string_kind, addr))) ? 1 : 0;
}

#endif
Expand Down