Files
swift-mirror/test/Interpreter/SDK/archiving_generic_swift_class.swift
David Farler e958f99acf Revert "Mangle declared interface type into NominalTypeDescriptor's Name"
This reverts commit 2262bd579a.

This information isn't necessary for field descriptor lookup,
after all. It's only the fields that need to have generic information,
which is already in the field descriptor.
2016-03-03 12:55:35 -08:00

214 lines
6.6 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 = nil
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 archiverArgv: [UnsafeMutablePointer<Int8>] = [
Process.unsafeArgv[0],
UnsafeMutablePointer(("-archive" as StaticString).utf8Start),
nil
]
guard posix_spawn(&archiver, Process.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 = nil
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.
var unarchiver: pid_t = 0
let unarchiverArgv: [UnsafeMutablePointer<Int8>] = [
Process.unsafeArgv[0],
UnsafeMutablePointer(("-unarchive" as StaticString).utf8Start),
nil
]
guard posix_spawn(&unarchiver, Process.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)
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 Process.arguments.count < 2 {
driver()
} else if Process.arguments[1] == "-archive" {
archive()
} else if Process.arguments[1] == "-unarchive" {
unarchive()
} else {
fatalError("invalid commandline argument")
}