From 71f697068674cf97b59a850f7aae9f016705c66d Mon Sep 17 00:00:00 2001 From: Alsey Coleman Miller Date: Sat, 6 Jun 2020 15:20:53 -0500 Subject: [PATCH] Fixed `Date` for WASM Use JS implementation for now --- Sources/SwiftFoundation/Date.swift | 46 +++++- Sources/SwiftFoundation/POSIXError.swift | 2 +- Sources/SwiftFoundation/POSIXTime.swift | 183 ++++++++++++++++------- 3 files changed, 170 insertions(+), 61 deletions(-) diff --git a/Sources/SwiftFoundation/Date.swift b/Sources/SwiftFoundation/Date.swift index 994d59c..a1c3e8a 100644 --- a/Sources/SwiftFoundation/Date.swift +++ b/Sources/SwiftFoundation/Date.swift @@ -27,25 +27,60 @@ public struct Date: Equatable, Hashable { The distant past is in terms of centuries. */ public static var distantPast: SwiftFoundation.Date { return Date(timeIntervalSinceReferenceDate: -63114076800.0) } + + /// The interval between 00:00:00 UTC on 1 January 2001 and the current date and time. + public static var timeIntervalSinceReferenceDate: TimeInterval { + return self.timeIntervalSince1970 - self.timeIntervalBetween1970AndReferenceDate + } // MARK: - Properties /// The time interval between the date and the reference date (1 January 2001, GMT). public var timeIntervalSinceReferenceDate: TimeInterval - /// The time interval between the current date and 1 January 1970, GMT. + /** + The time interval between the date and the current date and time. + + If the date is earlier than the current date and time, the this property’s value is negative. + + - SeeAlso: `timeIntervalSince(_:)` + - SeeAlso: `timeIntervalSince1970` + - SeeAlso: `timeIntervalSinceReferenceDate` + */ + public var timeIntervalSinceNow: TimeInterval { + return timeIntervalSinceReferenceDate - Date.timeIntervalSinceReferenceDate + } + + /** + The interval between the date object and 00:00:00 UTC on 1 January 1970. + + This property’s value is negative if the date object is earlier than 00:00:00 UTC on 1 January 1970. + + - SeeAlso: `timeIntervalSince(_:)` + - SeeAlso: `timeIntervalSinceNow` + - SeeAlso: `timeIntervalSinceReferenceDate` + */ public var timeIntervalSince1970: TimeInterval { - get { return timeIntervalSinceReferenceDate + Date.timeIntervalBetween1970AndReferenceDate } - set { timeIntervalSinceReferenceDate = newValue - Date.timeIntervalBetween1970AndReferenceDate } + return timeIntervalSinceReferenceDate + Date.timeIntervalBetween1970AndReferenceDate } // MARK: - Initialization + /// Returns a `Date` initialized to the current date and time. + public init() { + self.init(timeIntervalSinceReferenceDate: Date.timeIntervalSinceReferenceDate) + } + /// Returns an `Date` initialized relative to 00:00:00 UTC on 1 January 2001 by a given number of seconds. public init(timeIntervalSinceReferenceDate timeInterval: TimeInterval) { self.timeIntervalSinceReferenceDate = timeInterval } + /// Returns a `Date` initialized relative to the current date and time by a given number of seconds. + public init(timeIntervalSinceNow: TimeInterval) { + self.timeIntervalSinceReferenceDate = timeIntervalSinceNow + Date.timeIntervalSinceReferenceDate + } + /// Returns a `Date` initialized relative to 00:00:00 UTC on 1 January 1970 by a given number of seconds. public init(timeIntervalSince1970: TimeInterval) { self.timeIntervalSinceReferenceDate = timeIntervalSince1970 - Date.timeIntervalBetween1970AndReferenceDate @@ -95,13 +130,12 @@ public struct Date: Equatable, Hashable { } } -#if !arch(wasm32) - // MARK: - CustomStringConvertible extension SwiftFoundation.Date: CustomStringConvertible { public var description: String { + // TODO: Custom date printing return timeIntervalSinceReferenceDate.description } } @@ -115,8 +149,6 @@ extension SwiftFoundation.Date: CustomDebugStringConvertible { } } -#endif - // MARK: - Comparable extension SwiftFoundation.Date: Comparable { diff --git a/Sources/SwiftFoundation/POSIXError.swift b/Sources/SwiftFoundation/POSIXError.swift index b15daf8..16f3e23 100644 --- a/Sources/SwiftFoundation/POSIXError.swift +++ b/Sources/SwiftFoundation/POSIXError.swift @@ -32,7 +32,7 @@ internal extension POSIXError { function: StaticString = #function) -> POSIXError { guard let code = POSIXErrorCode(rawValue: errno) - else { fatalError("Invalid POSIX Error \(errno)") } + else { fatalError("Invalid POSIX Error \(errno)", file: file, line: line) } return POSIXError(code: code) } diff --git a/Sources/SwiftFoundation/POSIXTime.swift b/Sources/SwiftFoundation/POSIXTime.swift index 3b2415a..2a99225 100644 --- a/Sources/SwiftFoundation/POSIXTime.swift +++ b/Sources/SwiftFoundation/POSIXTime.swift @@ -12,47 +12,45 @@ import Darwin.C import Glibc #endif -#if !arch(wasm32) - // MARK: - Date -public extension Date { - - /// The interval between 00:00:00 UTC on 1 January 2001 and the current date and time. - static var timeIntervalSinceReferenceDate: TimeInterval { - do { return try timeval.timeOfDay().timeInterval - Date.timeIntervalBetween1970AndReferenceDate } - catch { fatalError("Unable to load current time") } - } - - /// Returns a `Date` initialized to the current date and time. - init() { - self.timeIntervalSinceReferenceDate = Date.timeIntervalSinceReferenceDate - } +internal extension Date { - /// Returns a `Date` initialized relative to the current date and time by a given number of seconds. - init(timeIntervalSinceNow: TimeInterval) { - self.timeIntervalSinceReferenceDate = timeIntervalSinceNow + Date.timeIntervalSinceReferenceDate + /// Get seconds since Unix epoch (01/01/1970). + static var timeIntervalSince1970: TimeInterval { + + do { + #if arch(wasm32) + return WebAssembly.timeIntervalSince1970() + #else + if #available(macOS 10.12, iOS 10, tvOS 10.0, *) { + return try SystemClock.realTime.time().timeInterval + } else { + return try timeval.now().timeInterval + } + #endif + } + catch { fatalError("Unable to get current date \(error)") } } +} + +// MARK: - WASM Fix + +#if arch(wasm32) +public extension Date { - /** - The time interval between the date and the current date and time. - - If the date is earlier than the current date and time, the this property’s value is negative. - - - SeeAlso: `timeIntervalSince(_:)` - - SeeAlso: `timeIntervalSince1970` - - SeeAlso: `timeIntervalSinceReferenceDate` - */ - var timeIntervalSinceNow: TimeInterval { - return timeIntervalSinceReferenceDate - Date.timeIntervalSinceReferenceDate + public enum WebAssembly { + + public static var timeIntervalSince1970: () -> TimeInterval = { return try! timeval.now().timeInterval } // won't work } } +#endif // MARK: - POSIX Time internal extension timeval { - static func timeOfDay() throws -> timeval { + static func now() throws -> timeval { var timeStamp = timeval() guard gettimeofday(&timeStamp, nil) == 0 @@ -60,50 +58,38 @@ internal extension timeval { return timeStamp } - init(timeInterval: SwiftFoundation.TimeInterval) { + init(timeInterval: Double) { let (integerValue, decimalValue) = modf(timeInterval) - - let million: SwiftFoundation.TimeInterval = 1000000.0 - + let million: Double = 1000000.0 let microseconds = decimalValue * million - self.init(tv_sec: .init(integerValue), tv_usec: .init(microseconds)) } - var timeInterval: SwiftFoundation.TimeInterval { - - let secondsSince1970 = SwiftFoundation.TimeInterval(self.tv_sec) - - let million: SwiftFoundation.TimeInterval = 1000000.0 - - let microseconds = SwiftFoundation.TimeInterval(self.tv_usec) / million + var timeInterval: Double { + let secondsSince1970 = Double(self.tv_sec) + let million: Double = 1000000.0 + let microseconds = Double(self.tv_usec) / million return secondsSince1970 + microseconds } } internal extension timespec { - init(timeInterval: SwiftFoundation.TimeInterval) { + init(timeInterval: Double) { let (integerValue, decimalValue) = modf(timeInterval) - - let billion: SwiftFoundation.TimeInterval = 1000000000.0 - + let billion: Double = 1000000000.0 let nanoseconds = decimalValue * billion - self.init(tv_sec: .init(integerValue), tv_nsec: .init(nanoseconds)) } - var timeInterval: SwiftFoundation.TimeInterval { - - let secondsSince1970 = SwiftFoundation.TimeInterval(self.tv_sec) - - let billion: SwiftFoundation.TimeInterval = 1000000000.0 - - let nanoseconds = SwiftFoundation.TimeInterval(self.tv_nsec) / billion + var timeInterval: Double { + let secondsSince1970 = Double(self.tv_sec) + let billion: Double = 1000000000.0 + let nanoseconds = Double(self.tv_nsec) / billion return secondsSince1970 + nanoseconds } } @@ -123,8 +109,99 @@ internal extension tm { } } +#if arch(wasm32) +internal struct SystemClock: RawRepresentable, Equatable, Hashable { + let rawValue: UInt32 + init(rawValue: UInt32) { + self.rawValue = rawValue + } +} +#else +@available(macOS 10.12, iOS 10, tvOS 10.0, *) +internal typealias SystemClock = clockid_t #endif +#if !arch(wasm32) +@available(macOS 10.12, iOS 10, tvOS 10.0, *) +internal extension SystemClock { + + + func time() throws -> timespec { + var time = timespec() + guard clock_gettime(self, &time) == 0 + else { throw POSIXError.fromErrno() } + return time + } + + #if os(macOS) || os(Linux) // not supported on iOS + func setTime(_ newValue: timespec) throws { + guard withUnsafePointer(to: newValue, { clock_settime(self, $0) == 0 }) + else { throw POSIXError.fromErrno() } + } + #endif +} + +#endif + +#if arch(wasm32) +@_silgen_name("clock_gettime") +internal func wasm_clock_gettime() -> Int32 // crashes with current swift-wasm + +/** + + Return the time value of a clock. Note: This is similar to clock_gettime in POSIX. + + `clock_time_get(id: clockid, precision: timestamp) -> (errno, timestamp)` + + - Parameter id: clockid The clock for which to return the time. + - Parameter precision: timestamp The maximum lag (exclusive) that the returned time value may have, compared to its actual value. +- Returns: error: errno + time: timestamp The time value of the clock. + + https://github.com/WebAssembly/WASI/blob/master/phases/snapshot/docs.md#-clock_time_getid-clockid-precision-timestamp---errno-timestamp + */ +@_silgen_name("clock_time_get") +internal func wasm_clock_time_get(_ id: UInt32, _ precision: UInt64, _ time: inout UInt64) -> Int16 +#endif + +@available(macOS 10.12, iOS 10, tvOS 10.0, *) +internal extension SystemClock { + + /** + System-wide clock that measures real (i.e., wall-clock) time. Setting this clock requires appropriate privileges. This clock is affected by discontinuous jumps in the system time (e.g., if the system administrator manually changes the clock), and by the incremental adjustments performed by adjtime(3) and NTP. + */ + static var realTime: SystemClock { + #if arch(wasm32) + return .init(rawValue: 0) + #else + return CLOCK_REALTIME + #endif + } + + /** + Clock that cannot be set and represents monotonic time since some unspecified starting point. This clock is not affected by discontinuous jumps in the system time (e.g., if the system administrator manually changes the clock), but is affected by the incremental adjustments performed by adjtime(3) and NTP. + */ + static var monotonic: SystemClock { + #if arch(wasm32) + return .init(rawValue: 1) + #else + return CLOCK_MONOTONIC + #endif + } + + #if os(Linux) + /// A faster but less precise version of CLOCK_REALTIME. Use when you need very fast, but not fine-grained timestamps. + static var realTimeCourse: SystemClock { // (since Linux 2.6.32; Linux-specific) + return CLOCK_REALTIME_COARSE + } + + /// A faster but less precise version of CLOCK_MONOTONIC. Use when you need very fast, but not fine-grained timestamps. + static var monotonicCourse: SystemClock { // (since Linux 2.6.32; Linux-specific) + return CLOCK_MONOTONIC_COARSE + } + #endif +} + // MARK: - Cross-Platform Support #if canImport(Darwin)