github.com/wasilibs/wazerox@v0.0.0-20240124024944-4923be63ab5f/internal/testing/binaryencoding/encoder_test.go (about) 1 package binaryencoding 2 3 import ( 4 "testing" 5 6 "github.com/wasilibs/wazerox/internal/leb128" 7 "github.com/wasilibs/wazerox/internal/testing/require" 8 "github.com/wasilibs/wazerox/internal/wasm" 9 ) 10 11 func TestModule_Encode(t *testing.T) { 12 i32, f32 := wasm.ValueTypeI32, wasm.ValueTypeF32 13 zero := uint32(0) 14 15 tests := []struct { 16 name string 17 input *wasm.Module 18 expected []byte 19 }{ 20 { 21 name: "empty", 22 input: &wasm.Module{}, 23 expected: append(Magic, version...), 24 }, 25 { 26 name: "only name section", 27 input: &wasm.Module{NameSection: &wasm.NameSection{ModuleName: "simple"}}, 28 expected: append(append(Magic, version...), 29 wasm.SectionIDCustom, 0x0e, // 14 bytes in this section 30 0x04, 'n', 'a', 'm', 'e', 31 subsectionIDModuleName, 0x07, // 7 bytes in this subsection 32 0x06, // the Module name simple is 6 bytes long 33 's', 'i', 'm', 'p', 'l', 'e'), 34 }, 35 { 36 name: "type section", 37 input: &wasm.Module{ 38 TypeSection: []wasm.FunctionType{ 39 {}, 40 {Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32}}, 41 {Params: []wasm.ValueType{i32, i32, i32, i32}, Results: []wasm.ValueType{i32}}, 42 }, 43 }, 44 expected: append(append(Magic, version...), 45 wasm.SectionIDType, 0x12, // 18 bytes in this section 46 0x03, // 3 types 47 0x60, 0x00, 0x00, // func=0x60 no param no result 48 0x60, 0x02, i32, i32, 0x01, i32, // func=0x60 2 params and 1 result 49 0x60, 0x04, i32, i32, i32, i32, 0x01, i32, // func=0x60 4 params and 1 result 50 ), 51 }, 52 { 53 name: "type and import section", 54 input: &wasm.Module{ 55 TypeSection: []wasm.FunctionType{ 56 {Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32}}, 57 {Params: []wasm.ValueType{f32, f32}, Results: []wasm.ValueType{f32}}, 58 }, 59 ImportSection: []wasm.Import{ 60 { 61 Module: "Math", Name: "Mul", 62 Type: wasm.ExternTypeFunc, 63 DescFunc: 1, 64 }, { 65 Module: "Math", Name: "Add", 66 Type: wasm.ExternTypeFunc, 67 DescFunc: 0, 68 }, 69 }, 70 }, 71 expected: append(append(Magic, version...), 72 wasm.SectionIDType, 0x0d, // 13 bytes in this section 73 0x02, // 2 types 74 0x60, 0x02, i32, i32, 0x01, i32, // func=0x60 2 params and 1 result 75 0x60, 0x02, f32, f32, 0x01, f32, // func=0x60 2 params and 1 result 76 wasm.SectionIDImport, 0x17, // 23 bytes in this section 77 0x02, // 2 imports 78 0x04, 'M', 'a', 't', 'h', 0x03, 'M', 'u', 'l', wasm.ExternTypeFunc, 79 0x01, // type index 80 0x04, 'M', 'a', 't', 'h', 0x03, 'A', 'd', 'd', wasm.ExternTypeFunc, 81 0x00, // type index 82 ), 83 }, 84 { 85 name: "type function and start section", 86 input: &wasm.Module{ 87 TypeSection: []wasm.FunctionType{{}}, 88 ImportSection: []wasm.Import{{ 89 Module: "", Name: "hello", 90 Type: wasm.ExternTypeFunc, 91 DescFunc: 0, 92 }}, 93 StartSection: &zero, 94 }, 95 expected: append(append(Magic, version...), 96 wasm.SectionIDType, 0x04, // 4 bytes in this section 97 0x01, // 1 type 98 0x60, 0x0, 0x0, // func=0x60 0 params and 0 result 99 wasm.SectionIDImport, 0x0a, // 10 bytes in this section 100 0x01, // 1 import 101 0x00, 0x05, 'h', 'e', 'l', 'l', 'o', wasm.ExternTypeFunc, 102 0x00, // type index 103 wasm.SectionIDStart, 0x01, 104 0x00, // start function index 105 ), 106 }, 107 { 108 name: "table and memory section", 109 input: &wasm.Module{ 110 TableSection: []wasm.Table{{Min: 3, Type: wasm.RefTypeFuncref}}, 111 MemorySection: &wasm.Memory{Min: 1, Max: 1, IsMaxEncoded: true}, 112 }, 113 expected: append(append(Magic, version...), 114 wasm.SectionIDTable, 0x04, // 4 bytes in this section 115 0x01, // 1 table 116 wasm.RefTypeFuncref, 0x0, 0x03, // func, only min: 3 117 wasm.SectionIDMemory, 0x04, // 4 bytes in this section 118 0x01, // 1 memory 119 0x01, 0x01, 0x01, // min and max = 1 120 ), 121 }, 122 { 123 name: "exported func with instructions", 124 input: &wasm.Module{ 125 TypeSection: []wasm.FunctionType{ 126 {Params: []wasm.ValueType{i32, i32}, Results: []wasm.ValueType{i32}}, 127 }, 128 FunctionSection: []wasm.Index{0}, 129 CodeSection: []wasm.Code{ 130 {Body: []byte{wasm.OpcodeLocalGet, 0, wasm.OpcodeLocalGet, 1, wasm.OpcodeI32Add, wasm.OpcodeEnd}}, 131 }, 132 ExportSection: []wasm.Export{ 133 {Name: "AddInt", Type: wasm.ExternTypeFunc, Index: wasm.Index(0)}, 134 }, 135 NameSection: &wasm.NameSection{ 136 FunctionNames: wasm.NameMap{{Index: wasm.Index(0), Name: "addInt"}}, 137 LocalNames: wasm.IndirectNameMap{ 138 {Index: wasm.Index(0), NameMap: wasm.NameMap{ 139 {Index: wasm.Index(0), Name: "value_1"}, 140 {Index: wasm.Index(1), Name: "value_2"}, 141 }}, 142 }, 143 }, 144 }, 145 expected: append(append(Magic, version...), 146 wasm.SectionIDType, 0x07, // 7 bytes in this section 147 0x01, // 1 type 148 0x60, 0x02, i32, i32, 0x01, i32, // func=0x60 2 params and 1 result 149 wasm.SectionIDFunction, 0x02, // 2 bytes in this section 150 0x01, // 1 function 151 0x00, // func[0] type index 0 152 wasm.SectionIDExport, 0x0a, // 10 bytes in this section 153 0x01, // 1 export 154 0x06, 'A', 'd', 'd', 'I', 'n', 't', // size of "AddInt", "AddInt" 155 wasm.ExternTypeFunc, 0x00, // func[0] 156 wasm.SectionIDCode, 0x09, // 9 bytes in this section 157 0o1, // one code section 158 0o7, // length of the body + locals 159 0o0, // count of local blocks 160 wasm.OpcodeLocalGet, 0, // local.get 0 161 wasm.OpcodeLocalGet, 1, // local.get 1 162 wasm.OpcodeI32Add, // i32.add 163 wasm.OpcodeEnd, // end of instructions/code 164 wasm.SectionIDCustom, 0x27, // 39 bytes in this section 165 0x04, 'n', 'a', 'm', 'e', 166 subsectionIDFunctionNames, 0x09, // 9 bytes 167 0x01, // two function names 168 0x00, 0x06, 'a', 'd', 'd', 'I', 'n', 't', // index 0, size of "addInt", "addInt" 169 subsectionIDLocalNames, 0x15, // 21 bytes 170 0x01, // one function 171 0x00, 0x02, // index 0 has 2 locals 172 0x00, 0x07, 'v', 'a', 'l', 'u', 'e', '_', '1', // index 0, size of "value_1", "value_1" 173 0x01, 0x07, 'v', 'a', 'l', 'u', 'e', '_', '2', // index 1, size of "value_2", "value_2" 174 ), 175 }, 176 { 177 name: "exported global var", 178 input: &wasm.Module{ 179 GlobalSection: []wasm.Global{ 180 { 181 Type: wasm.GlobalType{ValType: i32, Mutable: true}, 182 Init: wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: leb128.EncodeInt32(0)}, 183 }, 184 }, 185 ExportSection: []wasm.Export{ 186 {Name: "sp", Type: wasm.ExternTypeGlobal, Index: wasm.Index(0)}, 187 }, 188 }, 189 expected: append(append(Magic, version...), 190 wasm.SectionIDGlobal, 0x06, // 6 bytes in this section 191 0x01, wasm.ValueTypeI32, 0x01, // 1 global i32 mutable 192 wasm.OpcodeI32Const, 0x00, wasm.OpcodeEnd, // arbitrary init to zero 193 wasm.SectionIDExport, 0x06, // 6 bytes in this section 194 0x01, // 1 export 195 0x02, 's', 'p', // size of "sp", "sp" 196 wasm.ExternTypeGlobal, 0x00, // global[0] 197 ), 198 }, 199 } 200 201 for _, tt := range tests { 202 tc := tt 203 204 t.Run(tc.name, func(t *testing.T) { 205 bytes := EncodeModule(tc.input) 206 require.Equal(t, tc.expected, bytes) 207 }) 208 } 209 } 210 211 func TestModule_Encode_HostFunctionSection_Unsupported(t *testing.T) { 212 // We don't currently have an approach to serialize reflect.Value pointers 213 fn := func() {} 214 215 captured := require.CapturePanic(func() { 216 EncodeModule(&wasm.Module{ 217 TypeSection: []wasm.FunctionType{{}}, 218 CodeSection: []wasm.Code{wasm.MustParseGoReflectFuncCode(fn)}, 219 }) 220 }) 221 require.EqualError(t, captured, "BUG: GoFunction is not encodable") 222 }