mirror of
				https://github.com/bytecodealliance/wasm-micro-runtime.git
				synced 2025-10-30 21:02:27 +00:00 
			
		
		
		
	 5b1dcf2fa2
			
		
	
	
		5b1dcf2fa2
		
			
		
	
	
	
	
		
			
			Implement Go binding APIs of runtime, module and instance Add sample, build scripts and update the document Co-authored-by: venus-taibai <97893654+venus-taibai@users.noreply.github.com>
		
			
				
	
	
		
			386 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			386 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
| /*
 | |
|  * Copyright (C) 2019 Intel Corporation.  All rights reserved.
 | |
|  * SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | |
|  */
 | |
| 
 | |
| package wamr
 | |
| 
 | |
| /*
 | |
| #include <stdlib.h>
 | |
| #include <wasm_export.h>
 | |
| 
 | |
| static inline void
 | |
| PUT_I64_TO_ADDR(uint32_t *addr, int64_t value)
 | |
| {
 | |
|     union {
 | |
|         int64_t val;
 | |
|         uint32_t parts[2];
 | |
|     } u;
 | |
|     u.val = value;
 | |
|     addr[0] = u.parts[0];
 | |
|     addr[1] = u.parts[1];
 | |
| }
 | |
| 
 | |
| static inline void
 | |
| PUT_F64_TO_ADDR(uint32_t *addr, double value)
 | |
| {
 | |
|     union {
 | |
|         double val;
 | |
|         uint32_t parts[2];
 | |
|     } u;
 | |
|     u.val = value;
 | |
|     addr[0] = u.parts[0];
 | |
|     addr[1] = u.parts[1];
 | |
| }
 | |
| 
 | |
| static inline int64_t
 | |
| GET_I64_FROM_ADDR(uint32_t *addr)
 | |
| {
 | |
|     union {
 | |
|         int64_t val;
 | |
|         uint32_t parts[2];
 | |
|     } u;
 | |
|     u.parts[0] = addr[0];
 | |
|     u.parts[1] = addr[1];
 | |
|     return u.val;
 | |
| }
 | |
| 
 | |
| static inline double
 | |
| GET_F64_FROM_ADDR(uint32_t *addr)
 | |
| {
 | |
|     union {
 | |
|         double val;
 | |
|         uint32_t parts[2];
 | |
|     } u;
 | |
|     u.parts[0] = addr[0];
 | |
|     u.parts[1] = addr[1];
 | |
|     return u.val;
 | |
| }
 | |
| */
 | |
| import "C"
 | |
| 
 | |
| import (
 | |
|     "runtime"
 | |
|     "unsafe"
 | |
|     "fmt"
 | |
| )
 | |
| 
 | |
| type Instance struct {
 | |
|     _instance C.wasm_module_inst_t
 | |
|     _exec_env C.wasm_exec_env_t
 | |
|     _module *Module
 | |
|     _exportsCache map[string]C.wasm_function_inst_t
 | |
| }
 | |
| 
 | |
| /* Create instance from the module */
 | |
| func NewInstance(module *Module,
 | |
|                  stackSize uint, heapSize uint) (*Instance, error) {
 | |
|     if (module == nil) {
 | |
|         return nil, fmt.Errorf("NewInstance error: invalid input")
 | |
|     }
 | |
| 
 | |
|     errorBytes := make([]byte, 128)
 | |
|     errorPtr := (*C.char)(unsafe.Pointer(&errorBytes[0]))
 | |
|     errorLen := C.uint(len(errorBytes))
 | |
| 
 | |
|     instance := C.wasm_runtime_instantiate(module.module, C.uint(stackSize),
 | |
|                                            C.uint(heapSize), errorPtr, errorLen)
 | |
|     if (instance == nil) {
 | |
|         return nil, fmt.Errorf("NewInstance Error: %s", string(errorBytes))
 | |
|     }
 | |
| 
 | |
|     exec_env := C.wasm_runtime_create_exec_env(instance, C.uint(stackSize))
 | |
|     if (exec_env == nil) {
 | |
|         C.wasm_runtime_deinstantiate(instance)
 | |
|         return nil, fmt.Errorf("NewInstance Error: create exec_env failed")
 | |
|     }
 | |
| 
 | |
|     self := &Instance{
 | |
|         _instance: instance,
 | |
|         _exec_env: exec_env,
 | |
|         _module: module,
 | |
|         _exportsCache: make(map[string]C.wasm_function_inst_t),
 | |
|     }
 | |
| 
 | |
|     runtime.SetFinalizer(self, func(self *Instance) {
 | |
|         self.Destroy()
 | |
|     })
 | |
| 
 | |
|     return self, nil
 | |
| }
 | |
| 
 | |
| /* Destroy the instance */
 | |
| func (self *Instance) Destroy() {
 | |
|     runtime.SetFinalizer(self, nil)
 | |
|     if (self._instance != nil) {
 | |
|         C.wasm_runtime_deinstantiate(self._instance)
 | |
|     }
 | |
|     if (self._exec_env != nil) {
 | |
|         C.wasm_runtime_destroy_exec_env(self._exec_env)
 | |
|     }
 | |
| }
 | |
| 
 | |
| /* Call the wasm function with argument in the uint32 array, and store
 | |
|    the return values back into the array */
 | |
| func (self *Instance) CallFunc(funcName string,
 | |
|                                argc uint32, args []uint32) error {
 | |
|     _func := self._exportsCache[funcName]
 | |
|     if _func == nil {
 | |
|         cName := C.CString(funcName)
 | |
|         defer C.free(unsafe.Pointer(cName))
 | |
| 
 | |
|         _func = C.wasm_runtime_lookup_function(self._instance,
 | |
|                                                cName, (*C.char)(C.NULL))
 | |
|         if _func == nil {
 | |
|             return fmt.Errorf("CallFunc error: lookup function failed")
 | |
|         }
 | |
|         self._exportsCache[funcName] = _func
 | |
|     }
 | |
| 
 | |
|     thread_env_inited := Runtime().ThreadEnvInited()
 | |
|     if (!thread_env_inited) {
 | |
|         Runtime().InitThreadEnv()
 | |
|     }
 | |
| 
 | |
|     var args_C *C.uint32_t
 | |
|     if (argc > 0) {
 | |
|         args_C = (*C.uint32_t)(unsafe.Pointer(&args[0]))
 | |
|     }
 | |
|     if (!C.wasm_runtime_call_wasm(self._exec_env, _func,
 | |
|                                   C.uint(argc), args_C)) {
 | |
|         if (!thread_env_inited) {
 | |
|             Runtime().DestroyThreadEnv()
 | |
|         }
 | |
|         return fmt.Errorf("CallFunc error: %s", string(self.GetException()))
 | |
|     }
 | |
| 
 | |
|     if (!thread_env_inited) {
 | |
|         Runtime().DestroyThreadEnv()
 | |
|     }
 | |
|     return nil
 | |
| }
 | |
| 
 | |
| /* Call the wasm function with variant arguments, and store the return
 | |
|    values back into the results array */
 | |
| func (self *Instance) CallFuncV(funcName string,
 | |
|                                 num_results uint32, results []interface{},
 | |
|                                 args ... interface{}) error {
 | |
|     _func := self._exportsCache[funcName]
 | |
|     if _func == nil {
 | |
|         cName := C.CString(funcName)
 | |
|         defer C.free(unsafe.Pointer(cName))
 | |
| 
 | |
|         _func = C.wasm_runtime_lookup_function(self._instance,
 | |
|                                                cName, (*C.char)(C.NULL))
 | |
|         if _func == nil {
 | |
|             return fmt.Errorf("CallFunc error: lookup function failed")
 | |
|         }
 | |
|         self._exportsCache[funcName] = _func
 | |
|     }
 | |
| 
 | |
|     param_count := uint32(C.wasm_func_get_param_count(_func, self._instance))
 | |
|     result_count := uint32(C.wasm_func_get_result_count(_func, self._instance))
 | |
| 
 | |
|     if (num_results < result_count) {
 | |
|         str := "CallFunc error: invalid result count %d, " +
 | |
|                "must be no smaller than %d"
 | |
|         return fmt.Errorf(str, num_results, result_count)
 | |
|     }
 | |
| 
 | |
|     param_types := make([]C.uchar, param_count, param_count)
 | |
|     result_types := make([]C.uchar, result_count, result_count)
 | |
|     if (param_count > 0) {
 | |
|         C.wasm_func_get_param_types(_func, self._instance,
 | |
|                                     (*C.uchar)(unsafe.Pointer(¶m_types[0])))
 | |
|     }
 | |
|     if (result_count > 0) {
 | |
|         C.wasm_func_get_result_types(_func, self._instance,
 | |
|                                      (*C.uchar)(unsafe.Pointer(&result_types[0])))
 | |
|     }
 | |
| 
 | |
|     argv_size := param_count * 2
 | |
|     if (result_count > param_count) {
 | |
|         argv_size = result_count * 2
 | |
|     }
 | |
|     argv := make([]uint32, argv_size, argv_size)
 | |
| 
 | |
|     var i, argc uint32
 | |
|     for _, arg := range args {
 | |
|         if (i >= param_count) {
 | |
|             break;
 | |
|         }
 | |
|         switch arg.(type) {
 | |
|             case int32:
 | |
|                 if (param_types[i] != C.WASM_I32 &&
 | |
|                     param_types[i] != C.WASM_FUNCREF &&
 | |
|                     param_types[i] != C.WASM_ANYREF) {
 | |
|                     str := "CallFunc error: invalid param type %d, " +
 | |
|                            "expect i32 but got other"
 | |
|                     return fmt.Errorf(str, param_types[i])
 | |
|                 }
 | |
|                 argv[argc] = (uint32)(arg.(int32))
 | |
|                 argc++
 | |
|                 break
 | |
|             case int64:
 | |
|                 if (param_types[i] != C.WASM_I64) {
 | |
|                     str := "CallFunc error: invalid param type %d, " +
 | |
|                            "expect i64 but got other"
 | |
|                     return fmt.Errorf(str, param_types[i])
 | |
|                 }
 | |
|                 addr := (*C.uint32_t)(unsafe.Pointer(&argv[argc]))
 | |
|                 C.PUT_I64_TO_ADDR(addr, (C.int64_t)(arg.(int64)))
 | |
|                 argc += 2
 | |
|                 break
 | |
|             case float32:
 | |
|                 if (param_types[i] != C.WASM_F32) {
 | |
|                     str := "CallFunc error: invalid param type %d, " +
 | |
|                            "expect f32 but got other"
 | |
|                     return fmt.Errorf(str, param_types[i])
 | |
|                 }
 | |
|                 *(*C.float)(unsafe.Pointer(&argv[argc])) = (C.float)(arg.(float32))
 | |
|                 argc++
 | |
|                 break
 | |
|             case float64:
 | |
|                 if (param_types[i] != C.WASM_F64) {
 | |
|                     str := "CallFunc error: invalid param type %d, " +
 | |
|                            "expect f64 but got other"
 | |
|                     return fmt.Errorf(str, param_types[i])
 | |
|                 }
 | |
|                 addr := (*C.uint32_t)(unsafe.Pointer(&argv[argc]))
 | |
|                 C.PUT_F64_TO_ADDR(addr, (C.double)(arg.(float64)))
 | |
|                 argc += 2
 | |
|                 break
 | |
|             default:
 | |
|                 return fmt.Errorf("CallFunc error: unknown param type %d",
 | |
|                                   param_types[i])
 | |
|         }
 | |
|         i++
 | |
|     }
 | |
| 
 | |
|     if (i < param_count) {
 | |
|         str := "CallFunc error: invalid param count, " +
 | |
|                "must be no smaller than %d"
 | |
|         return fmt.Errorf(str, param_count)
 | |
|     }
 | |
| 
 | |
|     err := self.CallFunc(funcName, argc, argv)
 | |
|     if (err != nil) {
 | |
|         return err
 | |
|     }
 | |
| 
 | |
|     argc = 0
 | |
|     for i = 0; i < result_count; i++ {
 | |
|         switch result_types[i] {
 | |
|             case C.WASM_I32:
 | |
|             case C.WASM_FUNCREF:
 | |
|             case C.WASM_ANYREF:
 | |
|                 i32 := (int32)(argv[argc])
 | |
|                 results[i] = i32
 | |
|                 argc++
 | |
|                 break
 | |
|             case C.WASM_I64:
 | |
|                 addr := (*C.uint32_t)(unsafe.Pointer(&argv[argc]))
 | |
|                 results[i] = (int64)(C.GET_I64_FROM_ADDR(addr))
 | |
|                 argc += 2
 | |
|                 break
 | |
|             case C.WASM_F32:
 | |
|                 addr := (*C.float)(unsafe.Pointer(&argv[argc]))
 | |
|                 results[i] = (float32)(*addr)
 | |
|                 argc++
 | |
|                 break
 | |
|             case C.WASM_F64:
 | |
|                 addr := (*C.uint32_t)(unsafe.Pointer(&argv[argc]))
 | |
|                 results[i] = (float64)(C.GET_F64_FROM_ADDR(addr))
 | |
|                 argc += 2
 | |
|                 break
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return nil
 | |
| }
 | |
| 
 | |
| /* Get exception info of the instance */
 | |
| func (self *Instance) GetException() string {
 | |
|     cStr := C.wasm_runtime_get_exception(self._instance)
 | |
|     goStr := C.GoString(cStr)
 | |
|     return goStr
 | |
| }
 | |
| 
 | |
| /* Allocate memory from the heap of the instance */
 | |
| func (self Instance) ModuleMalloc(size uint32) (uint32, *uint8) {
 | |
|     var offset C.uint32_t
 | |
|     native_addrs := make([]*uint8, 1, 1)
 | |
|     ptr := unsafe.Pointer(&native_addrs[0])
 | |
|     offset = C.wasm_runtime_module_malloc(self._instance, (C.uint32_t)(size),
 | |
|                                           (*unsafe.Pointer)(ptr))
 | |
|     return (uint32)(offset), native_addrs[0]
 | |
| }
 | |
| 
 | |
| /* Free memory to the heap of the instance */
 | |
| func (self Instance) ModuleFree(offset uint32) {
 | |
|     C.wasm_runtime_module_free(self._instance, (C.uint32_t)(offset))
 | |
| }
 | |
| 
 | |
| func (self Instance) ValidateAppAddr(app_offset uint32, size uint32) bool {
 | |
|     ret := C.wasm_runtime_validate_app_addr(self._instance,
 | |
|                                             (C.uint32_t)(app_offset),
 | |
|                                             (C.uint32_t)(size))
 | |
|     return (bool)(ret)
 | |
| }
 | |
| 
 | |
| func (self Instance) ValidateStrAddr(app_str_offset uint32) bool {
 | |
|     ret := C.wasm_runtime_validate_app_str_addr(self._instance,
 | |
|                                                 (C.uint32_t)(app_str_offset))
 | |
|     return (bool)(ret)
 | |
| }
 | |
| 
 | |
| func (self Instance) ValidateNativeAddr(native_ptr *uint8, size uint32) bool {
 | |
|     native_ptr_C := (unsafe.Pointer)(native_ptr)
 | |
|     ret := C.wasm_runtime_validate_native_addr(self._instance,
 | |
|                                                native_ptr_C,
 | |
|                                                (C.uint32_t)(size))
 | |
|     return (bool)(ret)
 | |
| }
 | |
| 
 | |
| func (self Instance) AddrAppToNative(app_offset uint32) *uint8 {
 | |
|     native_ptr := C.wasm_runtime_addr_app_to_native(self._instance,
 | |
|                                                     (C.uint32_t)(app_offset))
 | |
|     return (*uint8)(native_ptr)
 | |
| }
 | |
| 
 | |
| func (self Instance) AddrNativeToApp(native_ptr *uint8) uint32 {
 | |
|     native_ptr_C := (unsafe.Pointer)(native_ptr)
 | |
|     offset := C.wasm_runtime_addr_native_to_app(self._instance,
 | |
|                                                 native_ptr_C)
 | |
|     return (uint32)(offset)
 | |
| }
 | |
| 
 | |
| func (self Instance) GetAppAddrRange(app_offset uint32) (bool,
 | |
|                                                          uint32,
 | |
|                                                          uint32) {
 | |
|     var start_offset, end_offset C.uint32_t
 | |
|     ret := C.wasm_runtime_get_app_addr_range(self._instance,
 | |
|                                              (C.uint32_t)(app_offset),
 | |
|                                              &start_offset, &end_offset)
 | |
|     return (bool)(ret), (uint32)(start_offset), (uint32)(end_offset)
 | |
| }
 | |
| 
 | |
| func (self Instance) GetNativeAddrRange(native_ptr *uint8) (bool,
 | |
|                                                             *uint8,
 | |
|                                                             *uint8) {
 | |
|     var start_addr, end_addr *C.uint8_t
 | |
|     native_ptr_C := (*C.uint8_t)((unsafe.Pointer)(native_ptr))
 | |
|     ret := C.wasm_runtime_get_native_addr_range(self._instance,
 | |
|                                                 native_ptr_C,
 | |
|                                                 &start_addr, &end_addr)
 | |
|     return (bool)(ret), (*uint8)(start_addr), (*uint8)(end_addr)
 | |
| }
 | |
| 
 | |
| func (self Instance) DumpMemoryConsumption() {
 | |
|     C.wasm_runtime_dump_mem_consumption(self._exec_env)
 | |
| }
 | |
| 
 | |
| func (self Instance) DumpCallStack() {
 | |
|     C.wasm_runtime_dump_call_stack(self._exec_env)
 | |
| }
 |