Repository: tetratelabs/wazero
Pull request #1414 (2023-04-29 00:09:15)

Adds SplitCallStack for use in CallWithStack
This adds an internal function `wasm.SplitCallStack` for use in #1407. This is separate because the diff is a lot larger than the destination change ;)


Patch:
diff --git a/internal/wasm/func_validation.go b/internal/wasm/func_validation.go
index 6e185295..e9aef72c 100644
--- a/internal/wasm/func_validation.go
+++ b/internal/wasm/func_validation.go
@@ -1935,3 +1935,20 @@ var (
 	blockType_v_funcref   = &FunctionType{Results: []ValueType{ValueTypeFuncref}, ResultNumInUint64: 1}
 	blockType_v_externref = &FunctionType{Results: []ValueType{ValueTypeExternref}, ResultNumInUint64: 1}
 )
+
+// SplitCallStack returns the input stack resliced to the count of params and
+// results, or errors if it isn't long enough for either.
+func SplitCallStack(ft *FunctionType, stack []uint64) (params []uint64, results []uint64, err error) {
+	stackLen := len(stack)
+	if n := ft.ParamNumInUint64; n > stackLen {
+		return nil, nil, fmt.Errorf("need %d params, but stack size is %d", n, stackLen)
+	} else if n > 0 {
+		params = stack[:n]
+	}
+	if n := ft.ResultNumInUint64; n > stackLen {
+		return nil, nil, fmt.Errorf("need %d results, but stack size is %d", n, stackLen)
+	} else if n > 0 {
+		results = stack[:n]
+	}
+	return
+}


Test patch:
diff --git a/internal/wasm/func_validation_test.go b/internal/wasm/func_validation_test.go
index 4f75f094..e571ad2e 100644
--- a/internal/wasm/func_validation_test.go
+++ b/internal/wasm/func_validation_test.go
@@ -673,23 +673,31 @@ func TestModule_ValidateFunction_BulkMemoryOperations(t *testing.T) {
 }
 
 var (
-	f32, f64, i32, i64, externref = ValueTypeF32, ValueTypeF64, ValueTypeI32, ValueTypeI64, ValueTypeExternref
-	f32i32_v                      = FunctionType{Params: []ValueType{f32, i32}}
-	i32_i32                       = FunctionType{Params: []ValueType{i32}, Results: []ValueType{i32}}
-	i32f64_v                      = FunctionType{Params: []ValueType{i32, f64}}
-	i32i32_i32                    = FunctionType{Params: []ValueType{i32, i32}, Results: []ValueType{i32}}
-	i32_v                         = FunctionType{Params: []ValueType{i32}}
-	v_v                           = FunctionType{}
-	v_f32                         = FunctionType{Results: []ValueType{f32}}
-	v_f32f32                      = FunctionType{Results: []ValueType{f32, f32}}
-	v_f64i32                      = FunctionType{Results: []ValueType{f64, i32}}
-	v_f64f64                      = FunctionType{Results: []ValueType{f64, f64}}
-	v_i32                         = FunctionType{Results: []ValueType{i32}}
-	v_i32i32                      = FunctionType{Results: []ValueType{i32, i32}}
-	v_i32i64                      = FunctionType{Results: []ValueType{i32, i64}}
-	v_i64i64                      = FunctionType{Results: []ValueType{i64, i64}}
+	f32, f64, i32, i64, v128, externref = ValueTypeF32, ValueTypeF64, ValueTypeI32, ValueTypeI64, ValueTypeV128, ValueTypeExternref
+	f32i32_v                            = initFt([]ValueType{f32, i32}, nil)
+	f64f32_i64                          = initFt([]ValueType{f64, f32}, []ValueType{i64})
+	f64i32_v128i64                      = initFt([]ValueType{f64, i32}, []ValueType{v128, i64})
+	i32_i32                             = initFt([]ValueType{i32}, []ValueType{i32})
+	i32f64_v                            = initFt([]ValueType{i32, f64}, nil)
+	i32i32_i32                          = initFt([]ValueType{i32, i32}, []ValueType{i32})
+	i32_v                               = initFt([]ValueType{i32}, nil)
+	v_v                                 = FunctionType{}
+	v_f32                               = initFt(nil, []ValueType{f32})
+	v_f32f32                            = initFt(nil, []ValueType{f32, f32})
+	v_f64i32                            = initFt(nil, []ValueType{f64, i32})
+	v_f64f64                            = initFt(nil, []ValueType{f64, f64})
+	v_i32                               = initFt(nil, []ValueType{i32})
+	v_i32i32                            = initFt(nil, []ValueType{i32, i32})
+	v_i32i64                            = initFt(nil, []ValueType{i32, i64})
+	v_i64i64                            = initFt(nil, []ValueType{i64, i64})
 )
 
+func initFt(params, results []ValueType) FunctionType {
+	ft := FunctionType{Params: params, Results: results}
+	ft.CacheNumInUint64()
+	return ft
+}
+
 // TestModule_ValidateFunction_MultiValue_TypeMismatch are "type mismatch" tests when "multi-value" was merged.
 //
 // See https://github.com/WebAssembly/spec/commit/484180ba3d9d7638ba1cb400b699ffede796927c
@@ -3591,3 +3599,82 @@ func TestFunctionValidation_redundantElse(t *testing.T) {
 		0, nil, nil, nil, nil, nil, bytes.NewReader(nil))
 	require.EqualError(t, err, "redundant Else instruction at 0x1")
 }
+
+func Test_SplitCallStack(t *testing.T) {
+	oneToEight := []uint64{1, 2, 3, 4, 5, 6, 7, 8}
+
+	tests := []struct {
+		name                                   string
+		ft                                     *FunctionType
+		stack, expectedParams, expectedResults []uint64
+		expectedErr                            string
+	}{
+		{
+			name:            "v_v",
+			ft:              &v_v,
+			stack:           oneToEight,
+			expectedParams:  nil,
+			expectedResults: nil,
+		},
+		{
+			name:            "v_v - stack nil",
+			ft:              &v_v,
+			expectedParams:  nil,
+			expectedResults: nil,
+		},
+		{
+			name:            "v_i32",
+			ft:              &v_i32,
+			stack:           oneToEight,
+			expectedParams:  nil,
+			expectedResults: []uint64{1},
+		},
+		{
+			name:            "f32i32_v",
+			ft:              &f32i32_v,
+			stack:           oneToEight,
+			expectedParams:  []uint64{1, 2},
+			expectedResults: nil,
+		},
+		{
+			name:            "f64f32_i64",
+			ft:              &f64f32_i64,
+			stack:           oneToEight,
+			expectedParams:  []uint64{1, 2},
+			expectedResults: []uint64{1},
+		},
+		{
+			name:            "f64i32_v128i64",
+			ft:              &f64i32_v128i64,
+			stack:           oneToEight,
+			expectedParams:  []uint64{1, 2},
+			expectedResults: []uint64{1, 2, 3},
+		},
+		{
+			name:        "not enough room for params",
+			ft:          &f64i32_v128i64,
+			stack:       oneToEight[0:1],
+			expectedErr: "need 2 params, but stack size is 1",
+		},
+		{
+			name:        "not enough room for results",
+			ft:          &f64i32_v128i64,
+			stack:       oneToEight[0:2],
+			expectedErr: "need 3 results, but stack size is 2",
+		},
+	}
+
+	for _, tt := range tests {
+		tc := tt
+
+		t.Run(tc.name, func(t *testing.T) {
+			params, results, err := SplitCallStack(tc.ft, tc.stack)
+			if tc.expectedErr != "" {
+				require.EqualError(t, err, tc.expectedErr)
+			} else {
+				require.Equal(t, tc.expectedParams, params)
+				require.Equal(t, tc.expectedResults, results)
+			}
+		})
+	}
+}
diff --git a/internal/wasm/function_definition_test.go b/internal/wasm/function_definition_test.go
index acc6be03..5eca0fac 100644
--- a/internal/wasm/function_definition_test.go
+++ b/internal/wasm/function_definition_test.go
@@ -103,8 +103,8 @@ func TestModule_BuildFunctionDefinitions(t *testing.T) {
 				},
 				TypeSection: []FunctionType{
 					v_v,
-					{Params: []ValueType{ValueTypeF64, ValueTypeI32}, Results: []ValueType{ValueTypeV128, ValueTypeI64}},
-					{Params: []ValueType{ValueTypeF64, ValueTypeF32}, Results: []ValueType{ValueTypeI64}},
+					f64i32_v128i64,
+					f64f32_i64,
 				},
 			},
 			expected: []FunctionDefinition{
@@ -112,13 +112,13 @@ func TestModule_BuildFunctionDefinitions(t *testing.T) {
 					index:       0,
 					debugName:   ".$0",
 					exportNames: []string{"function_index=0"},
-					funcType:    &FunctionType{Params: []ValueType{ValueTypeF64, ValueTypeI32}, Results: []ValueType{ValueTypeV128, ValueTypeI64}},
+					funcType:    &f64i32_v128i64,
 				},
 				{
 					index:       1,
 					debugName:   ".$1",
 					exportNames: []string{"function_index=1"},
-					funcType:    &FunctionType{Params: []ValueType{ValueTypeF64, ValueTypeF32}, Results: []ValueType{ValueTypeI64}},
+					funcType:    &f64f32_i64,
 				},
 				{
 					index:       2,
@@ -132,13 +132,13 @@ func TestModule_BuildFunctionDefinitions(t *testing.T) {
 					index:       0,
 					debugName:   ".$0",
 					exportNames: []string{"function_index=0"},
-					funcType:    &FunctionType{Params: []ValueType{ValueTypeF64, ValueTypeI32}, Results: []ValueType{ValueTypeV128, ValueTypeI64}},
+					funcType:    &f64i32_v128i64,
 				},
 				"function_index=1": &FunctionDefinition{
 					index:       1,
 					exportNames: []string{"function_index=1"},
 					debugName:   ".$1",
-					funcType:    &FunctionType{Params: []ValueType{ValueTypeF64, ValueTypeF32}, Results: []ValueType{ValueTypeI64}},
+					funcType:    &f64f32_i64,
 				},
 				"function_index=2": &FunctionDefinition{
 					index:       2,
@@ -162,8 +162,8 @@ func TestModule_BuildFunctionDefinitions(t *testing.T) {
 				CodeSection:     []Code{{Body: []byte{OpcodeEnd}}, {Body: []byte{OpcodeEnd}}},
 				TypeSection: []FunctionType{
 					v_v,
-					{Params: []ValueType{ValueTypeF64, ValueTypeI32}, Results: []ValueType{ValueTypeV128, ValueTypeI64}},
-					{Params: []ValueType{ValueTypeF64, ValueTypeF32}, Results: []ValueType{ValueTypeI64}},
+					f64i32_v128i64,
+					f64f32_i64,
 				},
 			},
 			expected: []FunctionDefinition{
@@ -172,13 +172,13 @@ func TestModule_BuildFunctionDefinitions(t *testing.T) {
 					debugName:   ".$0",
 					importDesc:  imp,
 					exportNames: []string{"imported_function"},
-					funcType:    &FunctionType{Params: []ValueType{ValueTypeF64, ValueTypeF32}, Results: []ValueType{ValueTypeI64}},
+					funcType:    &f64f32_i64,
 				},
 				{
 					index:       1,
 					debugName:   ".$1",
 					exportNames: []string{"function_index=1"},
-					funcType:    &FunctionType{Params: []ValueType{ValueTypeF64, ValueTypeI32}, Results: []ValueType{ValueTypeV128, ValueTypeI64}},
+					funcType:    &f64i32_v128i64,
 				},
 				{
 					index:       2,
@@ -193,7 +193,7 @@ func TestModule_BuildFunctionDefinitions(t *testing.T) {
 					debugName:   ".$0",
 					importDesc:  imp,
 					exportNames: []string{"imported_function"},
-					funcType:    &FunctionType{Params: []ValueType{ValueTypeF64, ValueTypeF32}, Results: []ValueType{ValueTypeI64}},
+					funcType:    &f64f32_i64,
 				},
 			},
 			expectedExports: map[string]api.FunctionDefinition{
@@ -202,13 +202,13 @@ func TestModule_BuildFunctionDefinitions(t *testing.T) {
 					debugName:   ".$0",
 					importDesc:  imp,
 					exportNames: []string{"imported_function"},
-					funcType:    &FunctionType{Params: []ValueType{ValueTypeF64, ValueTypeF32}, Results: []ValueType{ValueTypeI64}},
+					funcType:    &f64f32_i64,
 				},
 				"function_index=1": &FunctionDefinition{
 					index:       1,
 					debugName:   ".$1",
 					exportNames: []string{"function_index=1"},
-					funcType:    &FunctionType{Params: []ValueType{ValueTypeF64, ValueTypeI32}, Results: []ValueType{ValueTypeV128, ValueTypeI64}},
+					funcType:    &f64i32_v128i64,
 				},
 				"function_index=2": &FunctionDefinition{
 					index:       2,