File: Control.swift

package info (click to toggle)
swiftlang 6.0.3-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,519,992 kB
  • sloc: cpp: 9,107,863; ansic: 2,040,022; asm: 1,135,751; python: 296,500; objc: 82,456; f90: 60,502; lisp: 34,951; pascal: 19,946; sh: 18,133; perl: 7,482; ml: 4,937; javascript: 4,117; makefile: 3,840; awk: 3,535; xml: 914; fortran: 619; cs: 573; ruby: 573
file content (164 lines) | stat: -rw-r--r-- 6,276 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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
/// > Note:
/// <https://webassembly.github.io/spec/core/exec/instructions.html#control-instructions>
extension ExecutionState {
    func unreachable(runtime: Runtime) throws {
        throw Trap.unreachable
    }
    mutating func nop(runtime: Runtime) throws {
        programCounter += 1
    }
    private func getTypeSection(store: Store) -> [FunctionType] {
        store.module(address: stack.currentFrame.module).types
    }

    typealias BlockType = Instruction.BlockType

    mutating func block(runtime: Runtime, endRef: ExpressionRef, type: BlockType) {
        enter(
            jumpTo: programCounter + 1,
            continuation: programCounter + endRef.relativeOffset,
            arity: Int(type.results),
            pushPopValues: Int(type.parameters)
        )
    }
    mutating func loop(runtime: Runtime, type: BlockType) {
        let paramSize = Int(type.parameters)
        enter(jumpTo: programCounter + 1, continuation: programCounter, arity: paramSize, pushPopValues: paramSize)
    }

    mutating func ifThen(runtime: Runtime, endRef: ExpressionRef, type: BlockType) {
        let isTrue = stack.popValue().i32 != 0
        if isTrue {
            enter(
                jumpTo: programCounter + 1,
                continuation: programCounter.advanced(by: endRef.relativeOffset),
                arity: Int(type.results),
                pushPopValues: Int(type.parameters)
            )
        } else {
            programCounter += endRef.relativeOffset
        }
    }

    mutating func ifThenElse(runtime: Runtime, elseRef: ExpressionRef, endRef: ExpressionRef, type: BlockType) {
        let isTrue = stack.popValue().i32 != 0
        let addendToPC: Int
        if isTrue {
            addendToPC = 1
        } else {
            addendToPC = elseRef.relativeOffset
        }
        enter(
            jumpTo: programCounter + addendToPC,
            continuation: programCounter + endRef.relativeOffset,
            arity: Int(type.results),
            pushPopValues: Int(type.parameters)
        )
    }
    mutating func end(runtime: Runtime) {
        if let currentLabel = self.stack.currentLabel {
            stack.exit(label: currentLabel)
        }
        programCounter += 1
    }
    mutating func `else`(runtime: Runtime) {
        let label = self.stack.currentLabel!
        stack.exit(label: label)
        programCounter = label.continuation // if-then-else's continuation points the "end"
    }

    private mutating func branch(labelIndex: Int, runtime: Runtime) throws {
        if stack.numberOfLabelsInCurrentFrame() == labelIndex {
            try self.return(runtime: runtime)
            return
        }
        let label = stack.getLabel(index: Int(labelIndex))
        let values = stack.popValues(count: label.arity)

        stack.unwindLabels(upto: labelIndex)

        stack.push(values: values)
        programCounter = label.continuation
    }
    mutating func br(runtime: Runtime, labelIndex: LabelIndex) throws {
        try branch(labelIndex: Int(labelIndex), runtime: runtime)
    }
    mutating func brIf(runtime: Runtime, labelIndex: LabelIndex) throws {
        guard stack.popValue().i32 != 0 else {
            programCounter += 1
            return
        }
        try br(runtime: runtime, labelIndex: labelIndex)
    }
    mutating func brTable(runtime: Runtime, brTable: Instruction.BrTable) throws {
        let labelIndices = brTable.labelIndices
        let defaultIndex = brTable.defaultIndex
        let value = stack.popValue().i32
        let labelIndex: LabelIndex
        if labelIndices.indices.contains(Int(value)) {
            labelIndex = labelIndices[Int(value)]
        } else {
            labelIndex = defaultIndex
        }

        try branch(labelIndex: Int(labelIndex), runtime: runtime)
    }
    mutating func `return`(runtime: Runtime) throws {
        let currentFrame = stack.currentFrame!
        _ = stack.exit(frame: currentFrame)
        try endOfFunction(runtime: runtime, currentFrame: currentFrame)
    }

    mutating func endOfFunction(runtime: Runtime) throws {
        try self.endOfFunction(runtime: runtime, currentFrame: stack.currentFrame)
    }

    mutating func endOfExecution(runtime: Runtime) throws {
        reachedEndOfExecution = true
    }

    private mutating func endOfFunction(runtime: Runtime, currentFrame: Frame) throws {
        // When reached at "end" of function
        #if DEBUG
        if let address = currentFrame.address {
            runtime.interceptor?.onExitFunction(address, store: runtime.store)
        }
        #endif
        let values = stack.popValues(count: currentFrame.arity)
        stack.popFrame()
        stack.push(values: values)
        programCounter = currentFrame.returnPC
    }

    mutating func call(runtime: Runtime, functionIndex: UInt32) throws {
        let functionAddresses = runtime.store.module(address: stack.currentFrame.module).functionAddresses

        guard functionAddresses.indices.contains(Int(functionIndex)) else {
            throw Trap.invalidFunctionIndex(functionIndex)
        }

        try invoke(functionAddress: functionAddresses[Int(functionIndex)], runtime: runtime)
    }

    mutating func callIndirect(runtime: Runtime, tableIndex: TableIndex, typeIndex: TypeIndex) throws {
        let moduleInstance = runtime.store.module(address: stack.currentFrame.module)
        let tableAddresses = moduleInstance.tableAddresses[Int(tableIndex)]
        let tableInstance = runtime.store.tables[tableAddresses]
        let expectedType = moduleInstance.types[Int(typeIndex)]
        let value = stack.popValue().i32
        let elementIndex = Int(value)
        guard elementIndex < tableInstance.elements.count else {
            throw Trap.undefinedElement
        }
        guard case let .function(functionAddress?) = tableInstance.elements[elementIndex]
        else {
            throw Trap.tableUninitialized(ElementIndex(elementIndex))
        }
        let function = runtime.store.functions[functionAddress]
        guard function.type == expectedType else {
            throw Trap.callIndirectFunctionTypeMismatch(actual: function.type, expected: expectedType)
        }

        try invoke(functionAddress: functionAddress, runtime: runtime)
    }
}