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

     1  package binary
     2  
     3  import (
     4  	"bytes"
     5  	"strconv"
     6  	"testing"
     7  
     8  	"github.com/bananabytelabs/wazero/api"
     9  	"github.com/bananabytelabs/wazero/internal/testing/require"
    10  	"github.com/bananabytelabs/wazero/internal/wasm"
    11  )
    12  
    13  func Test_ensureElementKindFuncRef(t *testing.T) {
    14  	require.NoError(t, ensureElementKindFuncRef(bytes.NewReader([]byte{0x0})))
    15  	require.Error(t, ensureElementKindFuncRef(bytes.NewReader([]byte{0x1})))
    16  }
    17  
    18  func Test_decodeElementInitValueVector(t *testing.T) {
    19  	tests := []struct {
    20  		in     []byte
    21  		exp    []wasm.Index
    22  		expErr string
    23  	}{
    24  		{
    25  			in:  []byte{0},
    26  			exp: []wasm.Index{},
    27  		},
    28  		{
    29  			in:  []byte{5, 1, 2, 3, 4, 5},
    30  			exp: []wasm.Index{1, 2, 3, 4, 5},
    31  		},
    32  		{
    33  			in: []byte{
    34  				1,
    35  				0xff, 0xff, 0xff, 0xff, 0xf,
    36  			},
    37  			expErr: "too large function index in Element init: 4294967295",
    38  		},
    39  	}
    40  
    41  	for i, tt := range tests {
    42  		tc := tt
    43  		t.Run(strconv.Itoa(i), func(t *testing.T) {
    44  			actual, err := decodeElementInitValueVector(bytes.NewReader(tc.in))
    45  			if tc.expErr != "" {
    46  				require.EqualError(t, err, tc.expErr)
    47  			} else {
    48  				require.NoError(t, err)
    49  				require.Equal(t, tc.exp, actual)
    50  			}
    51  		})
    52  	}
    53  }
    54  
    55  func Test_decodeElementConstExprVector(t *testing.T) {
    56  	tests := []struct {
    57  		in       []byte
    58  		refType  wasm.RefType
    59  		exp      []wasm.Index
    60  		features api.CoreFeatures
    61  	}{
    62  		{
    63  			in:       []byte{0},
    64  			exp:      []wasm.Index{},
    65  			refType:  wasm.RefTypeFuncref,
    66  			features: api.CoreFeatureBulkMemoryOperations,
    67  		},
    68  		{
    69  			in: []byte{
    70  				2, // Two indexes.
    71  				wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
    72  				wasm.OpcodeRefFunc, 100, wasm.OpcodeEnd,
    73  			},
    74  			exp:      []wasm.Index{wasm.ElementInitNullReference, 100},
    75  			refType:  wasm.RefTypeFuncref,
    76  			features: api.CoreFeatureBulkMemoryOperations,
    77  		},
    78  		{
    79  			in: []byte{
    80  				4, // Three indexes.
    81  				wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
    82  				wasm.OpcodeRefFunc,
    83  				0x80, 0x7f,
    84  				wasm.OpcodeEnd,
    85  				wasm.OpcodeGlobalGet, 1, wasm.OpcodeEnd,
    86  				wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
    87  			},
    88  			exp: []wasm.Index{
    89  				wasm.ElementInitNullReference,
    90  				16256,
    91  				wasm.ElementInitImportedGlobalFunctionReference | 1,
    92  				wasm.ElementInitNullReference,
    93  			},
    94  			refType:  wasm.RefTypeFuncref,
    95  			features: api.CoreFeatureBulkMemoryOperations,
    96  		},
    97  		{
    98  			in: []byte{
    99  				2, // Two indexes.
   100  				wasm.OpcodeRefNull, wasm.RefTypeExternref, wasm.OpcodeEnd,
   101  				wasm.OpcodeRefNull, wasm.RefTypeExternref, wasm.OpcodeEnd,
   102  			},
   103  			exp:      []wasm.Index{wasm.ElementInitNullReference, wasm.ElementInitNullReference},
   104  			refType:  wasm.RefTypeExternref,
   105  			features: api.CoreFeatureBulkMemoryOperations,
   106  		},
   107  	}
   108  
   109  	for i, tt := range tests {
   110  		tc := tt
   111  		t.Run(strconv.Itoa(i), func(t *testing.T) {
   112  			actual, err := decodeElementConstExprVector(bytes.NewReader(tc.in), tc.refType, tc.features)
   113  			require.NoError(t, err)
   114  			require.Equal(t, tc.exp, actual)
   115  		})
   116  	}
   117  }
   118  
   119  func Test_decodeElementConstExprVector_errors(t *testing.T) {
   120  	tests := []struct {
   121  		name     string
   122  		in       []byte
   123  		refType  wasm.RefType
   124  		expErr   string
   125  		features api.CoreFeatures
   126  	}{
   127  		{
   128  			name:   "eof",
   129  			expErr: "failed to get the size of constexpr vector: EOF",
   130  		},
   131  		{
   132  			name:   "feature",
   133  			in:     []byte{1, wasm.OpcodeRefNull, wasm.RefTypeExternref, wasm.OpcodeEnd},
   134  			expErr: "ref.null is not supported as feature \"bulk-memory-operations\" is disabled",
   135  		},
   136  		{
   137  			name:     "type mismatch - ref.null",
   138  			in:       []byte{1, wasm.OpcodeRefNull, wasm.RefTypeExternref, wasm.OpcodeEnd},
   139  			refType:  wasm.RefTypeFuncref,
   140  			features: api.CoreFeaturesV2,
   141  			expErr:   "element type mismatch: want funcref, but constexpr has externref",
   142  		},
   143  		{
   144  			name:     "type mismatch - ref.null",
   145  			in:       []byte{1, wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd},
   146  			refType:  wasm.RefTypeExternref,
   147  			features: api.CoreFeaturesV2,
   148  			expErr:   "element type mismatch: want externref, but constexpr has funcref",
   149  		},
   150  		{
   151  			name:     "invalid ref type",
   152  			in:       []byte{1, wasm.OpcodeRefNull, 0xff, wasm.OpcodeEnd},
   153  			refType:  wasm.RefTypeExternref,
   154  			features: api.CoreFeaturesV2,
   155  			expErr:   "invalid type for ref.null: 0xff",
   156  		},
   157  		{
   158  			name:     "type mismatch - ref.fuc",
   159  			in:       []byte{1, wasm.OpcodeRefFunc, 0, wasm.OpcodeEnd},
   160  			refType:  wasm.RefTypeExternref,
   161  			features: api.CoreFeaturesV2,
   162  			expErr:   "element type mismatch: want externref, but constexpr has funcref",
   163  		},
   164  		{
   165  			name:     "too large index - ref.fuc",
   166  			in:       []byte{1, wasm.OpcodeRefFunc, 0xff, 0xff, 0xff, 0xff, 0xf, wasm.OpcodeEnd},
   167  			refType:  wasm.RefTypeFuncref,
   168  			features: api.CoreFeaturesV2,
   169  			expErr:   "too large function index in Element init: 4294967295",
   170  		},
   171  		{
   172  			name:     "type mismatch - global.get",
   173  			in:       []byte{1, wasm.OpcodeGlobalGet, 0, wasm.OpcodeEnd},
   174  			refType:  wasm.RefTypeExternref,
   175  			features: api.CoreFeaturesV2,
   176  			expErr:   "element type mismatch: want externref, but requires funcref",
   177  		},
   178  	}
   179  
   180  	for _, tt := range tests {
   181  		tc := tt
   182  		t.Run(tc.name, func(t *testing.T) {
   183  			_, err := decodeElementConstExprVector(bytes.NewReader(tc.in), tc.refType, tc.features)
   184  			require.EqualError(t, err, tc.expErr)
   185  		})
   186  	}
   187  }
   188  
   189  func TestDecodeElementSegment(t *testing.T) {
   190  	tests := []struct {
   191  		name     string
   192  		in       []byte
   193  		exp      wasm.ElementSegment
   194  		expErr   string
   195  		features api.CoreFeatures
   196  	}{
   197  		{
   198  			name: "legacy",
   199  			in: []byte{
   200  				0, // Prefix (which is previously the table index fixed to zero)
   201  				// Offset const expr.
   202  				wasm.OpcodeI32Const, 1, wasm.OpcodeEnd,
   203  				// Init vector.
   204  				5, 1, 2, 3, 4, 5,
   205  			},
   206  			exp: wasm.ElementSegment{
   207  				OffsetExpr: wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: []byte{1}},
   208  				Init:       []wasm.Index{1, 2, 3, 4, 5},
   209  				Mode:       wasm.ElementModeActive,
   210  				Type:       wasm.RefTypeFuncref,
   211  			},
   212  			features: api.CoreFeatureBulkMemoryOperations,
   213  		},
   214  		{
   215  			name: "legacy multi byte const expr data",
   216  			in: []byte{
   217  				0, // Prefix (which is previously the table index fixed to zero)
   218  				// Offset const expr.
   219  				wasm.OpcodeI32Const, 0x80, 0, wasm.OpcodeEnd,
   220  				// Init vector.
   221  				5, 1, 2, 3, 4, 5,
   222  			},
   223  			exp: wasm.ElementSegment{
   224  				OffsetExpr: wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: []byte{0x80, 0}},
   225  				Init:       []wasm.Index{1, 2, 3, 4, 5},
   226  				Mode:       wasm.ElementModeActive,
   227  				Type:       wasm.RefTypeFuncref,
   228  			},
   229  			features: api.CoreFeatureBulkMemoryOperations,
   230  		},
   231  		{
   232  			name: "passive value vector",
   233  			in: []byte{
   234  				1, // Prefix.
   235  				0, // Elem kind must be fixed to zero.
   236  				// Init vector.
   237  				5, 1, 2, 3, 4, 5,
   238  			},
   239  			exp: wasm.ElementSegment{
   240  				Init: []wasm.Index{1, 2, 3, 4, 5},
   241  				Mode: wasm.ElementModePassive,
   242  				Type: wasm.RefTypeFuncref,
   243  			},
   244  			features: api.CoreFeatureBulkMemoryOperations,
   245  		},
   246  		{
   247  			name: "active with table index encoded.",
   248  			in: []byte{
   249  				2, // Prefix.
   250  				0,
   251  				// Offset const expr.
   252  				wasm.OpcodeI32Const, 0x80, 0, wasm.OpcodeEnd,
   253  				0, // Elem kind must be fixed to zero.
   254  				// Init vector.
   255  				5, 1, 2, 3, 4, 5,
   256  			},
   257  			exp: wasm.ElementSegment{
   258  				OffsetExpr: wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: []byte{0x80, 0}},
   259  				Init:       []wasm.Index{1, 2, 3, 4, 5},
   260  				Mode:       wasm.ElementModeActive,
   261  				Type:       wasm.RefTypeFuncref,
   262  			},
   263  			features: api.CoreFeatureBulkMemoryOperations,
   264  		},
   265  		{
   266  			name: "active with non zero table index encoded.",
   267  			in: []byte{
   268  				2, // Prefix.
   269  				10,
   270  				// Offset const expr.
   271  				wasm.OpcodeI32Const, 0x80, 0, wasm.OpcodeEnd,
   272  				0, // Elem kind must be fixed to zero.
   273  				// Init vector.
   274  				5, 1, 2, 3, 4, 5,
   275  			},
   276  			exp: wasm.ElementSegment{
   277  				OffsetExpr: wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: []byte{0x80, 0}},
   278  				Init:       []wasm.Index{1, 2, 3, 4, 5},
   279  				Mode:       wasm.ElementModeActive,
   280  				Type:       wasm.RefTypeFuncref,
   281  				TableIndex: 10,
   282  			},
   283  			features: api.CoreFeatureBulkMemoryOperations | api.CoreFeatureReferenceTypes,
   284  		},
   285  		{
   286  			name: "active with non zero table index encoded but reference-types disabled",
   287  			in: []byte{
   288  				2, // Prefix.
   289  				10,
   290  				// Offset const expr.
   291  				wasm.OpcodeI32Const, 0x80, 0, wasm.OpcodeEnd,
   292  				0, // Elem kind must be fixed to zero.
   293  				// Init vector.
   294  				5, 1, 2, 3, 4, 5,
   295  			},
   296  			expErr:   `table index must be zero but was 10: feature "reference-types" is disabled`,
   297  			features: api.CoreFeatureBulkMemoryOperations,
   298  		},
   299  		{
   300  			name: "declarative",
   301  			in: []byte{
   302  				3, // Prefix.
   303  				0, // Elem kind must be fixed to zero.
   304  				// Init vector.
   305  				5, 1, 2, 3, 4, 5,
   306  			},
   307  			exp: wasm.ElementSegment{
   308  				Init: []wasm.Index{1, 2, 3, 4, 5},
   309  				Mode: wasm.ElementModeDeclarative,
   310  				Type: wasm.RefTypeFuncref,
   311  			},
   312  			features: api.CoreFeatureBulkMemoryOperations,
   313  		},
   314  		{
   315  			name: "active const expr vector",
   316  			in: []byte{
   317  				4, // Prefix.
   318  				// Offset expr.
   319  				wasm.OpcodeI32Const, 0x80, 1, wasm.OpcodeEnd,
   320  				// Init const expr vector.
   321  				3, // number of const expr.
   322  				wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
   323  				wasm.OpcodeRefFunc,
   324  				0x80, 0x7f,
   325  				wasm.OpcodeEnd,
   326  				wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
   327  			},
   328  			exp: wasm.ElementSegment{
   329  				OffsetExpr: wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: []byte{0x80, 1}},
   330  				Init:       []wasm.Index{wasm.ElementInitNullReference, 16256, wasm.ElementInitNullReference},
   331  				Mode:       wasm.ElementModeActive,
   332  				Type:       wasm.RefTypeFuncref,
   333  			},
   334  			features: api.CoreFeatureBulkMemoryOperations,
   335  		},
   336  		{
   337  			name: "passive const expr vector - funcref",
   338  			in: []byte{
   339  				5, // Prefix.
   340  				wasm.RefTypeFuncref,
   341  				// Init const expr vector.
   342  				3, // number of const expr.
   343  				wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
   344  				wasm.OpcodeRefFunc,
   345  				0x80, 0x7f,
   346  				wasm.OpcodeEnd,
   347  				wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
   348  			},
   349  			exp: wasm.ElementSegment{
   350  				Init: []wasm.Index{wasm.ElementInitNullReference, 16256, wasm.ElementInitNullReference},
   351  				Mode: wasm.ElementModePassive,
   352  				Type: wasm.RefTypeFuncref,
   353  			},
   354  			features: api.CoreFeatureBulkMemoryOperations,
   355  		},
   356  		{
   357  			name: "passive const expr vector - unknown ref type",
   358  			in: []byte{
   359  				5, // Prefix.
   360  				0xff,
   361  			},
   362  			expErr:   `ref type must be funcref or externref for element as of WebAssembly 2.0`,
   363  			features: api.CoreFeatureBulkMemoryOperations | api.CoreFeatureReferenceTypes,
   364  		},
   365  		{
   366  			name: "active with table index and const expr vector",
   367  			in: []byte{
   368  				6, // Prefix.
   369  				0,
   370  				// Offset expr.
   371  				wasm.OpcodeI32Const, 0x80, 1, wasm.OpcodeEnd,
   372  				wasm.RefTypeFuncref,
   373  				// Init const expr vector.
   374  				3, // number of const expr.
   375  				wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
   376  				wasm.OpcodeRefFunc,
   377  				0x80, 0x7f,
   378  				wasm.OpcodeEnd,
   379  				wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
   380  			},
   381  			exp: wasm.ElementSegment{
   382  				OffsetExpr: wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: []byte{0x80, 1}},
   383  				Init:       []wasm.Index{wasm.ElementInitNullReference, 16256, wasm.ElementInitNullReference},
   384  				Mode:       wasm.ElementModeActive,
   385  				Type:       wasm.RefTypeFuncref,
   386  			},
   387  			features: api.CoreFeatureBulkMemoryOperations,
   388  		},
   389  		{
   390  			name: "active with non zero table index and const expr vector",
   391  			in: []byte{
   392  				6, // Prefix.
   393  				10,
   394  				// Offset expr.
   395  				wasm.OpcodeI32Const, 0x80, 1, wasm.OpcodeEnd,
   396  				wasm.RefTypeFuncref,
   397  				// Init const expr vector.
   398  				3, // number of const expr.
   399  				wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
   400  				wasm.OpcodeRefFunc,
   401  				0x80, 0x7f,
   402  				wasm.OpcodeEnd,
   403  				wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
   404  			},
   405  			exp: wasm.ElementSegment{
   406  				OffsetExpr: wasm.ConstantExpression{Opcode: wasm.OpcodeI32Const, Data: []byte{0x80, 1}},
   407  				Init:       []wasm.Index{wasm.ElementInitNullReference, 16256, wasm.ElementInitNullReference},
   408  				Mode:       wasm.ElementModeActive,
   409  				Type:       wasm.RefTypeFuncref,
   410  				TableIndex: 10,
   411  			},
   412  			features: api.CoreFeatureBulkMemoryOperations | api.CoreFeatureReferenceTypes,
   413  		},
   414  		{
   415  			name: "active with non zero table index and const expr vector but feature disabled",
   416  			in: []byte{
   417  				6, // Prefix.
   418  				10,
   419  				// Offset expr.
   420  				wasm.OpcodeI32Const, 0x80, 1, wasm.OpcodeEnd,
   421  				wasm.RefTypeFuncref,
   422  				// Init const expr vector.
   423  				3, // number of const expr.
   424  				wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
   425  				wasm.OpcodeRefFunc,
   426  				0x80, 0x80, 0x80, 0x4f, // 165675008 in varint encoding.
   427  				wasm.OpcodeEnd,
   428  				wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
   429  			},
   430  			expErr:   `table index must be zero but was 10: feature "reference-types" is disabled`,
   431  			features: api.CoreFeatureBulkMemoryOperations,
   432  		},
   433  		{
   434  			name: "declarative const expr vector",
   435  			in: []byte{
   436  				7, // Prefix.
   437  				wasm.RefTypeFuncref,
   438  				// Init const expr vector.
   439  				2, // number of const expr.
   440  				wasm.OpcodeRefNull, wasm.RefTypeFuncref, wasm.OpcodeEnd,
   441  				wasm.OpcodeRefFunc,
   442  				0x80, 0x7f,
   443  				wasm.OpcodeEnd,
   444  			},
   445  			exp: wasm.ElementSegment{
   446  				Init: []wasm.Index{wasm.ElementInitNullReference, 16256},
   447  				Mode: wasm.ElementModeDeclarative,
   448  				Type: wasm.RefTypeFuncref,
   449  			},
   450  			features: api.CoreFeatureBulkMemoryOperations,
   451  		},
   452  	}
   453  
   454  	for _, tt := range tests {
   455  		tc := tt
   456  		t.Run(tc.name, func(t *testing.T) {
   457  			var actual wasm.ElementSegment
   458  			err := decodeElementSegment(bytes.NewReader(tc.in), tc.features, &actual)
   459  			if tc.expErr != "" {
   460  				require.EqualError(t, err, tc.expErr)
   461  			} else {
   462  				require.NoError(t, err)
   463  				require.Equal(t, actual, tc.exp)
   464  			}
   465  		})
   466  	}
   467  }
   468  
   469  func TestDecodeElementSegment_errors(t *testing.T) {
   470  	var actual wasm.ElementSegment
   471  	err := decodeElementSegment(bytes.NewReader([]byte{1}), api.CoreFeatureMultiValue, &actual)
   472  	require.EqualError(t, err, `non-zero prefix for element segment is invalid as feature "bulk-memory-operations" is disabled`)
   473  }