| //===--- BridgeStorage.swift.gyb ------------------------------*- 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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Bridged types are notionally single-word beasts that either store |
| // an objc class or a native Swift class. We'd like to be able to |
| // distinguish these cases efficiently. |
| // |
| //===----------------------------------------------------------------------===// |
| // RUN: %target-run-simple-swiftgyb |
| // REQUIRES: executable_test |
| |
| // REQUIRES: objc_interop |
| |
| import Swift |
| |
| //===--- Code mimics the stdlib without using spare pointer bits ----------===// |
| import SwiftShims |
| |
| protocol BridgeStorage { |
| associatedtype Native : AnyObject |
| associatedtype ObjC : AnyObject |
| |
| init(native: Native, bits: Int) |
| init(native: Native) |
| init(objC: ObjC) |
| |
| mutating func isUniquelyReferencedNative() -> Bool |
| mutating func isUniquelyReferenced_native_noSpareBits() -> Bool |
| var isNative: Bool {get} |
| var isObjC: Bool {get} |
| var nativeInstance: Native {get} |
| var nativeInstance_noSpareBits: Native {get} |
| var objCInstance: ObjC {get} |
| var spareBits: Int {get} |
| } |
| |
| extension _BridgeStorage : BridgeStorage {} |
| |
| |
| //===----------------------------------------------------------------------===// |
| //===--- Testing code -----------------------------------------------------===// |
| //===----------------------------------------------------------------------===// |
| import StdlibUnittest |
| var allTests = TestSuite("DiscriminatedBridgeObject") |
| |
| class C { |
| deinit { |
| print("bye C!") |
| } |
| } |
| import Foundation |
| |
| func isOSAtLeast(_ major: Int, _ minor: Int, patch: Int = 0) -> Bool { |
| // isOperatingSystemAtLeastVersion() is unavailable on some OS versions. |
| if #available(iOS 8.0, OSX 10.10, *) { |
| let procInfo: AnyObject = ProcessInfo.processInfo |
| return procInfo.isOperatingSystemAtLeast( |
| OperatingSystemVersion(majorVersion: major, minorVersion: minor, |
| patchVersion: patch)) |
| } |
| |
| return false |
| } |
| |
| func expectTagged(_ s: NSString, _ expected: Bool) -> NSString { |
| #if arch(x86_64) |
| let mask: UInt = 0x8000000000000001 |
| #elseif arch(arm64) |
| let mask: UInt = 0x8000000000000000 |
| #else |
| let mask: UInt = 0 |
| #endif |
| |
| var osSupportsTaggedStrings: Bool |
| #if os(iOS) |
| // NSTaggedPointerString is enabled starting in iOS 9.0. |
| osSupportsTaggedStrings = isOSAtLeast(9,0) |
| #elseif os(tvOS) || os(watchOS) |
| // NSTaggedPointerString is supported in all versions of TVOS and watchOS. |
| osSupportsTaggedStrings = true |
| #elseif os(OSX) |
| // NSTaggedPointerString is enabled starting in OS X 10.10. |
| osSupportsTaggedStrings = isOSAtLeast(10,10) |
| #endif |
| |
| let taggedStringsSupported = osSupportsTaggedStrings && mask != 0 |
| |
| let tagged = unsafeBitCast(s, to: UInt.self) & mask != 0 |
| |
| if taggedStringsSupported && expected == tagged { |
| // okay |
| } else if !taggedStringsSupported && !tagged { |
| // okay |
| } else { |
| let un = !tagged ? "un" : "" |
| fatalError("Unexpectedly \(un)tagged pointer for string \"\(s)\"") |
| } |
| |
| return s |
| } |
| |
| var taggedNSString : NSString { |
| return expectTagged(NSString(format: "foo"), true) |
| } |
| |
| var unTaggedNSString : NSString { |
| return expectTagged("fûtbōl" as NSString, false) |
| } |
| |
| % for Self in ['_BridgeStorage']: |
| allTests.test("${Self}") { |
| typealias B = ${Self}<C, NSString> |
| |
| let oy: NSString = "oy" |
| expectTrue(B(objC: oy).objCInstance == oy) |
| |
| for i in 0..<2 { |
| do { |
| var b = B(native: C(), bits: i) |
| expectFalse(b.isObjC) |
| expectTrue(b.isNative) |
| expectTrue(b.isUniquelyReferencedNative()) |
| if i == 0 { |
| expectTrue(b.isUniquelyReferenced_native_noSpareBits()) |
| } |
| expectEqual(i, b.spareBits) |
| } |
| |
| do { |
| let c = C() |
| var b = B(native: c, bits: i) |
| expectFalse(b.isObjC) |
| expectTrue(b.isNative) |
| expectFalse(b.isUniquelyReferencedNative()) |
| expectEqual(i, b.spareBits) |
| expectTrue(b.nativeInstance === c) |
| if i == 0 { |
| expectTrue(b.nativeInstance_noSpareBits === c) |
| expectFalse(b.isUniquelyReferenced_native_noSpareBits()) |
| } |
| } |
| |
| } |
| |
| var b = B(native: C(), bits: 0) |
| expectTrue(b.isUniquelyReferencedNative()) |
| |
| // Add a reference and verify that it's still native but no longer unique |
| var c = b |
| expectFalse(b.isUniquelyReferencedNative()) |
| _fixLifetime(c) // make sure c is not killed early |
| |
| let n = C() |
| var bb = B(native: n) |
| expectEqual(0, bb.spareBits) |
| expectTrue(bb.nativeInstance === n) |
| expectTrue(bb.isNative) |
| expectFalse(bb.isObjC) |
| |
| var d = B(objC: taggedNSString) |
| expectFalse(d.isUniquelyReferencedNative()) |
| expectFalse(d.isNative) |
| expectTrue(d.isObjC) |
| |
| d = B(objC: unTaggedNSString) |
| expectFalse(d.isUniquelyReferencedNative()) |
| expectFalse(d.isNative) |
| expectTrue(d.isObjC) |
| |
| } |
| % end |
| |
| runAllTests() |
| |