Skip to content

Commit 396a16e

Browse files
Drop Foundation dependency
Co-authored-by: Kabir Oberai <[email protected]>
1 parent e56f0fc commit 396a16e

File tree

11 files changed

+132
-93
lines changed

11 files changed

+132
-93
lines changed

Package.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,10 @@ let package = Package(
5858
),
5959
.target(
6060
name: "WasmParser",
61-
dependencies: ["WasmTypes"]
61+
dependencies: [
62+
"WasmTypes",
63+
.product(name: "SystemPackage", package: "swift-system"),
64+
]
6265
),
6366
.target(
6467
name: "WasmKitWASI",

Sources/CLI/Run/Run.swift

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import ArgumentParser
2-
import Foundation
32
import SystemPackage
43
import WasmKitWASI
54
import WasmKit
@@ -43,7 +42,7 @@ struct Run: ParsableCommand {
4342
log("Started parsing module", verbose: true)
4443

4544
let module: Module
46-
if verbose {
45+
if verbose, #available(macOS 13.0, *) {
4746
let (parsedModule, parseTime) = try measure {
4847
try parseWasm(filePath: FilePath(path))
4948
}
@@ -71,23 +70,27 @@ struct Run: ParsableCommand {
7170
invoke = entry
7271
}
7372

74-
let (_, invokeTime) = try measure(execution: invoke)
75-
76-
log("Finished invoking function \"\(path)\": \(invokeTime)", verbose: true)
73+
if #available(macOS 13.0, *) {
74+
let (_, invokeTime) = try measure(execution: invoke)
75+
log("Finished invoking function \"\(path)\": \(invokeTime)", verbose: true)
76+
} else {
77+
try invoke()
78+
}
7779
}
7880

7981
func deriveInterceptor() throws -> (interceptor: GuestTimeProfiler, finalize: () -> Void)? {
8082
guard let outputPath = self.profileOutput else { return nil }
81-
FileManager.default.createFile(atPath: outputPath, contents: nil)
82-
let fileHandle = try FileHandle(forWritingTo: URL(fileURLWithPath: outputPath))
83+
let fileHandle = try FileDescriptor.open(
84+
FilePath(outputPath), .writeOnly, options: .create
85+
)
8386
let profiler = GuestTimeProfiler { data in
84-
try? fileHandle.write(contentsOf: data)
87+
var data = data
88+
_ = data.withUTF8 { try! fileHandle.writeAll($0) }
8589
}
8690
return (
8791
profiler,
8892
{
8993
profiler.finalize()
90-
try! fileHandle.synchronize()
9194
try! fileHandle.close()
9295

9396
print("\nProfile Completed: \(outputPath) can be viewed using https://ui.perfetto.dev/")
@@ -144,23 +147,21 @@ struct Run: ParsableCommand {
144147
}
145148
}
146149

150+
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *)
147151
func measure<Result>(
148152
execution: () throws -> Result
149153
) rethrows -> (Result, String) {
150-
let start = DispatchTime.now()
151-
let result = try execution()
152-
let end = DispatchTime.now()
153-
154-
let numberFormatter = NumberFormatter()
155-
numberFormatter.numberStyle = .decimal
156-
let nanoseconds = NSNumber(value: end.uptimeNanoseconds - start.uptimeNanoseconds)
157-
let formattedTime = numberFormatter.string(from: nanoseconds)! + " ns"
158-
return (result, formattedTime)
154+
var result: Result!
155+
let formattedTime = try ContinuousClock().measure {
156+
result = try execution()
157+
}
158+
159+
return (result, formattedTime.description)
159160
}
160161

161162
@Sendable func log(_ message: String, verbose: Bool = false) {
162163
if !verbose || self.verbose {
163-
fputs(message + "\n", stderr)
164+
try! FileDescriptor.standardError.writeAll((message + "\n").utf8)
164165
}
165166
}
166167
}

Sources/WASI/FileSystem.swift

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import Foundation
21
import SystemPackage
32

43
struct FileAccessMode: OptionSet {

Sources/WASI/Platform/File.swift

Lines changed: 18 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import Foundation
21
import SystemPackage
32

43
protocol FdWASIEntry: WASIEntry {
@@ -31,48 +30,34 @@ extension FdWASIFile {
3130
throw WASIAbi.Errno.EBADF
3231
}
3332
// TODO: Use `writev`
34-
let handle = FileHandle(fileDescriptor: fd.rawValue)
3533
var bytesWritten: UInt32 = 0
3634
for iovec in buffer {
37-
try iovec.withHostBufferPointer {
38-
try handle.write(contentsOf: $0)
35+
bytesWritten += try iovec.withHostBufferPointer {
36+
UInt32(try fd.write(UnsafeRawBufferPointer($0)))
3937
}
40-
bytesWritten += iovec.length
4138
}
4239
return bytesWritten
4340
}
4441

4542
@inlinable
4643
func pwrite<Buffer: Sequence>(vectored buffer: Buffer, offset: WASIAbi.FileSize) throws -> WASIAbi.Size where Buffer.Element == WASIAbi.IOVec {
4744
// TODO: Use `pwritev`
48-
let handle = FileHandle(fileDescriptor: fd.rawValue)
49-
let savedOffset = try handle.offset()
50-
try handle.seek(toOffset: offset)
51-
let nwritten = try write(vectored: buffer)
52-
try handle.seek(toOffset: savedOffset)
53-
return nwritten
45+
var currentOffset: Int64 = Int64(offset)
46+
for iovec in buffer {
47+
currentOffset += try iovec.withHostBufferPointer {
48+
Int64(try fd.writeAll(toAbsoluteOffset: currentOffset, $0))
49+
}
50+
}
51+
let nwritten = WASIAbi.FileSize(currentOffset) - offset
52+
return WASIAbi.Size(nwritten)
5453
}
5554

5655
@inlinable
5756
func read<Buffer: Sequence>(into buffer: Buffer) throws -> WASIAbi.Size where Buffer.Element == WASIAbi.IOVec {
58-
// TODO: Use `readv`
59-
let handle = FileHandle(fileDescriptor: fd.rawValue)
6057
var nread: UInt32 = 0
6158
for iovec in buffer {
62-
try iovec.buffer.withHostPointer(count: Int(iovec.length)) { rawBufferStart in
63-
var bufferStart = rawBufferStart.baseAddress!.bindMemory(
64-
to: UInt8.self, capacity: Int(iovec.length)
65-
)
66-
let bufferEnd = bufferStart + Int(iovec.length)
67-
while bufferStart < bufferEnd {
68-
let remaining = bufferEnd - bufferStart
69-
guard let bytes = try handle.read(upToCount: remaining) else {
70-
break
71-
}
72-
bytes.copyBytes(to: bufferStart, count: bytes.count)
73-
bufferStart += bytes.count
74-
}
75-
nread += iovec.length - UInt32(bufferEnd - bufferStart)
59+
nread += try iovec.withHostBufferPointer {
60+
try UInt32(fd.read(into: $0))
7661
}
7762
}
7863
return nread
@@ -81,11 +66,12 @@ extension FdWASIFile {
8166
@inlinable
8267
func pread<Buffer: Sequence>(into buffer: Buffer, offset: WASIAbi.FileSize) throws -> WASIAbi.Size where Buffer.Element == WASIAbi.IOVec {
8368
// TODO: Use `preadv`
84-
let handle = FileHandle(fileDescriptor: fd.rawValue)
85-
let savedOffset = try handle.offset()
86-
try handle.seek(toOffset: offset)
87-
let nread = try read(into: buffer)
88-
try handle.seek(toOffset: savedOffset)
69+
var nread: UInt32 = 0
70+
for iovec in buffer {
71+
nread += try iovec.withHostBufferPointer {
72+
try UInt32(fd.read(fromAbsoluteOffset: Int64(offset + UInt64(nread)), into: $0))
73+
}
74+
}
8975
return nread
9076
}
9177
}

Sources/WASI/RandomBufferGenerator.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ extension RandomBufferGenerator where Self: RandomNumberGenerator {
2222
withUnsafeBytes(of: random) { randomBytes in
2323
let startOffset = i * 8
2424
let destination = UnsafeMutableBufferPointer(rebasing: buffer[startOffset..<(startOffset + 8)])
25-
randomBytes.copyBytes(to: destination)
25+
UnsafeMutableRawBufferPointer(destination).copyMemory(from: randomBytes)
2626
}
2727
}
2828

@@ -33,7 +33,9 @@ extension RandomBufferGenerator where Self: RandomNumberGenerator {
3333
withUnsafeBytes(of: random) { randomBytes in
3434
let startOffset = count * 8
3535
let destination = UnsafeMutableBufferPointer(rebasing: buffer[startOffset..<(startOffset + remaining)])
36-
randomBytes.copyBytes(to: destination)
36+
UnsafeMutableRawBufferPointer(destination).copyMemory(
37+
from: UnsafeRawBufferPointer(start: randomBytes.baseAddress, count: remaining)
38+
)
3739
}
3840
}
3941
}

Sources/WASI/WASI.swift

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
1-
import Foundation
1+
#if os(macOS) || os(iOS) || os(watchOS) || os(tvOS)
2+
import Darwin
3+
#elseif os(Linux) || os(FreeBSD) || os(Android)
4+
import Glibc
5+
#elseif os(Windows)
6+
import ucrt
7+
#else
8+
#error("Unsupported Platform")
9+
#endif
210
import SystemExtras
311
import SystemPackage
412
import WasmTypes
@@ -376,9 +384,9 @@ enum WASIAbi {
376384
let buffer: UnsafeGuestRawPointer
377385
let length: WASIAbi.Size
378386

379-
func withHostBufferPointer<R>(_ body: (UnsafeRawBufferPointer) throws -> R) rethrows -> R {
387+
func withHostBufferPointer<R>(_ body: (UnsafeMutableRawBufferPointer) throws -> R) rethrows -> R {
380388
try buffer.withHostPointer(count: Int(length)) { hostPointer in
381-
try body(UnsafeRawBufferPointer(hostPointer))
389+
try body(hostPointer)
382390
}
383391
}
384392

@@ -1382,7 +1390,7 @@ public class WASIBridgeToHost: WASI {
13821390
let fd = open(cHostPath, O_DIRECTORY)
13831391
if fd < 0 {
13841392
let errno = errno
1385-
throw POSIXError(POSIXErrorCode(rawValue: errno)!)
1393+
throw WASIError(description: "Failed to open preopen path '\(hostPath)': \(String(cString: strerror(errno)))")
13861394
}
13871395
return FileDescriptor(rawValue: fd)
13881396
}
@@ -1409,8 +1417,8 @@ public class WASIBridgeToHost: WASI {
14091417
offsets += 1
14101418
let count = arg.utf8CString.withUnsafeBytes { bytes in
14111419
let count = UInt32(bytes.count)
1412-
_ = buffer.raw.withHostPointer(count: bytes.count) { hostDestBuffer in
1413-
bytes.copyBytes(to: hostDestBuffer)
1420+
buffer.raw.withHostPointer(count: bytes.count) { hostDestBuffer in
1421+
hostDestBuffer.copyMemory(from: bytes)
14141422
}
14151423
return count
14161424
}
@@ -1434,8 +1442,8 @@ public class WASIBridgeToHost: WASI {
14341442
offsets += 1
14351443
let count = "\(key)=\(value)".utf8CString.withUnsafeBytes { bytes in
14361444
let count = UInt32(bytes.count)
1437-
_ = buffer.raw.withHostPointer(count: bytes.count) { hostDestBuffer in
1438-
bytes.copyBytes(to: hostDestBuffer)
1445+
buffer.raw.withHostPointer(count: bytes.count) { hostDestBuffer in
1446+
hostDestBuffer.copyMemory(from: bytes)
14391447
}
14401448
return count
14411449
}
@@ -1589,8 +1597,8 @@ public class WASIBridgeToHost: WASI {
15891597
guard bytes.count <= maxPathLength else {
15901598
throw WASIAbi.Errno.ENAMETOOLONG
15911599
}
1592-
_ = path.withHostPointer(count: Int(maxPathLength)) { buffer in
1593-
bytes.copyBytes(to: buffer)
1600+
path.withHostPointer(count: Int(maxPathLength)) { buffer in
1601+
UnsafeMutableRawBufferPointer(buffer).copyBytes(from: bytes)
15941602
}
15951603
}
15961604
}
@@ -1650,8 +1658,10 @@ public class WASIBridgeToHost: WASI {
16501658
let copyingBytes = min(entry.dirNameLen, totalBufferSize - bufferUsed)
16511659
let rangeStart = buffer.baseAddress.raw.advanced(by: bufferUsed)
16521660
name.withUTF8 { bytes in
1653-
_ = rangeStart.withHostPointer(count: Int(copyingBytes)) { hostBuffer in
1654-
bytes.copyBytes(to: hostBuffer, count: Int(copyingBytes))
1661+
rangeStart.withHostPointer(count: Int(copyingBytes)) { hostBuffer in
1662+
hostBuffer.copyMemory(
1663+
from: UnsafeRawBufferPointer(start: bytes.baseAddress, count: Int(copyingBytes))
1664+
)
16551665
}
16561666
}
16571667
bufferUsed += copyingBytes

Sources/WasmKit/Execution/Runtime/Profiler.swift

Lines changed: 45 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import Foundation
21
import SystemExtras
32
import SystemPackage
43

@@ -15,30 +14,29 @@ public class GuestTimeProfiler: RuntimeInterceptor {
1514
let pid: Int
1615
let name: String
1716
let ts: Int
17+
18+
var jsonLine: String {
19+
#"{"ph":"\#(ph.rawValue)","pid":\#(pid),"name":"\#(JSON.serialize(name))","ts":\#(ts)}"#
20+
}
1821
}
1922

20-
private var output: (Data) -> Void
23+
private var output: (_ line: String) -> Void
2124
private var hasFirstEvent: Bool = false
22-
private let encoder = JSONEncoder()
2325
private let startTime: UInt64
2426

25-
public init(output: @escaping (Data) -> Void) {
27+
public init(output: @escaping (_ line: String) -> Void) {
2628
self.output = output
2729
self.startTime = Self.getTimestamp()
2830
}
2931

30-
private func eventLine(_ event: Event) -> Data? {
31-
return try? encoder.encode(event)
32-
}
33-
3432
private func addEventLine(_ event: Event) {
35-
guard let line = eventLine(event) else { return }
33+
let line = event.jsonLine
3634
if !hasFirstEvent {
37-
self.output("[\n".data(using: .utf8)!)
35+
self.output("[\n")
3836
self.output(line)
3937
hasFirstEvent = true
4038
} else {
41-
self.output(",\n".data(using: .utf8)!)
39+
self.output(",\n")
4240
self.output(line)
4341
}
4442
}
@@ -65,7 +63,7 @@ public class GuestTimeProfiler: RuntimeInterceptor {
6563
let functionName = try? store.nameRegistry.lookup(address)
6664
let event = Event(
6765
ph: .begin, pid: 1,
68-
name: functionName ?? "unknown function(\(String(format: "0x%x", address)))",
66+
name: functionName ?? "unknown function(0x\(String(address, radix: 16)))",
6967
ts: getDurationSinceStart()
7068
)
7169
addEventLine(event)
@@ -75,13 +73,46 @@ public class GuestTimeProfiler: RuntimeInterceptor {
7573
let functionName = try? store.nameRegistry.lookup(address)
7674
let event = Event(
7775
ph: .end, pid: 1,
78-
name: functionName ?? "unknown function(\(String(format: "0x%x", address)))",
76+
name: functionName ?? "unknown function(0x\(String(address, radix: 16)))",
7977
ts: getDurationSinceStart()
8078
)
8179
addEventLine(event)
8280
}
8381

8482
public func finalize() {
85-
output("\n]".data(using: .utf8)!)
83+
output("\n]")
84+
}
85+
}
86+
87+
/// Foundation-less JSON serialization
88+
private enum JSON {
89+
static func serialize(_ value: String) -> String {
90+
// https://www.ietf.org/rfc/rfc4627.txt
91+
var output = "\""
92+
for scalar in value.unicodeScalars {
93+
switch scalar {
94+
case "\"":
95+
output += "\\\""
96+
case "\\":
97+
output += "\\\\"
98+
case "\u{08}":
99+
output += "\\b"
100+
case "\u{0C}":
101+
output += "\\f"
102+
case "\n":
103+
output += "\\n"
104+
case "\r":
105+
output += "\\r"
106+
case "\t":
107+
output += "\\t"
108+
case "\u{20}"..."\u{21}", "\u{23}"..."\u{5B}", "\u{5D}"..."\u{10FFFF}":
109+
output.unicodeScalars.append(scalar)
110+
default:
111+
var hex = String(scalar.value, radix: 16, uppercase: true)
112+
hex = String(repeating: "0", count: 4 - hex.count) + hex
113+
output += "\\u" + hex
114+
}
115+
}
116+
return output
86117
}
87118
}

0 commit comments

Comments
 (0)