Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions FuzzTesting/Sources/FuzzDifferential/FuzzDifferential.swift
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ extension ValueType {
return .ref(.function(nil))
case .abstract(.externRef):
return .ref(.extern(nil))
case .abstract(.exnRef):
return .ref(.exception(nil))
case .concrete:
// We don't model GC reference heap types yet; use a null externref.
return .ref(.extern(nil))
Expand Down
2 changes: 2 additions & 0 deletions FuzzTesting/Sources/FuzzExecute/FuzzExecute.swift
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ extension ValueType {
return .ref(.function(nil))
case .abstract(.externRef):
return .ref(.extern(nil))
case .abstract(.exnRef):
return .ref(.exception(nil))
case .concrete:
// We don't model GC reference heap types yet; use a null externref.
return .ref(.extern(nil))
Expand Down
4 changes: 4 additions & 0 deletions FuzzTesting/Sources/WasmKitFuzzing/WasmKitFuzzing.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ public func fuzzInstantiation(bytes: [UInt8]) throws {
value = try Memory(store: store, type: memoryType)
case .table(let tableType):
value = try Table(store: store, type: tableType)
case .tag(let typeIndex):
guard typeIndex < module.types.count else { return }
let type = module.types[Int(typeIndex)]
value = Tag(store: store, type: type)
}
imports.define(module: importEntry.module, name: importEntry.name, value.externalValue)
}
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ Proposals are grouped by their [phase](https://github.com/WebAssembly/meetings/b
| Proposal | Status | WasmKit version |
|----------|--------|-----------------|
| [Bulk Memory Operations](https://github.com/WebAssembly/bulk-memory-operations) | ✅ Implemented | [0.0.2] |
| [Exception Handling](https://github.com/WebAssembly/exception-handling) | ✅ Implemented | `main` branch |
| [Fixed-width SIMD](https://github.com/webassembly/simd) | ✅ Implemented | `main` branch |
| [Import/Export of Mutable Globals](https://github.com/WebAssembly/mutable-global) | ✅ Implemented | [0.0.2] |
| [Memory64](https://github.com/WebAssembly/memory64) | ✅ Implemented | [0.0.2] |
Expand All @@ -102,7 +103,6 @@ Proposals are grouped by their [phase](https://github.com/WebAssembly/meetings/b
| [Typed Function References](https://github.com/WebAssembly/function-references) | 🚧 Parser implemented | [0.2.0] |
| [Branch Hinting](https://github.com/WebAssembly/branch-hinting) | ❌ Not implemented | |
| [Custom Annotation Syntax in the Text Format](https://github.com/WebAssembly/annotations) | ❌ Not implemented | |
| [Exception Handling](https://github.com/WebAssembly/exception-handling) | ❌ Not implemented | |
| [Extended Constant Expressions](https://github.com/WebAssembly/extended-const) | ❌ Not implemented | |
| [Garbage Collection](https://github.com/WebAssembly/gc) | ❌ Not implemented | |
| [Multiple Memories](https://github.com/WebAssembly/multi-memory) | ❌ Not implemented | |
Expand Down
11 changes: 11 additions & 0 deletions Sources/WAT/BinaryEncoding/BinaryInstructionEncoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ protocol BinaryInstructionEncoder: InstructionVisitor {
mutating func encodeImmediates(memory: UInt32) throws(VisitorError)
mutating func encodeImmediates(relativeDepth: UInt32) throws(VisitorError)
mutating func encodeImmediates(table: UInt32) throws(VisitorError)
mutating func encodeImmediates(tagIndex: UInt32) throws(VisitorError)
mutating func encodeImmediates(targets: BrTable) throws(VisitorError)
mutating func encodeImmediates(type: HeapType) throws(VisitorError)
mutating func encodeImmediates(type: ValueType) throws(VisitorError)
Expand All @@ -33,6 +34,7 @@ protocol BinaryInstructionEncoder: InstructionVisitor {
mutating func encodeImmediates(value: Int32) throws(VisitorError)
mutating func encodeImmediates(value: Int64) throws(VisitorError)
mutating func encodeImmediates(value: V128) throws(VisitorError)
mutating func encodeImmediates(blockType: BlockType, tryCatch: TryCatch) throws(VisitorError)
mutating func encodeImmediates(dstMem: UInt32, srcMem: UInt32) throws(VisitorError)
mutating func encodeImmediates(dstTable: UInt32, srcTable: UInt32) throws(VisitorError)
mutating func encodeImmediates(elemIndex: UInt32, table: UInt32) throws(VisitorError)
Expand All @@ -57,6 +59,11 @@ extension BinaryInstructionEncoder {
try encodeImmediates(blockType: blockType)
}
mutating func visitElse() throws(VisitorError) { try encodeInstruction([0x05]) }
mutating func visitThrow(tagIndex: UInt32) throws(VisitorError) {
try encodeInstruction([0x08])
try encodeImmediates(tagIndex: tagIndex)
}
mutating func visitThrowRef() throws(VisitorError) { try encodeInstruction([0x0A]) }
mutating func visitEnd() throws(VisitorError) { try encodeInstruction([0x0B]) }
mutating func visitBr(relativeDepth: UInt32) throws(VisitorError) {
try encodeInstruction([0x0C])
Expand Down Expand Up @@ -95,6 +102,10 @@ extension BinaryInstructionEncoder {
try encodeInstruction([0x15])
try encodeImmediates(typeIndex: typeIndex)
}
mutating func visitTryTable(blockType: BlockType, tryCatch: TryCatch) throws(VisitorError) {
try encodeInstruction([0x1F])
try encodeImmediates(blockType: blockType, tryCatch: tryCatch)
}
mutating func visitDrop() throws(VisitorError) { try encodeInstruction([0x1A]) }
mutating func visitSelect() throws(VisitorError) { try encodeInstruction([0x1B]) }
mutating func visitTypedSelect(type: ValueType) throws(VisitorError) {
Expand Down
52 changes: 52 additions & 0 deletions Sources/WAT/BinaryEncoding/Encoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ extension ReferenceType: WasmEncodable {
// Use short form when available
case (true, .externRef): encoder.output.append(0x6F)
case (true, .funcRef): encoder.output.append(0x70)
case (true, .exnRef): encoder.output.append(0x69)
default:
encoder.output.append(isNullable ? 0x63 : 0x64)
encoder.encode(heapType)
Expand All @@ -153,6 +154,7 @@ extension HeapType: WasmEncodable {
switch self {
case .abstract(.externRef): encoder.output.append(0x6F)
case .abstract(.funcRef): encoder.output.append(0x70)
case .abstract(.exnRef): encoder.output.append(0x69)
case .concrete(let typeIndex):
// Note that the typeIndex is decoded as s33,
// so we need to encode it as signed.
Expand Down Expand Up @@ -346,6 +348,9 @@ extension Export: WasmEncodable {
case .global(let index):
encoder.output.append(0x03)
encoder.writeUnsignedLEB128(UInt32(index))
case .tag(let index):
encoder.output.append(0x04)
encoder.writeUnsignedLEB128(UInt32(index))
}
}
}
Expand Down Expand Up @@ -417,6 +422,10 @@ extension Import: WasmEncodable {
case .global(let globalType):
encoder.output.append(0x03)
globalType.encode(to: &encoder)
case .tag(let typeIndex):
encoder.output.append(0x04)
encoder.output.append(0x00) // attribute: exception
encoder.writeUnsignedLEB128(UInt32(typeIndex))
}
}
}
Expand Down Expand Up @@ -556,6 +565,29 @@ struct ExpressionEncoder: BinaryInstructionEncoder {
mutating func encodeImmediates(value: WasmTypes.V128) { encoder.output.append(contentsOf: value.bytes) }
mutating func encodeImmediates(value: WasmParser.IEEE754.Float32) { encodeFixedWidth(value.bitPattern) }
mutating func encodeImmediates(value: WasmParser.IEEE754.Float64) { encodeFixedWidth(value.bitPattern) }
mutating func encodeImmediates(tagIndex: UInt32) { encodeUnsigned(tagIndex) }
mutating func encodeImmediates(blockType: WasmParser.BlockType, tryCatch: WasmParser.TryCatch) {
encodeImmediates(blockType: blockType)
encoder.writeUnsignedLEB128(UInt32(tryCatch.catches.count))
for clause in tryCatch.catches {
switch clause {
case .catch(let tagIndex, let labelIndex):
encoder.output.append(0x00)
encoder.writeUnsignedLEB128(tagIndex)
encoder.writeUnsignedLEB128(labelIndex)
case .catchRef(let tagIndex, let labelIndex):
encoder.output.append(0x01)
encoder.writeUnsignedLEB128(tagIndex)
encoder.writeUnsignedLEB128(labelIndex)
case .catchAll(let labelIndex):
encoder.output.append(0x02)
encoder.writeUnsignedLEB128(labelIndex)
case .catchAllRef(let labelIndex):
encoder.output.append(0x03)
encoder.writeUnsignedLEB128(labelIndex)
}
}
}
mutating func encodeImmediates(dstMem: UInt32, srcMem: UInt32) {
encodeUnsigned(dstMem)
encodeUnsigned(srcMem)
Expand Down Expand Up @@ -586,6 +618,7 @@ func encode(module: inout Wat, options: EncodeOptions) throws(WatParserError) ->
return (locals, function)
}
var functionSection: [UInt32] = []
var tagSection: [UInt32] = []
var hasDataSegmentInstruction = false
var functionLabelNames: [[(Int, String)]] = []

Expand Down Expand Up @@ -622,6 +655,13 @@ func encode(module: inout Wat, options: EncodeOptions) throws(WatParserError) ->
}
}

// Pre-resolve tag type indices so their types are in the type section.
let tagDefinitions = module.tagsMap.definitions()
for tag in tagDefinitions {
let typeIndex = try module.types.resolveIndex(use: tag.typeUse)
tagSection.append(UInt32(typeIndex))
}

// Section 1: Type section
if !module.types.isEmpty {
encoder.section(id: 0x01) { encoder in
Expand Down Expand Up @@ -663,6 +703,18 @@ func encode(module: inout Wat, options: EncodeOptions) throws(WatParserError) ->
}
}

// Section 13: Tag section
// Note: tagsec is placed between memsec and globalsec in the module grammar.
// https://webassembly.github.io/exception-handling/core/binary/modules.html#binary-module
if !tagSection.isEmpty {
encoder.section(id: 0x0D) { encoder in
encoder.encodeVector(tagSection) { typeIndex, encoder in
encoder.output.append(0x00) // attribute: exception
encoder.writeUnsignedLEB128(typeIndex)
}
}
}

// Section 6: Global section
let globals = module.globals.definitions()
if !globals.isEmpty {
Expand Down
7 changes: 7 additions & 0 deletions Sources/WAT/ParseTextInstruction.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ func parseTextInstruction<V: InstructionVisitor>(
let (blockType) = try expressionParser.visitIf(wat: &wat)
return { visitor throws(V.VisitorError) in return try visitor.visitIf(blockType: blockType) }
case "else": return { visitor throws(V.VisitorError) in return try visitor.visitElse() }
case "throw":
let (tagIndex) = try expressionParser.visitThrow(wat: &wat)
return { visitor throws(V.VisitorError) in return try visitor.visitThrow(tagIndex: tagIndex) }
case "throw_ref": return { visitor throws(V.VisitorError) in return try visitor.visitThrowRef() }
case "end": return { visitor throws(V.VisitorError) in return try visitor.visitEnd() }
case "br":
let (relativeDepth) = try expressionParser.visitBr(wat: &wat)
Expand Down Expand Up @@ -59,6 +63,9 @@ func parseTextInstruction<V: InstructionVisitor>(
case "return_call_ref":
let (typeIndex) = try expressionParser.visitReturnCallRef(wat: &wat)
return { visitor throws(V.VisitorError) in return try visitor.visitReturnCallRef(typeIndex: typeIndex) }
case "try_table":
let (blockType, tryCatch) = try expressionParser.visitTryTable(wat: &wat)
return { visitor throws(V.VisitorError) in return try visitor.visitTryTable(blockType: blockType, tryCatch: tryCatch) }
case "drop": return { visitor throws(V.VisitorError) in return try visitor.visitDrop() }
case "select": return { visitor throws(V.VisitorError) in return try visitor.visitSelect() }
case "local.get":
Expand Down
47 changes: 44 additions & 3 deletions Sources/WAT/Parser/ExpressionParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -331,10 +331,10 @@ struct ExpressionParser<Visitor: InstructionVisitor> where Visitor.VisitorError
this.labelStack.pop()
return try visitor.visitEnd()
})
case "block", "loop":
case "block", "loop", "try_table":
// Visit the block instruction itself
_ = try visit(&visitor)
// Visit child expr here because folded "block" and "loop"
// Visit child expr here because folded "block", "loop", and "try_table"
// allows unfolded child instructions unlike others.
try parse(visitor: &visitor, wat: &wat)
suspense = Suspense(visit: { visitor, this throws(WatParserError) in
Expand Down Expand Up @@ -468,11 +468,13 @@ struct ExpressionParser<Visitor: InstructionVisitor> where Visitor.VisitorError
return .funcRef
} else if try parser.takeKeyword("extern") {
return .externRef
} else if try parser.takeKeyword("exn") {
return .exnRef
} else if let id = try parser.takeIndexOrId() {
let (_, index) = try wat.types.resolve(use: id)
return .concrete(typeIndex: UInt32(index))
}
throw WatParserError("expected \"func\", \"extern\" or type index", location: parser.lexer.location())
throw WatParserError("expected \"func\", \"extern\", \"exn\" or type index", location: parser.lexer.location())
}

private mutating func memArg(defaultAlign: UInt32) throws(WatParserError) -> MemArg {
Expand Down Expand Up @@ -566,6 +568,45 @@ extension ExpressionParser {
mutating func visitReturnCallRef(wat: inout Wat) throws(WatParserError) -> UInt32 {
return try visitCallRef(wat: &wat)
}
mutating func visitThrow(wat: inout Wat) throws(WatParserError) -> UInt32 {
let use = try parser.expectIndexOrId()
return UInt32(try wat.tagsMap.resolve(use: use).index)
}
mutating func visitTryTable(wat: inout Wat) throws(WatParserError) -> (blockType: BlockType, tryCatch: TryCatch) {
let label = try parser.takeId()
let bt = try blockType(wat: &wat)
var catches: [CatchClause] = []
while true {
if try parser.takeParenBlockStart("catch") {
let tagUse = try parser.expectIndexOrId()
let tagIndex = UInt32(try wat.tagsMap.resolve(use: tagUse).index)
let label = try labelIndex()
try parser.expect(.rightParen)
catches.append(.catch(tagIndex: tagIndex, labelIndex: label))
} else if try parser.takeParenBlockStart("catch_ref") {
let tagUse = try parser.expectIndexOrId()
let tagIndex = UInt32(try wat.tagsMap.resolve(use: tagUse).index)
let label = try labelIndex()
try parser.expect(.rightParen)
catches.append(.catchRef(tagIndex: tagIndex, labelIndex: label))
} else if try parser.takeParenBlockStart("catch_all") {
let label = try labelIndex()
try parser.expect(.rightParen)
catches.append(.catchAll(labelIndex: label))
} else if try parser.takeParenBlockStart("catch_all_ref") {
let label = try labelIndex()
try parser.expect(.rightParen)
catches.append(.catchAllRef(labelIndex: label))
} else {
break
}
}
// Push the try_table's label AFTER parsing catch clauses, since catch clause
// labels are resolved from the enclosing scope (the try_table's own label
// is only in scope for the body instructions).
self.labelStack.push(label)
return (bt, TryCatch(catches: catches))
}
mutating func visitBrOnNull(wat: inout Wat) throws(WatParserError) -> UInt32 {
return try labelIndex()
}
Expand Down
6 changes: 6 additions & 0 deletions Sources/WAT/Parser/WastParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ public enum WastDirective {
case assertReturn(execute: WastExecute, results: [WastExpectValue])
case assertTrap(execute: WastExecute, message: String)
case assertExhaustion(call: WastInvoke, message: String)
case assertException(execute: WastExecute)
case assertUnlinkable(module: Wat, message: String)
case register(name: String, moduleId: String?)
case invoke(WastInvoke)
Expand Down Expand Up @@ -278,6 +279,11 @@ public enum WastDirective {
let message = try wastParser.parser.expectString()
try wastParser.parser.expect(.rightParen)
return .assertExhaustion(call: call, message: message)
case "assert_exception":
try wastParser.parser.consume()
let execute = try wastParser.parens { wastParser throws(WatParserError) in try WastExecute.parse(wastParser: &wastParser) }
try wastParser.parser.expect(.rightParen)
return .assertException(execute: execute)
case "assert_unlinkable":
try wastParser.parser.consume()
let features = wastParser.features
Expand Down
Loading
Loading