github.com/bananabytelabs/wazero@v0.0.0-20240105073314-54b22a776da8/internal/wasm/function_definition_test.go (about)

     1  package wasm
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/bananabytelabs/wazero/api"
     7  	"github.com/bananabytelabs/wazero/internal/testing/hammer"
     8  	"github.com/bananabytelabs/wazero/internal/testing/require"
     9  )
    10  
    11  func TestModule_BuildFunctionDefinitions(t *testing.T) {
    12  	imp := &Import{
    13  		Type:     ExternTypeFunc,
    14  		DescFunc: 2, // Index of type.
    15  	}
    16  
    17  	nopCode := Code{Body: []byte{OpcodeEnd}}
    18  	fn := func(uint32) uint32 { return 1 }
    19  	tests := []struct {
    20  		name            string
    21  		m               *Module
    22  		expected        []FunctionDefinition
    23  		expectedImports []api.FunctionDefinition
    24  		expectedExports map[string]api.FunctionDefinition
    25  	}{
    26  		{
    27  			name:            "no exports",
    28  			m:               &Module{},
    29  			expected:        []FunctionDefinition{},
    30  			expectedExports: map[string]api.FunctionDefinition{},
    31  		},
    32  		{
    33  			name:     "no functions",
    34  			expected: []FunctionDefinition{},
    35  			m: &Module{
    36  				ExportSection: []Export{{Type: ExternTypeGlobal, Index: 0}},
    37  				GlobalSection: []Global{{}},
    38  			},
    39  			expectedExports: map[string]api.FunctionDefinition{},
    40  		},
    41  		{
    42  			name: "only imported functions",
    43  			expected: []FunctionDefinition{
    44  				{name: "fn", Debugname: ".fn", importDesc: &Import{Module: "foo", Name: "bar", Type: ExternTypeFunc}, Functype: &FunctionType{}},
    45  			},
    46  			m: &Module{
    47  				ExportSection:       []Export{{Type: ExternTypeGlobal, Index: 0}},
    48  				GlobalSection:       []Global{{}},
    49  				TypeSection:         []FunctionType{{}},
    50  				ImportFunctionCount: 1,
    51  				ImportSection:       []Import{{Type: ExternTypeFunc, Name: "bar", Module: "foo"}},
    52  				NameSection:         &NameSection{FunctionNames: NameMap{{Index: Index(0), Name: "fn"}}},
    53  			},
    54  			expectedImports: []api.FunctionDefinition{
    55  				&FunctionDefinition{
    56  					name: "fn", Debugname: ".fn", importDesc: &Import{Module: "foo", Name: "bar", Type: ExternTypeFunc},
    57  					Functype: &FunctionType{},
    58  				},
    59  			},
    60  			expectedExports: map[string]api.FunctionDefinition{},
    61  		},
    62  		{
    63  			name: "host func go",
    64  			m: &Module{
    65  				TypeSection:     []FunctionType{i32_i32},
    66  				FunctionSection: []Index{0},
    67  				CodeSection:     []Code{MustParseGoReflectFuncCode(fn)},
    68  				NameSection: &NameSection{
    69  					ModuleName:    "m",
    70  					FunctionNames: NameMap{{Index: Index(0), Name: "fn"}},
    71  					LocalNames:    IndirectNameMap{{Index: Index(0), NameMap: NameMap{{Index: Index(0), Name: "x"}}}},
    72  					ResultNames:   IndirectNameMap{{Index: Index(0), NameMap: NameMap{{Index: Index(0), Name: "y"}}}},
    73  				},
    74  			},
    75  			expected: []FunctionDefinition{
    76  				{
    77  					index:       0,
    78  					name:        "fn",
    79  					moduleName:  "m",
    80  					Debugname:   "m.fn",
    81  					goFunc:      MustParseGoReflectFuncCode(fn).GoFunc,
    82  					Functype:    &i32_i32,
    83  					paramNames:  []string{"x"},
    84  					resultNames: []string{"y"},
    85  				},
    86  			},
    87  			expectedExports: map[string]api.FunctionDefinition{},
    88  		},
    89  		{
    90  			name: "without imports",
    91  			m: &Module{
    92  				ExportSection: []Export{
    93  					{Name: "function_index=0", Type: ExternTypeFunc, Index: 0},
    94  					{Name: "function_index=2", Type: ExternTypeFunc, Index: 2},
    95  					{Name: "", Type: ExternTypeGlobal, Index: 0},
    96  					{Name: "function_index=1", Type: ExternTypeFunc, Index: 1},
    97  				},
    98  				GlobalSection:   []Global{{}},
    99  				FunctionSection: []Index{1, 2, 0},
   100  				CodeSection: []Code{
   101  					{Body: []byte{OpcodeEnd}},
   102  					{Body: []byte{OpcodeEnd}},
   103  					{Body: []byte{OpcodeEnd}},
   104  				},
   105  				TypeSection: []FunctionType{
   106  					v_v,
   107  					f64i32_v128i64,
   108  					f64f32_i64,
   109  				},
   110  			},
   111  			expected: []FunctionDefinition{
   112  				{
   113  					index:       0,
   114  					Debugname:   ".$0",
   115  					exportNames: []string{"function_index=0"},
   116  					Functype:    &f64i32_v128i64,
   117  				},
   118  				{
   119  					index:       1,
   120  					Debugname:   ".$1",
   121  					exportNames: []string{"function_index=1"},
   122  					Functype:    &f64f32_i64,
   123  				},
   124  				{
   125  					index:       2,
   126  					Debugname:   ".$2",
   127  					exportNames: []string{"function_index=2"},
   128  					Functype:    &v_v,
   129  				},
   130  			},
   131  			expectedExports: map[string]api.FunctionDefinition{
   132  				"function_index=0": &FunctionDefinition{
   133  					index:       0,
   134  					Debugname:   ".$0",
   135  					exportNames: []string{"function_index=0"},
   136  					Functype:    &f64i32_v128i64,
   137  				},
   138  				"function_index=1": &FunctionDefinition{
   139  					index:       1,
   140  					exportNames: []string{"function_index=1"},
   141  					Debugname:   ".$1",
   142  					Functype:    &f64f32_i64,
   143  				},
   144  				"function_index=2": &FunctionDefinition{
   145  					index:       2,
   146  					Debugname:   ".$2",
   147  					exportNames: []string{"function_index=2"},
   148  					Functype:    &v_v,
   149  				},
   150  			},
   151  		},
   152  		{
   153  			name: "with imports",
   154  			m: &Module{
   155  				ImportFunctionCount: 1,
   156  				ImportSection:       []Import{*imp},
   157  				ExportSection: []Export{
   158  					{Name: "imported_function", Type: ExternTypeFunc, Index: 0},
   159  					{Name: "function_index=1", Type: ExternTypeFunc, Index: 1},
   160  					{Name: "function_index=2", Type: ExternTypeFunc, Index: 2},
   161  				},
   162  				FunctionSection: []Index{1, 0},
   163  				CodeSection:     []Code{{Body: []byte{OpcodeEnd}}, {Body: []byte{OpcodeEnd}}},
   164  				TypeSection: []FunctionType{
   165  					v_v,
   166  					f64i32_v128i64,
   167  					f64f32_i64,
   168  				},
   169  			},
   170  			expected: []FunctionDefinition{
   171  				{
   172  					index:       0,
   173  					Debugname:   ".$0",
   174  					importDesc:  imp,
   175  					exportNames: []string{"imported_function"},
   176  					Functype:    &f64f32_i64,
   177  				},
   178  				{
   179  					index:       1,
   180  					Debugname:   ".$1",
   181  					exportNames: []string{"function_index=1"},
   182  					Functype:    &f64i32_v128i64,
   183  				},
   184  				{
   185  					index:       2,
   186  					Debugname:   ".$2",
   187  					exportNames: []string{"function_index=2"},
   188  					Functype:    &v_v,
   189  				},
   190  			},
   191  			expectedImports: []api.FunctionDefinition{
   192  				&FunctionDefinition{
   193  					index:       0,
   194  					Debugname:   ".$0",
   195  					importDesc:  imp,
   196  					exportNames: []string{"imported_function"},
   197  					Functype:    &f64f32_i64,
   198  				},
   199  			},
   200  			expectedExports: map[string]api.FunctionDefinition{
   201  				"imported_function": &FunctionDefinition{
   202  					index:       0,
   203  					Debugname:   ".$0",
   204  					importDesc:  imp,
   205  					exportNames: []string{"imported_function"},
   206  					Functype:    &f64f32_i64,
   207  				},
   208  				"function_index=1": &FunctionDefinition{
   209  					index:       1,
   210  					Debugname:   ".$1",
   211  					exportNames: []string{"function_index=1"},
   212  					Functype:    &f64i32_v128i64,
   213  				},
   214  				"function_index=2": &FunctionDefinition{
   215  					index:       2,
   216  					Debugname:   ".$2",
   217  					exportNames: []string{"function_index=2"},
   218  					Functype:    &v_v,
   219  				},
   220  			},
   221  		},
   222  		{
   223  			name: "with names",
   224  			m: &Module{
   225  				ImportFunctionCount: 1,
   226  				TypeSection:         []FunctionType{v_v},
   227  				ImportSection:       []Import{{Module: "i", Name: "f", Type: ExternTypeFunc}},
   228  				NameSection: &NameSection{
   229  					ModuleName: "module",
   230  					FunctionNames: NameMap{
   231  						{Index: Index(2), Name: "two"},
   232  						{Index: Index(4), Name: "four"},
   233  						{Index: Index(5), Name: "five"},
   234  					},
   235  				},
   236  				FunctionSection: []Index{0, 0, 0, 0, 0},
   237  				CodeSection:     []Code{nopCode, nopCode, nopCode, nopCode, nopCode},
   238  			},
   239  			expected: []FunctionDefinition{
   240  				{moduleName: "module", index: 0, Debugname: "module.$0", importDesc: &Import{Module: "i", Name: "f"}, Functype: &v_v},
   241  				{moduleName: "module", index: 1, Debugname: "module.$1", Functype: &v_v},
   242  				{moduleName: "module", index: 2, Debugname: "module.two", Functype: &v_v, name: "two"},
   243  				{moduleName: "module", index: 3, Debugname: "module.$3", Functype: &v_v},
   244  				{moduleName: "module", index: 4, Debugname: "module.four", Functype: &v_v, name: "four"},
   245  				{moduleName: "module", index: 5, Debugname: "module.five", Functype: &v_v, name: "five"},
   246  			},
   247  			expectedImports: []api.FunctionDefinition{
   248  				&FunctionDefinition{moduleName: "module", index: 0, Debugname: "module.$0", importDesc: &Import{Module: "i", Name: "f"}, Functype: &v_v},
   249  			},
   250  			expectedExports: map[string]api.FunctionDefinition{},
   251  		},
   252  	}
   253  
   254  	for _, tc := range tests {
   255  		tc := tc
   256  		t.Run(tc.name, func(t *testing.T) {
   257  			tc.m.buildFunctionDefinitions()
   258  			require.Equal(t, tc.expected, tc.m.FunctionDefinitionSection)
   259  			require.Equal(t, tc.expectedImports, tc.m.ImportedFunctions())
   260  			require.Equal(t, tc.expectedExports, tc.m.ExportedFunctions())
   261  		})
   262  	}
   263  
   264  	// Execute the same tests with n=`concurrentCount` goroutines invoking `buildFunctionDefinitions()` at once.
   265  	const nGoroutines = 100
   266  	const nIterations = 10
   267  	for _, tc := range tests {
   268  		tc := tc
   269  		testName := tc.name + " (concurrent)"
   270  		t.Run(testName, func(t *testing.T) {
   271  			hammer.NewHammer(t, nGoroutines, nIterations).
   272  				Run(func(name string) {
   273  					tc.m.buildFunctionDefinitions()
   274  				}, nil)
   275  
   276  			require.Equal(t, tc.expected, tc.m.FunctionDefinitionSection)
   277  			require.Equal(t, tc.expectedImports, tc.m.ImportedFunctions())
   278  			require.Equal(t, tc.expectedExports, tc.m.ExportedFunctions())
   279  		})
   280  	}
   281  }