github.com/tetratelabs/wazero@v1.7.3-0.20240513003603-48f702e154b5/internal/wasm/binary/element_test.go (about)

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