mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
216 lines
6.8 KiB
Swift
216 lines
6.8 KiB
Swift
// RUN: %target-run-simple-swift | %FileCheck %s
|
|
// REQUIRES: executable_test
|
|
// REQUIRES: objc_interop
|
|
// UNSUPPORTED: OS=tvos
|
|
// UNSUPPORTED: OS=watchos
|
|
|
|
import Foundation
|
|
|
|
final class Foo<T: NSCoding>: NSObject, NSCoding {
|
|
var one, two: T
|
|
|
|
init(one: T, two: T) {
|
|
self.one = one
|
|
self.two = two
|
|
}
|
|
|
|
@objc required convenience init(coder: NSCoder) {
|
|
let one = coder.decodeObject(forKey: "one") as! T
|
|
let two = coder.decodeObject(forKey :"two") as! T
|
|
self.init(one: one, two: two)
|
|
}
|
|
|
|
@objc(encodeWithCoder:) func encode(with encoder: NSCoder) {
|
|
encoder.encode(one, forKey: "one")
|
|
encoder.encode(two, forKey: "two")
|
|
}
|
|
}
|
|
|
|
// FIXME: W* macro equivalents should be in the Darwin/Glibc overlay
|
|
func WIFEXITED(_ status: Int32) -> Bool {
|
|
return (status & 0o177) == 0
|
|
}
|
|
func WEXITSTATUS(_ status: Int32) -> Int32 {
|
|
return (status >> 8) & 0xFF
|
|
}
|
|
|
|
// FIXME: "environ" should be in the Darwin overlay too
|
|
@_silgen_name("_NSGetEnviron")
|
|
func _NSGetEnviron() -> UnsafeMutablePointer<UnsafeMutablePointer<UnsafeMutablePointer<CChar>?>>
|
|
|
|
var environ: UnsafeMutablePointer<UnsafeMutablePointer<CChar>?> {
|
|
return _NSGetEnviron().pointee
|
|
}
|
|
|
|
func driver() {
|
|
// Create a pipe to connect the archiver to the unarchiver.
|
|
var pipes: [Int32] = [0, 0]
|
|
guard pipe(&pipes) == 0 else { fatalError("pipe failed") }
|
|
|
|
let pipeRead = pipes[0], pipeWrite = pipes[1]
|
|
|
|
var archiver: pid_t = 0, unarchiver: pid_t = 0
|
|
|
|
let envp = environ
|
|
|
|
do {
|
|
// Set up the archiver's stdout to feed into our pipe.
|
|
var archiverActions: posix_spawn_file_actions_t?
|
|
guard posix_spawn_file_actions_init(&archiverActions) == 0 else {
|
|
fatalError("posix_spawn_file_actions_init failed")
|
|
}
|
|
defer { posix_spawn_file_actions_destroy(&archiverActions) }
|
|
guard posix_spawn_file_actions_adddup2(&archiverActions,
|
|
pipeWrite,
|
|
STDOUT_FILENO) == 0
|
|
&& posix_spawn_file_actions_addclose(&archiverActions,
|
|
pipeRead) == 0
|
|
else {
|
|
fatalError("posix_spawn_file_actions_add failed")
|
|
}
|
|
|
|
// Spawn the archiver process.
|
|
let str: StaticString = "-archive"
|
|
let optStr = UnsafeMutableRawPointer(mutating: str.utf8Start).bindMemory(
|
|
to: CChar.self, capacity: str.utf8CodeUnitCount)
|
|
let archiverArgv: [UnsafeMutablePointer<Int8>?] = [
|
|
CommandLine.unsafeArgv[0], optStr, nil
|
|
]
|
|
guard posix_spawn(&archiver, CommandLine.unsafeArgv[0],
|
|
&archiverActions, nil,
|
|
archiverArgv, envp) == 0 else {
|
|
fatalError("posix_spawn failed")
|
|
}
|
|
}
|
|
|
|
do {
|
|
// Set up the unarchiver's stdin to read from our pipe.
|
|
var unarchiverActions: posix_spawn_file_actions_t?
|
|
guard posix_spawn_file_actions_init(&unarchiverActions) == 0 else {
|
|
fatalError("posix_spawn_file_actions_init failed")
|
|
}
|
|
defer { posix_spawn_file_actions_destroy(&unarchiverActions) }
|
|
guard posix_spawn_file_actions_adddup2(&unarchiverActions,
|
|
pipeRead,
|
|
STDIN_FILENO) == 0
|
|
&& posix_spawn_file_actions_addclose(&unarchiverActions,
|
|
pipeWrite) == 0
|
|
else {
|
|
fatalError("posix_spawn_file_actions_add failed")
|
|
}
|
|
|
|
// Spawn the unarchiver process.
|
|
let str = "-unarchive" as StaticString
|
|
let optStr = UnsafeMutableRawPointer(mutating: str.utf8Start).bindMemory(
|
|
to: CChar.self, capacity: str.utf8CodeUnitCount)
|
|
var unarchiver: pid_t = 0
|
|
let unarchiverArgv: [UnsafeMutablePointer<Int8>?] = [
|
|
CommandLine.unsafeArgv[0], optStr, nil
|
|
]
|
|
guard posix_spawn(&unarchiver, CommandLine.unsafeArgv[0],
|
|
&unarchiverActions, nil,
|
|
unarchiverArgv, envp) == 0 else {
|
|
fatalError("posix_spawn failed")
|
|
}
|
|
}
|
|
|
|
// Wash our hands of the pipe, now that the subprocesses have started.
|
|
close(pipeRead)
|
|
close(pipeWrite)
|
|
|
|
// Wait for the subprocesses to finish.
|
|
var waiting: Set<pid_t> = [archiver, unarchiver]
|
|
while !waiting.isEmpty {
|
|
var status: Int32 = 0
|
|
let pid = wait(&status)
|
|
if pid == -1 {
|
|
// If the error was EINTR, just wait again.
|
|
if errno == EINTR { continue }
|
|
// If we have no children to wait for, stop.
|
|
if errno == ECHILD { break }
|
|
fatalError("wait failed")
|
|
}
|
|
waiting.remove(pid)
|
|
// Ensure the process exited successfully.
|
|
guard WIFEXITED(status) && WEXITSTATUS(status) == 0 else {
|
|
fatalError("subprocess exited abnormally")
|
|
}
|
|
}
|
|
}
|
|
|
|
func archive() {
|
|
let data = NSMutableData()
|
|
let archiver = NSKeyedArchiver(forWritingWith: data)
|
|
archiver.encode(Foo<NSString>(one: "one", two: "two"), forKey: "strings")
|
|
archiver.encode(Foo<NSNumber>(one: 1, two: 2), forKey: "numbers")
|
|
archiver.finishEncoding()
|
|
|
|
// Output the archived data over stdout, which should be piped to stdin
|
|
// on the unarchiver process.
|
|
while true {
|
|
let status = write(STDOUT_FILENO, data.bytes, data.length)
|
|
if status == data.length { break }
|
|
if errno == EINTR { continue }
|
|
fatalError("write failed")
|
|
}
|
|
}
|
|
|
|
func unarchive() {
|
|
// FIXME: Pre-instantiate the generic classes that were archived, since
|
|
// the ObjC runtime doesn't know how.
|
|
NSStringFromClass(Foo<NSNumber>.self)
|
|
NSStringFromClass(Foo<NSString>.self)
|
|
|
|
// Read in the data from stdin, where the archiver process should have
|
|
// written it.
|
|
var rawData: [UInt8] = []
|
|
|
|
var buffer = [UInt8](repeating: 0, count: 4096)
|
|
|
|
while true {
|
|
let count = read(STDIN_FILENO, &buffer, 4096)
|
|
if count == 0 { break }
|
|
if count == -1 {
|
|
if errno == EINTR { continue }
|
|
fatalError("read failed")
|
|
}
|
|
rawData += buffer[0..<count]
|
|
}
|
|
|
|
// Feed it into an unarchiver.
|
|
let data = NSData(bytes: rawData, length: rawData.count)
|
|
let unarchiver = NSKeyedUnarchiver(forReadingWith: data as Data)
|
|
|
|
guard let strings
|
|
= unarchiver.decodeObject(forKey: "strings") as? Foo<NSString> else {
|
|
fatalError("unable to unarchive Foo<NSString>")
|
|
}
|
|
guard let numbers
|
|
= unarchiver.decodeObject(forKey: "numbers") as? Foo<NSNumber> else {
|
|
fatalError("unable to unarchive Foo<NSNumber>")
|
|
}
|
|
|
|
// CHECK-LABEL: <_TtGC4main3FooCSo8NSString_: {{0x[0-9a-f]+}}> #0
|
|
// CHECK: one: one
|
|
// CHECK: two: two
|
|
// CHECK-LABEL: <_TtGC4main3FooCSo8NSNumber_: {{0x[0-9a-f]+}}> #0
|
|
// CHECK: one: 1
|
|
// CHECK: two: 2
|
|
dump(strings)
|
|
dump(numbers)
|
|
}
|
|
|
|
// Pick a mode based on the command-line arguments.
|
|
// The test launches as a "driver" which then respawns itself into reader
|
|
// and writer subprocesses.
|
|
if CommandLine.arguments.count < 2 {
|
|
driver()
|
|
} else if CommandLine.arguments[1] == "-archive" {
|
|
archive()
|
|
} else if CommandLine.arguments[1] == "-unarchive" {
|
|
unarchive()
|
|
} else {
|
|
fatalError("invalid commandline argument")
|
|
}
|
|
|