diff --git a/Sources/WASTRunner/TestCase.swift b/Sources/WASTRunner/TestCase.swift index 2108de9bc..279ca7a96 100644 --- a/Sources/WASTRunner/TestCase.swift +++ b/Sources/WASTRunner/TestCase.swift @@ -522,6 +522,8 @@ extension WASTRunContext { } case .refExtern(let value): return .ref(.extern(Int(value))) case .refFunc(let value): return .ref(.function(Int(value))) + case .refHost: + throw SpectestError("ref.host is not supported yet") } } return try function.invoke(args) diff --git a/Sources/WAT/BinaryEncoding/BinaryInstructionEncoder.swift b/Sources/WAT/BinaryEncoding/BinaryInstructionEncoder.swift index 19d09a875..fc4700a33 100644 --- a/Sources/WAT/BinaryEncoding/BinaryInstructionEncoder.swift +++ b/Sources/WAT/BinaryEncoding/BinaryInstructionEncoder.swift @@ -36,11 +36,17 @@ protocol BinaryInstructionEncoder: InstructionVisitor { mutating func encodeImmediates(value: V128) throws(VisitorError) mutating func encodeImmediates(blockType: BlockType, tryCatch: TryCatch) throws(VisitorError) mutating func encodeImmediates(dataIndex: UInt32, memory: UInt32) throws(VisitorError) + mutating func encodeImmediates(destType: UInt32, srcType: UInt32) 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) mutating func encodeImmediates(memarg: MemArg, lane: UInt8) throws(VisitorError) + mutating func encodeImmediates(typeIndex: UInt32, dataIndex: UInt32) throws(VisitorError) + mutating func encodeImmediates(typeIndex: UInt32, elemIndex: UInt32) throws(VisitorError) + mutating func encodeImmediates(typeIndex: UInt32, fieldIndex: UInt32) throws(VisitorError) + mutating func encodeImmediates(typeIndex: UInt32, size: UInt32) throws(VisitorError) mutating func encodeImmediates(typeIndex: UInt32, tableIndex: UInt32) throws(VisitorError) + mutating func encodeImmediates(relativeDepth: UInt32, castFrom: ReferenceType, castTo: ReferenceType) throws(VisitorError) } // BinaryInstructionEncoder implements the InstructionVisitor protocol to call the corresponding encode method. @@ -934,4 +940,115 @@ extension BinaryInstructionEncoder { try encodeInstruction(opcode) try encodeImmediates(memarg: memarg, lane: lane) } + mutating func visitRefI31() throws(VisitorError) { try encodeInstruction([0xFB, 0x1C]) } + mutating func visitI31GetS() throws(VisitorError) { try encodeInstruction([0xFB, 0x1D]) } + mutating func visitI31GetU() throws(VisitorError) { try encodeInstruction([0xFB, 0x1E]) } + mutating func visitStructNew(typeIndex: UInt32) throws(VisitorError) { + try encodeInstruction([0xFB, 0x00]) + try encodeImmediates(typeIndex: typeIndex) + } + mutating func visitStructNewDefault(typeIndex: UInt32) throws(VisitorError) { + try encodeInstruction([0xFB, 0x01]) + try encodeImmediates(typeIndex: typeIndex) + } + mutating func visitStructGet(typeIndex: UInt32, fieldIndex: UInt32) throws(VisitorError) { + try encodeInstruction([0xFB, 0x02]) + try encodeImmediates(typeIndex: typeIndex, fieldIndex: fieldIndex) + } + mutating func visitStructGetS(typeIndex: UInt32, fieldIndex: UInt32) throws(VisitorError) { + try encodeInstruction([0xFB, 0x03]) + try encodeImmediates(typeIndex: typeIndex, fieldIndex: fieldIndex) + } + mutating func visitStructGetU(typeIndex: UInt32, fieldIndex: UInt32) throws(VisitorError) { + try encodeInstruction([0xFB, 0x04]) + try encodeImmediates(typeIndex: typeIndex, fieldIndex: fieldIndex) + } + mutating func visitStructSet(typeIndex: UInt32, fieldIndex: UInt32) throws(VisitorError) { + try encodeInstruction([0xFB, 0x05]) + try encodeImmediates(typeIndex: typeIndex, fieldIndex: fieldIndex) + } + mutating func visitArrayNew(typeIndex: UInt32) throws(VisitorError) { + try encodeInstruction([0xFB, 0x06]) + try encodeImmediates(typeIndex: typeIndex) + } + mutating func visitArrayNewDefault(typeIndex: UInt32) throws(VisitorError) { + try encodeInstruction([0xFB, 0x07]) + try encodeImmediates(typeIndex: typeIndex) + } + mutating func visitArrayNewFixed(typeIndex: UInt32, size: UInt32) throws(VisitorError) { + try encodeInstruction([0xFB, 0x08]) + try encodeImmediates(typeIndex: typeIndex, size: size) + } + mutating func visitArrayGet(typeIndex: UInt32) throws(VisitorError) { + try encodeInstruction([0xFB, 0x0B]) + try encodeImmediates(typeIndex: typeIndex) + } + mutating func visitArrayGetS(typeIndex: UInt32) throws(VisitorError) { + try encodeInstruction([0xFB, 0x0C]) + try encodeImmediates(typeIndex: typeIndex) + } + mutating func visitArrayGetU(typeIndex: UInt32) throws(VisitorError) { + try encodeInstruction([0xFB, 0x0D]) + try encodeImmediates(typeIndex: typeIndex) + } + mutating func visitArraySet(typeIndex: UInt32) throws(VisitorError) { + try encodeInstruction([0xFB, 0x0E]) + try encodeImmediates(typeIndex: typeIndex) + } + mutating func visitArrayLen() throws(VisitorError) { try encodeInstruction([0xFB, 0x0F]) } + mutating func visitRefEq() throws(VisitorError) { try encodeInstruction([0xD3]) } + mutating func visitAnyConvertExtern() throws(VisitorError) { try encodeInstruction([0xFB, 0x1A]) } + mutating func visitExternConvertAny() throws(VisitorError) { try encodeInstruction([0xFB, 0x1B]) } + mutating func visitArrayNewData(typeIndex: UInt32, dataIndex: UInt32) throws(VisitorError) { + try encodeInstruction([0xFB, 0x09]) + try encodeImmediates(typeIndex: typeIndex, dataIndex: dataIndex) + } + mutating func visitArrayNewElem(typeIndex: UInt32, elemIndex: UInt32) throws(VisitorError) { + try encodeInstruction([0xFB, 0x0A]) + try encodeImmediates(typeIndex: typeIndex, elemIndex: elemIndex) + } + mutating func visitArrayFill(typeIndex: UInt32) throws(VisitorError) { + try encodeInstruction([0xFB, 0x10]) + try encodeImmediates(typeIndex: typeIndex) + } + mutating func visitArrayCopy(destType: UInt32, srcType: UInt32) throws(VisitorError) { + try encodeInstruction([0xFB, 0x11]) + try encodeImmediates(destType: destType, srcType: srcType) + } + mutating func visitArrayInitData(typeIndex: UInt32, dataIndex: UInt32) throws(VisitorError) { + try encodeInstruction([0xFB, 0x12]) + try encodeImmediates(typeIndex: typeIndex, dataIndex: dataIndex) + } + mutating func visitArrayInitElem(typeIndex: UInt32, elemIndex: UInt32) throws(VisitorError) { + try encodeInstruction([0xFB, 0x13]) + try encodeImmediates(typeIndex: typeIndex, elemIndex: elemIndex) + } + mutating func visitRefTest(_ refTest: Instruction.RefTest, type: HeapType) throws(VisitorError) { + let opcode: [UInt8] + switch refTest { + case .refTest: opcode = [0xFB, 0x14] + case .refTestNull: opcode = [0xFB, 0x15] + } + + try encodeInstruction(opcode) + try encodeImmediates(type: type) + } + mutating func visitRefCast(_ refCast: Instruction.RefCast, type: HeapType) throws(VisitorError) { + let opcode: [UInt8] + switch refCast { + case .refCast: opcode = [0xFB, 0x16] + case .refCastNull: opcode = [0xFB, 0x17] + } + + try encodeInstruction(opcode) + try encodeImmediates(type: type) + } + mutating func visitBrOnCast(relativeDepth: UInt32, castFrom: ReferenceType, castTo: ReferenceType) throws(VisitorError) { + try encodeInstruction([0xFB, 0x18]) + try encodeImmediates(relativeDepth: relativeDepth, castFrom: castFrom, castTo: castTo) + } + mutating func visitBrOnCastFail(relativeDepth: UInt32, castFrom: ReferenceType, castTo: ReferenceType) throws(VisitorError) { + try encodeInstruction([0xFB, 0x19]) + try encodeImmediates(relativeDepth: relativeDepth, castFrom: castFrom, castTo: castTo) + } } diff --git a/Sources/WAT/BinaryEncoding/Encoder.swift b/Sources/WAT/BinaryEncoding/Encoder.swift index 9fc6bc126..5429957d3 100644 --- a/Sources/WAT/BinaryEncoding/Encoder.swift +++ b/Sources/WAT/BinaryEncoding/Encoder.swift @@ -257,8 +257,15 @@ extension TableType: WasmEncodable { struct ElementExprCollector: AnyInstructionVisitor { typealias VisitorError = WatParserError var binaryOffset: Int = 0 - var isAllRefFunc: Bool = true - var instructions: [Instruction] = [] + private(set) var items: [[Instruction]] = [] + private var currentItem: [Instruction] = [] + + var isAllRefFunc: Bool { + items.allSatisfy { item in + if item.count == 1, case .refFunc = item[0] { return true } + return false + } + } mutating func parse(indices: WatParser.ElementDecl.Indices, wat: inout Wat) throws(WatParserError) { switch indices { @@ -271,7 +278,7 @@ struct ElementExprCollector: AnyInstructionVisitor { } private mutating func addFunctionIndex(_ index: UInt32) { - instructions.append(.refFunc(functionIndex: index)) + items.append([.refFunc(functionIndex: index)]) } private mutating func parseFunctionList(lexer: Lexer, wat: Wat) throws(WatParserError) { @@ -283,11 +290,12 @@ struct ElementExprCollector: AnyInstructionVisitor { } mutating func visit(_ instruction: Instruction) { - if case .refFunc = instruction { - } else { - isAllRefFunc = false - } - instructions.append(instruction) + currentItem.append(instruction) + } + + mutating func finishItem() { + items.append(currentItem) + currentItem = [] } } @@ -377,24 +385,17 @@ extension WAT.WatParser.ElementDecl { } if useExpression { - try encoder.encodeVector(collector.instructions) { instruction, encoder throws(WatParserError) in + try encoder.encodeVector(collector.items) { item, encoder throws(WatParserError) in var exprEncoder = ExpressionEncoder() - switch instruction { - case .globalGet(let globalIndex): - try exprEncoder.visitGlobalGet(globalIndex: globalIndex) - case .refFunc(let functionIndex): - try exprEncoder.visitRefFunc(functionIndex: functionIndex) - case .refNull(let type): - try exprEncoder.visitRefNull(type: type) - default: - throw WatParserError("unexpected instruction in element expression \(instruction)", location: nil) + for instruction in item { + try exprEncoder.visit(instruction) } try exprEncoder.visitEnd() encoder.output.append(contentsOf: exprEncoder.encoder.output) } } else { - encoder.encodeVector(collector.instructions) { instruction, encoder in - guard case .refFunc(let funcIndex) = instruction else { fatalError("non-ref.func instruction in non-expression mode") } + encoder.encodeVector(collector.items) { item, encoder in + guard item.count == 1, case .refFunc(let funcIndex) = item[0] else { fatalError("non-ref.func item in non-expression mode") } encoder.writeUnsignedLEB128(funcIndex) } } @@ -700,6 +701,35 @@ struct ExpressionEncoder: BinaryInstructionEncoder { encodeUnsigned(typeIndex) encodeUnsigned(tableIndex) } + mutating func encodeImmediates(typeIndex: UInt32, fieldIndex: UInt32) { + encodeUnsigned(typeIndex) + encodeUnsigned(fieldIndex) + } + mutating func encodeImmediates(typeIndex: UInt32, size: UInt32) { + encodeUnsigned(typeIndex) + encodeUnsigned(size) + } + mutating func encodeImmediates(typeIndex: UInt32, dataIndex: UInt32) { + encodeUnsigned(typeIndex) + encodeImmediates(dataIndex: dataIndex) + } + mutating func encodeImmediates(typeIndex: UInt32, elemIndex: UInt32) { + encodeUnsigned(typeIndex) + encodeUnsigned(elemIndex) + } + mutating func encodeImmediates(destType: UInt32, srcType: UInt32) { + encodeUnsigned(destType) + encodeUnsigned(srcType) + } + mutating func encodeImmediates(relativeDepth: UInt32, castFrom: WasmTypes.ReferenceType, castTo: WasmTypes.ReferenceType) { + var flags: UInt8 = 0 + if castFrom.isNullable { flags |= 0b01 } + if castTo.isNullable { flags |= 0b10 } + encodeByte(flags) + encodeUnsigned(relativeDepth) + encoder.encode(castFrom.heapType) + encoder.encode(castTo.heapType) + } } func encode(module: inout Wat, options: EncodeOptions) throws(WatParserError) -> [UInt8] { diff --git a/Sources/WAT/ParseTextInstruction.swift b/Sources/WAT/ParseTextInstruction.swift index 1de1f8e68..9e23ca25f 100644 --- a/Sources/WAT/ParseTextInstruction.swift +++ b/Sources/WAT/ParseTextInstruction.swift @@ -887,6 +887,76 @@ func parseTextInstruction( case "i16x8.relaxed_q15mulr_s": return { visitor throws(V.VisitorError) in return try visitor.visitSimd(.i16x8RelaxedQ15MulrS) } case "i16x8.relaxed_dot_i8x16_i7x16_s": return { visitor throws(V.VisitorError) in return try visitor.visitSimd(.i16x8RelaxedDotI8X16I7X16S) } case "i32x4.relaxed_dot_i8x16_i7x16_add_s": return { visitor throws(V.VisitorError) in return try visitor.visitSimd(.i32x4RelaxedDotI8X16I7X16AddS) } + case "ref.i31": return { visitor throws(V.VisitorError) in return try visitor.visitRefI31() } + case "i31.get_s": return { visitor throws(V.VisitorError) in return try visitor.visitI31GetS() } + case "i31.get_u": return { visitor throws(V.VisitorError) in return try visitor.visitI31GetU() } + case "struct.new": + let (typeIndex) = try expressionParser.visitStructNew(wat: &wat) + return { visitor throws(V.VisitorError) in return try visitor.visitStructNew(typeIndex: typeIndex) } + case "struct.new_default": + let (typeIndex) = try expressionParser.visitStructNewDefault(wat: &wat) + return { visitor throws(V.VisitorError) in return try visitor.visitStructNewDefault(typeIndex: typeIndex) } + case "struct.get": + let (typeIndex, fieldIndex) = try expressionParser.visitStructGet(wat: &wat) + return { visitor throws(V.VisitorError) in return try visitor.visitStructGet(typeIndex: typeIndex, fieldIndex: fieldIndex) } + case "struct.get_s": + let (typeIndex, fieldIndex) = try expressionParser.visitStructGetS(wat: &wat) + return { visitor throws(V.VisitorError) in return try visitor.visitStructGetS(typeIndex: typeIndex, fieldIndex: fieldIndex) } + case "struct.get_u": + let (typeIndex, fieldIndex) = try expressionParser.visitStructGetU(wat: &wat) + return { visitor throws(V.VisitorError) in return try visitor.visitStructGetU(typeIndex: typeIndex, fieldIndex: fieldIndex) } + case "struct.set": + let (typeIndex, fieldIndex) = try expressionParser.visitStructSet(wat: &wat) + return { visitor throws(V.VisitorError) in return try visitor.visitStructSet(typeIndex: typeIndex, fieldIndex: fieldIndex) } + case "array.new": + let (typeIndex) = try expressionParser.visitArrayNew(wat: &wat) + return { visitor throws(V.VisitorError) in return try visitor.visitArrayNew(typeIndex: typeIndex) } + case "array.new_default": + let (typeIndex) = try expressionParser.visitArrayNewDefault(wat: &wat) + return { visitor throws(V.VisitorError) in return try visitor.visitArrayNewDefault(typeIndex: typeIndex) } + case "array.new_fixed": + let (typeIndex, size) = try expressionParser.visitArrayNewFixed(wat: &wat) + return { visitor throws(V.VisitorError) in return try visitor.visitArrayNewFixed(typeIndex: typeIndex, size: size) } + case "array.get": + let (typeIndex) = try expressionParser.visitArrayGet(wat: &wat) + return { visitor throws(V.VisitorError) in return try visitor.visitArrayGet(typeIndex: typeIndex) } + case "array.get_s": + let (typeIndex) = try expressionParser.visitArrayGetS(wat: &wat) + return { visitor throws(V.VisitorError) in return try visitor.visitArrayGetS(typeIndex: typeIndex) } + case "array.get_u": + let (typeIndex) = try expressionParser.visitArrayGetU(wat: &wat) + return { visitor throws(V.VisitorError) in return try visitor.visitArrayGetU(typeIndex: typeIndex) } + case "array.set": + let (typeIndex) = try expressionParser.visitArraySet(wat: &wat) + return { visitor throws(V.VisitorError) in return try visitor.visitArraySet(typeIndex: typeIndex) } + case "array.len": return { visitor throws(V.VisitorError) in return try visitor.visitArrayLen() } + case "ref.eq": return { visitor throws(V.VisitorError) in return try visitor.visitRefEq() } + case "any.convert_extern": return { visitor throws(V.VisitorError) in return try visitor.visitAnyConvertExtern() } + case "extern.convert_any": return { visitor throws(V.VisitorError) in return try visitor.visitExternConvertAny() } + case "array.new_data": + let (typeIndex, dataIndex) = try expressionParser.visitArrayNewData(wat: &wat) + return { visitor throws(V.VisitorError) in return try visitor.visitArrayNewData(typeIndex: typeIndex, dataIndex: dataIndex) } + case "array.new_elem": + let (typeIndex, elemIndex) = try expressionParser.visitArrayNewElem(wat: &wat) + return { visitor throws(V.VisitorError) in return try visitor.visitArrayNewElem(typeIndex: typeIndex, elemIndex: elemIndex) } + case "array.fill": + let (typeIndex) = try expressionParser.visitArrayFill(wat: &wat) + return { visitor throws(V.VisitorError) in return try visitor.visitArrayFill(typeIndex: typeIndex) } + case "array.copy": + let (destType, srcType) = try expressionParser.visitArrayCopy(wat: &wat) + return { visitor throws(V.VisitorError) in return try visitor.visitArrayCopy(destType: destType, srcType: srcType) } + case "array.init_data": + let (typeIndex, dataIndex) = try expressionParser.visitArrayInitData(wat: &wat) + return { visitor throws(V.VisitorError) in return try visitor.visitArrayInitData(typeIndex: typeIndex, dataIndex: dataIndex) } + case "array.init_elem": + let (typeIndex, elemIndex) = try expressionParser.visitArrayInitElem(wat: &wat) + return { visitor throws(V.VisitorError) in return try visitor.visitArrayInitElem(typeIndex: typeIndex, elemIndex: elemIndex) } + case "br_on_cast": + let (relativeDepth, castFrom, castTo) = try expressionParser.visitBrOnCast(wat: &wat) + return { visitor throws(V.VisitorError) in return try visitor.visitBrOnCast(relativeDepth: relativeDepth, castFrom: castFrom, castTo: castTo) } + case "br_on_cast_fail": + let (relativeDepth, castFrom, castTo) = try expressionParser.visitBrOnCastFail(wat: &wat) + return { visitor throws(V.VisitorError) in return try visitor.visitBrOnCastFail(relativeDepth: relativeDepth, castFrom: castFrom, castTo: castTo) } default: return nil } } diff --git a/Sources/WAT/Parser/ExpressionParser.swift b/Sources/WAT/Parser/ExpressionParser.swift index 0b1f73b99..72383083a 100644 --- a/Sources/WAT/Parser/ExpressionParser.swift +++ b/Sources/WAT/Parser/ExpressionParser.swift @@ -109,15 +109,15 @@ struct ExpressionParser where Visitor.VisitorError } } - mutating func parseElemExprList(visitor: inout Visitor, wat: inout Wat) throws(WatParserError) { + mutating func parseElemExprList(visitor: inout Visitor, wat: inout Wat) throws(WatParserError) where Visitor == ElementExprCollector { while true { - let needRightParen = try parser.takeParenBlockStart("item") - guard try instruction(visitor: &visitor, wat: &wat) else { - break - } - if needRightParen { + if try parser.takeParenBlockStart("item") { + try parse(visitor: &visitor, wat: &wat) try parser.expect(.rightParen) + } else if try !instruction(visitor: &visitor, wat: &wat) { + break } + visitor.finishItem() } } @@ -131,6 +131,11 @@ struct ExpressionParser where Visitor.VisitorError try parser.expect(.rightParen) return true } + if try parser.takeParenBlockStart("ref.host") { + try visitor.visitRefHost(value: parser.expectUnsignedInt()) + try parser.expect(.rightParen) + return true + } // WAST const expr only accepts folded instructions if try foldedInstruction(visitor: &visitor, wat: &wat) { return true @@ -268,6 +273,22 @@ struct ExpressionParser where Visitor.VisitorError try parser.expect(.rightParen) return .refExtern(value: nil) } + if try parser.takeParenBlockStart("ref.i31"), try parser.isEndOfParen() { + try parser.expect(.rightParen) + return .refI31 + } + if try parser.takeParenBlockStart("ref.struct"), try parser.isEndOfParen() { + try parser.expect(.rightParen) + return .refStruct + } + if try parser.takeParenBlockStart("ref.array"), try parser.isEndOfParen() { + try parser.expect(.rightParen) + return .refArray + } + if try parser.takeParenBlockStart("ref.eq"), try parser.isEndOfParen() { + try parser.expect(.rightParen) + return .refEq + } parser = initialParser return nil } @@ -392,6 +413,16 @@ struct ExpressionParser where Visitor.VisitorError return try visitor.visitSelect() } } + case "ref.test": + let type = try withWatParser({ parser throws(WatParserError) in try parser.refType() }).resolve(wat.types) + return { visitor in + try visitor.visitRefTest(type.isNullable ? .refTestNull : .refTest, type: type.heapType) + } + case "ref.cast": + let type = try withWatParser({ parser throws(WatParserError) in try parser.refType() }).resolve(wat.types) + return { visitor in + try visitor.visitRefCast(type.isNullable ? .refCastNull : .refCast, type: type.heapType) + } case "else": // This path should not be reached when parsing folded "if" instruction. // It should be separately handled in foldedInstruction(). @@ -443,6 +474,33 @@ struct ExpressionParser where Visitor.VisitorError return UInt32(try wat.functionsMap.resolve(use: funcUse).index) } + private mutating func typeIndex(wat: inout Wat) throws(WatParserError) -> UInt32 { + let use = try parser.expectIndexOrId() + return UInt32(try wat.types.resolve(use: use).index) + } + + private mutating func structField(wat: inout Wat) throws(WatParserError) -> (typeIndex: UInt32, fieldIndex: UInt32) { + let (type, typeIndex) = try wat.types.nameMapping.resolve(use: try parser.expectIndexOrId()) + let fieldUse = try parser.expectIndexOrId() + switch fieldUse { + case .index(let fieldIndex, _): + return (UInt32(typeIndex), fieldIndex) + case .id(let name, let location): + guard let fieldIndex = type.fieldNames[name.value] else { + throw WatParserError("unknown field \(name.value)", location: location) + } + return (UInt32(typeIndex), UInt32(fieldIndex)) + } + } + + private mutating func requiredDataIndex(wat: inout Wat) throws(WatParserError) -> UInt32 { + UInt32(try wat.data.resolve(use: try parser.expectIndexOrId()).index) + } + + private mutating func requiredElementIndex(wat: inout Wat) throws(WatParserError) -> UInt32 { + UInt32(try wat.elementsMap.resolve(use: try parser.expectIndexOrId()).index) + } + private mutating func memoryIndex(wat: inout Wat) throws(WatParserError) -> UInt32 { guard let use = try parser.takeIndexOrId() else { return 0 } return UInt32(try wat.memories.resolve(use: use).index) @@ -617,8 +675,7 @@ extension ExpressionParser { return UInt32(try wat.functionsMap.resolve(use: use).index) } mutating func visitCallRef(wat: inout Wat) throws(WatParserError) -> UInt32 { - let use = try parser.expectIndexOrId() - return UInt32(try wat.types.resolve(use: use).index) + return try typeIndex(wat: &wat) } mutating func visitReturnCallRef(wat: inout Wat) throws(WatParserError) -> UInt32 { return try visitCallRef(wat: &wat) @@ -668,6 +725,77 @@ extension ExpressionParser { mutating func visitBrOnNonNull(wat: inout Wat) throws(WatParserError) -> UInt32 { return try labelIndex() } + mutating func visitBrOnCast(wat: inout Wat) throws(WatParserError) -> (relativeDepth: UInt32, castFrom: ReferenceType, castTo: ReferenceType) { + let relativeDepth = try labelIndex() + let castFrom = try withWatParser({ parser throws(WatParserError) in try parser.refType() }).resolve(wat.types) + let castTo = try withWatParser({ parser throws(WatParserError) in try parser.refType() }).resolve(wat.types) + return (relativeDepth, castFrom, castTo) + } + mutating func visitBrOnCastFail(wat: inout Wat) throws(WatParserError) -> (relativeDepth: UInt32, castFrom: ReferenceType, castTo: ReferenceType) { + return try visitBrOnCast(wat: &wat) + } + mutating func visitStructNew(wat: inout Wat) throws(WatParserError) -> UInt32 { + return try typeIndex(wat: &wat) + } + mutating func visitStructNewDefault(wat: inout Wat) throws(WatParserError) -> UInt32 { + return try typeIndex(wat: &wat) + } + mutating func visitStructGet(wat: inout Wat) throws(WatParserError) -> (typeIndex: UInt32, fieldIndex: UInt32) { + return try structField(wat: &wat) + } + mutating func visitStructGetS(wat: inout Wat) throws(WatParserError) -> (typeIndex: UInt32, fieldIndex: UInt32) { + return try structField(wat: &wat) + } + mutating func visitStructGetU(wat: inout Wat) throws(WatParserError) -> (typeIndex: UInt32, fieldIndex: UInt32) { + return try structField(wat: &wat) + } + mutating func visitStructSet(wat: inout Wat) throws(WatParserError) -> (typeIndex: UInt32, fieldIndex: UInt32) { + return try structField(wat: &wat) + } + mutating func visitArrayNew(wat: inout Wat) throws(WatParserError) -> UInt32 { + return try typeIndex(wat: &wat) + } + mutating func visitArrayNewDefault(wat: inout Wat) throws(WatParserError) -> UInt32 { + return try typeIndex(wat: &wat) + } + mutating func visitArrayNewFixed(wat: inout Wat) throws(WatParserError) -> (typeIndex: UInt32, size: UInt32) { + let typeIndex = try self.typeIndex(wat: &wat) + let size = try parser.expectUnsignedInt(UInt32.self) + return (typeIndex, size) + } + mutating func visitArrayGet(wat: inout Wat) throws(WatParserError) -> UInt32 { + return try typeIndex(wat: &wat) + } + mutating func visitArrayGetS(wat: inout Wat) throws(WatParserError) -> UInt32 { + return try typeIndex(wat: &wat) + } + mutating func visitArrayGetU(wat: inout Wat) throws(WatParserError) -> UInt32 { + return try typeIndex(wat: &wat) + } + mutating func visitArraySet(wat: inout Wat) throws(WatParserError) -> UInt32 { + return try typeIndex(wat: &wat) + } + mutating func visitArrayNewData(wat: inout Wat) throws(WatParserError) -> (typeIndex: UInt32, dataIndex: UInt32) { + let typeIndex = try self.typeIndex(wat: &wat) + return (typeIndex, try requiredDataIndex(wat: &wat)) + } + mutating func visitArrayNewElem(wat: inout Wat) throws(WatParserError) -> (typeIndex: UInt32, elemIndex: UInt32) { + let typeIndex = try self.typeIndex(wat: &wat) + return (typeIndex, try requiredElementIndex(wat: &wat)) + } + mutating func visitArrayFill(wat: inout Wat) throws(WatParserError) -> UInt32 { + return try typeIndex(wat: &wat) + } + mutating func visitArrayCopy(wat: inout Wat) throws(WatParserError) -> (destType: UInt32, srcType: UInt32) { + let destType = try typeIndex(wat: &wat) + return (destType, try typeIndex(wat: &wat)) + } + mutating func visitArrayInitData(wat: inout Wat) throws(WatParserError) -> (typeIndex: UInt32, dataIndex: UInt32) { + return try visitArrayNewData(wat: &wat) + } + mutating func visitArrayInitElem(wat: inout Wat) throws(WatParserError) -> (typeIndex: UInt32, elemIndex: UInt32) { + return try visitArrayNewElem(wat: &wat) + } mutating func visitCallIndirect(wat: inout Wat) throws(WatParserError) -> (typeIndex: UInt32, tableIndex: UInt32) { let tableIndex: UInt32 if let tableId = try parser.takeIndexOrId() { diff --git a/Sources/WAT/Parser/WASTParser.swift b/Sources/WAT/Parser/WASTParser.swift index 732f6d8d3..e3a651357 100644 --- a/Sources/WAT/Parser/WASTParser.swift +++ b/Sources/WAT/Parser/WASTParser.swift @@ -3,6 +3,7 @@ import WasmTypes protocol WASTConstInstructionVisitor: InstructionVisitor { mutating func visitRefExtern(value: UInt32) throws(WatParserError) + mutating func visitRefHost(value: UInt32) throws(WatParserError) } /// A parser for WAST format. @@ -53,24 +54,29 @@ struct WASTParser { return result } - struct ConstExpressionCollector: WASTConstInstructionVisitor { + struct ConstExpressionCollector: WASTConstInstructionVisitor, AnyInstructionVisitor { + typealias VisitorError = WatParserError var binaryOffset: Int = 0 let addValue: (WASTConstValue) -> Void - mutating func visitI32Const(value: Int32) throws(WatParserError) { addValue(.i32(UInt32(bitPattern: value))) } - mutating func visitI64Const(value: Int64) throws(WatParserError) { addValue(.i64(UInt64(bitPattern: value))) } - mutating func visitF32Const(value: IEEE754.Float32) throws(WatParserError) { addValue(.f32(value.bitPattern)) } - mutating func visitF64Const(value: IEEE754.Float64) throws(WatParserError) { addValue(.f64(value.bitPattern)) } - mutating func visitV128Const(value: V128) throws(WatParserError) { addValue(.v128(value)) } - mutating func visitRefFunc(functionIndex: UInt32) throws(WatParserError) { - addValue(.refFunc(functionIndex: functionIndex)) - } - mutating func visitRefNull(type: HeapType) throws(WatParserError) { - addValue(.refNull(type)) + mutating func visit(_ instruction: Instruction) throws(WatParserError) { + switch instruction { + case .i32Const(let value): addValue(.i32(UInt32(bitPattern: value))) + case .i64Const(let value): addValue(.i64(UInt64(bitPattern: value))) + case .f32Const(let value): addValue(.f32(value.bitPattern)) + case .f64Const(let value): addValue(.f64(value.bitPattern)) + case .v128Const(let value): addValue(.v128(value)) + case .refFunc(let functionIndex): addValue(.refFunc(functionIndex: functionIndex)) + case .refNull(let type): addValue(.refNull(type)) + default: throw WatParserError("unexpected instruction \(instruction) in a WAST value", location: nil) + } } func visitRefExtern(value: UInt32) throws(WatParserError) { addValue(.refExtern(value: value)) } + func visitRefHost(value: UInt32) throws(WatParserError) { + addValue(.refHost(value: value)) + } } mutating func argumentValues() throws(WatParserError) -> [WASTConstValue] { @@ -95,6 +101,7 @@ struct WASTParser { case .refNull(let heapTy): value = .refNull(heapTy) case .refFunc(let index): value = .refFunc(functionIndex: index) case .refExtern(let v): value = .refExtern(value: v) + case .refHost(let v): value = .refHost(value: v) } values.append(value) }) @@ -156,6 +163,7 @@ public enum WASTConstValue { case refNull(HeapType) case refFunc(functionIndex: UInt32) case refExtern(value: UInt32) + case refHost(value: UInt32) } public struct WASTInvoke { @@ -210,6 +218,16 @@ public enum WASTExpectValue { /// A value that is expected to be a non-null reference /// to an extern, optionally with a specific value. case refExtern(value: UInt32?) + /// A value that is expected to be a non-null i31 reference. + case refI31 + /// A value that is expected to be a non-null struct reference. + case refStruct + /// A value that is expected to be a non-null array reference. + case refArray + /// A value that is expected to be a non-null reference to an i31, struct or array. + case refEq + /// A value that is expected to be a host reference with a specific value. + case refHost(value: UInt32) /// A value that is expected to be a canonical NaN. /// Corresponds to `f32.const nan:canonical` in WAST. case f32CanonicalNaN diff --git a/Sources/WasmKit/Execution/ConstEvaluation.swift b/Sources/WasmKit/Execution/ConstEvaluation.swift index fb3f30bcd..8e854cfc7 100644 --- a/Sources/WasmKit/Execution/ConstEvaluation.swift +++ b/Sources/WasmKit/Execution/ConstEvaluation.swift @@ -138,6 +138,10 @@ extension WasmParser.ElementSegment { static func _evaluateInits( context: C, expression: ConstExpression ) throws -> Reference { + // Function-index items have no `end`. + guard expression.count == 1 || (expression.count == 2 && expression[1] == .end) else { + throw WasmKitError(message: .unexpectedElementInitializer(expression: "\(expression)")) + } switch expression[0] { case .refFunc(let index): return try context.functionRef(index) diff --git a/Sources/WasmKit/Translator.swift b/Sources/WasmKit/Translator.swift index d32f78544..78edc33da 100644 --- a/Sources/WasmKit/Translator.swift +++ b/Sources/WasmKit/Translator.swift @@ -5332,6 +5332,100 @@ struct InstructionTranslator: ~Copyable, InstructionVisitor { try iseqBuilder.pinLabelHere(onBranchNotTaken) } + // MARK: - Garbage collection + + // Without these, the visitor's defaults would translate each GC instruction to nothing. + mutating func visitRefI31() throws(WasmKitError) -> Output { + throw WasmKitError("ref.i31 is not supported yet") + } + mutating func visitI31GetS() throws(WasmKitError) -> Output { + throw WasmKitError("i31.get_s is not supported yet") + } + mutating func visitI31GetU() throws(WasmKitError) -> Output { + throw WasmKitError("i31.get_u is not supported yet") + } + mutating func visitStructNew(typeIndex: UInt32) throws(WasmKitError) -> Output { + throw WasmKitError("struct.new is not supported yet") + } + mutating func visitStructNewDefault(typeIndex: UInt32) throws(WasmKitError) -> Output { + throw WasmKitError("struct.new_default is not supported yet") + } + mutating func visitStructGet(typeIndex: UInt32, fieldIndex: UInt32) throws(WasmKitError) -> Output { + throw WasmKitError("struct.get is not supported yet") + } + mutating func visitStructGetS(typeIndex: UInt32, fieldIndex: UInt32) throws(WasmKitError) -> Output { + throw WasmKitError("struct.get_s is not supported yet") + } + mutating func visitStructGetU(typeIndex: UInt32, fieldIndex: UInt32) throws(WasmKitError) -> Output { + throw WasmKitError("struct.get_u is not supported yet") + } + mutating func visitStructSet(typeIndex: UInt32, fieldIndex: UInt32) throws(WasmKitError) -> Output { + throw WasmKitError("struct.set is not supported yet") + } + mutating func visitArrayNew(typeIndex: UInt32) throws(WasmKitError) -> Output { + throw WasmKitError("array.new is not supported yet") + } + mutating func visitArrayNewDefault(typeIndex: UInt32) throws(WasmKitError) -> Output { + throw WasmKitError("array.new_default is not supported yet") + } + mutating func visitArrayNewFixed(typeIndex: UInt32, size: UInt32) throws(WasmKitError) -> Output { + throw WasmKitError("array.new_fixed is not supported yet") + } + mutating func visitArrayGet(typeIndex: UInt32) throws(WasmKitError) -> Output { + throw WasmKitError("array.get is not supported yet") + } + mutating func visitArrayGetS(typeIndex: UInt32) throws(WasmKitError) -> Output { + throw WasmKitError("array.get_s is not supported yet") + } + mutating func visitArrayGetU(typeIndex: UInt32) throws(WasmKitError) -> Output { + throw WasmKitError("array.get_u is not supported yet") + } + mutating func visitArraySet(typeIndex: UInt32) throws(WasmKitError) -> Output { + throw WasmKitError("array.set is not supported yet") + } + mutating func visitArrayLen() throws(WasmKitError) -> Output { + throw WasmKitError("array.len is not supported yet") + } + mutating func visitRefEq() throws(WasmKitError) -> Output { + throw WasmKitError("ref.eq is not supported yet") + } + mutating func visitAnyConvertExtern() throws(WasmKitError) -> Output { + throw WasmKitError("any.convert_extern is not supported yet") + } + mutating func visitExternConvertAny() throws(WasmKitError) -> Output { + throw WasmKitError("extern.convert_any is not supported yet") + } + mutating func visitArrayNewData(typeIndex: UInt32, dataIndex: UInt32) throws(WasmKitError) -> Output { + throw WasmKitError("array.new_data is not supported yet") + } + mutating func visitArrayNewElem(typeIndex: UInt32, elemIndex: UInt32) throws(WasmKitError) -> Output { + throw WasmKitError("array.new_elem is not supported yet") + } + mutating func visitArrayFill(typeIndex: UInt32) throws(WasmKitError) -> Output { + throw WasmKitError("array.fill is not supported yet") + } + mutating func visitArrayCopy(destType: UInt32, srcType: UInt32) throws(WasmKitError) -> Output { + throw WasmKitError("array.copy is not supported yet") + } + mutating func visitArrayInitData(typeIndex: UInt32, dataIndex: UInt32) throws(WasmKitError) -> Output { + throw WasmKitError("array.init_data is not supported yet") + } + mutating func visitArrayInitElem(typeIndex: UInt32, elemIndex: UInt32) throws(WasmKitError) -> Output { + throw WasmKitError("array.init_elem is not supported yet") + } + mutating func visitRefTest(_ refTest: WasmParser.Instruction.RefTest, type: HeapType) throws(WasmKitError) -> Output { + throw WasmKitError("ref.test is not supported yet") + } + mutating func visitRefCast(_ refCast: WasmParser.Instruction.RefCast, type: HeapType) throws(WasmKitError) -> Output { + throw WasmKitError("ref.cast is not supported yet") + } + mutating func visitBrOnCast(relativeDepth: UInt32, castFrom: ReferenceType, castTo: ReferenceType) throws(WasmKitError) -> Output { + throw WasmKitError("br_on_cast is not supported yet") + } + mutating func visitBrOnCastFail(relativeDepth: UInt32, castFrom: ReferenceType, castTo: ReferenceType) throws(WasmKitError) -> Output { + throw WasmKitError("br_on_cast_fail is not supported yet") + } + private mutating func visitUnary(_ operand: ValueType, _ instruction: @escaping (Instruction.UnaryOperand) -> Instruction) throws(WasmKitError) { try popPushEmit(operand, operand) { value, result in return instruction(Instruction.UnaryOperand(result: LVReg(result), input: LVReg(value))) diff --git a/Sources/WasmParser/BinaryInstructionDecoder.swift b/Sources/WasmParser/BinaryInstructionDecoder.swift index 7a07704f9..b44d18bab 100644 --- a/Sources/WasmParser/BinaryInstructionDecoder.swift +++ b/Sources/WasmParser/BinaryInstructionDecoder.swift @@ -214,6 +214,52 @@ protocol BinaryInstructionDecoder { @inlinable mutating func visitSimdLane(_: Instruction.SimdLane) throws(WasmParserError) -> UInt8 /// Decode `simdMemLane` category immediates @inlinable mutating func visitSimdMemLane(_: Instruction.SimdMemLane) throws(WasmParserError) -> (memarg: MemArg, lane: UInt8) + /// Decode `struct.new` immediates + @inlinable mutating func visitStructNew() throws(WasmParserError) -> UInt32 + /// Decode `struct.new_default` immediates + @inlinable mutating func visitStructNewDefault() throws(WasmParserError) -> UInt32 + /// Decode `struct.get` immediates + @inlinable mutating func visitStructGet() throws(WasmParserError) -> (typeIndex: UInt32, fieldIndex: UInt32) + /// Decode `struct.get_s` immediates + @inlinable mutating func visitStructGetS() throws(WasmParserError) -> (typeIndex: UInt32, fieldIndex: UInt32) + /// Decode `struct.get_u` immediates + @inlinable mutating func visitStructGetU() throws(WasmParserError) -> (typeIndex: UInt32, fieldIndex: UInt32) + /// Decode `struct.set` immediates + @inlinable mutating func visitStructSet() throws(WasmParserError) -> (typeIndex: UInt32, fieldIndex: UInt32) + /// Decode `array.new` immediates + @inlinable mutating func visitArrayNew() throws(WasmParserError) -> UInt32 + /// Decode `array.new_default` immediates + @inlinable mutating func visitArrayNewDefault() throws(WasmParserError) -> UInt32 + /// Decode `array.new_fixed` immediates + @inlinable mutating func visitArrayNewFixed() throws(WasmParserError) -> (typeIndex: UInt32, size: UInt32) + /// Decode `array.get` immediates + @inlinable mutating func visitArrayGet() throws(WasmParserError) -> UInt32 + /// Decode `array.get_s` immediates + @inlinable mutating func visitArrayGetS() throws(WasmParserError) -> UInt32 + /// Decode `array.get_u` immediates + @inlinable mutating func visitArrayGetU() throws(WasmParserError) -> UInt32 + /// Decode `array.set` immediates + @inlinable mutating func visitArraySet() throws(WasmParserError) -> UInt32 + /// Decode `array.new_data` immediates + @inlinable mutating func visitArrayNewData() throws(WasmParserError) -> (typeIndex: UInt32, dataIndex: UInt32) + /// Decode `array.new_elem` immediates + @inlinable mutating func visitArrayNewElem() throws(WasmParserError) -> (typeIndex: UInt32, elemIndex: UInt32) + /// Decode `array.fill` immediates + @inlinable mutating func visitArrayFill() throws(WasmParserError) -> UInt32 + /// Decode `array.copy` immediates + @inlinable mutating func visitArrayCopy() throws(WasmParserError) -> (destType: UInt32, srcType: UInt32) + /// Decode `array.init_data` immediates + @inlinable mutating func visitArrayInitData() throws(WasmParserError) -> (typeIndex: UInt32, dataIndex: UInt32) + /// Decode `array.init_elem` immediates + @inlinable mutating func visitArrayInitElem() throws(WasmParserError) -> (typeIndex: UInt32, elemIndex: UInt32) + /// Decode `refTest` category immediates + @inlinable mutating func visitRefTest(_: Instruction.RefTest) throws(WasmParserError) -> HeapType + /// Decode `refCast` category immediates + @inlinable mutating func visitRefCast(_: Instruction.RefCast) throws(WasmParserError) -> HeapType + /// Decode `br_on_cast` immediates + @inlinable mutating func visitBrOnCast() throws(WasmParserError) -> (relativeDepth: UInt32, castFrom: ReferenceType, castTo: ReferenceType) + /// Decode `br_on_cast_fail` immediates + @inlinable mutating func visitBrOnCastFail() throws(WasmParserError) -> (relativeDepth: UInt32, castFrom: ReferenceType, castTo: ReferenceType) } @inlinable @@ -655,6 +701,8 @@ func parseBinaryInstruction( case 0xD2: let (functionIndex) = try decoder.visitRefFunc() return .refFunc(functionIndex: functionIndex) + case 0xD3: + return .refEq case 0xD4: return .refAsNonNull case 0xD5: @@ -663,6 +711,100 @@ func parseBinaryInstruction( case 0xD6: let (relativeDepth) = try decoder.visitBrOnNonNull() return .brOnNonNull(relativeDepth: relativeDepth) + case 0xFB: + + let opcode1 = try decoder.claimNextByte() + switch opcode1 { + case 0x00: + let (typeIndex) = try decoder.visitStructNew() + return .structNew(typeIndex: typeIndex) + case 0x01: + let (typeIndex) = try decoder.visitStructNewDefault() + return .structNewDefault(typeIndex: typeIndex) + case 0x02: + let (typeIndex, fieldIndex) = try decoder.visitStructGet() + return .structGet(typeIndex: typeIndex, fieldIndex: fieldIndex) + case 0x03: + let (typeIndex, fieldIndex) = try decoder.visitStructGetS() + return .structGetS(typeIndex: typeIndex, fieldIndex: fieldIndex) + case 0x04: + let (typeIndex, fieldIndex) = try decoder.visitStructGetU() + return .structGetU(typeIndex: typeIndex, fieldIndex: fieldIndex) + case 0x05: + let (typeIndex, fieldIndex) = try decoder.visitStructSet() + return .structSet(typeIndex: typeIndex, fieldIndex: fieldIndex) + case 0x06: + let (typeIndex) = try decoder.visitArrayNew() + return .arrayNew(typeIndex: typeIndex) + case 0x07: + let (typeIndex) = try decoder.visitArrayNewDefault() + return .arrayNewDefault(typeIndex: typeIndex) + case 0x08: + let (typeIndex, size) = try decoder.visitArrayNewFixed() + return .arrayNewFixed(typeIndex: typeIndex, size: size) + case 0x09: + let (typeIndex, dataIndex) = try decoder.visitArrayNewData() + return .arrayNewData(typeIndex: typeIndex, dataIndex: dataIndex) + case 0x0A: + let (typeIndex, elemIndex) = try decoder.visitArrayNewElem() + return .arrayNewElem(typeIndex: typeIndex, elemIndex: elemIndex) + case 0x0B: + let (typeIndex) = try decoder.visitArrayGet() + return .arrayGet(typeIndex: typeIndex) + case 0x0C: + let (typeIndex) = try decoder.visitArrayGetS() + return .arrayGetS(typeIndex: typeIndex) + case 0x0D: + let (typeIndex) = try decoder.visitArrayGetU() + return .arrayGetU(typeIndex: typeIndex) + case 0x0E: + let (typeIndex) = try decoder.visitArraySet() + return .arraySet(typeIndex: typeIndex) + case 0x0F: + return .arrayLen + case 0x10: + let (typeIndex) = try decoder.visitArrayFill() + return .arrayFill(typeIndex: typeIndex) + case 0x11: + let (destType, srcType) = try decoder.visitArrayCopy() + return .arrayCopy(destType: destType, srcType: srcType) + case 0x12: + let (typeIndex, dataIndex) = try decoder.visitArrayInitData() + return .arrayInitData(typeIndex: typeIndex, dataIndex: dataIndex) + case 0x13: + let (typeIndex, elemIndex) = try decoder.visitArrayInitElem() + return .arrayInitElem(typeIndex: typeIndex, elemIndex: elemIndex) + case 0x14: + let (type) = try decoder.visitRefTest(.refTest) + return .refTest(.refTest, type: type) + case 0x15: + let (type) = try decoder.visitRefTest(.refTestNull) + return .refTest(.refTestNull, type: type) + case 0x16: + let (type) = try decoder.visitRefCast(.refCast) + return .refCast(.refCast, type: type) + case 0x17: + let (type) = try decoder.visitRefCast(.refCastNull) + return .refCast(.refCastNull, type: type) + case 0x18: + let (relativeDepth, castFrom, castTo) = try decoder.visitBrOnCast() + return .brOnCast(relativeDepth: relativeDepth, castFrom: castFrom, castTo: castTo) + case 0x19: + let (relativeDepth, castFrom, castTo) = try decoder.visitBrOnCastFail() + return .brOnCastFail(relativeDepth: relativeDepth, castFrom: castFrom, castTo: castTo) + case 0x1A: + return .anyConvertExtern + case 0x1B: + return .externConvertAny + case 0x1C: + return .refI31 + case 0x1D: + return .i31GetS + case 0x1E: + return .i31GetU + default: + try decoder.throwUnknown([opcode0, opcode1]) + } case 0xFC: let opcode1 = try decoder.claimNextByte() diff --git a/Sources/WasmParser/InstructionVisitor.swift b/Sources/WasmParser/InstructionVisitor.swift index 70d0d4022..1c7f97ddc 100644 --- a/Sources/WasmParser/InstructionVisitor.swift +++ b/Sources/WasmParser/InstructionVisitor.swift @@ -448,6 +448,14 @@ public enum Instruction: Equatable, Sendable { case v128Store32Lane case v128Store64Lane } + public enum RefTest: Equatable, Sendable { + case refTest + case refTestNull + } + public enum RefCast: Equatable, Sendable { + case refCast + case refCastNull + } case `unreachable` case `nop` case `block`(blockType: BlockType) @@ -566,6 +574,36 @@ public enum Instruction: Equatable, Sendable { case `simd`(Instruction.Simd) case `simdLane`(Instruction.SimdLane, lane: UInt8) case `simdMemLane`(Instruction.SimdMemLane, memarg: MemArg, lane: UInt8) + case `refI31` + case `i31GetS` + case `i31GetU` + case `structNew`(typeIndex: UInt32) + case `structNewDefault`(typeIndex: UInt32) + case `structGet`(typeIndex: UInt32, fieldIndex: UInt32) + case `structGetS`(typeIndex: UInt32, fieldIndex: UInt32) + case `structGetU`(typeIndex: UInt32, fieldIndex: UInt32) + case `structSet`(typeIndex: UInt32, fieldIndex: UInt32) + case `arrayNew`(typeIndex: UInt32) + case `arrayNewDefault`(typeIndex: UInt32) + case `arrayNewFixed`(typeIndex: UInt32, size: UInt32) + case `arrayGet`(typeIndex: UInt32) + case `arrayGetS`(typeIndex: UInt32) + case `arrayGetU`(typeIndex: UInt32) + case `arraySet`(typeIndex: UInt32) + case `arrayLen` + case `refEq` + case `anyConvertExtern` + case `externConvertAny` + case `arrayNewData`(typeIndex: UInt32, dataIndex: UInt32) + case `arrayNewElem`(typeIndex: UInt32, elemIndex: UInt32) + case `arrayFill`(typeIndex: UInt32) + case `arrayCopy`(destType: UInt32, srcType: UInt32) + case `arrayInitData`(typeIndex: UInt32, dataIndex: UInt32) + case `arrayInitElem`(typeIndex: UInt32, elemIndex: UInt32) + case `refTest`(Instruction.RefTest, type: HeapType) + case `refCast`(Instruction.RefCast, type: HeapType) + case `brOnCast`(relativeDepth: UInt32, castFrom: ReferenceType, castTo: ReferenceType) + case `brOnCastFail`(relativeDepth: UInt32, castFrom: ReferenceType, castTo: ReferenceType) } /// A visitor that visits all instructions by a single visit method. @@ -693,6 +731,36 @@ extension AnyInstructionVisitor { public mutating func visitSimd(_ simd: Instruction.Simd) throws(VisitorError) { return try self.visit(.simd(simd)) } public mutating func visitSimdLane(_ simdLane: Instruction.SimdLane, lane: UInt8) throws(VisitorError) { return try self.visit(.simdLane(simdLane, lane: lane)) } public mutating func visitSimdMemLane(_ simdMemLane: Instruction.SimdMemLane, memarg: MemArg, lane: UInt8) throws(VisitorError) { return try self.visit(.simdMemLane(simdMemLane, memarg: memarg, lane: lane)) } + public mutating func visitRefI31() throws(VisitorError) { return try self.visit(.refI31) } + public mutating func visitI31GetS() throws(VisitorError) { return try self.visit(.i31GetS) } + public mutating func visitI31GetU() throws(VisitorError) { return try self.visit(.i31GetU) } + public mutating func visitStructNew(typeIndex: UInt32) throws(VisitorError) { return try self.visit(.structNew(typeIndex: typeIndex)) } + public mutating func visitStructNewDefault(typeIndex: UInt32) throws(VisitorError) { return try self.visit(.structNewDefault(typeIndex: typeIndex)) } + public mutating func visitStructGet(typeIndex: UInt32, fieldIndex: UInt32) throws(VisitorError) { return try self.visit(.structGet(typeIndex: typeIndex, fieldIndex: fieldIndex)) } + public mutating func visitStructGetS(typeIndex: UInt32, fieldIndex: UInt32) throws(VisitorError) { return try self.visit(.structGetS(typeIndex: typeIndex, fieldIndex: fieldIndex)) } + public mutating func visitStructGetU(typeIndex: UInt32, fieldIndex: UInt32) throws(VisitorError) { return try self.visit(.structGetU(typeIndex: typeIndex, fieldIndex: fieldIndex)) } + public mutating func visitStructSet(typeIndex: UInt32, fieldIndex: UInt32) throws(VisitorError) { return try self.visit(.structSet(typeIndex: typeIndex, fieldIndex: fieldIndex)) } + public mutating func visitArrayNew(typeIndex: UInt32) throws(VisitorError) { return try self.visit(.arrayNew(typeIndex: typeIndex)) } + public mutating func visitArrayNewDefault(typeIndex: UInt32) throws(VisitorError) { return try self.visit(.arrayNewDefault(typeIndex: typeIndex)) } + public mutating func visitArrayNewFixed(typeIndex: UInt32, size: UInt32) throws(VisitorError) { return try self.visit(.arrayNewFixed(typeIndex: typeIndex, size: size)) } + public mutating func visitArrayGet(typeIndex: UInt32) throws(VisitorError) { return try self.visit(.arrayGet(typeIndex: typeIndex)) } + public mutating func visitArrayGetS(typeIndex: UInt32) throws(VisitorError) { return try self.visit(.arrayGetS(typeIndex: typeIndex)) } + public mutating func visitArrayGetU(typeIndex: UInt32) throws(VisitorError) { return try self.visit(.arrayGetU(typeIndex: typeIndex)) } + public mutating func visitArraySet(typeIndex: UInt32) throws(VisitorError) { return try self.visit(.arraySet(typeIndex: typeIndex)) } + public mutating func visitArrayLen() throws(VisitorError) { return try self.visit(.arrayLen) } + public mutating func visitRefEq() throws(VisitorError) { return try self.visit(.refEq) } + public mutating func visitAnyConvertExtern() throws(VisitorError) { return try self.visit(.anyConvertExtern) } + public mutating func visitExternConvertAny() throws(VisitorError) { return try self.visit(.externConvertAny) } + public mutating func visitArrayNewData(typeIndex: UInt32, dataIndex: UInt32) throws(VisitorError) { return try self.visit(.arrayNewData(typeIndex: typeIndex, dataIndex: dataIndex)) } + public mutating func visitArrayNewElem(typeIndex: UInt32, elemIndex: UInt32) throws(VisitorError) { return try self.visit(.arrayNewElem(typeIndex: typeIndex, elemIndex: elemIndex)) } + public mutating func visitArrayFill(typeIndex: UInt32) throws(VisitorError) { return try self.visit(.arrayFill(typeIndex: typeIndex)) } + public mutating func visitArrayCopy(destType: UInt32, srcType: UInt32) throws(VisitorError) { return try self.visit(.arrayCopy(destType: destType, srcType: srcType)) } + public mutating func visitArrayInitData(typeIndex: UInt32, dataIndex: UInt32) throws(VisitorError) { return try self.visit(.arrayInitData(typeIndex: typeIndex, dataIndex: dataIndex)) } + public mutating func visitArrayInitElem(typeIndex: UInt32, elemIndex: UInt32) throws(VisitorError) { return try self.visit(.arrayInitElem(typeIndex: typeIndex, elemIndex: elemIndex)) } + public mutating func visitRefTest(_ refTest: Instruction.RefTest, type: HeapType) throws(VisitorError) { return try self.visit(.refTest(refTest, type: type)) } + public mutating func visitRefCast(_ refCast: Instruction.RefCast, type: HeapType) throws(VisitorError) { return try self.visit(.refCast(refCast, type: type)) } + public mutating func visitBrOnCast(relativeDepth: UInt32, castFrom: ReferenceType, castTo: ReferenceType) throws(VisitorError) { return try self.visit(.brOnCast(relativeDepth: relativeDepth, castFrom: castFrom, castTo: castTo)) } + public mutating func visitBrOnCastFail(relativeDepth: UInt32, castFrom: ReferenceType, castTo: ReferenceType) throws(VisitorError) { return try self.visit(.brOnCastFail(relativeDepth: relativeDepth, castFrom: castFrom, castTo: castTo)) } } /// A visitor for WebAssembly instructions. @@ -941,6 +1009,66 @@ public protocol InstructionVisitor: ~Copyable { mutating func visitSimdLane(_: Instruction.SimdLane, lane: UInt8) throws(VisitorError) /// Visiting `simdMemLane` category instruction. mutating func visitSimdMemLane(_: Instruction.SimdMemLane, memarg: MemArg, lane: UInt8) throws(VisitorError) + /// Visiting `ref.i31` instruction. + mutating func visitRefI31() throws(VisitorError) + /// Visiting `i31.get_s` instruction. + mutating func visitI31GetS() throws(VisitorError) + /// Visiting `i31.get_u` instruction. + mutating func visitI31GetU() throws(VisitorError) + /// Visiting `struct.new` instruction. + mutating func visitStructNew(typeIndex: UInt32) throws(VisitorError) + /// Visiting `struct.new_default` instruction. + mutating func visitStructNewDefault(typeIndex: UInt32) throws(VisitorError) + /// Visiting `struct.get` instruction. + mutating func visitStructGet(typeIndex: UInt32, fieldIndex: UInt32) throws(VisitorError) + /// Visiting `struct.get_s` instruction. + mutating func visitStructGetS(typeIndex: UInt32, fieldIndex: UInt32) throws(VisitorError) + /// Visiting `struct.get_u` instruction. + mutating func visitStructGetU(typeIndex: UInt32, fieldIndex: UInt32) throws(VisitorError) + /// Visiting `struct.set` instruction. + mutating func visitStructSet(typeIndex: UInt32, fieldIndex: UInt32) throws(VisitorError) + /// Visiting `array.new` instruction. + mutating func visitArrayNew(typeIndex: UInt32) throws(VisitorError) + /// Visiting `array.new_default` instruction. + mutating func visitArrayNewDefault(typeIndex: UInt32) throws(VisitorError) + /// Visiting `array.new_fixed` instruction. + mutating func visitArrayNewFixed(typeIndex: UInt32, size: UInt32) throws(VisitorError) + /// Visiting `array.get` instruction. + mutating func visitArrayGet(typeIndex: UInt32) throws(VisitorError) + /// Visiting `array.get_s` instruction. + mutating func visitArrayGetS(typeIndex: UInt32) throws(VisitorError) + /// Visiting `array.get_u` instruction. + mutating func visitArrayGetU(typeIndex: UInt32) throws(VisitorError) + /// Visiting `array.set` instruction. + mutating func visitArraySet(typeIndex: UInt32) throws(VisitorError) + /// Visiting `array.len` instruction. + mutating func visitArrayLen() throws(VisitorError) + /// Visiting `ref.eq` instruction. + mutating func visitRefEq() throws(VisitorError) + /// Visiting `any.convert_extern` instruction. + mutating func visitAnyConvertExtern() throws(VisitorError) + /// Visiting `extern.convert_any` instruction. + mutating func visitExternConvertAny() throws(VisitorError) + /// Visiting `array.new_data` instruction. + mutating func visitArrayNewData(typeIndex: UInt32, dataIndex: UInt32) throws(VisitorError) + /// Visiting `array.new_elem` instruction. + mutating func visitArrayNewElem(typeIndex: UInt32, elemIndex: UInt32) throws(VisitorError) + /// Visiting `array.fill` instruction. + mutating func visitArrayFill(typeIndex: UInt32) throws(VisitorError) + /// Visiting `array.copy` instruction. + mutating func visitArrayCopy(destType: UInt32, srcType: UInt32) throws(VisitorError) + /// Visiting `array.init_data` instruction. + mutating func visitArrayInitData(typeIndex: UInt32, dataIndex: UInt32) throws(VisitorError) + /// Visiting `array.init_elem` instruction. + mutating func visitArrayInitElem(typeIndex: UInt32, elemIndex: UInt32) throws(VisitorError) + /// Visiting `refTest` category instruction. + mutating func visitRefTest(_: Instruction.RefTest, type: HeapType) throws(VisitorError) + /// Visiting `refCast` category instruction. + mutating func visitRefCast(_: Instruction.RefCast, type: HeapType) throws(VisitorError) + /// Visiting `br_on_cast` instruction. + mutating func visitBrOnCast(relativeDepth: UInt32, castFrom: ReferenceType, castTo: ReferenceType) throws(VisitorError) + /// Visiting `br_on_cast_fail` instruction. + mutating func visitBrOnCastFail(relativeDepth: UInt32, castFrom: ReferenceType, castTo: ReferenceType) throws(VisitorError) /// Returns: `true` if the parser should silently proceed parsing. mutating func visitUnknown(_ opcode: [UInt8]) throws(VisitorError) -> Bool } @@ -1073,6 +1201,36 @@ extension InstructionVisitor where Self: ~Copyable { case let .simd(simd): return try visitSimd(simd) case let .simdLane(simdLane, lane): return try visitSimdLane(simdLane, lane: lane) case let .simdMemLane(simdMemLane, memarg, lane): return try visitSimdMemLane(simdMemLane, memarg: memarg, lane: lane) + case .refI31: return try visitRefI31() + case .i31GetS: return try visitI31GetS() + case .i31GetU: return try visitI31GetU() + case let .structNew(typeIndex): return try visitStructNew(typeIndex: typeIndex) + case let .structNewDefault(typeIndex): return try visitStructNewDefault(typeIndex: typeIndex) + case let .structGet(typeIndex, fieldIndex): return try visitStructGet(typeIndex: typeIndex, fieldIndex: fieldIndex) + case let .structGetS(typeIndex, fieldIndex): return try visitStructGetS(typeIndex: typeIndex, fieldIndex: fieldIndex) + case let .structGetU(typeIndex, fieldIndex): return try visitStructGetU(typeIndex: typeIndex, fieldIndex: fieldIndex) + case let .structSet(typeIndex, fieldIndex): return try visitStructSet(typeIndex: typeIndex, fieldIndex: fieldIndex) + case let .arrayNew(typeIndex): return try visitArrayNew(typeIndex: typeIndex) + case let .arrayNewDefault(typeIndex): return try visitArrayNewDefault(typeIndex: typeIndex) + case let .arrayNewFixed(typeIndex, size): return try visitArrayNewFixed(typeIndex: typeIndex, size: size) + case let .arrayGet(typeIndex): return try visitArrayGet(typeIndex: typeIndex) + case let .arrayGetS(typeIndex): return try visitArrayGetS(typeIndex: typeIndex) + case let .arrayGetU(typeIndex): return try visitArrayGetU(typeIndex: typeIndex) + case let .arraySet(typeIndex): return try visitArraySet(typeIndex: typeIndex) + case .arrayLen: return try visitArrayLen() + case .refEq: return try visitRefEq() + case .anyConvertExtern: return try visitAnyConvertExtern() + case .externConvertAny: return try visitExternConvertAny() + case let .arrayNewData(typeIndex, dataIndex): return try visitArrayNewData(typeIndex: typeIndex, dataIndex: dataIndex) + case let .arrayNewElem(typeIndex, elemIndex): return try visitArrayNewElem(typeIndex: typeIndex, elemIndex: elemIndex) + case let .arrayFill(typeIndex): return try visitArrayFill(typeIndex: typeIndex) + case let .arrayCopy(destType, srcType): return try visitArrayCopy(destType: destType, srcType: srcType) + case let .arrayInitData(typeIndex, dataIndex): return try visitArrayInitData(typeIndex: typeIndex, dataIndex: dataIndex) + case let .arrayInitElem(typeIndex, elemIndex): return try visitArrayInitElem(typeIndex: typeIndex, elemIndex: elemIndex) + case let .refTest(refTest, type): return try visitRefTest(refTest, type: type) + case let .refCast(refCast, type): return try visitRefCast(refCast, type: type) + case let .brOnCast(relativeDepth, castFrom, castTo): return try visitBrOnCast(relativeDepth: relativeDepth, castFrom: castFrom, castTo: castTo) + case let .brOnCastFail(relativeDepth, castFrom, castTo): return try visitBrOnCastFail(relativeDepth: relativeDepth, castFrom: castFrom, castTo: castTo) } } } @@ -1197,6 +1355,36 @@ extension InstructionVisitor where Self: ~Copyable { public mutating func visitSimd(_ simd: Instruction.Simd) throws(VisitorError) {} public mutating func visitSimdLane(_ simdLane: Instruction.SimdLane, lane: UInt8) throws(VisitorError) {} public mutating func visitSimdMemLane(_ simdMemLane: Instruction.SimdMemLane, memarg: MemArg, lane: UInt8) throws(VisitorError) {} + public mutating func visitRefI31() throws(VisitorError) {} + public mutating func visitI31GetS() throws(VisitorError) {} + public mutating func visitI31GetU() throws(VisitorError) {} + public mutating func visitStructNew(typeIndex: UInt32) throws(VisitorError) {} + public mutating func visitStructNewDefault(typeIndex: UInt32) throws(VisitorError) {} + public mutating func visitStructGet(typeIndex: UInt32, fieldIndex: UInt32) throws(VisitorError) {} + public mutating func visitStructGetS(typeIndex: UInt32, fieldIndex: UInt32) throws(VisitorError) {} + public mutating func visitStructGetU(typeIndex: UInt32, fieldIndex: UInt32) throws(VisitorError) {} + public mutating func visitStructSet(typeIndex: UInt32, fieldIndex: UInt32) throws(VisitorError) {} + public mutating func visitArrayNew(typeIndex: UInt32) throws(VisitorError) {} + public mutating func visitArrayNewDefault(typeIndex: UInt32) throws(VisitorError) {} + public mutating func visitArrayNewFixed(typeIndex: UInt32, size: UInt32) throws(VisitorError) {} + public mutating func visitArrayGet(typeIndex: UInt32) throws(VisitorError) {} + public mutating func visitArrayGetS(typeIndex: UInt32) throws(VisitorError) {} + public mutating func visitArrayGetU(typeIndex: UInt32) throws(VisitorError) {} + public mutating func visitArraySet(typeIndex: UInt32) throws(VisitorError) {} + public mutating func visitArrayLen() throws(VisitorError) {} + public mutating func visitRefEq() throws(VisitorError) {} + public mutating func visitAnyConvertExtern() throws(VisitorError) {} + public mutating func visitExternConvertAny() throws(VisitorError) {} + public mutating func visitArrayNewData(typeIndex: UInt32, dataIndex: UInt32) throws(VisitorError) {} + public mutating func visitArrayNewElem(typeIndex: UInt32, elemIndex: UInt32) throws(VisitorError) {} + public mutating func visitArrayFill(typeIndex: UInt32) throws(VisitorError) {} + public mutating func visitArrayCopy(destType: UInt32, srcType: UInt32) throws(VisitorError) {} + public mutating func visitArrayInitData(typeIndex: UInt32, dataIndex: UInt32) throws(VisitorError) {} + public mutating func visitArrayInitElem(typeIndex: UInt32, elemIndex: UInt32) throws(VisitorError) {} + public mutating func visitRefTest(_ refTest: Instruction.RefTest, type: HeapType) throws(VisitorError) {} + public mutating func visitRefCast(_ refCast: Instruction.RefCast, type: HeapType) throws(VisitorError) {} + public mutating func visitBrOnCast(relativeDepth: UInt32, castFrom: ReferenceType, castTo: ReferenceType) throws(VisitorError) {} + public mutating func visitBrOnCastFail(relativeDepth: UInt32, castFrom: ReferenceType, castTo: ReferenceType) throws(VisitorError) {} public mutating func visitUnknown(_ opcode: [UInt8]) throws(VisitorError) -> Bool { false } } diff --git a/Sources/WasmParser/WasmParser.swift b/Sources/WasmParser/WasmParser.swift index 936deb05a..f051c15e6 100644 --- a/Sources/WasmParser/WasmParser.swift +++ b/Sources/WasmParser/WasmParser.swift @@ -827,6 +827,88 @@ extension Parser: BinaryInstructionDecoder { @inlinable mutating func visitBrOnNull() throws(WasmParserError) -> UInt32 { try parseUnsigned() } @inlinable mutating func visitBrOnNonNull() throws(WasmParserError) -> UInt32 { try parseUnsigned() } + @inlinable mutating func visitStructNew() throws(WasmParserError) -> UInt32 { try parseUnsigned() } + @inlinable mutating func visitStructNewDefault() throws(WasmParserError) -> UInt32 { try parseUnsigned() } + @inlinable mutating func visitStructGet() throws(WasmParserError) -> (typeIndex: UInt32, fieldIndex: UInt32) { + let typeIndex: UInt32 = try parseUnsigned() + let fieldIndex: UInt32 = try parseUnsigned() + return (typeIndex, fieldIndex) + } + @inlinable mutating func visitStructGetS() throws(WasmParserError) -> (typeIndex: UInt32, fieldIndex: UInt32) { + let typeIndex: UInt32 = try parseUnsigned() + let fieldIndex: UInt32 = try parseUnsigned() + return (typeIndex, fieldIndex) + } + @inlinable mutating func visitStructGetU() throws(WasmParserError) -> (typeIndex: UInt32, fieldIndex: UInt32) { + let typeIndex: UInt32 = try parseUnsigned() + let fieldIndex: UInt32 = try parseUnsigned() + return (typeIndex, fieldIndex) + } + @inlinable mutating func visitStructSet() throws(WasmParserError) -> (typeIndex: UInt32, fieldIndex: UInt32) { + let typeIndex: UInt32 = try parseUnsigned() + let fieldIndex: UInt32 = try parseUnsigned() + return (typeIndex, fieldIndex) + } + @inlinable mutating func visitArrayNew() throws(WasmParserError) -> UInt32 { try parseUnsigned() } + @inlinable mutating func visitArrayNewDefault() throws(WasmParserError) -> UInt32 { try parseUnsigned() } + @inlinable mutating func visitArrayNewFixed() throws(WasmParserError) -> (typeIndex: UInt32, size: UInt32) { + let typeIndex: UInt32 = try parseUnsigned() + let size: UInt32 = try parseUnsigned() + return (typeIndex, size) + } + @inlinable mutating func visitArrayGet() throws(WasmParserError) -> UInt32 { try parseUnsigned() } + @inlinable mutating func visitArrayGetS() throws(WasmParserError) -> UInt32 { try parseUnsigned() } + @inlinable mutating func visitArrayGetU() throws(WasmParserError) -> UInt32 { try parseUnsigned() } + @inlinable mutating func visitArraySet() throws(WasmParserError) -> UInt32 { try parseUnsigned() } + @inlinable mutating func visitArrayNewData() throws(WasmParserError) -> (typeIndex: UInt32, dataIndex: UInt32) { + let typeIndex: UInt32 = try parseUnsigned() + let dataIndex: UInt32 = try parseUnsigned() + return (typeIndex, dataIndex) + } + @inlinable mutating func visitArrayNewElem() throws(WasmParserError) -> (typeIndex: UInt32, elemIndex: UInt32) { + let typeIndex: UInt32 = try parseUnsigned() + let elemIndex: UInt32 = try parseUnsigned() + return (typeIndex, elemIndex) + } + @inlinable mutating func visitArrayFill() throws(WasmParserError) -> UInt32 { try parseUnsigned() } + @inlinable mutating func visitArrayCopy() throws(WasmParserError) -> (destType: UInt32, srcType: UInt32) { + let destType: UInt32 = try parseUnsigned() + let srcType: UInt32 = try parseUnsigned() + return (destType, srcType) + } + @inlinable mutating func visitArrayInitData() throws(WasmParserError) -> (typeIndex: UInt32, dataIndex: UInt32) { + let typeIndex: UInt32 = try parseUnsigned() + let dataIndex: UInt32 = try parseUnsigned() + return (typeIndex, dataIndex) + } + @inlinable mutating func visitArrayInitElem() throws(WasmParserError) -> (typeIndex: UInt32, elemIndex: UInt32) { + let typeIndex: UInt32 = try parseUnsigned() + let elemIndex: UInt32 = try parseUnsigned() + return (typeIndex, elemIndex) + } + @inlinable mutating func visitRefTest(_: Instruction.RefTest) throws(WasmParserError) -> HeapType { + try stream.parseHeapType(features: features) + } + @inlinable mutating func visitRefCast(_: Instruction.RefCast) throws(WasmParserError) -> HeapType { + try stream.parseHeapType(features: features) + } + @inlinable mutating func visitBrOnCast() throws(WasmParserError) -> (relativeDepth: UInt32, castFrom: ReferenceType, castTo: ReferenceType) { + try parseBrOnCastImmediates() + } + @inlinable mutating func visitBrOnCastFail() throws(WasmParserError) -> (relativeDepth: UInt32, castFrom: ReferenceType, castTo: ReferenceType) { + try parseBrOnCastImmediates() + } + @inlinable mutating func parseBrOnCastImmediates() throws(WasmParserError) -> (relativeDepth: UInt32, castFrom: ReferenceType, castTo: ReferenceType) { + let flags = try stream.consumeAny() + guard flags <= 0b11 else { + throw makeError(.invalidCastFlags(flags)) + } + let relativeDepth: UInt32 = try parseUnsigned() + let castFrom = ReferenceType(isNullable: flags & 0b01 != 0, heapType: try stream.parseHeapType(features: features)) + let castTo = ReferenceType(isNullable: flags & 0b10 != 0, heapType: try stream.parseHeapType(features: features)) + return (relativeDepth, castFrom, castTo) + } + @inlinable mutating func visitRefFunc() throws(WasmParserError) -> UInt32 { try parseUnsigned() } @inlinable mutating func visitMemoryInit() throws(WasmParserError) -> (dataIndex: UInt32, memory: UInt32) { let dataIndex: DataIndex = try parseUnsigned() @@ -950,6 +1032,9 @@ extension Parser: BinaryInstructionDecoder { @inline(__always) @inlinable mutating func parseInstruction() throws(WasmParserError) -> Instruction { + if !features.contains(.gc), let opcode = try stream.peek(), opcode == 0xFB || opcode == 0xD3 { + throw makeError(.illegalOpcode([opcode])) + } return try parseBinaryInstruction(decoder: &self) } diff --git a/Sources/WasmParser/WasmParserError.swift b/Sources/WasmParser/WasmParserError.swift index 41174d3c1..912cc115c 100644 --- a/Sources/WasmParser/WasmParserError.swift +++ b/Sources/WasmParser/WasmParserError.swift @@ -208,4 +208,9 @@ extension WasmParserError.Message { static func invalidCatchClauseId(_ id: UInt8) -> Self { Self("Invalid catch clause id: \(id)") } + + @usableFromInline + static func invalidCastFlags(_ flags: UInt8) -> Self { + Self("Invalid cast flags: \(flags)") + } } diff --git a/Tests/WATTests/EncoderTests.swift b/Tests/WATTests/EncoderTests.swift index 46d96c5ae..c08f1eb1d 100644 --- a/Tests/WATTests/EncoderTests.swift +++ b/Tests/WATTests/EncoderTests.swift @@ -504,7 +504,7 @@ struct EncoderTests { + (Spectest.unparsedByWasmTools[wastFile.lastPathComponent] ?? [:]).keys let script = try Self.script(of: wastFile, leavingOut: leftOut, starts: starts) let (json, wasmFiles) = try wast2json(wastContent: Array(script.utf8), wastFileName: wastFile.lastPathComponent) - return json.commands.filter { $0.type == "module" }.map { + return json.commands.filter { ["module", "module_definition"].contains($0.type) }.map { // wasm-tools names a module without its `$`, and writes a quoted module out as text. ReferenceModule( name: $0.name.map { "$" + $0 }, diff --git a/Tests/WATTests/ParserTests.swift b/Tests/WATTests/ParserTests.swift index e836bcbb6..d71d6cf33 100644 --- a/Tests/WATTests/ParserTests.swift +++ b/Tests/WATTests/ParserTests.swift @@ -181,6 +181,12 @@ struct ParserTests { #expect(wat.elementsMap.count == 14) } + @Test(arguments: [#"(invoke "f" (nop))"#, #"(assert_return (invoke "f") (nop))"#]) + func wastValueIsAValueInstruction(script: String) { + let error = #expect(throws: WatParserError.self) { try parseWast(script) } + #expect(error?.message == "unexpected instruction nop in a WAST value") + } + // NOTE: We do the same check as a part of the EncoderTests, so it's // usually redundant and time-wasting to run this test every time. // Keeping it here just for local unit testing purposes. diff --git a/Tests/WATTests/Spectest.swift b/Tests/WATTests/Spectest.swift index 32a78dd9e..fa92af1fa 100644 --- a/Tests/WATTests/Spectest.swift +++ b/Tests/WATTests/Spectest.swift @@ -26,135 +26,20 @@ enum Spectest { "select.wast", "table-sub.wast", "table.wast", "unreached-invalid.wast", "unreached-valid.wast", ] - private static let unparsedGC = "WAT does not parse GC instructions yet" + private static let unparsedByWast2json = "wast2json cannot parse GC types" private static let usesSkippedModule = "Uses a skipped module" /// Directives at the top level of the testsuite that the WAT tests skip, by the line they start /// on. They are also left out of the script that the reference tool encodes. static let skippedDirectives: [String: [Int: String]] = [ - "array.wast": [ - 60: unparsedGC, // Uses `array.new_default`, `array.new`, `array.get`, `array.set`, `array.len` - 97: unparsedGC, // Expects a `(ref.array)` result - 98: unparsedGC, // Expects a `(ref.eq)` result - 106: unparsedGC, // Uses `array.new_fixed`, `array.get`, `array.set`, `array.len` - 142: unparsedGC, // Expects a `(ref.array)` result - 143: unparsedGC, // Expects a `(ref.eq)` result - 151: unparsedGC, // Uses `array.new_data`, `array.get_s`, `array.get_u`, `array.set`, `array.len` - 202: unparsedGC, // Expects a `(ref.array)` result - 203: unparsedGC, // Expects a `(ref.eq)` result - 219: unparsedGC, // Uses `array.new_fixed`, `array.new_elem`, `array.new`, `array.get_u`, `array.get`, `array.set`, `array.len` - 276: unparsedGC, // Expects a `(ref.array)` result - 277: unparsedGC, // Expects a `(ref.eq)` result - 332: unparsedGC, // Uses `array.get`, `array.set` - ], - "array_copy.wast": [ - 54: unparsedGC // Uses `array.new_default`, `array.new_data`, `array.new`, `array.get_u`, `array.copy` - ], - "array_fill.wast": [ - 38: unparsedGC // Uses `array.new_default`, `array.new`, `array.get_u`, `array.fill` - ], - "array_init_data.wast": [ - 31: unparsedGC, // Uses `array.new_default`, `array.new`, `array.get_u`, `array.init_data` - 113: unparsedGC, // Uses `array.new_default`, `array.init_data` - ], - "array_init_elem.wast": [ - 44: unparsedGC, // Uses `array.new_default`, `array.get`, `array.init_elem` - 117: unparsedGC, // Uses `array.new_default`, `array.get`, `array.len`, `array.init_elem`, `ref.eq` - 160: unparsedGC, // Uses `array.new_default`, `array.get`, `array.init_elem`, `ref.eq` - ], - "array_new_data.wast": [ - 1: unparsedGC, // Uses `array.new_data` - 12: unparsedGC, // Expects a `(ref.array)` result - 13: unparsedGC, // Expects a `(ref.array)` result - 14: unparsedGC, // Expects a `(ref.array)` result - 15: unparsedGC, // Expects a `(ref.array)` result - 23: unparsedGC, // Uses `array.new_data` - 89: unparsedGC, // Uses `array.new_data`, `array.get_u` - 105: unparsedGC, // Uses `array.new_data`, `array.get` - 120: unparsedGC, // Uses `array.new_data`, `array.get_u` - ], - "array_new_elem.wast": [ - 3: unparsedGC, // Uses `array.new_elem`, `ref.i31` - 18: unparsedGC, // Expects a `(ref.array)` result - 19: unparsedGC, // Expects a `(ref.array)` result - 20: unparsedGC, // Expects a `(ref.array)` result - 21: unparsedGC, // Expects a `(ref.array)` result - 29: unparsedGC, // Uses `array.new_elem`, `array.get`, `ref.i31`, `i31.get_u` - 51: unparsedGC, // Uses `array.new_elem` - 66: unparsedGC, // Expects a `(ref.array)` result - 67: unparsedGC, // Expects a `(ref.array)` result - 68: unparsedGC, // Expects a `(ref.array)` result - 69: unparsedGC, // Expects a `(ref.array)` result - 77: unparsedGC, // Uses `array.new_elem`, `array.get` - 106: unparsedGC, // Uses `array.new_default`, `array.new_elem`, `array.get`, `ref.eq` - ], - "br_on_cast.wast": [ - 3: unparsedGC, // Uses `struct.new`, `struct.get_s`, `array.new`, `array.get_u`, `array.len`, `br_on_cast`, `any.convert_extern`, `ref.i31`, `i31.get_u` - 104: unparsedGC, // Uses `struct.new_default`, `br_on_cast` - 211: unparsedGC, // Uses `br_on_cast` - ], - "br_on_cast_fail.wast": [ - 3: unparsedGC, // Uses `struct.new`, `struct.get_s`, `array.new`, `array.get_u`, `array.len`, `br_on_cast_fail`, `br_on_cast`, `any.convert_extern`, `ref.i31`, `i31.get_u` - 104: unparsedGC, // Uses `struct.new_default`, `br_on_cast_fail` - 226: unparsedGC, // Uses `br_on_cast_fail` - ], - "extern.wast": [ - 1: unparsedGC, // Uses `struct.new_default`, `array.new_default`, `any.convert_extern`, `extern.convert_any`, `ref.i31` - 39: unparsedGC, // Expects a `(ref.host)` result - 42: unparsedGC, // Expects a `(ref.host)` result - 53: unparsedGC, // Expects a `(ref.i31)` result - 54: unparsedGC, // Expects a `(ref.struct)` result - 55: unparsedGC, // Expects a `(ref.array)` result - 56: unparsedGC, // Expects a `(ref.host)` result - ], - "i31.wast": [ - 1: unparsedGC, // Uses `ref.i31`, `i31.get_s`, `i31.get_u` - 33: unparsedGC, // Expects a `(ref.i31)` result - 61: unparsedGC, // Uses `ref.i31`, `i31.get_u` - 128: unparsedGC, // Uses `ref.i31`, `i31.get_u` - 140: unparsedGC, // Uses `ref.i31`, `i31.get_u` - 150: unparsedGC, // Uses `ref.cast`, `ref.i31`, `i31.get_u` - 168: unparsedGC, // Uses `ref.cast`, `ref.i31`, `i31.get_u` - ], - "ref_cast.wast": [ - 3: unparsedGC, // Uses `struct.new_default`, `array.new_default`, `ref.cast`, `any.convert_extern`, `ref.i31` - 99: unparsedGC, // Uses `struct.new_default`, `ref.cast` - ], - "ref_eq.wast": [ - 1: unparsedGC // Uses `struct.new_default`, `array.new_default`, `ref.i31`, `ref.eq` - ], - "ref_test.wast": [ - 3: unparsedGC, // Uses `struct.new_default`, `array.new_default`, `ref.test`, `any.convert_extern`, `extern.convert_any`, `ref.i31` - 182: unparsedGC, // Uses `struct.new_default`, `ref.test` - ], - "struct.wast": [ - 48: unparsedGC, // Uses `struct.get` - 70: unparsedGC, // Uses `struct.new_default`, `struct.new`, `struct.get`, `struct.set` - 122: unparsedGC, // Expects a `(ref.struct)` result - 145: unparsedGC, // Uses `struct.get`, `struct.set` - 160: unparsedGC, // Uses `struct.new`, `struct.get_s`, `struct.get_u`, `struct.set` - ], "table_init.wast": [ - 2272: unparsedGC, // Uses `array.new_default`, `ref.eq` + 2272: unparsedByWast2json, // `(type $arr (array (mut arrayref)))` 2286: usesSkippedModule, // Invokes the module on line 2272, which wast2json needs left out too ], "table_init64.wast": [ - 2457: unparsedGC, // Uses `array.new_default`, `ref.eq` + 2457: unparsedByWast2json, // `(type $arr (array (mut arrayref)))` 2471: usesSkippedModule, // Invokes the module on line 2457, which wast2json needs left out too ], - "type-subtyping.wast": [ - 283: unparsedGC, // Uses `ref.cast` - 344: unparsedGC, // Uses `ref.cast` - 402: unparsedGC, // Uses `ref.test` - 414: unparsedGC, // Uses `ref.test` - 432: unparsedGC, // Uses `ref.test` - 444: unparsedGC, // Uses `ref.test` - 455: unparsedGC, // Uses `ref.test` - 476: unparsedGC, // Uses `ref.test` - 492: unparsedGC, // Uses `ref.test` - 515: unparsedGC, // Uses `ref.test` - 525: unparsedGC, // Uses `ref.test` - ], ] /// Directives that wasm-tools cannot parse, by file name and the line they start on. They are diff --git a/Tests/WasmKitTests/WASTRunnerTests.swift b/Tests/WasmKitTests/WASTRunnerTests.swift index ecb94f2fe..d1855a42a 100644 --- a/Tests/WasmKitTests/WASTRunnerTests.swift +++ b/Tests/WasmKitTests/WASTRunnerTests.swift @@ -84,6 +84,18 @@ import WasmKit } } + @Test func refusesRefHostArgument() throws { + let script = """ + (module (func (export "f"))) + (invoke "f" (ref.host 1)) + """ + try withScript(script) { script in + let outcome = try SpectestRunner(configuration: EngineConfiguration()) + .evaluate(test: script, reporter: NullSpectestProgressReporter()) + #expect(outcome.failures.map { $0.reason } == ["ref.host is not supported yet"]) + } + } + /// Keeps the script out of `ExtraSuite`, which `SpectestTests` scans and would fail on it. private func withScript(_ text: String, _ body: (TestCase) throws -> Void) throws { let url = FileManager.default.temporaryDirectory diff --git a/Tests/WasmParserTests/GCInstructionDecodingTests.swift b/Tests/WasmParserTests/GCInstructionDecodingTests.swift new file mode 100644 index 000000000..be617b3db --- /dev/null +++ b/Tests/WasmParserTests/GCInstructionDecodingTests.swift @@ -0,0 +1,178 @@ +import Testing +import WasmTypes + +@testable import WasmParser + +@Suite struct GCInstructionDecodingTests { + private static func ref(_ heapType: AbstractHeapType, nullable: Bool) -> ReferenceType { + ReferenceType(isNullable: nullable, heapType: .abstract(heapType)) + } + + private static func ref(_ typeIndex: UInt32, nullable: Bool) -> ReferenceType { + ReferenceType(isNullable: nullable, heapType: .concrete(typeIndex: typeIndex)) + } + + private static func module(_ sections: [(id: UInt8, content: [UInt8])]) -> [UInt8] { + var bytes: [UInt8] = [0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00] + for section in sections { + bytes += [section.id] + unsignedLEB128(section.content.count) + section.content + } + return bytes + } + + private static func instructions(_ code: [UInt8], features: WasmFeatureSet) throws -> [Instruction]? { + let body: [UInt8] = [0x00] + code + [0x0B] + let bytes = module([ + (id: 1, content: [0x01, 0x60, 0x00, 0x00]), + (id: 3, content: [0x01, 0x00]), + (id: 10, content: [0x01] + unsignedLEB128(body.count) + body), + ]) + var parser = Parser(bytes: bytes, features: features) + while let payload = try parser.parseNext() { + guard case .codeSection(let codes) = payload else { continue } + var expression = ExpressionParser(code: codes[0]) + var result: [Instruction] = [] + while let visit = try expression.parse() { + result.append(visit.instruction) + } + return result + } + return nil + } + + private static func errorMessage(_ body: () throws -> Void) -> String? { + do { + try body() + return nil + } catch let error as WasmParserError { + guard case .message(let message) = error.kind else { return error.description } + return message.text + } catch { + return "\(error)" + } + } + + private static func unsignedLEB128(_ value: Int) -> [UInt8] { + var value = UInt(value) + var bytes: [UInt8] = [] + repeat { + var byte = UInt8(value & 0b0111_1111) + value >>= 7 + if value != 0 { + byte |= 0b1000_0000 + } + bytes.append(byte) + } while value != 0 + return bytes + } + + @Test func decodesEveryInstruction() throws { + let code: [UInt8] = [ + 0xFB, 0x1C, // ref.i31 + 0xFB, 0x1D, // i31.get_s + 0xFB, 0x1E, // i31.get_u + 0xFB, 0x00, 0x03, // struct.new 3 + 0xFB, 0x01, 0x03, // struct.new_default 3 + 0xFB, 0x02, 0x03, 0x05, // struct.get 3 5 + 0xFB, 0x03, 0x03, 0x05, // struct.get_s 3 5 + 0xFB, 0x04, 0x03, 0x05, // struct.get_u 3 5 + 0xFB, 0x05, 0x03, 0x05, // struct.set 3 5 + 0xFB, 0x06, 0x03, // array.new 3 + 0xFB, 0x07, 0x03, // array.new_default 3 + 0xFB, 0x08, 0x03, 0x05, // array.new_fixed 3 5 + 0xFB, 0x0B, 0x03, // array.get 3 + 0xFB, 0x0C, 0x03, // array.get_s 3 + 0xFB, 0x0D, 0x03, // array.get_u 3 + 0xFB, 0x0E, 0x03, // array.set 3 + 0xFB, 0x0F, // array.len + 0xD3, // ref.eq + 0xFB, 0x1A, // any.convert_extern + 0xFB, 0x1B, // extern.convert_any + 0xFB, 0x09, 0x03, 0x05, // array.new_data 3 5 + 0xFB, 0x0A, 0x03, 0x05, // array.new_elem 3 5 + 0xFB, 0x10, 0x03, // array.fill 3 + 0xFB, 0x11, 0x03, 0x05, // array.copy 3 5 + 0xFB, 0x12, 0x03, 0x05, // array.init_data 3 5 + 0xFB, 0x13, 0x03, 0x05, // array.init_elem 3 5 + 0xFB, 0x14, 0x07, // ref.test (ref 7) + 0xFB, 0x15, 0x07, // ref.test (ref null 7) + 0xFB, 0x16, 0x07, // ref.cast (ref 7) + 0xFB, 0x17, 0x07, // ref.cast (ref null 7) + 0xFB, 0x18, 0x00, 0x00, 0x03, 0x05, // br_on_cast 0 (ref 3) (ref 5) + 0xFB, 0x19, 0x03, 0x00, 0x03, 0x05, // br_on_cast_fail 0 (ref null 3) (ref null 5) + ] + let expected: [Instruction] = [ + .refI31, .i31GetS, .i31GetU, + .structNew(typeIndex: 3), .structNewDefault(typeIndex: 3), + .structGet(typeIndex: 3, fieldIndex: 5), .structGetS(typeIndex: 3, fieldIndex: 5), + .structGetU(typeIndex: 3, fieldIndex: 5), .structSet(typeIndex: 3, fieldIndex: 5), + .arrayNew(typeIndex: 3), .arrayNewDefault(typeIndex: 3), .arrayNewFixed(typeIndex: 3, size: 5), + .arrayGet(typeIndex: 3), .arrayGetS(typeIndex: 3), .arrayGetU(typeIndex: 3), .arraySet(typeIndex: 3), + .arrayLen, .refEq, .anyConvertExtern, .externConvertAny, + .arrayNewData(typeIndex: 3, dataIndex: 5), .arrayNewElem(typeIndex: 3, elemIndex: 5), + .arrayFill(typeIndex: 3), .arrayCopy(destType: 3, srcType: 5), + .arrayInitData(typeIndex: 3, dataIndex: 5), .arrayInitElem(typeIndex: 3, elemIndex: 5), + .refTest(.refTest, type: .concrete(typeIndex: 7)), .refTest(.refTestNull, type: .concrete(typeIndex: 7)), + .refCast(.refCast, type: .concrete(typeIndex: 7)), .refCast(.refCastNull, type: .concrete(typeIndex: 7)), + .brOnCast(relativeDepth: 0, castFrom: Self.ref(3, nullable: false), castTo: Self.ref(5, nullable: false)), + .brOnCastFail(relativeDepth: 0, castFrom: Self.ref(3, nullable: true), castTo: Self.ref(5, nullable: true)), + .end, + ] + #expect(try Self.instructions(code, features: .gc) == expected) + } + + @Test func decodesAbstractHeapTypesInCasts() throws { + #expect(try Self.instructions([0xFB, 0x14, 0x6E], features: .gc) == [.refTest(.refTest, type: .abstract(.any)), .end]) + #expect(try Self.instructions([0xFB, 0x15, 0x6E], features: .gc) == [.refTest(.refTestNull, type: .abstract(.any)), .end]) + #expect(try Self.instructions([0xFB, 0x16, 0x6C], features: .gc) == [.refCast(.refCast, type: .abstract(.i31)), .end]) + #expect(try Self.instructions([0xFB, 0x17, 0x6C], features: .gc) == [.refCast(.refCastNull, type: .abstract(.i31)), .end]) + } + + @Test func decodesCastFlags() throws { + // br_on_cast 0 anyref (ref eq) + #expect( + try Self.instructions([0xFB, 0x18, 0x01, 0x00, 0x6E, 0x6D], features: .gc) == [ + .brOnCast(relativeDepth: 0, castFrom: Self.ref(.any, nullable: true), castTo: Self.ref(.eq, nullable: false)), .end, + ]) + // br_on_cast 0 (ref any) (ref null eq) + #expect( + try Self.instructions([0xFB, 0x18, 0x02, 0x00, 0x6E, 0x6D], features: .gc) == [ + .brOnCast(relativeDepth: 0, castFrom: Self.ref(.any, nullable: false), castTo: Self.ref(.eq, nullable: true)), .end, + ]) + // br_on_cast_fail 0 (ref null any) (ref null eq) + #expect( + try Self.instructions([0xFB, 0x19, 0x03, 0x00, 0x6E, 0x6D], features: .gc) == [ + .brOnCastFail(relativeDepth: 0, castFrom: Self.ref(.any, nullable: true), castTo: Self.ref(.eq, nullable: true)), .end, + ]) + // br_on_cast 2 structref (ref array) + #expect( + try Self.instructions([0xFB, 0x18, 0x01, 0x02, 0x6B, 0x6A], features: .gc) == [ + .brOnCast(relativeDepth: 2, castFrom: Self.ref(.structRef, nullable: true), castTo: Self.ref(.arrayRef, nullable: false)), + .end, + ]) + } + + @Test func rejectsWithoutGC() { + #expect(Self.errorMessage { _ = try Self.instructions([0xD3], features: .default) } == "Illegal opcode: [211]") + #expect(Self.errorMessage { _ = try Self.instructions([0xFB, 0x1C], features: .default) } == "Illegal opcode: [251]") + } + + @Test func rejectsInConstantExpressionWithoutGC() throws { + // (global i32 (i31.get_s (ref.i31 (i32.const 0)))) + let bytes = Self.module([(id: 6, content: [0x01, 0x7F, 0x00, 0x41, 0x00, 0xFB, 0x1C, 0xFB, 0x1D, 0x0B])]) + var parser = Parser(bytes: bytes, features: .default) + #expect(Self.errorMessage { while try parser.parseNext() != nil {} } == "Illegal opcode: [251]") + parser = Parser(bytes: bytes, features: .gc) + while try parser.parseNext() != nil {} + } + + @Test func rejectsUnknownCastFlags() { + let message = Self.errorMessage { _ = try Self.instructions([0xFB, 0x18, 0x04, 0x00, 0x6E, 0x6D], features: .gc) } + #expect(message == "Invalid cast flags: 4") + } + + @Test func rejectsUnknownGCOpcode() { + let message = Self.errorMessage { _ = try Self.instructions([0xFB, 0x1F], features: .gc) } + #expect(message == "Illegal opcode: [251, 31]") + } +} diff --git a/Utilities/Instructions.json b/Utilities/Instructions.json index f68e81901..7a8aa96d4 100644 --- a/Utilities/Instructions.json +++ b/Utilities/Instructions.json @@ -532,5 +532,37 @@ ["relaxedSimd" , "f64x2.relaxed_max" , ["0xFD", "0x90", "0x02"], [] , "simd" ], ["relaxedSimd" , "i16x8.relaxed_q15mulr_s" , ["0xFD", "0x91", "0x02"], [] , "simd" ], ["relaxedSimd" , "i16x8.relaxed_dot_i8x16_i7x16_s" , ["0xFD", "0x92", "0x02"], [] , "simd" ], - ["relaxedSimd" , "i32x4.relaxed_dot_i8x16_i7x16_add_s", ["0xFD", "0x93", "0x02"], [] , "simd" ] + ["relaxedSimd" , "i32x4.relaxed_dot_i8x16_i7x16_add_s", ["0xFD", "0x93", "0x02"], [] , "simd" ], + ["gc" , "ref.i31" , ["0xFB", "0x1C"] , [] , null ], + ["gc" , "i31.get_s" , ["0xFB", "0x1D"] , [] , null ], + ["gc" , "i31.get_u" , ["0xFB", "0x1E"] , [] , null ], + ["gc" , "struct.new" , ["0xFB", "0x00"] , [["typeIndex", "UInt32"]] , null ], + ["gc" , "struct.new_default" , ["0xFB", "0x01"] , [["typeIndex", "UInt32"]] , null ], + ["gc" , "struct.get" , ["0xFB", "0x02"] , [["typeIndex", "UInt32"], ["fieldIndex", "UInt32"]], null ], + ["gc" , "struct.get_s" , ["0xFB", "0x03"] , [["typeIndex", "UInt32"], ["fieldIndex", "UInt32"]], null ], + ["gc" , "struct.get_u" , ["0xFB", "0x04"] , [["typeIndex", "UInt32"], ["fieldIndex", "UInt32"]], null ], + ["gc" , "struct.set" , ["0xFB", "0x05"] , [["typeIndex", "UInt32"], ["fieldIndex", "UInt32"]], null ], + ["gc" , "array.new" , ["0xFB", "0x06"] , [["typeIndex", "UInt32"]] , null ], + ["gc" , "array.new_default" , ["0xFB", "0x07"] , [["typeIndex", "UInt32"]] , null ], + ["gc" , "array.new_fixed" , ["0xFB", "0x08"] , [["typeIndex", "UInt32"], ["size", "UInt32"]] , null ], + ["gc" , "array.get" , ["0xFB", "0x0B"] , [["typeIndex", "UInt32"]] , null ], + ["gc" , "array.get_s" , ["0xFB", "0x0C"] , [["typeIndex", "UInt32"]] , null ], + ["gc" , "array.get_u" , ["0xFB", "0x0D"] , [["typeIndex", "UInt32"]] , null ], + ["gc" , "array.set" , ["0xFB", "0x0E"] , [["typeIndex", "UInt32"]] , null ], + ["gc" , "array.len" , ["0xFB", "0x0F"] , [] , null ], + ["gc" , "ref.eq" , ["0xD3"] , [] , null ], + ["gc" , "any.convert_extern" , ["0xFB", "0x1A"] , [] , null ], + ["gc" , "extern.convert_any" , ["0xFB", "0x1B"] , [] , null ], + ["gc" , "array.new_data" , ["0xFB", "0x09"] , [["typeIndex", "UInt32"], ["dataIndex", "UInt32"]] , null ], + ["gc" , "array.new_elem" , ["0xFB", "0x0A"] , [["typeIndex", "UInt32"], ["elemIndex", "UInt32"]] , null ], + ["gc" , "array.fill" , ["0xFB", "0x10"] , [["typeIndex", "UInt32"]] , null ], + ["gc" , "array.copy" , ["0xFB", "0x11"] , [["destType", "UInt32"], ["srcType", "UInt32"]] , null ], + ["gc" , "array.init_data" , ["0xFB", "0x12"] , [["typeIndex", "UInt32"], ["dataIndex", "UInt32"]] , null ], + ["gc" , "array.init_elem" , ["0xFB", "0x13"] , [["typeIndex", "UInt32"], ["elemIndex", "UInt32"]] , null ], + ["gc" , {"enumCase": "refTest"} , ["0xFB", "0x14"] , [["type", "HeapType"]] , "refTest" ], + ["gc" , {"enumCase": "refTestNull"} , ["0xFB", "0x15"] , [["type", "HeapType"]] , "refTest" ], + ["gc" , {"enumCase": "refCast"} , ["0xFB", "0x16"] , [["type", "HeapType"]] , "refCast" ], + ["gc" , {"enumCase": "refCastNull"} , ["0xFB", "0x17"] , [["type", "HeapType"]] , "refCast" ], + ["gc" , "br_on_cast" , ["0xFB", "0x18"] , [["relativeDepth", "UInt32"], ["castFrom", "ReferenceType"], ["castTo", "ReferenceType"]] , null ], + ["gc" , "br_on_cast_fail" , ["0xFB", "0x19"] , [["relativeDepth", "UInt32"], ["castFrom", "ReferenceType"], ["castTo", "ReferenceType"]] , null ] ]