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 165 166 167 168 169 170 171 172 173 174 175 176
|
/// > Note:
/// <https://webassembly.github.io/spec/core/exec/instructions.html#table-instructions>
extension ExecutionState {
mutating func tableGet(runtime: Runtime, tableIndex: TableIndex) throws {
let (_, table) = getTable(tableIndex, store: runtime.store)
let elementIndex = try getElementIndex(table)
guard let reference = table.elements[Int(elementIndex)] else {
throw Trap.readingDroppedReference(index: elementIndex)
}
stack.push(value: .ref(reference))
}
mutating func tableSet(runtime: Runtime, tableIndex: TableIndex) throws {
let (tableAddress, table) = getTable(tableIndex, store: runtime.store)
let reference = stack.getReference()
let elementIndex = try getElementIndex(table)
setTableElement(store: runtime.store, tableAddress: tableAddress, elementIndex, reference)
}
mutating func tableSize(runtime: Runtime, tableIndex: TableIndex) {
let (_, table) = getTable(tableIndex, store: runtime.store)
stack.push(value: .i32(UInt32(table.elements.count)))
}
mutating func tableGrow(runtime: Runtime, tableIndex: TableIndex) {
let (tableAddress, table) = getTable(tableIndex, store: runtime.store)
let growthSize = stack.popValue()
guard case let .i32(growthSize) = growthSize else {
fatalError("invalid value at the top of the stack \(growthSize)")
}
let growthValue = stack.getReference()
let oldSize = UInt32(table.elements.count)
guard runtime.store.tables[tableAddress].grow(by: growthSize, value: growthValue) else {
stack.push(value: .i32(Int32(-1).unsigned))
return
}
stack.push(value: .i32(oldSize))
}
mutating func tableFill(runtime: Runtime, tableIndex: TableIndex) throws {
let (tableAddress, table) = getTable(tableIndex, store: runtime.store)
let fillCounter = stack.popValue().i32
let fillValue = stack.getReference()
let startIndex = stack.popValue().i32
guard fillCounter > 0 else {
return
}
guard Int(startIndex + fillCounter) <= table.elements.count else {
throw Trap.outOfBoundsTableAccess(index: startIndex + fillCounter)
}
for i in 0..<fillCounter {
setTableElement(store: runtime.store, tableAddress: tableAddress, startIndex + i, fillValue)
}
}
mutating func tableCopy(runtime: Runtime, dest destinationTableIndex: TableIndex, src sourceTableIndex: TableIndex) throws {
let (_, sourceTable) = getTable(sourceTableIndex, store: runtime.store)
let (destinationTableAddress, destinationTable) = getTable(destinationTableIndex, store: runtime.store)
let copyCounter = stack.popValue().i32
let sourceIndex = stack.popValue().i32
let destinationIndex = stack.popValue().i32
guard copyCounter > 0 else {
return
}
guard
!sourceIndex.addingReportingOverflow(copyCounter).overflow && !destinationIndex.addingReportingOverflow(copyCounter).overflow
else {
throw Trap.tableSizeOverflow
}
guard destinationIndex + copyCounter <= sourceTable.elements.count else {
throw Trap.outOfBoundsTableAccess(index: destinationIndex + copyCounter)
}
guard destinationIndex + copyCounter <= sourceTable.elements.count && sourceIndex + copyCounter <= destinationTable.elements.count else {
throw Trap.outOfBoundsTableAccess(index: destinationIndex + copyCounter)
}
for i in 0..<copyCounter {
setTableElement(
store: runtime.store,
tableAddress: destinationTableAddress,
destinationIndex + i,
sourceTable.elements[Int(sourceIndex + i)]
)
}
}
mutating func tableInit(runtime: Runtime, tableIndex: TableIndex, elementIndex: ElementIndex) throws {
let (destinationTableAddress, destinationTable) = getTable(tableIndex, store: runtime.store)
let elementAddress = currentModule(store: runtime.store).elementAddresses[Int(elementIndex)]
let sourceElement = runtime.store.elements[elementAddress]
let copyCounter = stack.popValue().i32
let sourceIndex = stack.popValue().i32
let destinationIndex = stack.popValue().i32
guard copyCounter > 0 else {
return
}
guard
!sourceIndex.addingReportingOverflow(copyCounter).overflow && !destinationIndex.addingReportingOverflow(copyCounter).overflow
else {
throw Trap.tableSizeOverflow
}
guard sourceIndex + copyCounter <= sourceElement.references.count else {
throw Trap.outOfBoundsTableAccess(index: sourceIndex + copyCounter)
}
guard destinationIndex + copyCounter <= destinationTable.elements.count else {
throw Trap.outOfBoundsTableAccess(index: destinationIndex + copyCounter)
}
for i in 0..<copyCounter {
let reference = sourceElement.references[Int(sourceIndex + i)]
setTableElement(
store: runtime.store,
tableAddress: destinationTableAddress,
destinationIndex + i,
reference
)
}
}
mutating func tableElementDrop(runtime: Runtime, elementIndex: ElementIndex) {
let elementAddress = currentModule(store: runtime.store).elementAddresses[Int(elementIndex)]
runtime.store.elements[elementAddress].drop()
}
fileprivate func setTableElement(
store: Store,
tableAddress: TableAddress,
_ elementIndex: ElementIndex,
_ reference: Reference?
) {
store.tables[tableAddress].elements[Int(elementIndex)] = reference
}
}
extension ExecutionState {
fileprivate func getTable(_ tableIndex: UInt32, store: Store) -> (TableAddress, TableInstance) {
let address = currentModule(store: store).tableAddresses[Int(tableIndex)]
return (address, store.tables[address])
}
fileprivate mutating func getElementIndex(_ table: TableInstance) throws -> ElementIndex {
let elementIndex = stack.popValue().i32
guard elementIndex < table.elements.count else {
throw Trap.outOfBoundsTableAccess(index: elementIndex)
}
return elementIndex
}
}
extension Stack {
fileprivate mutating func getReference() -> Reference {
let value = popValue()
guard case let .ref(reference) = value else {
fatalError("invalid value at the top of the stack \(value)")
}
return reference
}
}
|