github.com/bananabytelabs/wazero@v0.0.0-20240105073314-54b22a776da8/internal/testing/binaryencoding/encoder_test.go (about)

     1  package binaryencoding
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/bananabytelabs/wazero/internal/leb128"
     7  	"github.com/bananabytelabs/wazero/internal/testing/require"
     8  	"github.com/bananabytelabs/wazero/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  }