Files
swift-mirror/stdlib/objc/Foundation/BridgeNSString.mm
Joe Groff 19fb7d6b64 runtime: Allocate refcounted storage for swift_NSStringToString.
Instead of letting swift_NSStringToString leak malloced memory, use swift_allocPOD to allocate storage for the converted String, and stash the heap object into the created string.

Swift SVN r7872
2013-09-03 22:27:40 +00:00

235 lines
6.7 KiB
Plaintext

//===--- ObjCBridge.mm - Swift <-> Objective-C Bridging -------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2015 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See http://swift.org/LICENSE.txt for license information
// See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This implements runtime support for bridging between Swift and Objective-C
// types in cases where they aren't trivial.
//
//===----------------------------------------------------------------------===//
#include <Foundation/Foundation.h>
#include <objc/runtime.h>
#include "swift/Runtime/Alloc.h"
#include "swift/Runtime/Metadata.h"
#include "swift/Runtime/ObjCBridge.h"
using namespace swift;
struct SwiftString;
extern "C" {
int64_t
_TSS4sizefRSSFT_Si(void *swiftString);
uint32_t
_TSS11__subscriptFT3idxSi_Scg(uint64_t idx, void *swiftString);
void
swift_NSStringToString(NSString *nsstring, SwiftString *string);
NSString *
swift_StringToNSString(SwiftString *string);
NSDate *
swift_createDate(void);
}; // extern "C"
struct SwiftString {
const char *base;
size_t len;
HeapObject *owner;
};
struct _NSSwiftString_s {
Class isa;
SwiftString swiftString;
};
@interface _NSSwiftString : NSString {
@public
SwiftString swiftString;
}
- (unichar)characterAtIndex: (NSUInteger)index;
- (NSUInteger)length;
@end
@implementation _NSSwiftString
- (unichar)characterAtIndex: (NSUInteger)idx {
static_assert(sizeof(unichar) == 2, "NSString is no longer UTF16?");
// XXX FIXME
// Become bug-for-bug compatible with NSString being UTF16.
// In practice, this API is oblivious to UTF16 surrogate pairs.
return _TSS11__subscriptFT3idxSi_Scg(idx, &swiftString);
}
- (NSUInteger)length {
return _TSS4sizefRSSFT_Si(&swiftString);
}
// Disable the warning about chaining dealloc to super, we *specifically* don't
// want to do that here.
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wobjc-missing-super-calls"
- (void)dealloc {
swift_release(swiftString.owner);
objc_destructInstance(self); // fixup weak references
swift_rawDealloc(self, 4);
}
@end
#pragma clang diagnostic pop
// FIXME: This causes static constructors, which isn't awesome. It would be
// spiffier to use ifunc's if possible.
static const Class stringClasses[] = {
[_NSSwiftString self],
objc_lookUpClass("__NSCFConstantString"),
objc_lookUpClass("__NSCFString"),
};
/// Helper function to grow the allocated heap object for
/// swift_NSStringToString in the case where the initial allocation is
/// insufficient.
static BoxPair _swift_NSStringToString_realloc(BoxPair oldBox,
size_t oldBufSize,
size_t newBufSize) {
// Allocate the new box.
BoxPair newBox = swift_allocPOD(newBufSize, alignof(void*) - 1);
// Copy the data from the old box.
memcpy(newBox.value, oldBox.value, oldBufSize);
// Deallocate the old box. We know the box is POD and hasn't escaped, so we
// can use the swift_deallocPOD fast path.
swift_deallocPOD(oldBox.heapObject);
return newBox;
}
/// Convert an NSString to a Swift String in the worst case, where we have to
/// use -[NSString getBytes:...:] to reencode the string value.
__attribute__((noinline,used))
static void
_swift_NSStringToString_slow(NSString *nsstring, SwiftString *string) {
size_t len = [nsstring length];
size_t bufSize = len * 2 + 1;
// Allocate a POD heap object to hold the data.
BoxPair box = swift_allocPOD(bufSize, alignof(void*) - 1);
char *buf = reinterpret_cast<char *>(box.value);
char *p = buf;
NSRange rangeToEncode = NSMakeRange(0, len);
if (rangeToEncode.length != 0) {
size_t pSize = bufSize - 1;
for (;;) {
NSUInteger usedLength = 0;
// Copy the encoded string to our buffer.
BOOL ok = [nsstring getBytes:p
maxLength:pSize
usedLength:&usedLength
encoding:NSUTF8StringEncoding
options:0
range:rangeToEncode
remainingRange:&rangeToEncode];
// The operation should have encoded some bytes.
if (!ok)
__builtin_trap();
p += usedLength;
// If we encoded the entire string, we're done.
if (rangeToEncode.length == 0)
break;
// Otherwise, grow the buffer and try again.
size_t newBufSize = bufSize + pSize;
box = _swift_NSStringToString_realloc(box, bufSize, newBufSize);
bufSize = newBufSize;
buf = reinterpret_cast<char *>(box.value);
}
}
// getBytes:...: doesn't add a null terminator.
*p = '\0';
string->base = buf;
string->len = p - buf;
string->owner = box.heapObject;
}
void
swift_NSStringToString(NSString *nsstring, SwiftString *string) {
auto boxedString = reinterpret_cast<_NSSwiftString_s *>(nsstring);
if (boxedString->isa == stringClasses[0]) {
string->base = boxedString->swiftString.base;
string->len = boxedString->swiftString.len;
string->owner = boxedString->swiftString.owner;
_swift_retain(string->owner);
return;
} else if (*(Class *)nsstring == stringClasses[1]) {
// constant string
string->base = ((char **)nsstring)[2];
string->len = ((size_t *)nsstring)[3];
string->owner = NULL;
return;
}
_swift_NSStringToString_slow(nsstring, string);
}
NSString *
swift_StringToNSString(SwiftString *string) {
// sizeof(_NSSwiftString) is not allowed
auto r = static_cast<_NSSwiftString *>(swift_rawAlloc(4));
*((Class *)r) = stringClasses[0];
r->swiftString = *string;
_swift_retain(r->swiftString.owner);
return r;
}
// FIXME: Just a hack for testing!
NSDate *swift_createDate(void) {
return [NSDate date];
}
extern "C" bool swift_compareObjects(id x, id y) {
[x release];
[y release];
return x == y;
}
// FIXME: Assumes Int is 64-bit.
extern "C" int64_t swift_hashObject(id obj) {
[obj release];
return (int64_t)obj;
}
/// (String, UnsafePointer<BOOL>) -> () block shim
using block_type = void (^)(id, BOOL*);
using code_type = void (*)(const char*, size_t, HeapObject*, BOOL*, HeapObject*);
extern "C"
block_type _TTbbTSSGVSs13UnsafePointerV10ObjectiveC8ObjCBool__T_(
code_type code,
HeapObject *data) {
SwiftRAII dataRAII(data, true);
return Block_copy(^void (id a, BOOL *b) {
[a retain];
SwiftString s;
swift_NSStringToString((NSString*)a, &s);
return code(s.base, s.len, s.owner, b, swift_retain(*dataRAII));
});
}