Files
swift-mirror/stdlib/public/core/HeapBuffer.swift
Michael Gottesman 6d7b11c8eb [stdlib] Cleanup usage of Builtin.castToNativeObject(...).
Previously often times when casting a value, we would just pass along the
cleanup of the uncasted value. With semantic SIL this is no longer correct since
the cleanup now needs to be on the cast result.

This caused problems for certain usages of Builtin.castToNativeObject(...) by
the stdlib. Specifically, the stdlib was using this on AnyObject values that
were not necessarily native. Since we were recreating the cleanup on the native
value, a swift native release was being used =><=.

In this commit I solve this problem by:

1. Adding an assert in Builtin.castToNativeObject(...) that ensures that any value
passed to Builtin.castToNativeObject() is known conservatively to use swift
native reference counting.

2. I changed all uses where we do not have a precondition of a native ref
counting type to use Builtin.castToUnknownObject(...).

3. I added a new Builtin called Builtin.unsafeCastToNativeObject(...) that does
not have the compile time check. I used this to rewrite callsites in the stdlib
where we know via preconditions that an AnyObject will dynamically always be
native.

rdar://29791263
2017-03-14 00:10:16 -07:00

229 lines
6.7 KiB
Swift

//===----------------------------------------------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
import SwiftShims
typealias _HeapObject = SwiftShims.HeapObject
@_silgen_name("swift_bufferAllocate")
internal func _swift_bufferAllocate(
bufferType type: AnyClass,
size: Int,
alignmentMask: Int
) -> AnyObject
/// A class containing an ivar "value" of type Value, and
/// containing storage for an array of Element whose size is
/// determined at create time.
///
/// The analogous C++-ish class template would be:
///
/// template <class Value, class Element>
/// struct _HeapBuffer {
/// Value value;
/// Element baseAddress[]; // length determined at creation time
///
/// _HeapBuffer() = delete
/// static shared_ptr<_HeapBuffer> create(Value init, int capacity);
/// }
///
/// Note that the Element array is RAW MEMORY. You are expected to
/// construct and---if necessary---destroy Elements there yourself,
/// either in a derived class, or it can be in some manager object
/// that owns the _HeapBuffer.
public // @testable (test/Prototypes/MutableIndexableDict.swift)
class _HeapBufferStorage<Value, Element> {
public init() {}
/// The type used to actually manage instances of
/// `_HeapBufferStorage<Value, Element>`.
typealias Buffer = _HeapBuffer<Value, Element>
deinit {
Buffer(self)._value.deinitialize()
}
final func __getInstanceSizeAndAlignMask() -> (Int, Int) {
return Buffer(self)._allocatedSizeAndAlignMask()
}
}
/// Management API for `_HeapBufferStorage<Value, Element>`
public // @testable
struct _HeapBuffer<Value, Element> : Equatable {
/// A default type to use as a backing store.
typealias Storage = _HeapBufferStorage<Value, Element>
// _storage is passed inout to _isUnique. Although its value
// is unchanged, it must appear mutable to the optimizer.
internal var _storage: Builtin.NativeObject?
public // @testable
var storage: AnyObject? {
return _storage.map { Builtin.castFromNativeObject($0) }
}
internal static func _valueOffset() -> Int {
return _roundUp(
MemoryLayout<_HeapObject>.size,
toAlignment: MemoryLayout<Value>.alignment)
}
internal static func _elementOffset() -> Int {
return _roundUp(
_valueOffset() + MemoryLayout<Value>.size,
toAlignment: MemoryLayout<Element>.alignment)
}
internal static func _requiredAlignMask() -> Int {
// We can't use max here because it can allocate an array.
let heapAlign = MemoryLayout<_HeapObject>.alignment &- 1
let valueAlign = MemoryLayout<Value>.alignment &- 1
let elementAlign = MemoryLayout<Element>.alignment &- 1
return (heapAlign < valueAlign
? (valueAlign < elementAlign ? elementAlign : valueAlign)
: (heapAlign < elementAlign ? elementAlign : heapAlign))
}
internal var _address: UnsafeMutableRawPointer {
return UnsafeMutableRawPointer(
Builtin.bridgeToRawPointer(self._nativeObject))
}
internal var _value: UnsafeMutablePointer<Value> {
return (_HeapBuffer._valueOffset() + _address).assumingMemoryBound(
to: Value.self)
}
public // @testable
var baseAddress: UnsafeMutablePointer<Element> {
return (_HeapBuffer._elementOffset() + _address).assumingMemoryBound(
to: Element.self)
}
internal func _allocatedSize() -> Int {
return _swift_stdlib_malloc_size(_address)
}
internal func _allocatedAlignMask() -> Int {
return _HeapBuffer._requiredAlignMask()
}
internal func _allocatedSizeAndAlignMask() -> (Int, Int) {
return (_allocatedSize(), _allocatedAlignMask())
}
/// Returns the actual number of `Elements` we can possibly store.
internal func _capacity() -> Int {
return (_allocatedSize() - _HeapBuffer._elementOffset())
/ MemoryLayout<Element>.stride
}
internal init() {
self._storage = nil
}
public // @testable
init(_ storage: _HeapBufferStorage<Value, Element>) {
self._storage = Builtin.unsafeCastToNativeObject(storage)
}
internal init(_ storage: AnyObject) {
_sanityCheck(
_usesNativeSwiftReferenceCounting(type(of: storage)),
"HeapBuffer manages only native objects"
)
self._storage = Builtin.unsafeCastToNativeObject(storage)
}
internal init<T : AnyObject>(_ storage: T?) {
self = storage.map { _HeapBuffer($0) } ?? _HeapBuffer()
}
internal init(nativeStorage: Builtin.NativeObject?) {
self._storage = nativeStorage
}
/// Create a `_HeapBuffer` with `self.value = initializer` and
/// `self._capacity() >= capacity`.
public // @testable
init(
_ storageClass: AnyClass,
_ initializer: Value, _ capacity: Int
) {
_sanityCheck(capacity >= 0, "creating a _HeapBuffer with negative capacity")
_sanityCheck(
_usesNativeSwiftReferenceCounting(storageClass),
"HeapBuffer can only create native objects"
)
let totalSize = _HeapBuffer._elementOffset() +
capacity * MemoryLayout<Element>.stride
let alignMask = _HeapBuffer._requiredAlignMask()
let object: AnyObject = _swift_bufferAllocate(
bufferType: storageClass,
size: totalSize,
alignmentMask: alignMask)
self._storage = Builtin.unsafeCastToNativeObject(object)
self._value.initialize(to: initializer)
}
public // @testable
var value: Value {
unsafeAddress {
return UnsafePointer(_value)
}
nonmutating unsafeMutableAddress {
return _value
}
}
/// `true` if storage is non-`nil`.
internal var hasStorage: Bool {
return _storage != nil
}
internal subscript(i: Int) -> Element {
unsafeAddress {
return UnsafePointer(baseAddress + i)
}
nonmutating unsafeMutableAddress {
return baseAddress + i
}
}
internal var _nativeObject: Builtin.NativeObject {
return _storage!
}
internal static func fromNativeObject(_ x: Builtin.NativeObject) -> _HeapBuffer {
return _HeapBuffer(nativeStorage: x)
}
public // @testable
mutating func isUniquelyReferenced() -> Bool {
return _isUnique(&_storage)
}
public // @testable
mutating func isUniquelyReferencedOrPinned() -> Bool {
return _isUniqueOrPinned(&_storage)
}
}
// HeapBuffers are equal when they reference the same buffer
public // @testable
func == <Value, Element>(
lhs: _HeapBuffer<Value, Element>,
rhs: _HeapBuffer<Value, Element>) -> Bool {
return lhs._nativeObject == rhs._nativeObject
}