mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
Add an Array-based NSMutableArray subclass
This commit is contained in:
@@ -85,10 +85,7 @@ public protocol _ObjectiveCBridgeable {
|
||||
|
||||
#if _runtime(_ObjC)
|
||||
|
||||
@available(macOS, introduced: 9999, deprecated)
|
||||
@available(iOS, introduced: 9999, deprecated)
|
||||
@available(watchOS, introduced: 9999, deprecated)
|
||||
@available(tvOS, introduced: 9999, deprecated)
|
||||
@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *)
|
||||
@available(*, deprecated)
|
||||
@_cdecl("_SwiftCreateBridgedArray")
|
||||
@usableFromInline
|
||||
@@ -101,6 +98,19 @@ internal func _SwiftCreateBridgedArray(
|
||||
return Unmanaged<AnyObject>.passRetained(bridged)
|
||||
}
|
||||
|
||||
@available(macOS 9999, iOS 9999, tvOS 9999, watchOS 9999, *)
|
||||
@available(*, deprecated)
|
||||
@_cdecl("_SwiftCreateBridgedMutableArray")
|
||||
@usableFromInline
|
||||
internal func _SwiftCreateBridgedMutableArray(
|
||||
values: UnsafePointer<AnyObject>,
|
||||
numValues: Int
|
||||
) -> Unmanaged<AnyObject> {
|
||||
let bufPtr = UnsafeBufferPointer(start: values, count: numValues)
|
||||
let bridged = _SwiftNSMutableArray(Array(bufPtr))
|
||||
return Unmanaged<AnyObject>.passRetained(bridged)
|
||||
}
|
||||
|
||||
@_silgen_name("swift_stdlib_connectNSBaseClasses")
|
||||
internal func _connectNSBaseClasses() -> Bool
|
||||
|
||||
|
||||
@@ -276,6 +276,18 @@ internal class __SwiftNativeNSArray {
|
||||
deinit {}
|
||||
}
|
||||
|
||||
@_fixed_layout
|
||||
@usableFromInline
|
||||
@objc @_swift_native_objc_runtime_base(__SwiftNativeNSMutableArrayBase)
|
||||
internal class _SwiftNativeNSMutableArray {
|
||||
@inlinable
|
||||
@nonobjc
|
||||
internal init() {}
|
||||
// @objc public init(coder: AnyObject) {}
|
||||
@inlinable
|
||||
deinit {}
|
||||
}
|
||||
|
||||
@_fixed_layout
|
||||
@usableFromInline
|
||||
@objc @_swift_native_objc_runtime_base(__SwiftNativeNSDictionaryBase)
|
||||
|
||||
@@ -61,14 +61,16 @@ internal class __SwiftNativeNSArrayWithContiguousStorage
|
||||
}
|
||||
}
|
||||
|
||||
private let NSNotFound: Int = .max
|
||||
|
||||
// Implement the APIs required by NSArray
|
||||
extension __SwiftNativeNSArrayWithContiguousStorage: _NSArrayCore {
|
||||
@objc internal var count: Int {
|
||||
return withUnsafeBufferOfObjects { $0.count }
|
||||
}
|
||||
|
||||
@objc(objectAtIndex:)
|
||||
internal func objectAt(_ index: Int) -> AnyObject {
|
||||
@inline(__always)
|
||||
@nonobjc private func _objectAt(_ index: Int) -> AnyObject {
|
||||
return withUnsafeBufferOfObjects {
|
||||
objects in
|
||||
_precondition(
|
||||
@@ -78,6 +80,16 @@ extension __SwiftNativeNSArrayWithContiguousStorage: _NSArrayCore {
|
||||
}
|
||||
}
|
||||
|
||||
@objc(objectAtIndexedSubscript:)
|
||||
dynamic internal func objectAtSubscript(_ index: Int) -> AnyObject {
|
||||
return _objectAt(index)
|
||||
}
|
||||
|
||||
@objc(objectAtIndex:)
|
||||
dynamic internal func objectAt(_ index: Int) -> AnyObject {
|
||||
return _objectAt(index)
|
||||
}
|
||||
|
||||
@objc internal func getObjects(
|
||||
_ aBuffer: UnsafeMutablePointer<AnyObject>, range: _SwiftNSRange
|
||||
) {
|
||||
@@ -131,6 +143,172 @@ extension __SwiftNativeNSArrayWithContiguousStorage: _NSArrayCore {
|
||||
}
|
||||
}
|
||||
|
||||
@_fixed_layout
|
||||
@usableFromInline
|
||||
@objc internal final class _SwiftNSMutableArray :
|
||||
_SwiftNativeNSMutableArray, _NSArrayCore
|
||||
{
|
||||
internal var contents: [AnyObject]
|
||||
|
||||
internal init(_ array: [AnyObject]) {
|
||||
contents = array
|
||||
super.init()
|
||||
}
|
||||
|
||||
@objc internal var count: Int {
|
||||
return contents.count
|
||||
}
|
||||
|
||||
@objc(objectAtIndexedSubscript:)
|
||||
dynamic internal func objectAtSubscript(_ index: Int) -> AnyObject {
|
||||
//TODO: exception instead of precondition, once that's possible
|
||||
return contents[index]
|
||||
}
|
||||
|
||||
@objc(objectAtIndex:)
|
||||
dynamic internal func objectAt(_ index: Int) -> AnyObject {
|
||||
//TODO: exception instead of precondition, once that's possible
|
||||
return contents[index]
|
||||
}
|
||||
|
||||
@objc internal func getObjects(
|
||||
_ aBuffer: UnsafeMutablePointer<AnyObject>, range: _SwiftNSRange
|
||||
) {
|
||||
return contents.withContiguousStorageIfAvailable { objects in
|
||||
//TODO: exceptions instead of preconditions, once that's possible
|
||||
|
||||
_precondition(
|
||||
_isValidArrayIndex(range.location, count: objects.count),
|
||||
"Array index out of range")
|
||||
|
||||
_precondition(
|
||||
_isValidArrayIndex(
|
||||
range.location + range.length, count: objects.count),
|
||||
"Array index out of range")
|
||||
|
||||
if objects.isEmpty { return }
|
||||
|
||||
// These objects are "returned" at +0, so treat them as pointer values to
|
||||
// avoid retains. Copy bytes via a raw pointer to circumvent reference
|
||||
// counting while correctly aliasing with all other pointer types.
|
||||
UnsafeMutableRawPointer(aBuffer).copyMemory(
|
||||
from: objects.baseAddress! + range.location,
|
||||
byteCount: range.length * MemoryLayout<AnyObject>.stride)
|
||||
}!
|
||||
}
|
||||
|
||||
@objc(countByEnumeratingWithState:objects:count:)
|
||||
internal func countByEnumerating(
|
||||
with state: UnsafeMutablePointer<_SwiftNSFastEnumerationState>,
|
||||
objects: UnsafeMutablePointer<AnyObject>?, count: Int
|
||||
) -> Int {
|
||||
var enumerationState = state.pointee
|
||||
|
||||
if enumerationState.state != 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
return contents.withContiguousStorageIfAvailable {
|
||||
objects in
|
||||
enumerationState.mutationsPtr = _fastEnumerationStorageMutationsPtr
|
||||
enumerationState.itemsPtr =
|
||||
AutoreleasingUnsafeMutablePointer(objects.baseAddress)
|
||||
enumerationState.state = 1
|
||||
state.pointee = enumerationState
|
||||
return objects.count
|
||||
}!
|
||||
}
|
||||
|
||||
@objc(copyWithZone:)
|
||||
dynamic internal func copy(with _: _SwiftNSZone?) -> AnyObject {
|
||||
return contents._bridgeToObjectiveCImpl()
|
||||
}
|
||||
|
||||
@objc(insertObject:atIndex:)
|
||||
dynamic internal func insert(_ anObject: AnyObject, at index: Int) {
|
||||
contents.insert(anObject, at: index)
|
||||
}
|
||||
|
||||
@objc(removeObjectAtIndex:)
|
||||
dynamic internal func removeObject(at index: Int) {
|
||||
contents.remove(at: index)
|
||||
}
|
||||
|
||||
@objc(addObject:)
|
||||
dynamic internal func add(_ anObject: AnyObject) {
|
||||
contents.append(anObject)
|
||||
}
|
||||
|
||||
@objc(removeLastObject)
|
||||
dynamic internal func removeLastObject() {
|
||||
contents.removeLast()
|
||||
}
|
||||
|
||||
@objc(replaceObjectAtIndex:withObject:)
|
||||
dynamic internal func replaceObject(at index: Int, with anObject: AnyObject) {
|
||||
//enforces bounds, unlike set equivalent, which can append
|
||||
contents[index] = anObject
|
||||
}
|
||||
|
||||
//Non-core methods overridden for performance
|
||||
|
||||
@objc(exchangeObjectAtIndex:withObjectAtIndex:)
|
||||
dynamic internal func exchange(at index: Int, with index2: Int) {
|
||||
swap(&contents[index], &contents[index2])
|
||||
}
|
||||
|
||||
@objc(replaceObjectsInRange:withObjects:count:)
|
||||
dynamic internal func replaceObjects(in range: _SwiftNSRange,
|
||||
with objects: UnsafePointer<AnyObject>,
|
||||
count: Int) {
|
||||
let range = range.location ..< range.location + range.length
|
||||
let buf = UnsafeBufferPointer(start: objects, count: count)
|
||||
contents.replaceSubrange(range, with: buf)
|
||||
}
|
||||
|
||||
@objc(insertObjects:count:atIndex:)
|
||||
dynamic internal func insertObjects(_ objects: UnsafePointer<AnyObject>,
|
||||
count: Int,
|
||||
at index: Int) {
|
||||
let buf = UnsafeBufferPointer(start: objects, count: count)
|
||||
contents.insert(contentsOf: buf, at: index)
|
||||
}
|
||||
|
||||
@objc(indexOfObjectIdenticalTo:)
|
||||
dynamic internal func index(ofObjectIdenticalTo object: AnyObject) -> Int {
|
||||
return contents.firstIndex { $0 === object } ?? NSNotFound
|
||||
}
|
||||
|
||||
@objc(removeObjectsInRange:)
|
||||
dynamic internal func removeObjects(in range: _SwiftNSRange) {
|
||||
let range = range.location ..< range.location + range.length
|
||||
contents.replaceSubrange(range, with: [])
|
||||
}
|
||||
|
||||
@objc(removeAllObjects)
|
||||
dynamic internal func removeAllObjects() {
|
||||
contents = []
|
||||
}
|
||||
|
||||
@objc(setObject:atIndex:)
|
||||
dynamic internal func setObject(_ anObject: AnyObject, at index: Int) {
|
||||
if index == contents.count {
|
||||
contents.append(anObject)
|
||||
} else {
|
||||
contents[index] = anObject
|
||||
}
|
||||
}
|
||||
|
||||
@objc(setObject:atIndexedSubscript:) dynamic
|
||||
internal func setObjectSubscript(_ anObject: AnyObject, at index: Int) {
|
||||
if index == contents.count {
|
||||
contents.append(anObject)
|
||||
} else {
|
||||
contents[index] = anObject
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An `NSArray` whose contiguous storage is created and filled, upon
|
||||
/// first access, by bridging the elements of a Swift `Array`.
|
||||
///
|
||||
@@ -292,6 +470,18 @@ internal class __ContiguousArrayStorageBase
|
||||
_internalInvariantFailure(
|
||||
"Concrete subclasses must implement _getNonVerbatimBridgingBuffer")
|
||||
}
|
||||
|
||||
@objc(mutableCopyWithZone:)
|
||||
dynamic internal func mutableCopy(with _: _SwiftNSZone?) -> AnyObject {
|
||||
let arr = Array<AnyObject>(_ContiguousArrayBuffer(self))
|
||||
return _SwiftNSMutableArray(arr)
|
||||
}
|
||||
|
||||
@objc(indexOfObjectIdenticalTo:)
|
||||
dynamic internal func index(ofObjectIdenticalTo object: AnyObject) -> Int {
|
||||
let arr = Array<AnyObject>(_ContiguousArrayBuffer(self))
|
||||
return arr.firstIndex { $0 === object } ?? NSNotFound
|
||||
}
|
||||
#endif
|
||||
|
||||
@inlinable
|
||||
|
||||
@@ -50,7 +50,7 @@ using namespace swift;
|
||||
// NOTE: older runtimes called these _SwiftNativeNSXXXBase. The two must
|
||||
// coexist, so these were renamed. The old names must not be used in the new
|
||||
// runtime.
|
||||
% for Class in ('Array', 'Dictionary', 'Set', 'String', 'Enumerator'):
|
||||
% for Class in ('Array', 'MutableArray', 'Dictionary', 'Set', 'String', 'Enumerator'):
|
||||
SWIFT_RUNTIME_STDLIB_API
|
||||
@interface __SwiftNativeNS${Class}Base : NSObject
|
||||
{
|
||||
@@ -120,7 +120,7 @@ swift_stdlib_NSObject_isEqual(id lhs,
|
||||
SWIFT_CC(swift) SWIFT_RUNTIME_STDLIB_SPI
|
||||
bool
|
||||
swift_stdlib_connectNSBaseClasses() {
|
||||
% for Class in ('Array', 'Dictionary', 'Set', 'String', 'Enumerator'):
|
||||
% for Class in ('Array', 'MutableArray', 'Dictionary', 'Set', 'String', 'Enumerator'):
|
||||
Class NS${Class}Super = objc_lookUpClass("NS${Class}");
|
||||
if (!NS${Class}Super) return false;
|
||||
Class NS${Class}OurClass = objc_lookUpClass("__SwiftNativeNS${Class}Base");
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
Class _SwiftNSMutableArray is a new API without @available attribute
|
||||
Class _SwiftNativeNSMutableArray is a new API without @available attribute
|
||||
Func _collectReferencesInsideObject(_:) is a new API without @available attribute
|
||||
Func _loadDestroyTLSCounter() is a new API without @available attribute
|
||||
Protocol _RuntimeFunctionCountersStats is a new API without @available attribute
|
||||
|
||||
@@ -65,6 +65,7 @@ BOOL TestSwiftNativeNSBase_UnwantedCdtors()
|
||||
NSMutableSet *expectedClasses =
|
||||
[NSMutableSet setWithObjects:
|
||||
@"__SwiftNativeNSArrayBase",
|
||||
@"__SwiftNativeNSMutableArrayBase",
|
||||
@"__SwiftNativeNSDictionaryBase",
|
||||
@"__SwiftNativeNSSetBase",
|
||||
@"__SwiftNativeNSStringBase",
|
||||
|
||||
Reference in New Issue
Block a user