wasm-micro-runtime icon indicating copy to clipboard operation
wasm-micro-runtime copied to clipboard

Why do functions implemented in wasm require more than one parameter when calling CallFuncV?

Open littetimo567 opened this issue 1 year ago • 3 comments

  • I have a GetInt function in wasm:
// ...
//export GetInt
func GetInt() int32 {
        fmt.Println("this is a test.")
	return 15
}
// ...
  • there is no error when I calling CallFunc event though GetInt does not require any parameters:
func main(){
        // ...
	err = instance.CallFunc("GetInt", 1, []uint32{0})
	if err != nil {
		fmt.Println(err)
		return
	}
        // ...
}
  • But it must be an error when I calling CallFuncV: fatal error: "unexpected signal during runtime execution"
func main(){
        // ...
        results := make([]interface{}, 1)
	err = instance.CallFuncV("GetInt", 1, results)
	if err != nil {
		fmt.Println(err)
		return
	}
        // ...
}
  • Of course, there is no error if the GetInt is modified to:
// ...
//export GetInt
func GetInt(a int32) int32 {
	fmt.Println("this is a test.")
	return 15
}
// ...
  • And calling CallFuncV like that:
func main(){
        // ...
	results := make([]interface{}, 1)
	err = instance.CallFuncV("GetInt", 1, results, int32(1))
	if err != nil {
		fmt.Println(err)
		return
	}
        // ...
}
  • Finaly, I only want my function GetInt does not require any parameters. What should I do?

littetimo567 avatar Aug 09 '24 03:08 littetimo567

@littetimo567 Do you use wamr go binding to call the wasm function which hasn't parameter? Maybe you can get the function first and then get its parameter count, and then decide what to do?

https://github.com/bytecodealliance/wasm-micro-runtime/blob/main/language-bindings/go/wamr/instance.go#L167-L180

wenyongh avatar Aug 12 '24 08:08 wenyongh

@wenyongh I'm sure the function in my wasm has no parameter and return one value. Ex. I using the wamr-sdk, it's panic when I call the function of instance.CallFuncV. My function is :

package main

import "fmt"

func main() {}

//export GetInt
func GetInt() int32 {
	fmt.Println("this is a test.")
	return 15
}

I implemented a Call function in the Instance.go file by referring to CallFunc and CallFuncV. The Call function ignoring how many input parameters and how many output parameters. And solved my issue.

func (_self *Instance) Call(fn string, args ...interface{}) ([]interface{}, error) {
	f, err := _self.lookupFunction(fn)
	if err != nil {
		return nil, err
	}

	argc := uint32(C.wasm_func_get_param_count(f, _self._instance))
	resultCount := uint32(C.wasm_func_get_result_count(f, _self._instance))

	paramTypes := make([]C.uchar, argc)
	resultType := make([]C.uchar, resultCount)

	if argc > 0 {
		C.wasm_func_get_param_types(f, _self._instance,
			(*C.uchar)(unsafe.Pointer(&paramTypes[0])))
	}
	if resultCount > 0 {
		C.wasm_func_get_result_types(f, _self._instance,
			(*C.uchar)(unsafe.Pointer(&resultType[0])))
	}

	if len(args) != int(argc) {
		str := "invalid param count, must be equal to %d"
		return nil, fmt.Errorf(str, argc)
	}

	argvSize := argc * 2
	if resultCount > argc {
		argvSize = resultCount * 2
	}

	argv := make([]uint32, argvSize)
	index := uint32(0)
	for i, arg := range args {
		switch val := arg.(type) {
		case int32:
			if paramTypes[i] != C.WASM_I32 &&
				paramTypes[i] != C.WASM_FUNCREF &&
				paramTypes[i] != C.WASM_EXTERNREF {
				str := "invalid param type %d, " + "expect i32 but got other"
				return nil, fmt.Errorf(str, paramTypes[i])
			}
			argv[index] = uint32(val)
			index = index + 1
		case int64:
			if paramTypes[i] != C.WASM_I64 {
				str := "invalid param type %d, " + "expect i64 but got other"
				return nil, fmt.Errorf(str, paramTypes[i])
			}
			addr := (*C.uint32_t)(unsafe.Pointer(&argv[index]))
			C.PUT_I64_TO_ADDR(addr, (C.int64_t)(val))
			index += 2
		case float32:
			if paramTypes[i] != C.WASM_F32 {
				str := "invalid param type %d, " + "expect f32 but got other"
				return nil, fmt.Errorf(str, paramTypes[i])
			}
			*(*C.float)(unsafe.Pointer(&argv[index])) = (C.float)(val)
			index++
		case float64:
			if paramTypes[i] != C.WASM_F64 {
				str := "invalid param type %d, " + "expect f64 but got other"
				return nil, fmt.Errorf(str, paramTypes[i])
			}
			addr := (*C.uint32_t)(unsafe.Pointer(&argv[index]))
			C.PUT_F64_TO_ADDR(addr, (C.double)(val))
			index += 2
		default:
			return nil, fmt.Errorf("unknown param type %d", paramTypes[i])
		}
	}

	isThreadEnvInited := Runtime().ThreadEnvInited()
	if !isThreadEnvInited {
		Runtime().InitThreadEnv()
	}

	defer func() {
		if !isThreadEnvInited {
			Runtime().DestroyThreadEnv()
		}
	}()

	var argv_C *C.uint32_t
	if argc > 0 {
		argv_C = (*C.uint32_t)(unsafe.Pointer(&argv[0]))
	}

	argc_C := (C.uint)(argc)
	if !C.wasm_runtime_call_wasm(_self._exec_env, f, argc_C, argv_C) {
		return nil, fmt.Errorf("error: %s", string(_self.GetException()))
	}

	index = 0
	results := make([]interface{}, resultCount)
	for i := 0; i < int(resultCount); i++ {
		switch resultType[i] {
		case C.WASM_I32, C.WASM_FUNCREF, C.WASM_EXTERNREF:
			i32 := int32(argv[index])
			results[i] = i32
			index = index + 1
		case C.WASM_I64:
			addr := (*C.uint32_t)(unsafe.Pointer(&argv[index]))
			results[i] = (int64)(C.GET_I64_FROM_ADDR(addr))
			index = index + 2
		case C.WASM_F32:
			addr := (*C.float)(unsafe.Pointer(&argv[index]))
			results[i] = (float32)(*addr)
			index++
		case C.WASM_F64:
			addr := (*C.uint32_t)(unsafe.Pointer(&argv[index]))
			results[i] = (float64)(C.GET_F64_FROM_ADDR(addr))
			index += 2
		}
	}

	return results, nil
}

func (_self *Instance) lookupFunction(fn string) (C.wasm_function_inst_t, error) {
	_self._mutex.Lock()
	defer _self._mutex.Unlock()

	f := _self._exportsCache[fn]
	if f == nil {
		cfn := (C.CString)(fn)
		defer C.free(unsafe.Pointer(cfn))

		f = C.wasm_runtime_lookup_function(_self._instance, cfn)
		if f == nil {
			return nil, fmt.Errorf("lookup function failed")
		}

		_self._exportsCache[fn] = f
	}

	return f, nil
}

littetimo567 avatar Aug 15 '24 02:08 littetimo567

OK, thanks for update, how about renaming Call to CallFuncV2 and lookupFunction to LookupFunction? And could you submit a PR to upload the code?

wenyongh avatar Aug 15 '24 02:08 wenyongh