Repository: tetratelabs/wazero
Pull request #688 (2022-07-13 01:12:02)

Fixes a bug with funcref in block type 


Patch:
diff --git a/internal/wasm/func_validation.go b/internal/wasm/func_validation.go
index a8cd9f91..f9c645cc 100644
--- a/internal/wasm/func_validation.go
+++ b/internal/wasm/func_validation.go
@@ -8,6 +8,7 @@ import (
 	"strings"
 
 	"github.com/tetratelabs/wazero/api"
+	"github.com/tetratelabs/wazero/internal/buildoptions"
 	"github.com/tetratelabs/wazero/internal/leb128"
 )
 
@@ -76,6 +77,18 @@ func (m *Module) validateFunctionWithMaxStackValues(
 	// control blocks and value types to check the validity of all instructions.
 	for pc := uint64(0); pc < uint64(len(body)); pc++ {
 		op := body[pc]
+		if buildoptions.IsDebugMode {
+			var instName string
+			if op == OpcodeMiscPrefix {
+				instName = MiscInstructionName(body[pc+1])
+			} else if op == OpcodeVecPrefix {
+				instName = VectorInstructionName(body[pc+1])
+			} else {
+				instName = InstructionName(op)
+			}
+			fmt.Printf("handling %s, stack=%s, blocks: %v\n", instName, valueTypeStack, controlBlockStack)
+		}
+
 		if OpcodeI32Load <= op && op <= OpcodeI64Store32 {
 			if memory == nil {
 				return fmt.Errorf("unknown memory access")
@@ -1802,14 +1815,8 @@ func (s *valueTypeStack) String() string {
 		var str string
 		if v == valueTypeUnknown {
 			str = "unknown"
-		} else if v == ValueTypeI32 {
-			str = "i32"
-		} else if v == ValueTypeI64 {
-			str = "i64"
-		} else if v == ValueTypeF32 {
-			str = "f32"
-		} else if v == ValueTypeF64 {
-			str = "f64"
+		} else {
+			str = ValueTypeName(v)
 		}
 		typeStrs = append(typeStrs, str)
 	}
@@ -1852,10 +1859,10 @@ func DecodeBlockType(types []*FunctionType, r *bytes.Reader, enabledFeatures Fea
 		ret = &FunctionType{Results: []ValueType{ValueTypeF32}, ResultNumInUint64: 1}
 	case -4: // 0x7c in original byte = f64
 		ret = &FunctionType{Results: []ValueType{ValueTypeF64}, ResultNumInUint64: 1}
-	case -5: // 0x7b in original byte = f64
+	case -5: // 0x7b in original byte = v128
 		ret = &FunctionType{Results: []ValueType{ValueTypeV128}, ResultNumInUint64: 2}
 	case -16: // 0x70 in original byte = funcref
-		ret = &FunctionType{Results: []ValueType{ValueTypeExternref}, ResultNumInUint64: 1}
+		ret = &FunctionType{Results: []ValueType{ValueTypeFuncref}, ResultNumInUint64: 1}
 	case -17: // 0x6f in original byte = externref
 		ret = &FunctionType{Results: []ValueType{ValueTypeExternref}, ResultNumInUint64: 1}
 	default:


Test patch:
diff --git a/internal/wasm/func_validation_test.go b/internal/wasm/func_validation_test.go
index 11e37a90..51265377 100644
--- a/internal/wasm/func_validation_test.go
+++ b/internal/wasm/func_validation_test.go
@@ -1,6 +1,7 @@
 package wasm
 
 import (
+	"bytes"
 	"fmt"
 	"testing"
 
@@ -3296,3 +3297,54 @@ func TestModule_funcValidation_SIMD_error(t *testing.T) {
 		})
 	}
 }
+
+func TestDecodeBlockType(t *testing.T) {
+	t.Run("primitive", func(t *testing.T) {
+		for _, tc := range []struct {
+			name                 string
+			in                   byte
+			exp                  ValueType
+			expResultNumInUint64 int
+		}{
+			{name: "nil", in: 0x40},
+			{name: "i32", in: 0x7f, exp: ValueTypeI32, expResultNumInUint64: 1},
+			{name: "i64", in: 0x7e, exp: ValueTypeI64, expResultNumInUint64: 1},
+			{name: "f32", in: 0x7d, exp: ValueTypeF32, expResultNumInUint64: 1},
+			{name: "f64", in: 0x7c, exp: ValueTypeF64, expResultNumInUint64: 1},
+			{name: "v128", in: 0x7b, exp: ValueTypeV128, expResultNumInUint64: 2},
+			{name: "funcref", in: 0x70, exp: ValueTypeFuncref, expResultNumInUint64: 1},
+			{name: "externref", in: 0x6f, exp: ValueTypeExternref, expResultNumInUint64: 1},
+		} {
+			tc := tc
+			t.Run(tc.name, func(t *testing.T) {
+				actual, read, err := DecodeBlockType(nil, bytes.NewReader([]byte{tc.in}), Features20220419)
+				require.NoError(t, err)
+				require.Equal(t, uint64(1), read)
+				require.Equal(t, 0, len(actual.Params))
+				require.Equal(t, tc.expResultNumInUint64, actual.ResultNumInUint64)
+				require.Equal(t, 0, actual.ParamNumInUint64)
+				if tc.exp == 0 {
+					require.Equal(t, 0, len(actual.Results))
+				} else {
+					require.Equal(t, 1, len(actual.Results))
+					require.Equal(t, tc.exp, actual.Results[0])
+				}
+			})
+		}
+	})
+	t.Run("function type", func(t *testing.T) {
+		types := []*FunctionType{
+			{},
+			{Params: []ValueType{ValueTypeI32}},
+			{Results: []ValueType{ValueTypeI32}},
+			{Params: []ValueType{ValueTypeF32, ValueTypeV128}, Results: []ValueType{ValueTypeI32}},
+			{Params: []ValueType{ValueTypeF32, ValueTypeV128}, Results: []ValueType{ValueTypeI32, ValueTypeF32, ValueTypeV128}},
+		}
+		for index, expected := range types {
+			actual, read, err := DecodeBlockType(types, bytes.NewReader([]byte{byte(index)}), FeatureMultiValue)
+			require.NoError(t, err)
+			require.Equal(t, uint64(1), read)
+			require.Equal(t, expected, actual)
+		}
+	})
+}