wa-lang.org/wazero@v1.0.2/internal/wasm/binary/element_test.go (about)

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