1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
|
import SystemPackage
import WasmParser
#if os(Windows)
import ucrt
#endif
/// Parse a given file as a WebAssembly binary format file
/// > Note: <https://webassembly.github.io/spec/core/binary/index.html>
public func parseWasm(filePath: FilePath, features: WasmFeatureSet = .default) throws -> Module {
#if os(Windows)
// TODO: Upstream `O_BINARY` to `SystemPackage
let accessMode = FileDescriptor.AccessMode(
rawValue: FileDescriptor.AccessMode.readOnly.rawValue | O_BINARY
)
#else
let accessMode: FileDescriptor.AccessMode = .readOnly
#endif
let fileHandle = try FileDescriptor.open(filePath, accessMode)
defer { try? fileHandle.close() }
let stream = try FileHandleStream(fileHandle: fileHandle)
let module = try parseModule(stream: stream, features: features)
return module
}
/// Parse a given byte array as a WebAssembly binary format file
/// > Note: <https://webassembly.github.io/spec/core/binary/index.html>
public func parseWasm(bytes: [UInt8], features: WasmFeatureSet = .default) throws -> Module {
let stream = StaticByteStream(bytes: bytes)
let module = try parseModule(stream: stream, features: features)
return module
}
/// > Note:
/// <https://webassembly.github.io/spec/core/binary/modules.html#binary-module>
func parseModule<Stream: ByteStream>(stream: Stream, features: WasmFeatureSet = .default) throws -> Module {
var types: [FunctionType] = []
var typeIndices: [TypeIndex] = []
var codes: [Code] = []
var tables: [TableType] = []
var memories: [MemoryType] = []
var globals: [WasmParser.Global] = []
var elements: [ElementSegment] = []
var data: [DataSegment] = []
var start: FunctionIndex?
var imports: [Import] = []
var exports: [Export] = []
var customSections: [CustomSection] = []
var dataCount: UInt32?
var parser = WasmParser.Parser<Stream>(
stream: stream, features: features
)
while let payload = try parser.parseNext() {
switch payload {
case .header: break
case .customSection(let customSection):
customSections.append(customSection)
case .typeSection(let typeSection):
types = typeSection
case .importSection(let importSection):
imports = importSection
case .functionSection(let types):
typeIndices = types
case .tableSection(let tableSection):
tables = tableSection.map(\.type)
case .memorySection(let memorySection):
memories = memorySection.map(\.type)
case .globalSection(let globalSection):
globals = globalSection
case .exportSection(let exportSection):
exports = exportSection
case .startSection(let functionIndex):
start = functionIndex
case .elementSection(let elementSection):
elements = elementSection
case .codeSection(let codeSection):
codes = codeSection
case .dataSection(let dataSection):
data = dataSection
case .dataCount(let count):
dataCount = count
}
}
guard typeIndices.count == codes.count else {
throw ValidationError(
.inconsistentFunctionAndCodeLength(
functionCount: typeIndices.count,
codeCount: codes.count
))
}
if let dataCount = dataCount, dataCount != UInt32(data.count) {
throw ValidationError(
.inconsistentDataCountAndDataSectionLength(
dataCount: dataCount,
dataSection: data.count
))
}
let functions = try codes.enumerated().map { index, code in
// SAFETY: The number of typeIndices is guaranteed to be the same as the number of codes
let funcTypeIndex = typeIndices[index]
let funcType = try Module.resolveType(funcTypeIndex, typeSection: types)
return GuestFunction(
type: funcType,
code: code
)
}
return Module(
types: types,
functions: functions,
elements: elements,
data: data,
start: start,
imports: imports,
exports: exports,
globals: globals,
memories: memories,
tables: tables,
customSections: customSections,
features: features,
dataCount: dataCount
)
}
|