Skip to content

Commit f7b6c5f

Browse files
WasmParser: Add FileSystem trait to allow omitting file system helpers
Extracted from #357 Co-Authored-By: Joannis Orlandos <joannis@orlandos.nl>
1 parent e16765f commit f7b6c5f

8 files changed

Lines changed: 157 additions & 139 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ add_compile_definitions(
5353
include(FetchContent)
5454

5555
option(WASMKIT_BUILD_CLI "Build wasmkit-cli" ON)
56+
option(WASMKIT_ENABLE_FILESYSTEM "Enable FileSystem support" ON)
5657

5758
if(WASMKIT_BUILD_CLI)
5859
set(BUILD_TESTING OFF) # disable ArgumentParser tests

Package.swift

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ let package = Package(
3838
.library(name: "_CabiShims", targets: ["_CabiShims"]),
3939
],
4040
traits: [
41-
.default(enabledTraits: []),
41+
.default(enabledTraits: ["FileSystem"]),
42+
"FileSystem",
4243
"ComponentModel",
4344
"WasmDebuggingSupport",
4445
],
@@ -112,7 +113,10 @@ let package = Package(
112113
name: "WasmParser",
113114
dependencies: [
114115
"WasmTypes",
115-
.product(name: "SystemPackage", package: "swift-system"),
116+
.product(
117+
name: "SystemPackage", package: "swift-system",
118+
condition: .when(traits: ["FileSystem"])
119+
),
116120
.target(
117121
name: "ComponentModel",
118122
condition: .when(traits: ["ComponentModel"])

Sources/WasmKit/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ add_wasmkit_library(WasmKit
33
Imports.swift
44
Module.swift
55
ModuleParser.swift
6+
ModuleParser+FileSystem.swift
67
SIMDOpcode.swift
78
Translator.swift
89
Validator.swift
@@ -47,5 +48,8 @@ add_wasmkit_library(WasmKit
4748
Execution/V128Storage.swift
4849
)
4950

51+
target_compile_options(WasmKit PRIVATE
52+
$<$<COMPILE_LANGUAGE:Swift>:-D;FileSystem>)
53+
5054
target_link_wasmkit_libraries(WasmKit PUBLIC
5155
_CWasmKit WasmParser SystemExtras WasmTypes SystemPackage)
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#if FileSystem
2+
import WasmParser
3+
import SystemExtras
4+
import SystemPackage
5+
6+
#if os(Windows)
7+
import ucrt
8+
#endif
9+
10+
/// Parse a given file as a WebAssembly binary format file
11+
/// > Note: <https://webassembly.github.io/spec/core/binary/index.html>
12+
public func parseWasm(filePath: FilePath, features: WasmFeatureSet = .default) throws -> Module {
13+
#if os(Windows)
14+
// TODO: Upstream `O_BINARY` to `SystemPackage
15+
let accessMode = FileDescriptor.AccessMode(
16+
rawValue: FileDescriptor.AccessMode.readOnly.rawValue | O_BINARY
17+
)
18+
#else
19+
let accessMode: FileDescriptor.AccessMode = .readOnly
20+
#endif
21+
let fileHandle = try FileDescriptor.open(filePath, accessMode)
22+
return try withThrowing {
23+
try parseWasm(fileHandle: fileHandle, features: features)
24+
} defer: {
25+
try fileHandle.close()
26+
}
27+
}
28+
29+
/// Parse a WebAssembly binary file from a caller-owned file descriptor.
30+
///
31+
/// The descriptor is consumed from its current offset and is not closed by
32+
/// this function.
33+
public func parseWasm(fileHandle: FileDescriptor, features: WasmFeatureSet = .default) throws -> Module {
34+
let stream = try FileHandleStream(fileHandle: fileHandle)
35+
let module = try parseModule(stream: stream, features: features)
36+
return module
37+
}
38+
#endif

Sources/WasmKit/ModuleParser.swift

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,5 @@
1-
import SystemExtras
2-
import SystemPackage
31
import WasmParser
42

5-
#if os(Windows)
6-
import ucrt
7-
#endif
8-
9-
/// Parse a given file as a WebAssembly binary format file
10-
/// > Note: <https://webassembly.github.io/spec/core/binary/index.html>
11-
public func parseWasm(filePath: FilePath, features: WasmFeatureSet = .default) throws -> Module {
12-
#if os(Windows)
13-
// TODO: Upstream `O_BINARY` to `SystemPackage
14-
let accessMode = FileDescriptor.AccessMode(
15-
rawValue: FileDescriptor.AccessMode.readOnly.rawValue | O_BINARY
16-
)
17-
#else
18-
let accessMode: FileDescriptor.AccessMode = .readOnly
19-
#endif
20-
let fileHandle = try FileDescriptor.open(filePath, accessMode)
21-
return try withThrowing {
22-
try parseWasm(fileHandle: fileHandle, features: features)
23-
} defer: {
24-
try fileHandle.close()
25-
}
26-
}
27-
28-
/// Parse a WebAssembly binary file from a caller-owned file descriptor.
29-
///
30-
/// The descriptor is consumed from its current offset and is not closed by
31-
/// this function.
32-
public func parseWasm(fileHandle: FileDescriptor, features: WasmFeatureSet = .default) throws -> Module {
33-
let stream = try FileHandleStream(fileHandle: fileHandle)
34-
let module = try parseModule(stream: stream, features: features)
35-
return module
36-
}
37-
383
/// Parse a given byte array as a WebAssembly binary format file
394
/// > Note: <https://webassembly.github.io/spec/core/binary/index.html>
405
public func parseWasm(bytes: [UInt8], features: WasmFeatureSet = .default) throws(WasmKitError) -> Module {

Sources/WasmParser/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,8 @@ add_wasmkit_library(WasmParser
1010
WasmTypes.swift
1111
)
1212

13+
target_compile_options(WasmParser PRIVATE
14+
$<$<COMPILE_LANGUAGE:Swift>:-D;FileSystem>)
15+
1316
target_link_wasmkit_libraries(WasmParser PUBLIC
1417
SystemExtras WasmTypes SystemPackage)
Lines changed: 105 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,132 @@
1-
import struct SystemPackage.FileDescriptor
1+
// "FileSystem" trait can be turned off to support embedded platforms
2+
#if FileSystem
23

3-
public final class FileHandleStream: ByteStream {
4-
private(set) public var currentIndex: Int = 0
4+
import struct SystemPackage.FileDescriptor
5+
import struct SystemPackage.FilePath
56

6-
private let fileHandle: FileDescriptor
7-
private let bufferLength: Int
7+
#if os(Windows)
8+
import ucrt
9+
#endif
810

9-
private var endOffset: Int = 0
10-
private var startOffset: Int = 0
11-
private var bytes: [UInt8] = []
11+
extension Parser where Stream == FileHandleStream {
1212

13-
public init(fileHandle: FileDescriptor, bufferLength: Int = 1024 * 8) throws {
14-
self.fileHandle = fileHandle
15-
self.bufferLength = bufferLength
13+
/// Initialize a new parser with the given file handle
14+
///
15+
/// - Parameters:
16+
/// - fileHandle: The file handle to the WebAssembly binary file to parse
17+
/// - features: Enabled WebAssembly features for parsing
18+
public init(fileHandle: FileDescriptor, features: WasmFeatureSet = .default) throws {
19+
self.init(stream: try FileHandleStream(fileHandle: fileHandle), features: features)
20+
}
1621

17-
try readMoreIfNeeded()
22+
/// Initialize a new parser with the given file path
23+
///
24+
/// - Parameters:
25+
/// - filePath: The file path to the WebAssembly binary file to parse
26+
/// - features: Enabled WebAssembly features for parsing
27+
public init(filePath: FilePath, features: WasmFeatureSet = .default) throws {
28+
#if os(Windows)
29+
// TODO: Upstream `O_BINARY` to `SystemPackage
30+
let accessMode = FileDescriptor.AccessMode(
31+
rawValue: FileDescriptor.AccessMode.readOnly.rawValue | O_BINARY
32+
)
33+
#else
34+
let accessMode: FileDescriptor.AccessMode = .readOnly
35+
#endif
36+
let fileHandle = try FileDescriptor.open(filePath, accessMode)
37+
self.init(stream: try FileHandleStream(fileHandle: fileHandle), features: features)
38+
}
1839
}
1940

20-
private func readMoreIfNeeded() throws(WasmParserError) {
21-
guard Int(endOffset) == currentIndex else { return }
22-
startOffset = currentIndex
41+
public final class FileHandleStream: ByteStream {
42+
private(set) public var currentIndex: Int = 0
2343

24-
do {
25-
let data = try fileHandle.read(upToCount: bufferLength)
44+
private let fileHandle: FileDescriptor
45+
private let bufferLength: Int
2646

27-
bytes = [UInt8](data)
28-
} catch {
29-
throw WasmParserError("I/O error: \(error)", offset: currentIndex)
30-
}
31-
endOffset = startOffset + bytes.count
32-
}
47+
private var endOffset: Int = 0
48+
private var startOffset: Int = 0
49+
private var bytes: [UInt8] = []
50+
51+
public init(fileHandle: FileDescriptor, bufferLength: Int = 1024 * 8) throws {
52+
self.fileHandle = fileHandle
53+
self.bufferLength = bufferLength
3354

34-
@discardableResult
35-
public func consumeAny() throws(WasmParserError) -> UInt8 {
36-
guard let consumed = try peek() else {
37-
throw WasmParserError(message: .unexpectedEnd, offset: currentIndex)
55+
try readMoreIfNeeded()
3856
}
39-
currentIndex = bytes.index(after: currentIndex)
40-
return consumed
41-
}
4257

43-
public func consume(count: Int) throws(WasmParserError) -> ArraySlice<UInt8> {
44-
let bytesToRead = currentIndex + count - endOffset
58+
private func readMoreIfNeeded() throws(WasmParserError) {
59+
guard Int(endOffset) == currentIndex else { return }
60+
startOffset = currentIndex
4561

46-
guard bytesToRead > 0 else {
47-
let bytesIndex = currentIndex - startOffset
48-
let result = bytes[bytesIndex..<bytesIndex + count]
49-
currentIndex = currentIndex + count
50-
return result
51-
}
62+
do {
63+
let data = try fileHandle.read(upToCount: bufferLength)
5264

53-
let data: [UInt8]
54-
do {
55-
data = try fileHandle.read(upToCount: bytesToRead)
56-
} catch {
57-
throw WasmParserError("I/O error: \(error)", offset: currentIndex)
65+
bytes = [UInt8](data)
66+
} catch {
67+
throw WasmParserError("I/O error: \(error)", offset: currentIndex)
68+
}
69+
endOffset = startOffset + bytes.count
5870
}
59-
guard data.count == bytesToRead else {
60-
throw WasmParserError(kind: .parserUnexpectedEnd(expected: nil), offset: currentIndex)
71+
72+
@discardableResult
73+
public func consumeAny() throws(WasmParserError) -> UInt8 {
74+
guard let consumed = try peek() else {
75+
throw WasmParserError(message: .unexpectedEnd, offset: currentIndex)
76+
}
77+
currentIndex = bytes.index(after: currentIndex)
78+
return consumed
6179
}
6280

63-
bytes.append(contentsOf: [UInt8](data))
64-
endOffset = endOffset + data.count
81+
public func consume(count: Int) throws(WasmParserError) -> ArraySlice<UInt8> {
82+
let bytesToRead = currentIndex + count - endOffset
83+
84+
guard bytesToRead > 0 else {
85+
let bytesIndex = currentIndex - startOffset
86+
let result = bytes[bytesIndex..<bytesIndex + count]
87+
currentIndex = currentIndex + count
88+
return result
89+
}
90+
91+
let data: [UInt8]
92+
do {
93+
data = try fileHandle.read(upToCount: bytesToRead)
94+
} catch {
95+
throw WasmParserError("I/O error: \(error)", offset: currentIndex)
96+
}
97+
guard data.count == bytesToRead else {
98+
throw WasmParserError(kind: .parserUnexpectedEnd(expected: nil), offset: currentIndex)
99+
}
100+
101+
bytes.append(contentsOf: [UInt8](data))
102+
endOffset = endOffset + data.count
65103

66-
let bytesIndex = currentIndex - startOffset
67-
let result = bytes[bytesIndex..<bytesIndex + count]
104+
let bytesIndex = currentIndex - startOffset
105+
let result = bytes[bytesIndex..<bytesIndex + count]
68106

69-
currentIndex = endOffset
107+
currentIndex = endOffset
70108

71-
return result
72-
}
109+
return result
110+
}
73111

74-
public func peek() throws(WasmParserError) -> UInt8? {
75-
try readMoreIfNeeded()
112+
public func peek() throws(WasmParserError) -> UInt8? {
113+
try readMoreIfNeeded()
76114

77-
let index = currentIndex - startOffset
78-
guard bytes.indices.contains(index) else {
79-
return nil
80-
}
115+
let index = currentIndex - startOffset
116+
guard bytes.indices.contains(index) else {
117+
return nil
118+
}
81119

82-
return bytes[index]
120+
return bytes[index]
121+
}
83122
}
84-
}
85123

86-
extension FileDescriptor {
87-
fileprivate func read(upToCount maxLength: Int) throws -> [UInt8] {
88-
try [UInt8](unsafeUninitializedCapacity: maxLength) { buffer, outCount in
89-
outCount = try read(into: UnsafeMutableRawBufferPointer(buffer))
124+
extension FileDescriptor {
125+
fileprivate func read(upToCount maxLength: Int) throws -> [UInt8] {
126+
try [UInt8](unsafeUninitializedCapacity: maxLength) { buffer, outCount in
127+
outCount = try read(into: UnsafeMutableRawBufferPointer(buffer))
128+
}
90129
}
91130
}
92-
}
131+
132+
#endif

Sources/WasmParser/WasmParser.swift

Lines changed: 0 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,5 @@
11
import WasmTypes
22

3-
import struct SystemPackage.FileDescriptor
4-
import struct SystemPackage.FilePath
5-
6-
#if os(Windows)
7-
import ucrt
8-
#endif
9-
103
/// A streaming parser for WebAssembly binary format.
114
///
125
/// The parser is designed to be used to incrementally parse a WebAssembly binary bytestream.
@@ -54,36 +47,6 @@ extension Parser where Stream == StaticByteStream {
5447
}
5548
}
5649

57-
extension Parser where Stream == FileHandleStream {
58-
59-
/// Initialize a new parser with the given file handle
60-
///
61-
/// - Parameters:
62-
/// - fileHandle: The file handle to the WebAssembly binary file to parse
63-
/// - features: Enabled WebAssembly features for parsing
64-
public init(fileHandle: FileDescriptor, features: WasmFeatureSet = .default) throws {
65-
self.init(stream: try FileHandleStream(fileHandle: fileHandle), features: features)
66-
}
67-
68-
/// Initialize a new parser with the given file path
69-
///
70-
/// - Parameters:
71-
/// - filePath: The file path to the WebAssembly binary file to parse
72-
/// - features: Enabled WebAssembly features for parsing
73-
public init(filePath: FilePath, features: WasmFeatureSet = .default) throws {
74-
#if os(Windows)
75-
// TODO: Upstream `O_BINARY` to `SystemPackage
76-
let accessMode = FileDescriptor.AccessMode(
77-
rawValue: FileDescriptor.AccessMode.readOnly.rawValue | O_BINARY
78-
)
79-
#else
80-
let accessMode: FileDescriptor.AccessMode = .readOnly
81-
#endif
82-
let fileHandle = try FileDescriptor.open(filePath, accessMode)
83-
self.init(stream: try FileHandleStream(fileHandle: fileHandle), features: features)
84-
}
85-
}
86-
8750
@_documentation(visibility: internal)
8851
public struct ExpressionParser {
8952
/// The byte offset of the code in the module

0 commit comments

Comments
 (0)