File: ModuleParser.swift

package info (click to toggle)
swiftlang 6.2.3-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 2,856,264 kB
  • sloc: cpp: 9,995,718; ansic: 2,234,019; asm: 1,092,167; python: 313,940; objc: 82,726; f90: 80,126; lisp: 38,373; pascal: 25,580; sh: 20,378; ml: 5,058; perl: 4,751; makefile: 4,725; awk: 3,535; javascript: 3,018; xml: 918; fortran: 664; cs: 573; ruby: 396
file content (128 lines) | stat: -rw-r--r-- 4,427 bytes parent folder | download
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
    )
}