Skip to content

Commit 42b433b

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 42b433b

9 files changed

Lines changed: 181 additions & 153 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: 6 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,10 @@ add_wasmkit_library(WasmKit
4748
Execution/V128Storage.swift
4849
)
4950

51+
if(WASMKIT_ENABLE_FILESYSTEM)
52+
target_compile_options(WasmKit PRIVATE
53+
$<$<COMPILE_LANGUAGE:Swift>:-D;FileSystem>)
54+
endif()
55+
5056
target_link_wasmkit_libraries(WasmKit PUBLIC
5157
_CWasmKit WasmParser SystemExtras WasmTypes SystemPackage)

Sources/WasmKit/Execution/ParsedComponentBuilder.swift

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,9 @@
11
#if ComponentModel
22
import ComponentModel
3-
import SystemPackage
43
import WasmParser
54

65
// MARK: - Component Parsing
76

8-
/// Parse a component binary file from a caller-owned file descriptor.
9-
///
10-
/// The descriptor is consumed from its current offset and is not closed by
11-
/// this function.
12-
public func parseComponent(
13-
fileHandle: FileDescriptor,
14-
features: WasmFeatureSet = .default
15-
) throws -> ParsedComponent {
16-
let stream = try FileHandleStream(fileHandle: fileHandle)
17-
return try parseComponent(stream: stream, features: features)
18-
}
19-
207
/// Parse a component binary into a `ParsedComponent` ready for instantiation.
218
///
229
/// This function converts the streaming `ComponentParser` output into a
@@ -609,3 +596,19 @@
609596
}
610597

611598
#endif
599+
600+
#if ComponentModel && FileSystem
601+
import struct SystemPackage.FileDescriptor
602+
603+
/// Parse a component binary file from a caller-owned file descriptor.
604+
///
605+
/// The descriptor is consumed from its current offset and is not closed by
606+
/// this function.
607+
public func parseComponent(
608+
fileHandle: FileDescriptor,
609+
features: WasmFeatureSet = .default
610+
) throws -> ParsedComponent {
611+
let stream = try FileHandleStream(fileHandle: fileHandle)
612+
return try parseComponent(stream: stream, features: features)
613+
}
614+
#endif
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: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,12 @@ add_wasmkit_library(WasmParser
1111
)
1212

1313
target_link_wasmkit_libraries(WasmParser PUBLIC
14-
SystemExtras WasmTypes SystemPackage)
14+
WasmTypes)
15+
16+
if(WASMKIT_ENABLE_FILESYSTEM)
17+
target_compile_options(WasmParser PRIVATE
18+
$<$<COMPILE_LANGUAGE:Swift>:-D;FileSystem>)
19+
target_link_wasmkit_libraries(WasmParser PUBLIC
20+
SystemPackage)
21+
endif()
22+
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

0 commit comments

Comments
 (0)