Merge pull request #549 from bytecodealliance/main

Merge bytecodealliance:main into wenyongh:main
This commit is contained in:
Wenyong Huang 2022-06-01 16:27:22 +08:00 committed by GitHub
commit e0bf46aa5c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 7773 additions and 9 deletions

View File

@ -445,7 +445,7 @@ wasm_runtime_register_module_internal(const char *module_name,
WASMModuleCommon *module,
uint8 *orig_file_buf,
uint32 orig_file_buf_size,
char *error_buf, uint32_t error_buf_size)
char *error_buf, uint32 error_buf_size)
{
WASMRegisteredModule *node = NULL;
@ -500,7 +500,7 @@ wasm_runtime_register_module_internal(const char *module_name,
bool
wasm_runtime_register_module(const char *module_name, WASMModuleCommon *module,
char *error_buf, uint32_t error_buf_size)
char *error_buf, uint32 error_buf_size)
{
if (!error_buf || !error_buf_size) {
LOG_ERROR("error buffer is required");
@ -823,7 +823,7 @@ wasm_runtime_load(uint8 *buf, uint32 size, char *error_buf,
WASMModuleCommon *
wasm_runtime_load_from_sections(WASMSection *section_list, bool is_aot,
char *error_buf, uint32_t error_buf_size)
char *error_buf, uint32 error_buf_size)
{
WASMModuleCommon *module_common;
@ -980,6 +980,23 @@ wasm_runtime_destroy_thread_env(void)
#endif
}
bool
wasm_runtime_thread_env_inited(void)
{
#ifdef BH_PLATFORM_WINDOWS
if (!os_thread_env_inited())
return false;
#endif
#if WASM_ENABLE_AOT != 0
#ifdef OS_ENABLE_HW_BOUND_CHECK
if (!os_thread_signal_inited())
return false;
#endif
#endif
return true;
}
#if (WASM_ENABLE_MEMORY_PROFILING != 0) || (WASM_ENABLE_MEMORY_TRACING != 0)
void
wasm_runtime_dump_module_mem_consumption(const WASMModuleCommon *module)
@ -1222,8 +1239,81 @@ wasm_runtime_lookup_function(WASMModuleInstanceCommon *const module_inst,
return NULL;
}
uint32
wasm_func_get_param_count(WASMFunctionInstanceCommon *const func_inst,
WASMModuleInstanceCommon *const module_inst)
{
WASMType *type =
wasm_runtime_get_function_type(func_inst, module_inst->module_type);
return type->param_count;
}
uint32
wasm_func_get_result_count(WASMFunctionInstanceCommon *const func_inst,
WASMModuleInstanceCommon *const module_inst)
{
WASMType *type =
wasm_runtime_get_function_type(func_inst, module_inst->module_type);
return type->result_count;
}
static uint8
val_type_to_val_kind(uint8 value_type)
{
switch (value_type) {
case VALUE_TYPE_I32:
return WASM_I32;
case VALUE_TYPE_I64:
return WASM_I64;
case VALUE_TYPE_F32:
return WASM_F32;
case VALUE_TYPE_F64:
return WASM_F64;
case VALUE_TYPE_FUNCREF:
return WASM_FUNCREF;
case VALUE_TYPE_EXTERNREF:
return WASM_ANYREF;
default:
#if WASM_ENABLE_GC != 0
if (wasm_is_type_reftype(value_type))
return WASM_ANYREF;
#endif
bh_assert(0);
return 0;
}
}
void
wasm_func_get_param_types(WASMFunctionInstanceCommon *const func_inst,
WASMModuleInstanceCommon *const module_inst,
wasm_valkind_t *param_types)
{
WASMType *type =
wasm_runtime_get_function_type(func_inst, module_inst->module_type);
uint32 i;
for (i = 0; i < type->param_count; i++) {
param_types[i] = val_type_to_val_kind(type->types[i]);
}
}
void
wasm_func_get_result_types(WASMFunctionInstanceCommon *const func_inst,
WASMModuleInstanceCommon *const module_inst,
wasm_valkind_t *result_types)
{
WASMType *type =
wasm_runtime_get_function_type(func_inst, module_inst->module_type);
uint32 i;
for (i = 0; i < type->result_count; i++) {
result_types[i] =
val_type_to_val_kind(type->types[type->param_count + i]);
}
}
#if WASM_ENABLE_REF_TYPES != 0
/* (uintptr_t)externref -> (uint32_t)index */
/* (uintptr_t)externref -> (uint32)index */
/* argv -> *ret_argv */
static bool
wasm_runtime_prepare_call_function(WASMExecEnv *exec_env,
@ -1321,7 +1411,7 @@ wasm_runtime_prepare_call_function(WASMExecEnv *exec_env,
return true;
}
/* (uintptr_t)externref <- (uint32_t)index */
/* (uintptr_t)externref <- (uint32)index */
/* argv <- new_argv */
static bool
wasm_runtime_finalize_call_function(WASMExecEnv *exec_env,
@ -3952,8 +4042,8 @@ fail:
|| defined(BUILD_TARGET_RISCV64_LP64) */
bool
wasm_runtime_call_indirect(WASMExecEnv *exec_env, uint32_t element_indices,
uint32_t argc, uint32_t argv[])
wasm_runtime_call_indirect(WASMExecEnv *exec_env, uint32 element_indices,
uint32 argc, uint32 argv[])
{
if (!wasm_runtime_exec_env_check(exec_env)) {
LOG_ERROR("Invalid exec env stack info.");

View File

@ -303,6 +303,11 @@ typedef struct WASMModuleCommon {
Wasm_Module_AoT, and this structure should be treated as
AOTModule structure. */
uint32 module_type;
/* The following uint8[1] member is a dummy just to indicate
some module_type dependent members follow.
Typically it should be accessed by casting to the corresponding
actual module_type dependent structure, not via this member. */
uint8 module_data[1];
} WASMModuleCommon;
@ -314,6 +319,11 @@ typedef struct WASMModuleInstanceCommon {
Wasm_Module_AoT, and this structure should be treated as
AOTModuleInstance structure. */
uint32 module_type;
/* The following uint8[1] member is a dummy just to indicate
some module_type dependent members follow.
Typically it should be accessed by casting to the corresponding
actual module_type dependent structure, not via this member. */
uint8 module_inst_data[1];
} WASMModuleInstanceCommon;
@ -377,6 +387,11 @@ typedef struct WASMRegisteredModule {
typedef struct WASMMemoryInstanceCommon {
uint32 module_type;
/* The following uint8[1] member is a dummy just to indicate
some module_type dependent members follow.
Typically it should be accessed by casting to the corresponding
actual module_type dependent structure, not via this member. */
uint8 memory_inst_data[1];
} WASMMemoryInstanceCommon;
@ -456,6 +471,28 @@ WASMType *
wasm_runtime_get_function_type(const WASMFunctionInstanceCommon *function,
uint32 module_type);
/* See wasm_export.h for description */
WASM_RUNTIME_API_EXTERN uint32
wasm_func_get_param_count(WASMFunctionInstanceCommon *const func_inst,
WASMModuleInstanceCommon *const module_inst);
/* See wasm_export.h for description */
WASM_RUNTIME_API_EXTERN uint32
wasm_func_get_result_count(WASMFunctionInstanceCommon *const func_inst,
WASMModuleInstanceCommon *const module_inst);
/* See wasm_export.h for description */
WASM_RUNTIME_API_EXTERN void
wasm_func_get_param_types(WASMFunctionInstanceCommon *const func_inst,
WASMModuleInstanceCommon *const module_inst,
wasm_valkind_t *param_types);
/* See wasm_export.h for description */
WASM_RUNTIME_API_EXTERN void
wasm_func_get_result_types(WASMFunctionInstanceCommon *const func_inst,
WASMModuleInstanceCommon *const module_inst,
wasm_valkind_t *result_types);
/* See wasm_export.h for description */
WASM_RUNTIME_API_EXTERN WASMExecEnv *
wasm_runtime_create_exec_env(WASMModuleInstanceCommon *module_inst,

View File

@ -407,6 +407,54 @@ WASM_RUNTIME_API_EXTERN wasm_function_inst_t
wasm_runtime_lookup_function(wasm_module_inst_t const module_inst,
const char *name, const char *signature);
/**
* Get parameter count of the function instance
*
* @param func_inst the function instance
* @param module_inst the module instance the function instance belongs to
*
* @return the parameter count of the function instance
*/
WASM_RUNTIME_API_EXTERN uint32_t
wasm_func_get_param_count(wasm_function_inst_t const func_inst,
wasm_module_inst_t const module_inst);
/**
* Get result count of the function instance
*
* @param func_inst the function instance
* @param module_inst the module instance the function instance belongs to
*
* @return the result count of the function instance
*/
WASM_RUNTIME_API_EXTERN uint32_t
wasm_func_get_result_count(wasm_function_inst_t const func_inst,
wasm_module_inst_t const module_inst);
/**
* Get parameter types of the function instance
*
* @param func_inst the function instance
* @param module_inst the module instance the function instance belongs to
* @param param_types the parameter types returned
*/
WASM_RUNTIME_API_EXTERN void
wasm_func_get_param_types(wasm_function_inst_t const func_inst,
wasm_module_inst_t const module_inst,
wasm_valkind_t *param_types);
/**
* Get result types of the function instance
*
* @param func_inst the function instance
* @param module_inst the module instance the function instance belongs to
* @param result_types the result types returned
*/
WASM_RUNTIME_API_EXTERN void
wasm_func_get_result_types(wasm_function_inst_t const func_inst,
wasm_module_inst_t const module_inst,
wasm_valkind_t *result_types);
/**
* Create execution environment for a WASM module instance.
*
@ -449,7 +497,7 @@ WASM_RUNTIME_API_EXTERN uint32_t
wasm_runtime_start_debug_instance(wasm_exec_env_t exec_env);
/**
* Initialize thread environment.
* Initialize the thread environment.
* Note:
* If developer creates a child thread by himself to call the
* the wasm function in that thread, he should call this API
@ -464,11 +512,17 @@ WASM_RUNTIME_API_EXTERN bool
wasm_runtime_init_thread_env(void);
/**
* Destroy thread environment
* Destroy the thread environment
*/
WASM_RUNTIME_API_EXTERN void
wasm_runtime_destroy_thread_env(void);
/**
* Whether the thread environment is initialized
*/
WASM_RUNTIME_API_EXTERN bool
wasm_runtime_thread_env_inited(void);
/**
* Get WASM module instance from execution environment
*

View File

@ -107,6 +107,12 @@ os_thread_env_init();
void
os_thread_env_destroy();
/**
* Whether the thread environment is initialized
*/
bool
os_thread_env_inited();
/**
* Suspend execution of the calling thread for (at least)
* usec microseconds

View File

@ -360,6 +360,13 @@ os_thread_env_destroy()
}
}
bool
os_thread_env_inited()
{
os_thread_data *thread_data = TlsGetValue(thread_data_key);
return thread_data ? true : false;
}
int
os_sem_init(korp_sem *sem)
{

View File

@ -0,0 +1,104 @@
WAMR Go binding: Embedding WAMR in Go guideline
===============================================
This Go library uses CGO to consume the runtime APIs of the WAMR project which are defined in [core/iwasm/include/wasm_export.h](../../core/iwasm/include/wasm_export.h). The API details are available in the header files.
## Installation
### Installing from the source code
Installing from local source tree is in _development mode_.
Run `./build.sh` in this folder to build the package, which builds the WAMR runtime library firstly and then builds the Go binding library.
Run `./build.sh` under `samples` folder to build and test the sample.
```bash
cd samples
./build.sh
```
## Supported APIs
All the embedding APIs supported are defined under folder [wamr](./wamr).
### Runtime APIs
```Go
func Runtime() *_Runtime
func (self *_Runtime) FullInit(alloc_with_pool bool, heap_buf []byte,
max_thread_num uint) error
func (self *_Runtime) Init() error
func (self *_Runtime) Destroy()
func (self *_Runtime) SetLogLevel(level LogLevel)
func (self *_Runtime) Malloc(size uint32) *uint8
func (self *_Runtime) Free(ptr *uint8)
```
### Module APIs
```Go
func NewModule(wasmBytes []byte) (*Module, error)
func (self *Module) Destroy()
func (self *Module) SetWasiArgs(dirList [][]byte, mapDirList [][]byte,
env [][]byte, argv[][]byte)
func (self *Module) SetWasiArgsEx(dirList [][]byte, mapDirList [][]byte,
env [][]byte, argv[][]byte,
stdinfd int, stdoutfd int, stderrfd int)
func (self *Module) SetWasiAddrPool(addrPool [][]byte)
```
### Instance APIs
```Go
func NewInstance(module *Module,
stackSize uint, heapSize uint) (*Instance, error)
func (self *Instance) Destroy()
func (self *Instance) CallFunc(funcName string,
argc uint32, args []uint32) error
func (self *Instance) CallFuncV(funcName string,
num_results uint32, results []interface{},
args ... interface{}) error
func (self *Instance) GetException() string
func (self Instance) ModuleMalloc(size uint32) (uint32, *uint8)
func (self Instance) ModuleFree(offset uint32)
func (self Instance) ValidateAppAddr(app_offset uint32, size uint32) bool
func (self Instance) ValidateNativeAddr(native_ptr *uint8, size uint32) bool
func (self Instance) AddrAppToNative(app_offset uint32) *uint8
func (self Instance) AddrNativeToApp(native_ptr *uint8) uint32
func (self Instance) GetAppAddrRange(app_offset uint32) (bool, uint32, uint32)
func (self Instance) GetNativeAddrRange(native_ptr *uint8) (bool, *uint8, *uint8)
func (self Instance) DumpMemoryConsumption()
func (self Instance) DumpCallStack()
```
## Sample codes
```Go
var module *wamr.Module
var instance *wamr.Instance
var results []interface{}
var err error
/* Runtime initialization */
err = wamr.Runtime().FullInit(false, nil, 1)
/* Read WASM/AOT file into a memory buffer */
wasmBytes := read_wasm_binary_to_buffer(...)
/* Load WASM/AOT module from the memory buffer */
module, err = wamr.NewModule(wasmBytes)
/* Create WASM/AOT instance from the module */
instance, err = wamr.NewInstance(module, 16384, 16384)
/* Call the `fib` function */
results = make([]interface{}, 1, 1)
err = instance.CallFuncV("fib", 1, results, (int32)32)
fmt.Printf("fib(32) return: %d\n", results[0].(int32));
/* Destroy runtime */
wamr.Runtime().Destroy()
```
More samples can be found in [test.go](./samples/test.go)

21
language-bindings/go/build.sh Executable file
View File

@ -0,0 +1,21 @@
#!/usr/bin/env bash
# Copyright (C) 2019 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
PLATFORM=$(uname -s | tr A-Z a-z)
CUR_DIR=$PWD
WAMR_DIR=$PWD/../..
WAMR_GO_DIR=$PWD/wamr
cp -a ${WAMR_DIR}/core/iwasm/include/*.h ${WAMR_GO_DIR}/packaged/include
mkdir -p build && cd build
cmake ${WAMR_DIR}/product-mini/platforms/${PLATFORM} \
-DWAMR_BUILD_LIB_PTHREAD=1 -DWAMR_BUILD_DUMP_CALL_STACK=1 \
-DWAMR_BUILD_MEMORY_PROFILING=1
make -j ${nproc}
cp -a libvmlib.a ${WAMR_GO_DIR}/packaged/lib/${PLATFORM}-amd64
cd ${WAMR_GO_DIR}
go test

View File

@ -0,0 +1,5 @@
module gitlab.alipay-inc.com/TNT_Runtime/ant-runtime/bindings/go
go 1.15
require github.com/stretchr/testify v1.7.0

View File

@ -0,0 +1,10 @@
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -0,0 +1,23 @@
#!/usr/bin/env bash
# Copyright (C) 2019 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
PLATFORM=$(uname -s | tr A-Z a-z)
CUR_DIR=$PWD
WAMR_DIR=$PWD/../../..
WAMR_GO_DIR=$PWD/../wamr
cp -a ${WAMR_DIR}/core/iwasm/include/*.h ${WAMR_GO_DIR}/packaged/include
mkdir -p build && cd build
cmake ${WAMR_DIR}/product-mini/platforms/${PLATFORM} \
-DWAMR_BUILD_LIB_PTHREAD=1 -DWAMR_BUILD_DUMP_CALL_STACK=1 \
-DWAMR_BUILD_MEMORY_PROFILING=1
make -j ${nproc}
cp -a libvmlib.a ${WAMR_GO_DIR}/packaged/lib/${PLATFORM}-amd64
cd ${CUR_DIR}
rm -f test
go build test.go
./test

View File

@ -0,0 +1,7 @@
#!/usr/bin/env bash
# Copyright (C) 2019 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
go build test.go
./test

View File

@ -0,0 +1,235 @@
/*
* Copyright (C) 2019 Intel Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
package main
import (
"gitlab.alipay-inc.com/TNT_Runtime/ant-runtime/bindings/go/wamr"
"fmt"
)
var wasmBytes = []byte {
0x00, 0x61, 0x73, 0x6D, 0x01, 0x00, 0x00, 0x00, 0x01, 0x29, 0x07, 0x60,
0x01, 0x7F, 0x01, 0x7F, 0x60, 0x02, 0x7F, 0x7F, 0x01, 0x7F, 0x60, 0x01,
0x7F, 0x00, 0x60, 0x04, 0x7F, 0x7E, 0x7D, 0x7C, 0x00, 0x60, 0x02, 0x7E,
0x7E, 0x01, 0x7E, 0x60, 0x02, 0x7C, 0x7F, 0x01, 0x7D, 0x60, 0x02, 0x7D,
0x7C, 0x01, 0x7C, 0x02, 0x31, 0x04, 0x03, 0x65, 0x6E, 0x76, 0x06, 0x70,
0x72, 0x69, 0x6E, 0x74, 0x66, 0x00, 0x01, 0x03, 0x65, 0x6E, 0x76, 0x04,
0x70, 0x75, 0x74, 0x73, 0x00, 0x00, 0x03, 0x65, 0x6E, 0x76, 0x06, 0x6D,
0x61, 0x6C, 0x6C, 0x6F, 0x63, 0x00, 0x00, 0x03, 0x65, 0x6E, 0x76, 0x04,
0x66, 0x72, 0x65, 0x65, 0x00, 0x02, 0x03, 0x07, 0x06, 0x00, 0x03, 0x04,
0x06, 0x05, 0x01, 0x05, 0x03, 0x01, 0x00, 0x01, 0x06, 0x13, 0x03, 0x7F,
0x01, 0x41, 0x90, 0x29, 0x0B, 0x7F, 0x00, 0x41, 0x90, 0x09, 0x0B, 0x7F,
0x00, 0x41, 0x90, 0x29, 0x0B, 0x07, 0x5F, 0x09, 0x06, 0x6D, 0x65, 0x6D,
0x6F, 0x72, 0x79, 0x02, 0x00, 0x04, 0x66, 0x69, 0x62, 0x32, 0x00, 0x04,
0x05, 0x74, 0x65, 0x73, 0x74, 0x31, 0x00, 0x05, 0x05, 0x74, 0x65, 0x73,
0x74, 0x32, 0x00, 0x06, 0x05, 0x74, 0x65, 0x73, 0x74, 0x33, 0x00, 0x07,
0x05, 0x74, 0x65, 0x73, 0x74, 0x34, 0x00, 0x08, 0x10, 0x5F, 0x5F, 0x6D,
0x61, 0x69, 0x6E, 0x5F, 0x61, 0x72, 0x67, 0x63, 0x5F, 0x61, 0x72, 0x67,
0x76, 0x00, 0x09, 0x0A, 0x5F, 0x5F, 0x64, 0x61, 0x74, 0x61, 0x5F, 0x65,
0x6E, 0x64, 0x03, 0x01, 0x0B, 0x5F, 0x5F, 0x68, 0x65, 0x61, 0x70, 0x5F,
0x62, 0x61, 0x73, 0x65, 0x03, 0x02, 0x0A, 0xA5, 0x03, 0x06, 0x37, 0x01,
0x01, 0x7F, 0x41, 0x01, 0x21, 0x01, 0x20, 0x00, 0x41, 0x02, 0x4F, 0x04,
0x7F, 0x41, 0x00, 0x21, 0x01, 0x03, 0x40, 0x20, 0x00, 0x41, 0x02, 0x6B,
0x10, 0x04, 0x20, 0x01, 0x6A, 0x21, 0x01, 0x20, 0x00, 0x41, 0x01, 0x6B,
0x22, 0x00, 0x41, 0x01, 0x4B, 0x0D, 0x00, 0x0B, 0x20, 0x01, 0x41, 0x01,
0x6A, 0x05, 0x41, 0x01, 0x0B, 0x0B, 0x3F, 0x01, 0x01, 0x7F, 0x23, 0x00,
0x41, 0x20, 0x6B, 0x22, 0x04, 0x24, 0x00, 0x20, 0x04, 0x41, 0x18, 0x6A,
0x20, 0x03, 0x39, 0x03, 0x00, 0x20, 0x04, 0x41, 0x10, 0x6A, 0x20, 0x02,
0xBB, 0x39, 0x03, 0x00, 0x20, 0x04, 0x20, 0x01, 0x37, 0x03, 0x08, 0x20,
0x04, 0x20, 0x00, 0x36, 0x02, 0x00, 0x41, 0xD0, 0x08, 0x20, 0x04, 0x10,
0x00, 0x1A, 0x20, 0x04, 0x41, 0x20, 0x6A, 0x24, 0x00, 0x0B, 0x3B, 0x01,
0x01, 0x7F, 0x23, 0x00, 0x41, 0x20, 0x6B, 0x22, 0x02, 0x24, 0x00, 0x20,
0x02, 0x20, 0x00, 0x37, 0x03, 0x00, 0x20, 0x02, 0x20, 0x01, 0x37, 0x03,
0x08, 0x20, 0x02, 0x41, 0x10, 0x6A, 0x20, 0x00, 0x20, 0x01, 0x7C, 0x22,
0x00, 0x37, 0x03, 0x00, 0x41, 0xF6, 0x08, 0x20, 0x02, 0x10, 0x00, 0x1A,
0x20, 0x02, 0x41, 0x20, 0x6A, 0x24, 0x00, 0x20, 0x00, 0x0B, 0x40, 0x02,
0x01, 0x7F, 0x01, 0x7C, 0x23, 0x00, 0x41, 0x20, 0x6B, 0x22, 0x02, 0x24,
0x00, 0x20, 0x02, 0x20, 0x01, 0x39, 0x03, 0x08, 0x20, 0x02, 0x20, 0x00,
0xBB, 0x22, 0x03, 0x39, 0x03, 0x00, 0x20, 0x02, 0x41, 0x10, 0x6A, 0x20,
0x03, 0x20, 0x01, 0xA2, 0x22, 0x01, 0x39, 0x03, 0x00, 0x41, 0xB4, 0x08,
0x20, 0x02, 0x10, 0x00, 0x1A, 0x20, 0x02, 0x41, 0x20, 0x6A, 0x24, 0x00,
0x20, 0x01, 0x0B, 0x3D, 0x01, 0x01, 0x7F, 0x23, 0x00, 0x41, 0x20, 0x6B,
0x22, 0x02, 0x24, 0x00, 0x20, 0x02, 0x20, 0x00, 0x39, 0x03, 0x00, 0x20,
0x02, 0x20, 0x01, 0x36, 0x02, 0x08, 0x20, 0x02, 0x41, 0x10, 0x6A, 0x20,
0x00, 0x20, 0x01, 0xB7, 0xA3, 0x22, 0x00, 0x39, 0x03, 0x00, 0x41, 0xC2,
0x08, 0x20, 0x02, 0x10, 0x00, 0x1A, 0x20, 0x02, 0x41, 0x20, 0x6A, 0x24,
0x00, 0x20, 0x00, 0xB6, 0x0B, 0x70, 0x00, 0x23, 0x00, 0x41, 0x20, 0x6B,
0x22, 0x00, 0x24, 0x00, 0x41, 0x9A, 0x08, 0x10, 0x01, 0x1A, 0x02, 0x7F,
0x41, 0x80, 0x08, 0x10, 0x02, 0x22, 0x01, 0x45, 0x04, 0x40, 0x41, 0x88,
0x08, 0x10, 0x01, 0x1A, 0x41, 0x7F, 0x0C, 0x01, 0x0B, 0x20, 0x00, 0x20,
0x01, 0x36, 0x02, 0x10, 0x41, 0xA7, 0x08, 0x20, 0x00, 0x41, 0x10, 0x6A,
0x10, 0x00, 0x1A, 0x20, 0x01, 0x41, 0x04, 0x6A, 0x41, 0x8E, 0x09, 0x2F,
0x00, 0x00, 0x3B, 0x00, 0x00, 0x20, 0x01, 0x41, 0x8A, 0x09, 0x28, 0x00,
0x00, 0x36, 0x00, 0x00, 0x20, 0x00, 0x20, 0x01, 0x36, 0x02, 0x00, 0x41,
0x80, 0x08, 0x20, 0x00, 0x10, 0x00, 0x1A, 0x20, 0x01, 0x10, 0x03, 0x41,
0x00, 0x0B, 0x20, 0x00, 0x41, 0x20, 0x6A, 0x24, 0x00, 0x0B, 0x0B, 0x97,
0x01, 0x01, 0x00, 0x41, 0x80, 0x08, 0x0B, 0x8F, 0x01, 0x62, 0x75, 0x66,
0x3A, 0x20, 0x25, 0x73, 0x00, 0x6D, 0x61, 0x6C, 0x6C, 0x6F, 0x63, 0x20,
0x62, 0x75, 0x66, 0x20, 0x66, 0x61, 0x69, 0x6C, 0x65, 0x64, 0x00, 0x48,
0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, 0x21, 0x00,
0x62, 0x75, 0x66, 0x20, 0x70, 0x74, 0x72, 0x3A, 0x20, 0x25, 0x70, 0x0A,
0x00, 0x25, 0x66, 0x20, 0x2A, 0x20, 0x25, 0x66, 0x20, 0x3D, 0x20, 0x25,
0x66, 0x0A, 0x00, 0x25, 0x66, 0x20, 0x2F, 0x20, 0x25, 0x64, 0x20, 0x3D,
0x20, 0x25, 0x66, 0x0A, 0x00, 0x69, 0x33, 0x32, 0x3A, 0x20, 0x25, 0x64,
0x2C, 0x20, 0x69, 0x36, 0x34, 0x3A, 0x20, 0x25, 0x6C, 0x6C, 0x64, 0x2C,
0x20, 0x66, 0x33, 0x32, 0x3A, 0x20, 0x25, 0x66, 0x2C, 0x20, 0x66, 0x36,
0x34, 0x3A, 0x20, 0x25, 0x66, 0x0A, 0x00, 0x25, 0x6C, 0x6C, 0x64, 0x20,
0x2B, 0x20, 0x25, 0x6C, 0x6C, 0x64, 0x20, 0x3D, 0x20, 0x25, 0x6C, 0x6C,
0x64, 0x0A, 0x00, 0x31, 0x32, 0x33, 0x34, 0x0A }
var global_heap []byte = make([]byte, 128 * 1024, 128 * 1024)
func main() {
var module *wamr.Module
var instance *wamr.Instance
var argv []uint32
var results []interface{}
var offset uint32
var native_addr *uint8
var err error
fmt.Print("Init wasm runtime with global heap buf\n");
err = wamr.Runtime().FullInit(true, global_heap, 1)
if err != nil {
return
}
fmt.Print("Destroy runtime\n");
wamr.Runtime().Destroy()
fmt.Print("Init wasm runtime without global heap buf\n");
err = wamr.Runtime().FullInit(false, nil, 1)
if err != nil {
return
}
wamr.Runtime().SetLogLevel(wamr.LOG_LEVEL_WARNING)
fmt.Print("Load wasm module\n");
module, err = wamr.NewModule(wasmBytes)
if err != nil {
fmt.Println(err)
goto fail
}
fmt.Print("Instantiate wasm module\n");
instance, err = wamr.NewInstance(module, 16384, 16384)
if err != nil {
fmt.Println(err)
goto fail
}
results = make([]interface{}, 8, 8)
argv = make([]uint32, 8)
fmt.Print("\nCall func __main_argc_argv with CallFunc:\n");
err = instance.CallFunc("__main_argc_argv", 2, argv)
if err != nil {
fmt.Println(err)
goto fail
}
fmt.Print("\nCall func __main_argc_argv with CallFuncV:\n");
err = instance.CallFuncV("__main_argc_argv", 2, results,
(int32)(0), (int32)(0))
if err != nil {
fmt.Println(err)
goto fail
}
fmt.Print("\nCall func `i32 fib2(i32)` with CallFunc:\n");
argv[0] = 32
err = instance.CallFunc("fib2", 1, argv)
if err != nil {
fmt.Println(err)
goto fail
}
fmt.Printf("fib2(32) return: %d\n", argv[0]);
fmt.Print("\nCall func `void test1(i32, i64, f32, f64)` with CallFuncV:\n");
err = instance.CallFuncV("test1", 0, nil,
(int32)(12345678),
(int64)(3344556677889900),
(float32)(5678.1234),
(float64)(987654321.5678))
if err != nil {
fmt.Println(err)
goto fail
}
fmt.Print("\nCall func `i64 test2(i64, i64)` with CallFuncV:\n");
err = instance.CallFuncV("test2", 1, results,
(int64)(3344556677889900),
(int64)(1122331122110099))
if err != nil {
fmt.Println(err)
goto fail
}
fmt.Printf("test2(3344556677889900, 1122331122110099) return: %d\n",
results[0].(int64))
fmt.Print("\nCall func `f64 test3(f32, f64)` with CallFuncV:\n");
err = instance.CallFuncV("test3", 1, results,
(float32)(3456.1234),
(float64)(7890.4567))
if err != nil {
fmt.Println(err)
goto fail
}
fmt.Printf("test3(3456.1234, 7890.4567) return: %f\n",
results[0].(float64))
fmt.Print("\nCall func `f32 test4(f64, i32)` with CallFuncV:\n");
err = instance.CallFuncV("test4", 1, results,
(float64)(8912.3456),
(int32)(123))
if err != nil {
fmt.Println(err)
goto fail
}
fmt.Printf("test4(8912.3456, 123) return: %f\n",
results[0].(float32))
fmt.Print("\nTest ModuleMalloc")
offset, native_addr = instance.ModuleMalloc(1024)
fmt.Printf("ModuleMalloc(%d) return offset: %d, native addr: %p\n",
1024, offset, native_addr)
if (!instance.ValidateAppAddr(offset, 1024)) {
fmt.Print("Validate app addr failed\n")
}
if (!instance.ValidateNativeAddr(native_addr, 1024)) {
fmt.Print("Validate native addr failed\n")
}
if (native_addr != instance.AddrAppToNative(offset)) {
fmt.Print("Convert app addr to native addr failed\n")
}
if (offset != instance.AddrNativeToApp(native_addr)) {
fmt.Print("Convert app addr to native addr failed\n")
}
instance.ModuleFree(offset)
/*
instance.DumpMemoryConsumption()
instance.DumpCallStack()
*/
fmt.Print("\n");
fail:
if (instance != nil) {
fmt.Print("Destroy instance\n");
instance.Destroy()
}
if (module != nil) {
fmt.Print("Destroy module\n");
module.Destroy()
}
fmt.Print("Destroy wasm runtime\n");
wamr.Runtime().Destroy()
}

View File

@ -0,0 +1,32 @@
# Copyright (C) 2019 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
WAMR_DIR=${PWD}/../../..
echo "Build wasm app .."
/opt/wasi-sdk/bin/clang -O3 \
-z stack-size=4096 -Wl,--initial-memory=65536 \
-o test.wasm main.c \
-Wl,--export=main -Wl,--export=__main_argc_argv \
-Wl,--export=fib2 \
-Wl,--export=test1 \
-Wl,--export=test2 \
-Wl,--export=test3 \
-Wl,--export=test4 \
-Wl,--export=__data_end -Wl,--export=__heap_base \
-Wl,--strip-all,--no-entry \
-Wl,--allow-undefined \
-nostdlib \
echo "Build binarydump tool .."
rm -fr build && mkdir build && cd build
cmake ../../../../../test-tools/binarydump-tool
make
cd ..
echo "Generate test_wasm.h .."
./build/binarydump -o test_wasm.h -n wasm_test_file test.wasm
rm -fr build
echo "Done"

View File

@ -0,0 +1,65 @@
/*
* Copyright (C) 2019 Intel Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
#include <stdio.h>
#include <stdlib.h>
unsigned
fib2(unsigned n)
{
if (n < 2) {
return 1;
}
return fib2(n - 2) + fib2(n - 1);
}
void
test1(int32_t i32, int64_t i64, float f32, double f64)
{
printf("i32: %d, i64: %lld, f32: %f, f64: %f\n", i32, i64, f32, f64);
}
int64_t
test2(int64_t x, int64_t y)
{
printf("%lld + %lld = %lld\n", x, y, x + y);
return x + y;
}
double
test3(float x, double y)
{
printf("%f * %f = %f\n", x, y, x * y);
return x * y;
}
float
test4(double x, int32_t y)
{
printf("%f / %d = %f\n", x, y, x / y);
return x / y;
}
int
main(int argc, char **argv)
{
char *buf;
printf("Hello world!\n");
buf = malloc(1024);
if (!buf) {
printf("malloc buf failed\n");
return -1;
}
printf("buf ptr: %p\n", buf);
snprintf(buf, 1024, "%s", "1234\n");
printf("buf: %s", buf);
free(buf);
return 0;
}

View File

@ -0,0 +1,20 @@
/*
* Copyright (C) 2019 Intel Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
package wamr
// #cgo CFLAGS: -I${SRCDIR}/packaged/include
// #cgo LDFLAGS: -lvmlib -lm
//
// #cgo linux,amd64 LDFLAGS: -Wl,-rpath,${SRCDIR}/packaged/lib/linux-amd64 -L${SRCDIR}/packaged/lib/linux-amd64
// #cgo linux,arm64 LDFLAGS: -Wl,-rpath,${SRCDIR}/packaged/lib/linux-aarch64 -L${SRCDIR}/packaged/lib/linux-aarch64
// #cgo darwin,amd64 LDFLAGS: -Wl,-rpath,${SRCDIR}/packaged/lib/darwin-amd64 -L${SRCDIR}/packaged/lib/darwin-amd64
// #cgo darwin,arm64 LDFLAGS: -Wl,-rpath,${SRCDIR}/packaged/lib/darwin-aarch64 -L${SRCDIR}/packaged/lib/darwin-aarch64
//
// #include <wasm_export.h>
import "C"
import (
)

View File

@ -0,0 +1,385 @@
/*
* 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(&param_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)
}

View File

@ -0,0 +1,19 @@
/*
* Copyright (C) 2019 Intel Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
package wamr
import (
//"github.com/stretchr/testify/assert"
"testing"
)
func TestInstance(t *testing.T) {
/* TODO */
}
func TestCallFunc(t *testing.T) {
/* TODO */
}

View File

@ -0,0 +1,134 @@
/*
* Copyright (C) 2019 Intel Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
package wamr
// #include <wasm_export.h>
import "C"
import (
"unsafe"
"runtime"
"fmt"
)
type Module struct {
module C.wasm_module_t
}
/* Create WASM/AOT module from the memory buffer */
func NewModule(wasmBytes []byte) (*Module, error) {
if (wasmBytes == nil || len(wasmBytes) == 0) {
return nil, fmt.Errorf("NewModule error: invalid input")
}
wasmPtr := (*C.uint8_t)(unsafe.Pointer(&wasmBytes[0]))
wasmLen := C.uint(len(wasmBytes))
errorBytes := make([]byte, 128)
errorPtr := (*C.char)(unsafe.Pointer(&errorBytes[0]))
errorLen := C.uint(len(errorBytes))
m := C.wasm_runtime_load(wasmPtr, wasmLen, errorPtr, errorLen)
if (m == nil) {
return nil, fmt.Errorf("NewModule error: %s", string(errorBytes))
}
self := &Module{
module: m,
}
runtime.SetFinalizer(self, func(self *Module) {
self.Destroy()
})
return self, nil
}
/* Destroy the module */
func (self *Module) Destroy() {
runtime.SetFinalizer(self, nil)
if (self.module != nil) {
C.wasm_runtime_unload(self.module)
}
}
/* Set module's wasi arguments */
func (self *Module) SetWasiArgs(dirList [][]byte, mapDirList [][]byte,
env [][]byte, argv[][]byte) {
var dirPtr, mapDirPtr, envPtr, argvPtr **C.char
var dirCount, mapDirCount, envCount C.uint
var argc C.int
if (dirList != nil) {
dirPtr = (**C.char)(unsafe.Pointer(&dirList[0]))
dirCount = C.uint(len(dirList))
}
if (mapDirList != nil) {
mapDirPtr = (**C.char)(unsafe.Pointer(&mapDirList[0]))
mapDirCount = C.uint(len(mapDirList))
}
if (env != nil) {
envPtr = (**C.char)(unsafe.Pointer(&env[0]))
envCount = C.uint(len(env))
}
if (argv != nil) {
argvPtr = (**C.char)(unsafe.Pointer(&argv[0]))
argc = C.int(len(argv))
}
C.wasm_runtime_set_wasi_args(self.module, dirPtr, dirCount,
mapDirPtr, mapDirCount,
envPtr, envCount, argvPtr, argc)
}
/* Set module's wasi arguments */
func (self *Module) SetWasiArgsEx(dirList [][]byte, mapDirList [][]byte,
env [][]byte, argv[][]byte,
stdinfd int, stdoutfd int, stderrfd int) {
var dirPtr, mapDirPtr, envPtr, argvPtr **C.char
var dirCount, mapDirCount, envCount C.uint
var argc C.int
if (dirList != nil) {
dirPtr = (**C.char)(unsafe.Pointer(&dirList[0]))
dirCount = C.uint(len(dirList))
}
if (mapDirList != nil) {
mapDirPtr = (**C.char)(unsafe.Pointer(&mapDirList[0]))
mapDirCount = C.uint(len(mapDirList))
}
if (env != nil) {
envPtr = (**C.char)(unsafe.Pointer(&env[0]))
envCount = C.uint(len(env))
}
if (argv != nil) {
argvPtr = (**C.char)(unsafe.Pointer(&argv[0]))
argc = C.int(len(argv))
}
C.wasm_runtime_set_wasi_args_ex(self.module, dirPtr, dirCount,
mapDirPtr, mapDirCount,
envPtr, envCount, argvPtr, argc,
C.int(stdinfd), C.int(stdoutfd),
C.int(stderrfd))
}
/* Set module's wasi network address pool */
func (self *Module) SetWasiAddrPool(addrPool [][]byte) {
var addrPoolPtr **C.char
var addrPoolSize C.uint
if (addrPool != nil) {
addrPoolPtr = (**C.char)(unsafe.Pointer(&addrPool[0]))
addrPoolSize = C.uint(len(addrPool))
}
C.wasm_runtime_set_wasi_addr_pool(self.module, addrPoolPtr, addrPoolSize)
}

View File

@ -0,0 +1,15 @@
/*
* Copyright (C) 2019 Intel Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
package wamr
import (
//"github.com/stretchr/testify/assert"
"testing"
)
func TestModule(t *testing.T) {
/* TODO */
}

View File

@ -0,0 +1,6 @@
/*
* Copyright (C) 2019 Intel Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
package include

View File

@ -0,0 +1,6 @@
/*
* Copyright (C) 2019 Intel Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
package darwin_amd64

View File

@ -0,0 +1,6 @@
/*
* Copyright (C) 2019 Intel Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
package lib

View File

@ -0,0 +1,6 @@
/*
* Copyright (C) 2019 Intel Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
package linux_amd64

View File

@ -0,0 +1,153 @@
/*
* Copyright (C) 2019 Intel Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
package wamr
/*
#include <stdlib.h>
#include <string.h>
#include <wasm_export.h>
void
bh_log_set_verbose_level(uint32_t level);
bool
init_wamr_runtime(bool alloc_with_pool, uint8_t *heap_buf,
uint32_t heap_size, uint32_t max_thread_num)
{
RuntimeInitArgs init_args;
memset(&init_args, 0, sizeof(RuntimeInitArgs));
if (alloc_with_pool) {
init_args.mem_alloc_type = Alloc_With_Pool;
init_args.mem_alloc_option.pool.heap_buf = heap_buf;
init_args.mem_alloc_option.pool.heap_size = heap_size;
}
else {
init_args.mem_alloc_type = Alloc_With_System_Allocator;
}
return wasm_runtime_full_init(&init_args);
}
*/
import "C"
import (
"fmt"
"unsafe"
)
type LogLevel uint32
const (
LOG_LEVEL_FATAL LogLevel = 0
LOG_LEVEL_ERROR LogLevel = 1
LOG_LEVEL_WARNING LogLevel = 2
LOG_LEVEL_DEBUG LogLevel = 3
LOG_LEVEL_VERBOSE LogLevel = 4
)
/*
type NativeSymbol struct {
symbol string
func_ptr *uint8
signature string
}
*/
type _Runtime struct {
initialized bool
}
var _runtime_singleton *_Runtime
/* Return the runtime singleton */
func Runtime() *_Runtime {
if (_runtime_singleton == nil) {
self := &_Runtime{}
_runtime_singleton = self
}
return _runtime_singleton;
}
/* Initialize the WASM runtime environment */
func (self *_Runtime) FullInit(alloc_with_pool bool, heap_buf []byte,
max_thread_num uint) error {
var heap_buf_C *C.uchar
if (self.initialized) {
return nil
}
if (alloc_with_pool) {
if (heap_buf == nil) {
return fmt.Errorf("Failed to init WAMR runtime")
}
heap_buf_C = (*C.uchar)(unsafe.Pointer(&heap_buf[0]))
}
if (!C.init_wamr_runtime((C.bool)(alloc_with_pool), heap_buf_C,
(C.uint)(len(heap_buf)),
(C.uint)(max_thread_num))) {
return fmt.Errorf("Failed to init WAMR runtime")
}
self.initialized = true
return nil
}
/* Initialize the WASM runtime environment */
func (self *_Runtime) Init() error {
return self.FullInit(false, nil, 1)
}
/* Destroy the WASM runtime environment */
func (self *_Runtime) Destroy() {
if (self.initialized) {
C.wasm_runtime_destroy()
self.initialized = false
}
}
/* Set log verbose level (0 to 5, default is 2),
larger level with more log */
func (self *_Runtime) SetLogLevel(level LogLevel) {
C.bh_log_set_verbose_level(C.uint32_t(level))
}
/*
func (self *_Runtime) RegisterNatives(moduleName string,
nativeSymbols []NativeSymbol) {
}
*/ /* TODO */
func (self *_Runtime) InitThreadEnv() bool {
if (!C.wasm_runtime_init_thread_env()) {
return false
}
return true
}
func (self *_Runtime) DestroyThreadEnv() {
C.wasm_runtime_destroy_thread_env();
}
func (self *_Runtime) ThreadEnvInited() bool {
if (!C.wasm_runtime_thread_env_inited()) {
return false
}
return true
}
/* Allocate memory from runtime memory environment */
func (self *_Runtime) Malloc(size uint32) *uint8 {
ptr := C.wasm_runtime_malloc((C.uint32_t)(size))
return (*uint8)(ptr)
}
/* Free memory to runtime memory environment */
func (self *_Runtime) Free(ptr *uint8) {
C.wasm_runtime_free((unsafe.Pointer)(ptr))
}

View File

@ -0,0 +1,42 @@
/*
* Copyright (C) 2019 Intel Corporation. All rights reserved.
* SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
*/
package wamr
import (
"github.com/stretchr/testify/assert"
"testing"
)
func TestRuntime(t *testing.T) {
res := false
if (Runtime() != nil) {
res = true;
}
assert.Equal(t, res, true)
err := Runtime().Init()
assert.NoError(t, err)
Runtime().Destroy()
err = Runtime().FullInit(false, nil, 6)
assert.NoError(t, err)
Runtime().Destroy()
err = Runtime().FullInit(false, nil, 0)
assert.NoError(t, err)
Runtime().Destroy()
heap_buf := make([]byte, 128 * 1024)
err = Runtime().FullInit(true, heap_buf, 4)
assert.NoError(t, err)
Runtime().Destroy()
Runtime().FullInit(false, nil, 0)
err = Runtime().FullInit(false, nil, 0)
assert.NoError(t, err)
Runtime().Destroy()
Runtime().Destroy()
}

160
language-bindings/python/.gitignore vendored Normal file
View File

@ -0,0 +1,160 @@
# Refer to https://github.com/github/gitignore/blob/main/Python.gitignore
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# virtual environment
Pipfile
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintainted in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
# VSCode settings
.vscode/

View File

@ -0,0 +1 @@
../../LICENSE

View File

@ -0,0 +1,31 @@
# wamr-python
## Installation
### Installing from the source code
Installing from local source tree is in _development mode_. The package appears to be installed but still is editable from the source tree.
```bash
$ python -m pip install -e /path/to/wamr-root/binding/python
```
## Usage
```python
import wamr.ffi as ffi
```
### Preparation
The binding will load the shared library _libiwasm.so_ from the WAMR repo. So before running the binding, you need to build the library yourself.
The default compile options are good enough.
Please be aware that `wasm_frame_xxx` and `wasm_trap_xxx` only work well when enabling `WAMR_BUILD_DUMP_CALL_STACK`.
### Examples
There is a [simple example](./samples/hello_procedural.py) to show how to use bindings. Actually, the python binding follows C-APIs. There it should be easy if be familiar with _programming with wasm-c-api_.
Unit test cases under _./tests_ could be another but more complete references.

View File

@ -0,0 +1,708 @@
# how to implement a python binding of WAMR
A python language binding of Wasm runtime allows its users to call a set of APIs of
the runtime from the python world. Those APIs maybe implemented in C, C++, or Rust.
In the WAMR case, a python binding allows APIs in `core/iwasm/include/wasm_c_api.h`
to be used in the python scripts. To achieve that, we will create two kinds
of stuff: wrappers of structured data types and wrappers of functions under the
help of _ctypes_.
Cyptes is a tool in the standard library for creating Python bindings. It
provides a low-level toolset for loading shared libraries and marshaling
data between Python and C. Other options include _cffi_, _pybind11_,
_cpython_ and so on. Because we tend to make the binding depending on least
items. The built-in module, _ctypes_, is a good choice.
## General rules to marshal
The core of the idea of a language binding is how to translate different
representations of types in different language.
### load libraries
The `ctypes` supports locating a dynamic link library in a way similar to the
compiler does.
Currently, `ctypes.LoadLibrary` supports:
- `CDLL`. Those libraries use the standard C calling conversion.
- `OleDLL` and `WinDLL`. Those libraries use the `stdcall` calling conversion on
Windows only
### fundamental datatypes
_ctypes_ provides [primitive C compatiable data types](https://docs.python.org/3/library/ctypes.html#fundamental-data-types).
Like `c_bool`, `c_byte`, `c_int`, `c_long` and so on.
> `c_int` represents the _C_ `signed int` datatype. On platforms where
> `sizeof(int) == sizeof(long)` it is an alias to `c_long`.
| c datatypes | ctypes |
| ------------------- | ----------------------- |
| bool | c_bool |
| byte_t | c_ubyte |
| char | c_char |
| float32_t | c_float |
| float64_t | c_double |
| int32_t | c_int32 |
| int64_t | c_int64 |
| intptr_t | c_void_p |
| size_t | c_size_t |
| uint8_t | c_uint8 |
| uint32_t | c_uint32 |
| void | None |
| wasm_byte_t | c_ubyte |
| wasm_externkind_t | c_uint8 |
| wasm_memory_pages_t | c_uint32 |
| wasm_mutability_t | c_bool |
| wasm_table_size_t | c_uint32 |
| wasm_valkind_t | c_uint8 |
| wasm_data_type\* | POINTER(wasm_data_type) |
- `c_void_p` only represents `void *` only
- `None` represents `void` in function parameter lists and return lists
### structured datatypes
Create a corresponding concept for every native structured data type includes
`enum`, `struct` and `union`, in the python world.
#### Enum types
For example, if there is a `enum wams_mutability_enum` in native.
```c
typedef uint8_t wams_mutability_t;
enum wams_mutability_enum {
WASM_CONST,
WASM_VAR
};
```
Use `ctypes.int`(or any integer types in ctypes) to represents its value directly.
```python
# represents enum wams_mutability_enum
wasm_mutability_t = c_uint8
WASM_CONST = 0
WASM_VAR = 1
```
> C standard only requires "Each enumerated type shall be compatible with char,
> a signed integer type, or an unsigned integer type. The choice of the integer
> type is implementation-defined, but shall be capable of representing the
> values of all the members of the enumeration.
#### Struct types
If there is a `struct wasm_byte_vec_t` in native(in C).
```c
typedef struct wasm_byte_vec_t {
size_t size;
wasm_byte_t *data;
size_t num_elems;
size_t size_of_elem;
} wasm_byte_vec_t;
```
Use `ctypes.Structure` to create its corresponding data type in python.
```python
class wasm_byte_vec_t(ctypes.Structure):
_fileds_ = [
("size", ctypes.c_size_t),
("data", ctypes.POINTER(c_ubyte)),
("num_elems", ctypes.c_size_t),
("size_of_elem", ctypes.c_size_t),
]
```
a list of `Structures`
| name |
| ----------------- |
| wasm_engine_t |
| wasm_store_t |
| wasm_limits_t |
| wasm_valtype_t |
| wasm_functype_t |
| wasm_globaltype_t |
| wasm_tabletype_t |
| wasm_memorytype_t |
| wasm_externtype_t |
| wasm_importtype_t |
| wasm_exporttype_t |
| wasm_ref_t |
| wasm_ref_t |
| wasm_frame_t |
| wasm_trap_t |
| wasm_foreign_t |
| WASMModuleCommon |
| WASMModuleCommon |
| wasm_func_t |
| wasm_global_t |
| wasm_table_t |
| wasm_memory_t |
| wasm_extern_t |
| wasm_instance_t |
not supported `struct`
- wasm_config_t
If there is an anonymous `union` in native.
```c
typedef struct wasm_val_t {
wasm_valkind_t kind;
union {
int32_t i32;
int64_t i64;
float32_t f32;
float64_t f64;
} of;
} wasm_val_t;
```
Use `ctypes.Union` to create its corresponding data type in python.
```python
class _OF(ctypes.Union):
_fields_ = [
("i32", ctypes.c_int32),
("i64", ctypes.c_int64),
("f32", ctypes.c_float),
("f64", ctypes.c_double),
]
class wasm_val_t(ctypes.Structure):
_anonymous_ = ("of",)
_fields_ = [
("kind", ctypes.c_uint8)
("of", _OF)
]
```
### wrappers of functions
Foreign functions (C functions) can be accessed as attributes of loaded shared
libraries or an instance of function prototypes. Callback functions(python
functions) can only be accessed by instantiating function prototypes.
For example,
```c
void wasm_name_new(wasm_name_t* out, size_t len, wasm_byte_t [] data);
```
Assume there are:
- `class wasm_name_t` of python represents `wasm_name_t` of C
- `libiwasm` represents loaded _libiwasm.so_
If to access a c function like an attribute,
```python
def wasm_name_new(out, len, data):
_wasm_name_new = libiwasm.wasm_name_new
_wasm_name_new.argtypes = (ctypes.POINTER(wasm_name_t), ctypes.c_size_t, ctypes.POINTER(ctypes.c_ubyte))
_wasm_name_new.restype = None
return _wasm_name_new(out, len, data)
```
Or to instantiate a function prototype,
```python
def wasm_name_new(out, len, data):
return ctypes.CFUNCTYPE(None, (ctypes.POINTER(wasm_name_t), ctypes.c_size_t, ctypes.POINTER(ctypes.c_ubyte)))(
("wasm_name_new", libiwasm), out, len, data)
```
Now it is able to create a `wasm_name_t` with `wasm_name_new()` in python.
Sometimes, need to create a python function as a callback of c.
```c
wasm_trap_t* (*wasm_func_callback_t)(wasm_val_vec_t* args, wasm_val_vec_t *results);
```
Use `cyptes.CFUNCTYPE` to create a _pointer of function_
```python
def hello(args, results):
print("hello from a callback")
wasm_func_callback_t = ctypes.CFUNCTYPE(c_size_t, POINTER(wasm_val_vec_t), POINTER(wasm_val_vec_t))
hello_callback = wasm_func_callback_t(hello)
```
or with a decorator
```python
def wasm_func_cb_decl(func):
return @ctypes.CFUNCTYPE(ctypes.POINTER(wasm_trap_t), (ctypes.POINTER(wasm_val_vec_t), ctypes.POINTER(wasm_val_vec_t)))(func)
@wasm_func_cb_decl
def hello(args, results):
print("hello from a callback")
```
### programming tips
#### `struct` and `ctypes.Structure`
There are two kinds of `cytes.Structure` in `binding.py`.
- has `__field__` definition. like `class wasm_byte_vec_t(Structure)`
- doesn't have `__field__` definition. like `class wasm_config_t(Structure)`
Since, `ctypes` will create its C world _mirror_ variable according to `__field__`
information, `wasm_config_t()` will only create a python instance without binding
to any C variable. `wasm_byte_vec_t()` will return a python instance with an internal
C variable.
That is why `pointer(wasm_config_t())` is a NULL pointer which can not be dereferenced.
#### deal with pointers
`byref()` and `pointer()` are two functions can return a pointer.
```python
x = ctypes.c_int(2)
# use pointer() to creates a new pointer instance which would later be used in Python
x_ptr = ctypes.pointer(x)
...
struct_use_pointer = Mystruct()
struct_use_pointer.ptr = x_ptr
# use byref() pass a pointer to an object to a foreign function call
func(ctypes.byref(x))
```
The main difference is that `pointer()` does a lot more work since it
constructs a real pointer object. It is faster to use `byref(`) if don't need
the pointer object in Python itself(e.g. only use it as an argument to pass
to a function).
There is no doubt that `wasm_xxx_new()` which return type is `ctypes.POINTER`
can return a pointer. Plus, the return value of `wasm_xxx_t()` can also be
used as a pointer without casting by `byref` or `pointer`.
#### array
In [ctypes document](https://docs.python.org/3/library/ctypes.html#arrays),
it states that "The recommended way to create array types is by multiplying a
data type with a positive integer". So _multiplying a data type_ should be a
better way to create arrays
```python
from ctypes import *
class POINT(Structure):
_fields_ = ("x", c_int), ("y", c_int)
# multiplying a data type
# type(TenPointsArrayType) is <class '_ctypes.PyCArrayType'>
TenPointsArrayType = POINT * 10
# Instances are created in the usual way, by calling the class:
arr = TenPointsArrayType()
arr[0] = POINT(3,2)
for pt in arr:
print(pt.x, pt.y)
```
On both sides, it is OK to assign an array to a pointer.
```c
char buf[128] = {0};
char *ptr = buf;
```
```python
binary = wasm_byte_vec_t()
binary.data = (ctypes.c_ubyte * len(wasm)).from_buffer_copy(wasm)
```
#### exceptions and traps
Interfaces of _wasm-c-api_ have their return values to represent failures.
The python binding should just keep and transfer them to callers instead of
raising any additional exception.
The python binding should raise exceptions when the python partial is failed.
#### readonly buffer
```python
with open("hello.wasm", "rb") as f:
wasm = f.read()
binary = wasm_byte_vec_t()
wasm_byte_vec_new_uninitialized(byref(binary), len(wasm))
# create a ctypes instance (byte[] in c) and copy the content
# from wasm(bytearray in python)
binary.data = (ctypes.c_ubyte * len(wasm)).from_buffer_copy(wasm)
```
in the above example, `wasm` is a python-created readable buffer. It is not
writable and needs to be copied into a ctype array.
#### variable arguments
A function with _variable arugments_ makes it hard to specify the required
argument types for the function prototype. It leaves us one way to call it
directly without any arguments type checking.
```python
libc.printf(b"Hello, an int %d, a float %f, a string %s\n", c_int(1), c_doulbe(3.14), "World!")
```
#### Use `c_bool` to represent `wasm_mutability_t `
- `True` for `WASM_CONST`
- `False` for `WASM_VALUE`
#### customize class builtins
- `__eq__` for comparation.
- `__repr__` for printing.
### bindgen.py
`bindge.py` is a tool to create WAMR python binding automatically. `binding.py`
is generated. We should avoid modification on it. Additional helpers should go
to `ffi.py`.
`bindgen.py` uses _pycparser_. Visit the AST of `core/iwasm/include/wasm_c_api.h`
created by _gcc_ and generate necessary wrappers.
```python
from pycparser import c_ast
class Visitor(c_ast.NodeVisitor):
def visit_Struct(self, node):
pass
def visit_Union(self, node):
pass
def visit_TypeDef(self, node):
pass
def visit_FuncDecl(self, node):
pass
ast = parse_file(...)
v = Visitor()
v.visit(ast)
```
Before running _bindgen.py_, the shared library _libiwasm.so_ should be generated.
```bash
$ cd /path/to/wamr/repo
$ # if it is in linux
$ pushd product-mini/platforms/linux/
$ cmake -S . -B build ..
$ cmake --build build --target iwasm
$ popd
$ cd binding/python
$ python utils/bindgen.py
```
`wasm_frame_xxx` and `wasm_trap_xxx` only work well when enabling `WAMR_BUILD_DUMP_CALL_STACK`.
```bash
$ cmake -S . -B build -DWAMR_BUILD_DUMP_CALL_STACK=1 ..
```
## OOP wrappers
Based on the above general rules, there will be corresponding python
APIs for every C API in `wasm_c_api.h` with same name. Users can do procedural
programming with those.
In next phase, we will create OOP APIs. Almost follow the
[C++ version of wasm_c_api](https://github.com/WebAssembly/wasm-c-api/blob/master/include/wasm.hh)
## A big list
| WASM Concept | Procedural APIs | OOP APIs | OOP APIs methods |
| ------------ | ------------------------------ | ---------- | ---------------- |
| XXX_vec | wasm_xxx_vec_new | | list |
| | wasm_xxx_vec_new_uninitialized | | |
| | wasm_xxx_vec_new_empty | | |
| | wasm_xxx_vec_copy | | |
| | wasm_xxx_vec_delete | | |
| valtype | wasm_valtype_new | valtype | \_\_init\_\_ |
| | wasm_valtype_delete | | \_\_del\_\_ |
| | wasm_valtype_kind | | \_\_eq\_\_ |
| | wasm_valtype_copy | | |
| | _vector methods_ | | |
| functype | wasm_functype_new | functype | |
| | wasm_functype_delete | | |
| | wasm_functype_params | | |
| | wasm_functype_results | | |
| | wasm_functype_copy | | |
| | _vector methods_ | | |
| globaltype | wasm_globaltype_new | globaltype | \_\_init\_\_ |
| | wasm_globaltype_delete | | \_\_del\_\_ |
| | wasm_globaltype_content | | \_\_eq\_\_ |
| | wasm_globaltype_mutability | | |
| | wasm_globaltype_copy | | |
| | _vector methods_ | | |
| tabletype | wasm_tabletype_new | tabletype | \_\_init\_\_ |
| | wasm_tabletype_delete | | \_\_del\_\_ |
| | wasm_tabletype_element | | \_\_eq\_\_ |
| | wasm_tabletype_limits | | |
| | wasm_tabletype_copy | | |
| | _vector methods_ | | |
| memorytype | wasm_memorytype_new | memorytype | \_\_init\_\_ |
| | wasm_memorytype_delete | | \_\_del\_\_ |
| | wasm_memorytype_limits | | \_\_eq\_\_ |
| | wasm_memorytype_copy | | |
| | _vector methods_ | | |
| externtype | wasm_externtype_as_XXX | externtype | |
| | wasm_XXX_as_externtype | | |
| | wasm_externtype_copy | | |
| | wasm_externtype_delete | | |
| | wasm_externtype_kind | | |
| | _vector methods_ | | |
| importtype | wasm_importtype_new | importtype | |
| | wasm_importtype_delete | | |
| | wasm_importtype_module | | |
| | wasm_importtype_name | | |
| | wasm_importtype_type | | |
| | wasm_importtype_copy | | |
| | _vector methods_ | | |
| exportype | wasm_exporttype_new | exporttype | |
| | wasm_exporttype_delete | | |
| | wasm_exporttype_name | | |
| | wasm_exporttype_type | | |
| | wasm_exporttype_copy | | |
| | _vector methods_ | | |
| val | wasm_val_delete | val | |
| | wasm_val_copy | | |
| | _vector methods_ | | |
| frame | wasm_frame_delete | frame | |
| | wasm_frame_instance | | |
| | wasm_frame_func_index | | |
| | wasm_frame_func_offset | | |
| | wasm_frame_module_offset | | |
| | wasm_frame_copy | | |
| | _vector methods_ | | |
| trap | wasm_trap_new | trap | |
| | wasm_trap_delete | | |
| | wasm_trap_message | | |
| | wasm_trap_origin | | |
| | wasm_trap_trace | | |
| | _vector methods_ | | |
| foreign | wasm_foreign_new | foreign | |
| | wasm_foreign_delete | | |
| | _vector methods_ | | |
| engine | wasm_engine_new | engine | |
| | wasm_engine_new_with_args\* | | |
| | wasm_engine_new_with_config | | |
| | wasm_engine_delete | | |
| store | wasm_store_new | store | |
| | wasm_store_delete | | |
| | _vector methods_ | | |
| module | wasm_module_new | module | |
| | wasm_module_delete | | |
| | wasm_module_validate | | |
| | wasm_module_imports | | |
| | wasm_module_exports | | |
| instance | wasm_instance_new | instance | |
| | wasm_instance_delete | | |
| | wasm_instance_new_with_args\* | | |
| | wasm_instance_exports | | |
| | _vector methods_ | | |
| func | wasm_func_new | func | |
| | wasm_func_new_with_env | | |
| | wasm_func_delete | | |
| | wasm_func_type | | |
| | wasm_func_call | | |
| | wasm_func_param_arity | | |
| | wasm_func_result_arity | | |
| | _vector methods_ | | |
| global | wasm_global_new | global | |
| | wasm_global_delete | | |
| | wasm_global_type | | |
| | wasm_global_get | | |
| | wasm_global_set | | |
| | _vector methods_ | | |
| table | wasm_table_new | table | |
| | wasm_table_delete | | |
| | wasm_table_type | | |
| | wasm_table_get | | |
| | wasm_table_set | | |
| | wasm_table_size | | |
| | _vector methods_ | | |
| memory | wasm_memory_new | memory | |
| | wasm_memory_delete | | |
| | wasm_memory_type | | |
| | wasm_memory_data | | |
| | wasm_memory_data_size | | |
| | wasm_memory_size | | |
| | _vector methods_ | | |
| extern | wasm_extern_delete | extern | |
| | wasm_extern_as_XXX | | |
| | wasm_XXX_as_extern | | |
| | wasm_extern_kind | | |
| | wasm_extern_type | | |
| | _vector methods_ | | |
not supported _functions_
- wasm_config_XXX
- wasm_module_deserialize
- wasm_module_serialize
- wasm_ref_XXX
- wasm_XXX_as_ref
- wasm_XXX_as_ref_const
- wasm_XXX_copy
- wasm_XXX_get_host_info
- wasm_XXX_set_host_info
## test
there will be two kinds of tests in the project
- unit test. located in `./tests`. driven by _unittest_. run by
`$ python -m unittest` or `$ make test`.
- integration test. located in `./samples`.
The whole project is under test-driven development. Every wrapper function will
have two kinds of test cases. The first kind is a positive case. It checks a
wrapper function with expected and safe arguments combinations. Its goal is the
function should work well with expected inputs. Another kind is a negative
case. It feeds unexpected arguments combinations into a wrapper function. Arguments
should include but not be limited to `None`. It ensures that the function will
gracefully handle invalid input or unexpected behaviors.
## distribution
### package
Create a python package named `wamr`. Users should import it after installation
just like any other python module.
```python
from wamr import *
```
### PyPI
Refer to [tutorial provided by PyPA](https://packaging.python.org/en/latest/tutorials/packaging-projects/).
Steps to publish WAMR Python library:
1. Creating `pyproject.toml` tells build tools (like pip and build) what is
required to build a project. An example .toml file uses _setuptools_
```toml
[build-system]
requires = [
"setuptools>=42",
"wheel"
]
build-backend = "setuptools.build_meta"
```
2. Configuring metadata tells build tools about a package (such as the name
and the version), as well as which code files to include
- Static metadata (`setup.cfg`): guaranteed to be the same every time.
It is simpler, easier to read, and avoids many common errors, like
encoding errors.
- Dynamic metadata (`setup.py`): possibly non-deterministic. Any items that
are dynamic or determined at install-time, as well as extension modules
or extensions to setuptools, need to go into setup.py.
**_Static metadata should be preferred_**. Dynamic metadata should be used
only as an escape hatch when necessary. setup.py used to be
required, but can be omitted with newer versions of setuptools and pip.
3. Including other files in the distribution
- For [source distribution](https://packaging.python.org/en/latest/glossary/#term-Source-Distribution-or-sdist):
It's usually generated using `python setup.py sdist`, providing metadata
and the essential source files needed for installing by a tool like pip,
or for generating a Built Distribution.
It includes our Python modules, pyproject.toml, metadata, README.md,
LICENSE. If you want to control what goes in this explicitly,
see [Including files in source distributions with MANIFEST.in](https://packaging.python.org/en/latest/guides/using-manifest-in/#using-manifest-in).
- For [final built distribution](https://packaging.python.org/en/latest/glossary/#term-Built-Distribution)
A Distribution format containing files and metadata that only need to be
moved to the correct location on the target system, to be installed.
e.g. `Wheel`
It will have the Python files in the discovered or listed Python packages.
If you want to control what goes here, such as to add data files,
see [Including Data Files](https://setuptools.pypa.io/en/latest/userguide/datafiles.html) from the [setuptools docs](https://setuptools.pypa.io/en/latest/index.html).
4. Generating distribution archives. These are archives that are uploaded to
the Python Package Index and can be installed by pip.
example using `setuptools`
```shell
python3 -m pip install --upgrade build
python3 -m build
```
generated files:
```shell
dist/
WAMR-package-0.0.1-py3-none-any.whl
WAMR-package-0.0.1.tar.gz
```
The `tar.gz` file is a _source archive_ whereas the `.whl file` is a
_built distribution_. Newer pip versions preferentially install built
distributions but will fall back to source archives if needed. You should
always upload a source archive and provide built archives for compatibility
reasons.
5. Uploading the distribution archives
- Register an account on https://pypi.org.
- To securely upload your project, youll need a
[PyPI API token](https://pypi.org/help/#apitoken). It can create at
[here](https://pypi.org/manage/account/#api-tokens), and the “Scope”
the setting needs to be “Entire account”.
- After registration, now twine can be used to upload the distribution packages.
```shell
# install twine
python3 -m pip install --upgrade twine
# --repository is https://pypi.org/ by default.
# You will be prompted for a username and password. For the username, use __token__. For the password, use the token value, including the pypi- prefix.
twine upload dist/*
```
after all, the python binding will be installed with
```shell
$ pip install wamr
```
PS: A example lifecycle of a python package
![python-package-lifecycle](images/python_package_life_cycle.png)
## CI
There are several parts:
- code format check.
- test. include running all unit test cases and examples.
- publish built distribution.

Binary file not shown.

After

Width:  |  Height:  |  Size: 75 KiB

View File

@ -0,0 +1,12 @@
Use a python virtual environment tool to create an environment for development. All necessary packages are in _../requirements.txt_.
python code formatter is provided by _black_.
python code linter is provided by _pylint_ and default configuration.
Unit tests are driven by _unittest_.
```bash
$ python -m unittest -v tests/test_basics.py
$ python -m unittest -v tests/test_advanced.py
```

View File

@ -0,0 +1,3 @@
[build-system]
requires = ["setuptools>=42"]
build-backend = "setuptools.build_meta"

View File

@ -0,0 +1,5 @@
black
nose
pycparser
pylint

View File

@ -0,0 +1,4 @@
(module
(func $hello (import "" "hello"))
(func (export "run") (call $hello))
)

View File

@ -0,0 +1,41 @@
#!/usr/bin/env python3
#
# Copyright (C) 2019 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
import ctypes
from wamr import *
def hello_callback():
print("Calling back...")
print("> Hello World!")
def main():
print("Initializing...")
engine = Engine()
store = Store(engine)
print("Loading binary...")
print("Compiling module...")
module = Module.from_file(engine, "./hello.wasm")
print("Creating callback...")
hello = Func(store, FuncType([], []), hello_callback)
print("Instantiating module...")
instance = Instance(store, module, [hello])
print("Extracting export...")
run = instance.exports(store)["run"]
print("Calling export...")
run(store)
print("Shutting down...")
print("Done.")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,93 @@
# -*- coding: utf-8 -*-
#!/usr/bin/env python3
#
# Copyright (C) 2019 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
import ctypes
import wamr.ffi as ffi
WAMS_BINARY_CONTENT = (
b"\x00asm\x01\x00\x00\x00\x01\x84\x80\x80\x80\x00\x01`\x00\x00\x02\x8a\x80"
b"\x80\x80\x00\x01\x00\x05hello\x00\x00\x03\x82\x80\x80\x80\x00\x01\x00"
b"\x07\x87\x80\x80\x80\x00\x01\x03run\x00\x01\n\x8a\x80\x80\x80\x00\x01"
b"\x84\x80\x80\x80\x00\x00\x10\x00\x0b"
)
@ffi.wasm_func_cb_decl
def hello_callback(args, results):
print("Calling back...")
print("> Hello World!")
def main():
print("Initializing...")
engine = ffi.wasm_engine_new()
store = ffi.wasm_store_new(engine)
print("Loading binary...")
# for convenience, use binary content instead of open file
# with open("./hello.wasm", "rb") as f:
# wasm = f.read()
wasm = WAMS_BINARY_CONTENT
binary = ffi.wasm_byte_vec_t()
ffi.wasm_byte_vec_new_uninitialized(binary, len(wasm))
# underlying buffer is not writable
binary.data = (ctypes.c_ubyte * len(wasm)).from_buffer_copy(wasm)
print("Compiling module...")
module = ffi.wasm_module_new(store, binary)
if not module:
raise RuntimeError("Compiling module failed")
binary.data = None
ffi.wasm_byte_vec_delete(binary)
print("Creating callback...")
hello_type = ffi.wasm_functype_new_0_0()
hello_func = ffi.wasm_func_new(
store,
hello_type,
hello_callback,
)
ffi.wasm_functype_delete(hello_type)
print("Instantiating module...")
imports = ffi.wasm_extern_vec_t()
ffi.wasm_extern_vec_new((imports), 1, ffi.wasm_func_as_extern(hello_func))
instance = ffi.wasm_instance_new(store, module, imports, None)
ffi.wasm_func_delete(hello_func)
print("Extracting export...")
exports = ffi.wasm_extern_vec_t()
ffi.wasm_instance_exports(instance, exports)
run_func = ffi.wasm_extern_as_func(exports.data[0])
if not run_func:
raise RuntimeError("can not extract exported function")
ffi.wasm_instance_delete(instance)
ffi.wasm_module_delete(module)
print("Calling export...")
args = ffi.wasm_val_vec_t()
results = ffi.wasm_val_vec_t()
ffi.wasm_val_vec_new_empty(args)
ffi.wasm_val_vec_new_empty(results)
ffi.wasm_func_call(run_func, args, results)
print("Shutting down...")
ffi.wasm_store_delete(store)
ffi.wasm_engine_delete(engine)
print("Done.")
if __name__ == "__main__":
main()

View File

@ -0,0 +1,30 @@
# -*- coding: utf-8 -*-
#!/usr/bin/env python3
#
# Copyright (C) 2019 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
# pylint: disable=missing-class-docstring
# pylint: disable=missing-function-docstring
# pylint: disable=missing-module-docstring
from setuptools import setup, find_packages
with open("README.md") as f:
readme = f.read()
with open("LICENSE") as f:
license = f.read()
setup(
name="wamr-python",
version="0.1.0",
description="A WebAssembly runtime powered by WAMR",
long_description=readme,
author="The WAMR Project Developers",
author_email="hello@bytecodealliance.org",
url="https://github.com/bytecodealliance/wamr-python",
license=license,
packages=["wamr"],
)

View File

@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
#!/usr/bin/env python3
#
# Copyright (C) 2019 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
__all__ = ["test_basic", "test_advanced"]

View File

@ -0,0 +1,13 @@
# -*- coding: utf-8 -*-
#!/usr/bin/env python3
#
# Copyright (C) 2019 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
import sys
import os
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
import wamr

View File

@ -0,0 +1,525 @@
# -*- coding: utf-8 -*-
#!/usr/bin/env python3
#
# Copyright (C) 2019 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
# pylint: disable=missing-class-docstring
# pylint: disable=missing-function-docstring
# pylint: disable=missing-module-docstring
import ctypes as c
import math
import unittest
import wamr.ffi as ffi
# It is a module likes:
# (module
# (import "mod" "g0" (global i32))
# (import "mod" "f0" (func (param f32) (result f64)))
#
# (func (export "f1") (param i32 i64))
# (global (export "g1") (mut f32) (f32.const 3.14))
# (memory (export "m1") 1 2)
# (table (export "t1") 1 funcref)
#
# (func (export "f2") (unreachable))
# )
MODULE_BINARY = (
b"\x00asm\x01\x00\x00\x00\x01\x0e\x03`\x01}\x01|`\x02\x7f~\x00`\x00"
b"\x00\x02\x14\x02\x03mod\x02g0\x03\x7f\x00\x03mod\x02f0\x00\x00\x03\x03"
b"\x02\x01\x02\x04\x04\x01p\x00\x01\x05\x04\x01\x01\x01\x02\x06\t\x01}\x01C"
b"\xc3\xf5H@\x0b\x07\x1a\x05\x02f1\x00\x01\x02g1\x03\x01\x02m1\x02\x00\x02t1"
b"\x01\x00\x02f2\x00\x02\n\x08\x02\x02\x00\x0b\x03\x00\x00\x0b"
)
# False -> True when testing with a library enabling WAMR_BUILD_DUMP_CALL_STACK flag
TEST_WITH_WAMR_BUILD_DUMP_CALL_STACK = False
@ffi.wasm_func_cb_decl
def callback(args, results):
args = ffi.dereference(args)
results = ffi.dereference(results)
arg_v = args.data[0]
result_v = ffi.wasm_f64_val(arg_v.of.f32 * 2.0)
ffi.wasm_val_copy(results.data[0], result_v)
results.num_elems = 1
print(f"\nIn callback: {arg_v} --> {result_v}\n")
@ffi.wasm_func_with_env_cb_decl
def callback_with_env(env, args, results):
# pylint: disable=unused-argument
print("summer")
class AdvancedTestSuite(unittest.TestCase):
@classmethod
def setUpClass(cls):
print("Initializing...")
cls._wasm_engine = ffi.wasm_engine_new()
cls._wasm_store = ffi.wasm_store_new(cls._wasm_engine)
def assertIsNullPointer(self, pointer):
# pylint: disable=invalid-name
if not ffi.is_null_pointer(pointer):
self.fail("not a non-null pointer")
def assertIsNotNullPointer(self, pointer):
# pylint: disable=invalid-name
if ffi.is_null_pointer(pointer):
self.fail("not a non-null pointer")
def load_binary(self, binary_string):
print("Load binary...")
binary = ffi.load_module_file(binary_string)
binary = c.pointer(binary)
self.assertIsNotNullPointer(binary)
return binary
def compile(self, binary):
print("Compile...")
module = ffi.wasm_module_new(self._wasm_store, binary)
self.assertIsNotNullPointer(module)
return module
def prepare_imports_local(self):
print("Prepare imports...")
func_type = ffi.wasm_functype_new_1_1(
ffi.wasm_valtype_new(ffi.WASM_F32),
ffi.wasm_valtype_new(ffi.WASM_F64),
)
func = ffi.wasm_func_new(self._wasm_store, func_type, callback)
self.assertIsNotNullPointer(func)
ffi.wasm_functype_delete(func_type)
glbl_type = ffi.wasm_globaltype_new(ffi.wasm_valtype_new(ffi.WASM_I32), True)
init = ffi.wasm_i32_val(1024)
glbl = ffi.wasm_global_new(self._wasm_store, glbl_type, init)
self.assertIsNotNullPointer(glbl)
ffi.wasm_globaltype_delete(glbl_type)
imports = ffi.wasm_extern_vec_t()
data = ffi.list_to_carray(
c.POINTER(ffi.wasm_extern_t),
ffi.wasm_func_as_extern(func),
ffi.wasm_global_as_extern(glbl),
)
ffi.wasm_extern_vec_new(imports, 2, data)
imports = c.pointer(imports)
self.assertIsNotNullPointer(imports)
return imports
def instantiate(self, module, imports):
print("Instantiate module...")
instance = ffi.wasm_instance_new(
self._wasm_store, module, imports, ffi.create_null_pointer(ffi.wasm_trap_t)
)
self.assertIsNotNone(instance)
self.assertIsNotNullPointer(instance)
return instance
def extract_exports(self, instance):
print("Extracting exports...")
exports = ffi.wasm_extern_vec_t()
ffi.wasm_instance_exports(instance, exports)
exports = c.pointer(exports)
self.assertIsNotNullPointer(exports)
return exports
def setUp(self):
binary = self.load_binary(MODULE_BINARY)
self.module = self.compile(binary)
self.imports = self.prepare_imports_local()
self.instance = self.instantiate(self.module, self.imports)
self.exports = self.extract_exports(self.instance)
ffi.wasm_byte_vec_delete(binary)
def tearDown(self):
if self.imports:
ffi.wasm_extern_vec_delete(self.imports)
if self.exports:
ffi.wasm_extern_vec_delete(self.exports)
ffi.wasm_instance_delete(self.instance)
ffi.wasm_module_delete(self.module)
def test_wasm_func_call_wasm(self):
export_list = ffi.wasm_vec_to_list(self.exports)
print(export_list)
func = ffi.wasm_extern_as_func(export_list[0])
self.assertIsNotNullPointer(func)
# make a call
params = ffi.wasm_val_vec_t()
data = ffi.list_to_carray(
ffi.wasm_val_t,
ffi.wasm_i32_val(1024),
ffi.wasm_i64_val(1024 * 1024),
)
ffi.wasm_val_vec_new(params, 2, data)
results = ffi.wasm_val_vec_t()
ffi.wasm_val_vec_new_empty(results)
ffi.wasm_func_call(func, params, results)
def test_wasm_func_call_native(self):
import_list = ffi.wasm_vec_to_list(self.imports)
func = ffi.wasm_extern_as_func(import_list[0])
self.assertIsNotNullPointer(func)
params = ffi.wasm_val_vec_t()
ffi.wasm_val_vec_new(
params, 1, ffi.list_to_carray(ffi.wasm_val_t, ffi.wasm_f32_val(3.14))
)
results = ffi.wasm_val_vec_t()
ffi.wasm_val_vec_new_uninitialized(results, 1)
ffi.wasm_func_call(func, params, results)
self.assertEqual(params.data[0].of.f32 * 2, results.data[0].of.f64)
def test_wasm_func_call_wrong_params(self):
export_list = ffi.wasm_vec_to_list(self.exports)
func = ffi.wasm_extern_as_func(export_list[0])
# make a call
params = ffi.wasm_val_vec_t()
ffi.wasm_val_vec_new_empty(params)
results = ffi.wasm_val_vec_t()
ffi.wasm_val_vec_new_empty(results)
trap = ffi.wasm_func_call(func, params, results)
self.assertIsNotNullPointer(trap)
def test_wasm_func_call_unlinked(self):
ft = ffi.wasm_functype_new_0_0()
func = ffi.wasm_func_new(self._wasm_store, ft, callback)
params = ffi.wasm_val_vec_t()
ffi.wasm_val_vec_new_empty(params)
results = ffi.wasm_val_vec_t()
ffi.wasm_val_vec_new_empty(results)
trap = ffi.wasm_func_call(func, params, results)
ffi.wasm_func_delete(func)
def test_wasm_global_get_wasm(self):
export_list = ffi.wasm_vec_to_list(self.exports)
glb = ffi.wasm_extern_as_global(export_list[1])
self.assertIsNotNullPointer(glb)
# access the global
val = ffi.wasm_val_t()
ffi.wasm_global_get(glb, val)
self.assertAlmostEqual(val.of.f32, 3.14, places=3)
def test_wasm_global_get_native(self):
import_list = ffi.wasm_vec_to_list(self.imports)
glb = ffi.wasm_extern_as_global(import_list[1])
self.assertIsNotNullPointer(glb)
val = ffi.wasm_val_t()
ffi.wasm_global_get(glb, val)
self.assertEqual(val.of.i32, 1024)
def test_wasm_global_get_unlinked(self):
gt = ffi.wasm_globaltype_new(ffi.wasm_valtype_new(ffi.WASM_I32), True)
init = ffi.wasm_i32_val(32)
glbl = ffi.wasm_global_new(self._wasm_store, gt, init)
val_ret = ffi.wasm_f32_val(3.14)
ffi.wasm_global_get(glbl, val_ret)
ffi.wasm_global_delete(glbl)
# val_ret wasn't touched, keep the original value
self.assertAlmostEqual(val_ret.of.f32, 3.14, 3)
def test_wasm_global_get_null_val(self):
export_list = ffi.wasm_vec_to_list(self.exports)
glb = ffi.wasm_extern_as_global(export_list[1])
ffi.wasm_global_get(glb, ffi.create_null_pointer(ffi.wasm_val_t))
def test_wasm_global_get_null_global(self):
val = ffi.wasm_val_t()
ffi.wasm_global_get(ffi.create_null_pointer(ffi.wasm_global_t), val)
def test_wasm_global_set_wasm(self):
export_list = ffi.wasm_vec_to_list(self.exports)
glb = ffi.wasm_extern_as_global(export_list[1])
self.assertIsNotNullPointer(glb)
# access the global
new_val = ffi.wasm_f32_val(math.e)
ffi.wasm_global_set(glb, new_val)
val = ffi.wasm_val_t()
ffi.wasm_global_get(glb, val)
self.assertNotEqual(val.of.f32, 3.14)
def test_wasm_global_set_native(self):
import_list = ffi.wasm_vec_to_list(self.imports)
glb = ffi.wasm_extern_as_global(import_list[1])
self.assertIsNotNullPointer(glb)
new_val = ffi.wasm_i32_val(2048)
ffi.wasm_global_set(glb, new_val)
val = ffi.wasm_val_t()
ffi.wasm_global_get(glb, val)
self.assertEqual(val, new_val)
def test_wasm_global_set_unlinked(self):
gt = ffi.wasm_globaltype_new(ffi.wasm_valtype_new(ffi.WASM_I32), True)
init = ffi.wasm_i32_val(32)
glbl = ffi.wasm_global_new(self._wasm_store, gt, init)
val_ret = ffi.wasm_f32_val(3.14)
ffi.wasm_global_set(glbl, val_ret)
ffi.wasm_global_delete(glbl)
def test_wasm_global_set_null_v(self):
export_list = ffi.wasm_vec_to_list(self.exports)
glb = ffi.wasm_extern_as_global(export_list[1])
# access the global
ffi.wasm_global_set(glb, ffi.create_null_pointer(ffi.wasm_val_t))
def test_wasm_global_set_null_global(self):
# access the global
new_val = ffi.wasm_f32_val(math.e)
ffi.wasm_global_set(ffi.create_null_pointer(ffi.wasm_global_t), new_val)
def test_wasm_table_size(self):
export_list = ffi.wasm_vec_to_list(self.exports)
tbl = ffi.wasm_extern_as_table(export_list[3])
self.assertIsNotNullPointer(tbl)
tbl_sz = ffi.wasm_table_size(tbl)
self.assertEqual(tbl_sz, 1)
def test_wasm_table_size_unlink(self):
vt = ffi.wasm_valtype_new(ffi.WASM_FUNCREF)
limits = ffi.wasm_limits_new(10, 15)
tt = ffi.wasm_tabletype_new(vt, limits)
tbl = ffi.wasm_table_new(
self._wasm_store, tt, ffi.create_null_pointer(ffi.wasm_ref_t)
)
tbl_sz = ffi.wasm_table_size(tbl)
ffi.wasm_table_delete(tbl)
def test_wasm_table_size_null_table(self):
ffi.wasm_table_size(ffi.create_null_pointer(ffi.wasm_table_t))
def test_wasm_table_get(self):
export_list = ffi.wasm_vec_to_list(self.exports)
tbl = ffi.wasm_extern_as_table(export_list[3])
self.assertIsNotNullPointer(tbl)
ref = ffi.wasm_table_get(tbl, 0)
self.assertIsNullPointer(ref)
ref = ffi.wasm_table_get(tbl, 4096)
self.assertIsNullPointer(ref)
def test_wasm_table_get_unlinked(self):
vt = ffi.wasm_valtype_new(ffi.WASM_FUNCREF)
limits = ffi.wasm_limits_new(10, 15)
tt = ffi.wasm_tabletype_new(vt, limits)
tbl = ffi.wasm_table_new(
self._wasm_store, tt, ffi.create_null_pointer(ffi.wasm_ref_t)
)
ffi.wasm_table_get(tbl, 0)
ffi.wasm_table_delete(tbl)
def test_wasm_table_get_null_table(self):
ffi.wasm_table_get(ffi.create_null_pointer(ffi.wasm_table_t), 0)
def test_wasm_table_get_out_of_bounds(self):
export_list = ffi.wasm_vec_to_list(self.exports)
tbl = ffi.wasm_extern_as_table(export_list[3])
ffi.wasm_table_get(tbl, 1_000_000_000)
def test_wasm_ref(self):
export_list = ffi.wasm_vec_to_list(self.exports)
func = ffi.wasm_extern_as_func(export_list[0])
self.assertIsNotNullPointer(func)
ref = ffi.wasm_func_as_ref(func)
self.assertIsNotNullPointer(ref)
func_from_ref = ffi.wasm_ref_as_func(ref)
self.assertEqual(
ffi.dereference(ffi.wasm_func_type(func)),
ffi.dereference(ffi.wasm_func_type(func_from_ref)),
)
def test_wasm_table_set(self):
export_list = ffi.wasm_vec_to_list(self.exports)
tbl = ffi.wasm_extern_as_table(export_list[3])
self.assertIsNotNullPointer(tbl)
func = ffi.wasm_extern_as_func(export_list[0])
ref = ffi.wasm_func_as_ref(func)
ffi.wasm_table_set(tbl, 0, ref)
ref_ret = ffi.wasm_table_get(tbl, 0)
self.assertIsNotNullPointer(ref_ret)
func_ret = ffi.wasm_ref_as_func(ref_ret)
self.assertEqual(
ffi.dereference(ffi.wasm_func_type(func)),
ffi.dereference(ffi.wasm_func_type(func_ret)),
)
def test_wasm_table_set_unlinked(self):
vt = ffi.wasm_valtype_new(ffi.WASM_FUNCREF)
limits = ffi.wasm_limits_new(10, 15)
tt = ffi.wasm_tabletype_new(vt, limits)
tbl = ffi.wasm_table_new(
self._wasm_store, tt, ffi.create_null_pointer(ffi.wasm_ref_t)
)
export_list = ffi.wasm_vec_to_list(self.exports)
func = ffi.wasm_extern_as_func(export_list[0])
ref = ffi.wasm_func_as_ref(func)
ffi.wasm_table_set(tbl, 0, ref)
ffi.wasm_table_delete(tbl)
def test_wasm_table_set_null_table(self):
export_list = ffi.wasm_vec_to_list(self.exports)
func = ffi.wasm_extern_as_func(export_list[0])
ref = ffi.wasm_func_as_ref(func)
ffi.wasm_table_set(ffi.create_null_pointer(ffi.wasm_table_t), 0, ref)
def test_wasm_table_set_null_ref(self):
export_list = ffi.wasm_vec_to_list(self.exports)
tbl = ffi.wasm_extern_as_table(export_list[3])
ffi.wasm_table_set(tbl, 0, ffi.create_null_pointer(ffi.wasm_ref_t))
def test_wasm_table_set_out_of_bounds(self):
export_list = ffi.wasm_vec_to_list(self.exports)
tbl = ffi.wasm_extern_as_table(export_list[3])
func = ffi.wasm_extern_as_func(export_list[0])
ref = ffi.wasm_func_as_ref(func)
ffi.wasm_table_set(tbl, 1_000_000_000, ref)
def test_wasm_memory_size(self):
export_list = ffi.wasm_vec_to_list(self.exports)
mem = ffi.wasm_extern_as_memory(export_list[2])
self.assertIsNotNullPointer(mem)
pg_sz = ffi.wasm_memory_size(mem)
self.assertEqual(pg_sz, 1)
def test_wasm_memory_size_unlinked(self):
limits = ffi.wasm_limits_new(10, 12)
mt = ffi.wasm_memorytype_new(limits)
mem = ffi.wasm_memory_new(self._wasm_store, mt)
ffi.wasm_memory_size(mem)
ffi.wasm_memory_delete(mem)
def test_wasm_memory_data(self):
export_list = ffi.wasm_vec_to_list(self.exports)
mem = ffi.wasm_extern_as_memory(export_list[2])
self.assertIsNotNullPointer(mem)
data_base = ffi.wasm_memory_data(mem)
self.assertIsNotNone(data_base)
def test_wasm_memory_data_unlinked(self):
limits = ffi.wasm_limits_new(10, 12)
mt = ffi.wasm_memorytype_new(limits)
mem = ffi.wasm_memory_new(self._wasm_store, mt)
ffi.wasm_memory_data(mem)
ffi.wasm_memory_delete(mem)
def test_wasm_memory_data_size(self):
export_list = ffi.wasm_vec_to_list(self.exports)
mem = ffi.wasm_extern_as_memory(export_list[2])
self.assertIsNotNullPointer(mem)
mem_sz = ffi.wasm_memory_data_size(mem)
self.assertGreater(mem_sz, 0)
def test_wasm_memory_data_size_unlinked(self):
limits = ffi.wasm_limits_new(10, 12)
mt = ffi.wasm_memorytype_new(limits)
mem = ffi.wasm_memory_new(self._wasm_store, mt)
ffi.wasm_memory_data_size(mem)
ffi.wasm_memory_delete(mem)
def test_wasm_trap(self):
export_list = ffi.wasm_vec_to_list(self.exports)
func = ffi.wasm_extern_as_func(export_list[0])
# make a call
params = ffi.wasm_val_vec_t()
ffi.wasm_val_vec_new_empty(params)
results = ffi.wasm_val_vec_t()
ffi.wasm_val_vec_new_empty(results)
trap = ffi.wasm_func_call(func, params, results)
self.assertIsNotNullPointer(trap)
message = ffi.wasm_message_t()
ffi.wasm_trap_message(trap, message)
self.assertIsNotNullPointer(c.pointer(message))
# not a function internal exception
frame = ffi.wasm_trap_origin(trap)
self.assertIsNullPointer(frame)
@unittest.skipUnless(
TEST_WITH_WAMR_BUILD_DUMP_CALL_STACK,
"need to enable WAMR_BUILD_DUMP_CALL_STACK",
)
# assertions only works if enabling WAMR_BUILD_DUMP_CALL_STACK
def test_wasm_frame(self):
export_list = ffi.wasm_vec_to_list(self.exports)
func = ffi.wasm_extern_as_func(export_list[4])
# make a call
params = ffi.wasm_val_vec_t()
ffi.wasm_val_vec_new_empty(params)
results = ffi.wasm_val_vec_t()
ffi.wasm_val_vec_new_empty(results)
print("Making a call...")
trap = ffi.wasm_func_call(func, params, results)
message = ffi.wasm_message_t()
ffi.wasm_trap_message(trap, message)
self.assertIsNotNullPointer(c.pointer(message))
print(message)
frame = ffi.wasm_trap_origin(trap)
self.assertIsNotNullPointer(frame)
print(ffi.dereference(frame))
traces = ffi.wasm_frame_vec_t()
ffi.wasm_trap_trace(trap, traces)
self.assertIsNotNullPointer(c.pointer(frame))
instance = ffi.wasm_frame_instance(frame)
self.assertIsNotNullPointer(instance)
module_offset = ffi.wasm_frame_module_offset(frame)
func_index = ffi.wasm_frame_func_index(frame)
self.assertEqual(func_index, 2)
func_offset = ffi.wasm_frame_func_offset(frame)
self.assertGreater(func_offset, 0)
@classmethod
def tearDownClass(cls):
print("Shutting down...")
ffi.wasm_store_delete(cls._wasm_store)
ffi.wasm_engine_delete(cls._wasm_engine)
if __name__ == "__main__":
unittest.main()

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,386 @@
# -*- coding: utf-8 -*-
#!/usr/bin/env python3
#
# Copyright (C) 2019 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
# pylint: disable=missing-class-docstring
# pylint: disable=missing-function-docstring
# pylint: disable=missing-module-docstring
"""
- Need to run *download_wamr.py* firstly.
- Parse *./wasm-micro-runtime/core/iwasm/include/wasm_c_api.h* and generate
*wamr/binding.py*
"""
import os
import pathlib
import shutil
import sys
from pycparser import c_ast, parse_file
WASM_C_API_HEADER = "core/iwasm/include/wasm_c_api.h"
BINDING_PATH = "wamr/binding.py"
# 4 spaces as default indent
INDENT = " "
IGNORE_SYMOLS = (
"wasm_engine_new_with_args",
"wasm_valkind_is_num",
"wasm_valkind_is_ref",
"wasm_valtype_is_num",
"wasm_valtype_is_ref",
"wasm_valtype_new_i32",
"wasm_valtype_new_i64",
"wasm_valtype_new_f32",
"wasm_valtype_new_f64",
"wasm_valtype_new_anyref",
"wasm_valtype_new_funcref",
"wasm_functype_new_0_0",
"wasm_functype_new_0_0",
"wasm_functype_new_1_0",
"wasm_functype_new_2_0",
"wasm_functype_new_3_0",
"wasm_functype_new_0_1",
"wasm_functype_new_1_1",
"wasm_functype_new_2_1",
"wasm_functype_new_3_1",
"wasm_functype_new_0_2",
"wasm_functype_new_1_2",
"wasm_functype_new_2_2",
"wasm_functype_new_3_2",
"wasm_val_init_ptr",
"wasm_val_ptr",
"wasm_val_t",
"wasm_ref_t",
"wasm_name_new_from_string",
"wasm_name_new_from_string_nt",
)
class Visitor(c_ast.NodeVisitor):
def __init__(self):
self.type_map = {
"_Bool": "c_bool",
"byte_t": "c_ubyte",
"char": "c_char",
"errno_t": "c_int",
"int": "c_int",
"long": "c_long",
"size_t": "c_size_t",
"uint32_t": "c_uint32",
"uint8_t": "c_uint8",
"void": "None",
}
self.ret = (
"# -*- coding: utf-8 -*-\n"
"#!/usr/bin/env python3\n"
"#\n"
"# Copyright (C) 2019 Intel Corporation. All rights reserved.\n"
"# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n"
"#\n"
"#It is a generated file. DO NOT EDIT.\n"
"#\n"
"from ctypes import *\n"
"\n"
"from .ffi import dereference, libiwasm, wasm_ref_t, wasm_val_t\n"
"\n"
"\n"
)
def get_type_name(self, c_type):
if isinstance(c_type, c_ast.TypeDecl):
return self.get_type_name(c_type.type)
elif isinstance(c_type, c_ast.PtrDecl):
pointed_type = self.get_type_name(c_type.type)
if isinstance(c_type.type, c_ast.FuncDecl):
# CFUCNTYPE is a pointer of function
return pointed_type
if "None" == pointed_type:
return "c_void_p"
return f"POINTER({pointed_type})"
elif isinstance(c_type, c_ast.ArrayDecl):
return f"POINTER({self.get_type_name(c_type.type)})"
elif isinstance(c_type, c_ast.IdentifierType):
if len(c_type.names) > 1:
raise RuntimeError(f"unexpected type with a long names: {c_type}")
type_name = c_type.names[0]
if type_name.startswith("wasm_"):
return type_name
if not type_name in self.type_map:
raise RuntimeError(f"a new type should be in type_map: {type_name}")
return self.type_map.get(type_name)
elif isinstance(c_type, c_ast.Union):
if not c_type.name:
raise RuntimeError(f"found an anonymous union {c_type}")
return c_type.name
elif isinstance(c_type, c_ast.Struct):
if not c_type.name:
raise RuntimeError(f"found an anonymous union {c_type}")
return c_type.name
elif isinstance(c_type, c_ast.FuncDecl):
content = "CFUNCTYPE("
if isinstance(c_type.type, c_ast.PtrDecl):
# there is a bug in CFUNCTYPE if the result type is a pointer
content += "c_void_p"
else:
content += f"{self.get_type_name(c_type.type)}"
content += f",{self.get_type_name(c_type.args)}" if c_type.args else ""
content += ")"
return content
elif isinstance(c_type, c_ast.Decl):
return self.get_type_name(c_type.type)
elif isinstance(c_type, c_ast.ParamList):
content = ",".join(
[self.get_type_name(param.type) for param in c_type.params]
)
return content
else:
raise RuntimeError(f"unexpected type: {c_type.show()}")
def visit_Struct(self, node):
# pylint: disable=invalid-name
def gen_fields(info, indent):
content = ""
for k, v in info.items():
content += f'{indent}("{k}", {v}),\n'
return content[:-1]
def gen_equal(info, indent):
content = f"{indent}return"
for k, v in info.items():
# not compare pointer value in __eq__
if v.startswith("POINTER") or v.startswith("c_void_p"):
continue
content += f" self.{k} == other.{k} and"
return content[:-4]
def gen_repr(info, indent):
content = f'{indent}return f"{{{{'
for k, _ in info.items():
content += f"{k}={{self.{k}}}, "
content = content[:-2] + '}}"'
return content
def gen_vector_repr(info, indent):
content = f'{indent}ret = ""\n'
content += f"{indent}for i in range(self.num_elems):\n"
if 1 == info["data"].count("POINTER"):
# pointer
content += f"{2*indent}ret += str(self.data[i])\n"
else:
# pointer of pointer
content += f"{2*indent}ret += str(dereference(self.data[i]))\n"
content += f'{2*indent}ret += " "\n'
content += f"{indent}return ret\n"
return content
if not node.name or not node.name.lower().startswith("wasm"):
return
if node.name in IGNORE_SYMOLS:
return
name = node.name
info = {}
if node.decls:
for decl in node.decls:
info[decl.name] = self.get_type_name(decl.type)
if info:
self.ret += (
f"class {name}(Structure):\n"
f"{INDENT}_fields_ = [\n"
f"{gen_fields(info, INDENT*2)}\n"
f"{INDENT}]\n"
f"\n"
f"{INDENT}def __eq__(self, other):\n"
f"{INDENT*2}if not isinstance(other, {name}):\n"
f"{INDENT*3}return False\n"
f"{gen_equal(info, INDENT*2)}\n"
f"\n"
f"{INDENT}def __repr__(self):\n"
)
self.ret += (
f"{gen_vector_repr(info, INDENT*2)}\n"
if name.endswith("_vec_t")
else f"{gen_repr(info, INDENT*2)}\n"
)
self.ret += "\n"
else:
self.ret += f"class {name}(Structure):\n{INDENT}pass\n"
self.ret += "\n"
def visit_Union(self, node):
# pylint: disable=invalid-name
print(f"Union: {node.show()}")
def visit_Typedef(self, node):
# pylint: disable=invalid-name
# system defined
if not node.name:
return
if not node.name.startswith("wasm_"):
return
if node.name in IGNORE_SYMOLS:
return
self.visit(node.type)
if node.name == self.get_type_name(node.type):
return
else:
self.ret += f"{node.name} = {self.get_type_name(node.type)}\n"
self.ret += "\n"
def visit_FuncDecl(self, node):
# pylint: disable=invalid-name
restype = self.get_type_name(node.type)
if isinstance(node.type, c_ast.TypeDecl):
func_name = node.type.declname
elif isinstance(node.type, c_ast.PtrDecl):
func_name = node.type.type.declname
else:
raise RuntimeError(f"unexpected type in FuncDecl: {type}")
if not func_name.startswith("wasm_") or func_name.endswith("_t"):
return
if func_name in IGNORE_SYMOLS:
return
params_len = 0
for arg in node.args.params:
# ignore void but not void*
if isinstance(arg.type, c_ast.TypeDecl):
type_name = self.get_type_name(arg.type)
if "None" == type_name:
continue
params_len += 1
args = (
"" if not params_len else ",".join([f"arg{i}" for i in range(params_len)])
)
argtypes = f"[{self.get_type_name(node.args)}]" if params_len else "None"
self.ret += (
f"def {func_name}({args}):\n"
f"{INDENT}_{func_name} = libiwasm.{func_name}\n"
f"{INDENT}_{func_name}.restype = {restype}\n"
f"{INDENT}_{func_name}.argtypes = {argtypes}\n"
f"{INDENT}return _{func_name}({args})\n"
)
self.ret += "\n"
def visit_Enum(self, node):
# pylint: disable=invalid-name
elem_value = 0
# generate enum elementes directly as consts with values
for i, elem in enumerate(node.values.enumerators):
self.ret += f"{elem.name}"
if elem.value:
elem_value = int(elem.value.value)
else:
if 0 == i:
elem_value = 0
else:
elem_value += 1
self.ret += f" = {elem_value}\n"
self.ret += "\n"
def preflight_check(workspace):
wamr_repo = workspace.joinpath("wasm-micro-runtime")
file_check_list = [
wamr_repo.exists(),
wamr_repo.joinpath(WASM_C_API_HEADER).exists(),
]
if not all(file_check_list):
print(
"please run utils/download_wamr.py to download the repo, or re-download the repo"
)
return False
if not shutil.which("gcc"):
print("please install gcc")
return False
return True
def do_parse(workspace):
filename = workspace.joinpath(WASM_C_API_HEADER)
filename = str(filename)
ast = parse_file(
filename,
use_cpp=True,
cpp_path="gcc",
cpp_args=[
"-E",
"-D__attribute__(x)=",
"-D__asm__(x)=",
"-D__asm(x)=",
"-D__builtin_va_list=int",
"-D__extension__=",
"-D__inline__=",
"-D__restrict=",
"-D__restrict__=",
"-D_Static_assert(x, y)=",
"-D__signed=",
"-D__volatile__(x)=",
"-Dstatic_assert(x, y)=",
],
)
ast_visitor = Visitor()
ast_visitor.visit(ast)
return ast_visitor.ret
def main():
current_file = pathlib.Path(__file__)
if current_file.is_symlink():
current_file = pathlib.Path(os.readlink(current_file))
current_dir = current_file.parent.resolve()
root_dir = current_dir.joinpath("..").resolve()
if not preflight_check(root_dir):
return False
wamr_repo = root_dir.joinpath("wasm-micro-runtime")
binding_file_path = root_dir.joinpath(BINDING_PATH)
with open(binding_file_path, "wt", encoding="utf-8") as binding_file:
binding_file.write(do_parse(wamr_repo))
return True
if __name__ == "__main__":
sys.exit(0 if main() else 1)

View File

@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
#!/usr/bin/env python3
#
# Copyright (C) 2019 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
__all__ = ["ffi"]

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,642 @@
# -*- coding: utf-8 -*-
#!/usr/bin/env python3
#
# Copyright (C) 2019 Intel Corporation. All rights reserved.
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#
# pylint: disable=missing-class-docstring
# pylint: disable=missing-function-docstring
# pylint: disable=missing-module-docstring
import ctypes as c
import os
from pathlib import Path
import sys
#
# Prologue. Dependencies of binding
#
# how to open the library file of WAMR
if sys.platform == "linux":
BUILDING_DIR = "product-mini/platforms/linux/build"
LIBRARY_NAME = "libiwasm.so"
elif sys.platform == "win32":
BUILDING_DIR = "product-mini/platforms/windows/build"
LIBRARY_NAME = "iwasm.dll"
elif sys.platform == "darwin":
BUILDING_DIR = "product-mini/platforms/darwin/build"
LIBRARY_NAME = "libiwasm.dylib"
else:
raise RuntimeError(f"unsupported platform `{sys.platform}`")
# FIXME: should load libiwasm.so from current system library path
current_file = Path(__file__)
if current_file.is_symlink():
current_file = Path(os.readlink(current_file))
current_dir = current_file.parent.resolve()
root_dir = current_dir.parent.parent.parent.parent.resolve()
wamr_dir = root_dir.joinpath("wasm-micro-runtime").resolve()
if not wamr_dir.exists():
raise RuntimeError(f"not found the repo of wasm-micro-runtime under {root_dir}")
libpath = wamr_dir.joinpath(BUILDING_DIR).joinpath(LIBRARY_NAME).resolve()
if not libpath.exists():
raise RuntimeError(f"not found precompiled wamr library at {libpath}")
print(f"loading WAMR library from {libpath} ...")
libiwasm = c.cdll.LoadLibrary(libpath)
class wasm_ref_t(c.Structure):
# pylint: disable=invalid-name
pass
class wasm_val_union(c.Union):
# pylint: disable=invalid-name
_fields_ = [
("i32", c.c_int32),
("i64", c.c_int64),
("f32", c.c_float),
("f64", c.c_double),
("ref", c.POINTER(wasm_ref_t)),
]
class wasm_val_t(c.Structure):
# pylint: disable=invalid-name
_fields_ = [
("kind", c.c_uint8),
("of", wasm_val_union),
]
def dereference(p):
# pylint: disable=protected-access
if not isinstance(p, c._Pointer):
raise RuntimeError("not a pointer")
return p.contents
# HELPERs
def create_null_pointer(struct_type):
return c.POINTER(struct_type)()
def is_null_pointer(c_pointer):
# pylint: disable=protected-access
if isinstance(c_pointer, c._Pointer):
return False if c_pointer else True
else:
raise RuntimeError("not a pointer")
def wasm_vec_to_list(vec):
"""
Converts a vector or a POINTER(vector) to a list
vector of type pointers -> list of type pointers
"""
known_vec_type = [
wasm_byte_vec_t,
wasm_valtype_vec_t,
wasm_functype_vec_t,
wasm_globaltype_vec_t,
wasm_tabletype_vec_t,
wasm_memorytype_vec_t,
wasm_externtype_vec_t,
wasm_importtype_vec_t,
wasm_exporttype_vec_t,
wasm_val_vec_t,
wasm_frame_vec_t,
wasm_extern_vec_t,
]
known_vec_pointer_type = [POINTER(type) for type in known_vec_type]
if any([isinstance(vec, type) for type in known_vec_pointer_type]):
vec = dereference(vec)
return [vec.data[i] for i in range(vec.num_elems)]
elif any([isinstance(vec, type) for type in known_vec_type]):
return [vec.data[i] for i in range(vec.num_elems)]
else:
raise RuntimeError("not a known vector type")
def list_to_carray(elem_type, *args):
"""
Converts a python list into a C array
"""
data = (elem_type * len(args))(*args)
return data
def load_module_file(wasm_content):
binary = wasm_byte_vec_t()
wasm_byte_vec_new_uninitialized(binary, len(wasm_content))
# has to use malloced memory.
c.memmove(binary.data, wasm_content, len(wasm_content))
binary.num_elems = len(wasm_content)
return binary
#
# Enhancment of binding
#
from .binding import *
# Built-in functions for Structure
wasm_finalizer = CFUNCTYPE(None, c_void_p)
def __repr_wasm_limits_t(self):
return f"{self.min:#x} {self.max:#x}"
# overwrite
wasm_limits_t.__repr__ = __repr_wasm_limits_t
def __compare_wasm_valtype_t(self, other):
if not isinstance(other, wasm_valtype_t):
return False
return wasm_valtype_kind(byref(self)) == wasm_valtype_kind(byref(other))
def __repr_wasm_valtype_t(self):
val_kind = wasm_valtype_kind(byref(self))
if WASM_I32 == val_kind:
return "i32"
elif WASM_I64 == val_kind:
return "i64"
elif WASM_F32 == val_kind:
return "f32"
elif WASM_F64 == val_kind:
return "f64"
elif WASM_FUNCREF == val_kind:
return "funcref"
else:
return "anyref"
wasm_valtype_t.__eq__ = __compare_wasm_valtype_t
wasm_valtype_t.__repr__ = __repr_wasm_valtype_t
def __compare_wasm_byte_vec_t(self, other):
if not isinstance(other, wasm_byte_vec_t):
return False
if self.num_elems != other.num_elems:
return False
self_data = bytes(self.data[: self.num_elems])
other_data = bytes(other.data[: other.num_elems])
return self_data.decode() == other_data.decode()
def __repr_wasm_byte_vec_t(self):
data = bytes(self.data[: self.num_elems])
return data.decode() if self.size else ""
wasm_byte_vec_t.__eq__ = __compare_wasm_byte_vec_t
wasm_byte_vec_t.__repr__ = __repr_wasm_byte_vec_t
def __compare_wasm_functype_t(self, other):
if not isinstance(other, wasm_functype_t):
return False
params1 = dereference(wasm_functype_params(byref(self)))
params2 = dereference(wasm_functype_params(byref(other)))
results1 = dereference(wasm_functype_results(byref(self)))
results2 = dereference(wasm_functype_results(byref(other)))
return params1 == params2 and results1 == results2
def __repr_wasm_functype_t(self):
params = dereference(wasm_functype_params(byref(self)))
results = dereference(wasm_functype_results(byref(self)))
params = f" (params {params})" if params.size else ""
results = f" (results {results})" if results.size else ""
return f"(func{params}{results})"
wasm_functype_t.__eq__ = __compare_wasm_functype_t
wasm_functype_t.__repr__ = __repr_wasm_functype_t
def __compare_wasm_globaltype_t(self, other):
if not isinstance(other, wasm_globaltype_t):
return False
content1 = dereference(wasm_globaltype_content(byref(self)))
content2 = dereference(wasm_globaltype_content(byref(other)))
mutability1 = wasm_globaltype_mutability(byref(self))
mutability2 = wasm_globaltype_mutability(byref(other))
return content1 == content2 and mutability1 == mutability2
def __repr_wasm_globaltype_t(self):
mutability = f"{wasm_globaltype_mutability(byref(self))}"
content = f"{dereference(wasm_globaltype_content(byref(self)))}"
return f"(global{' mut ' if mutability else ' '}{content})"
wasm_globaltype_t.__eq__ = __compare_wasm_globaltype_t
wasm_globaltype_t.__repr__ = __repr_wasm_globaltype_t
def __compare_wasm_tabletype_t(self, other):
if not isinstance(other, wasm_tabletype_t):
return False
element1 = dereference(wasm_tabletype_element(byref(self)))
element2 = dereference(wasm_tabletype_element(byref(other)))
limits1 = dereference(wasm_tabletype_limits(byref(self)))
limits2 = dereference(wasm_tabletype_limits(byref(other)))
return element1 == element2 and limits1 == limits2
def __repr_wasm_tabletype_t(self):
element = dereference(wasm_tabletype_element(byref(self)))
limit = dereference(wasm_tabletype_limits(byref(self)))
return f"(table {limit} {element})"
wasm_tabletype_t.__eq__ = __compare_wasm_tabletype_t
wasm_tabletype_t.__repr__ = __repr_wasm_tabletype_t
def __compare_wasm_memorytype_t(self, other):
if not isinstance(other, wasm_memorytype_t):
return False
limits1 = dereference(wasm_memorytype_limits(byref(self)))
limits2 = dereference(wasm_memorytype_limits(byref(other)))
return limits1 == limits2
def __repr_wasm_memorytype_t(self):
limit = dereference(wasm_memorytype_limits(byref(self)))
return f"(memory {limit})"
wasm_memorytype_t.__eq__ = __compare_wasm_memorytype_t
wasm_memorytype_t.__repr__ = __repr_wasm_memorytype_t
def __compare_wasm_externtype_t(self, other):
if not isinstance(other, wasm_externtype_t):
return False
if wasm_externtype_kind(byref(self)) != wasm_externtype_kind(byref(other)):
return False
extern_kind = wasm_externtype_kind(byref(self))
if WASM_EXTERN_FUNC == extern_kind:
return dereference(wasm_externtype_as_functype(self)) == dereference(
wasm_externtype_as_functype(other)
)
elif WASM_EXTERN_GLOBAL == extern_kind:
return dereference(wasm_externtype_as_globaltype(self)) == dereference(
wasm_externtype_as_globaltype(other)
)
elif WASM_EXTERN_MEMORY == extern_kind:
return dereference(wasm_externtype_as_memorytype(self)) == dereference(
wasm_externtype_as_memorytype(other)
)
elif WASM_EXTERN_TABLE == extern_kind:
return dereference(wasm_externtype_as_tabletype(self)) == dereference(
wasm_externtype_as_tabletype(other)
)
else:
raise RuntimeError("not a valid wasm_externtype_t")
def __repr_wasm_externtype_t(self):
extern_kind = wasm_externtype_kind(byref(self))
if WASM_EXTERN_FUNC == extern_kind:
return str(dereference(wasm_externtype_as_functype(byref(self))))
elif WASM_EXTERN_GLOBAL == extern_kind:
return str(dereference(wasm_externtype_as_globaltype(byref(self))))
elif WASM_EXTERN_MEMORY == extern_kind:
return str(dereference(wasm_externtype_as_memorytype(byref(self))))
elif WASM_EXTERN_TABLE == extern_kind:
return str(dereference(wasm_externtype_as_tabletype(byref(self))))
else:
raise RuntimeError("not a valid wasm_externtype_t")
wasm_externtype_t.__eq__ = __compare_wasm_externtype_t
wasm_externtype_t.__repr__ = __repr_wasm_externtype_t
def __compare_wasm_importtype_t(self, other):
if not isinstance(other, wasm_importtype_t):
return False
if dereference(wasm_importtype_module(self)) != dereference(
wasm_importtype_module(other)
):
return False
if dereference(wasm_importtype_name(self)) != dereference(
wasm_importtype_name(other)
):
return False
self_type = dereference(wasm_importtype_type(byref(self)))
other_type = dereference(wasm_importtype_type(byref(other)))
return self_type == other_type
def __repr_wasm_importtype_t(self):
module = wasm_importtype_module(byref(self))
name = wasm_importtype_name(byref(self))
extern_type = wasm_importtype_type(byref(self))
return f'(import "{dereference(module)}" "{dereference(name)}" {dereference(extern_type)})'
wasm_importtype_t.__eq__ = __compare_wasm_importtype_t
wasm_importtype_t.__repr__ = __repr_wasm_importtype_t
def __compare_wasm_exporttype_t(self, other):
if not isinstance(other, wasm_exporttype_t):
return False
self_name = dereference(wasm_exporttype_name(byref(self)))
other_name = dereference(wasm_exporttype_name(byref(other)))
if self_name != other_name:
return False
self_type = dereference(wasm_exporttype_type(byref(self)))
other_type = dereference(wasm_exporttype_type(byref(other)))
return self_type == other_type
def __repr_wasm_exporttype_t(self):
name = wasm_exporttype_name(byref(self))
extern_type = wasm_exporttype_type(byref(self))
return f'(export "{dereference(name)}" {dereference(extern_type)})'
wasm_exporttype_t.__eq__ = __compare_wasm_exporttype_t
wasm_exporttype_t.__repr__ = __repr_wasm_exporttype_t
def __compare_wasm_val_t(self, other):
if not isinstance(other, wasm_val_t):
return False
if self.kind != other.kind:
return False
if WASM_I32 == self.kind:
return self.of.i32 == other.of.i32
elif WASM_I64 == self.kind:
return self.of.i64 == other.of.i64
elif WASM_F32 == self.kind:
return self.of.f32 == other.of.f32
elif WASM_F64 == self.kind:
return self.of.f64 == other.of.f63
elif WASM_ANYREF == self.kind:
raise RuntimeError("FIXME")
else:
raise RuntimeError("not a valid val kind")
def __repr_wasm_val_t(self):
if WASM_I32 == self.kind:
return f"i32 {self.of.i32}"
elif WASM_I64 == self.kind:
return f"i64 {self.of.i64}"
elif WASM_F32 == self.kind:
return f"f32 {self.of.f32}"
elif WASM_F64 == self.kind:
return f"f64 {self.of.f64}"
elif WASM_ANYREF == self.kind:
return f"anyref {self.of.ref}"
else:
raise RuntimeError("not a valid val kind")
wasm_val_t.__repr__ = __repr_wasm_val_t
wasm_val_t.__eq__ = __compare_wasm_val_t
def __repr_wasm_trap_t(self):
message = wasm_message_t()
wasm_trap_message(self, message)
return f'(trap "{str(message)}")'
wasm_trap_t.__repr__ = __repr_wasm_trap_t
def __repr_wasm_frame_t(self):
instance = wasm_frame_instance(self)
module_offset = wasm_frame_module_offset(self)
func_index = wasm_frame_func_index(self)
func_offset = wasm_frame_func_offset(self)
return f"> module:{module_offset:#x} => func#{func_index:#x}.{func_offset:#x}"
wasm_frame_t.__repr__ = __repr_wasm_frame_t
def __repr_wasm_module_t(self):
imports = wasm_importtype_vec_t()
wasm_module_imports(self, imports)
exports = wasm_exporttype_vec_t()
wasm_module_exports(self, exports)
ret = "(module"
ret += str(imports).replace("(import", "\n (import")
ret += str(exports).replace("(export", "\n (export")
ret += "\n)"
return ret
wasm_module_t.__repr__ = __repr_wasm_module_t
def __repr_wasm_instance_t(self):
exports = wasm_extern_vec_t()
wasm_instance_exports(self, exports)
ret = "(instance"
ret += str(exports).replace("(export", "\n (export")
ret += "\n)"
return ret
wasm_instance_t.__repr__ = __repr_wasm_instance_t
def __repr_wasm_func_t(self):
ft = wasm_func_type(self)
return f"{str(dereference(ft))[:-1]} ... )"
wasm_func_t.__repr__ = __repr_wasm_func_t
def __repr_wasm_global_t(self):
gt = wasm_global_type(self)
return f"{str(dereference(gt))[:-1]} ... )"
wasm_global_t.__repr__ = __repr_wasm_global_t
def __repr_wasm_table_t(self):
tt = wasm_table_type(self)
return f"{str(dereference(tt))[:-1]} ... )"
wasm_table_t.__repr__ = __repr_wasm_table_t
def __repr_wasm_memory_t(self):
mt = wasm_memory_type(self)
return f"{str(dereference(mt))[:-1]} ... )"
wasm_memory_t.__repr__ = __repr_wasm_memory_t
def __repr_wasm_extern_t(self):
ext_type = wasm_extern_type(self)
ext_kind = wasm_extern_kind(self)
ret = "(export "
if WASM_EXTERN_FUNC == ext_kind:
ft = wasm_externtype_as_functype(ext_type)
ret += str(dereference(ft))
elif WASM_EXTERN_GLOBAL == ext_kind:
gt = wasm_externtype_as_globaltype(ext_type)
ret += str(dereference(gt))
elif WASM_EXTERN_MEMORY == ext_kind:
mt = wasm_externtype_as_memorytype(ext_type)
ret += str(dereference(mt))
elif WASM_EXTERN_TABLE == ext_kind:
tt = wasm_externtype_as_tabletype(ext_type)
ret += str(dereference(tt))
else:
raise RuntimeError("not a valid extern kind")
ret += ")"
return ret
wasm_extern_t.__repr__ = __repr_wasm_extern_t
# Function Types construction short-hands
def wasm_name_new_from_string(s):
name = wasm_name_t()
data = ((c.c_ubyte) * len(s)).from_buffer_copy(s.encode())
wasm_byte_vec_new(byref(name), len(s), data)
return name
def __wasm_functype_new(param_list, result_list):
def __list_to_wasm_valtype_vec(l):
vec = wasm_valtype_vec_t()
if not l:
wasm_valtype_vec_new_empty(byref(vec))
else:
data_type = POINTER(wasm_valtype_t) * len(l)
data = data_type()
for i in range(len(l)):
data[i] = l[i]
wasm_valtype_vec_new(byref(vec), len(l), data)
return vec
params = __list_to_wasm_valtype_vec(param_list)
results = __list_to_wasm_valtype_vec(result_list)
return wasm_functype_new(byref(params), byref(results))
def wasm_functype_new_0_0():
return __wasm_functype_new([], [])
def wasm_functype_new_1_0(p1):
return __wasm_functype_new([p1], [])
def wasm_functype_new_2_0(p1, p2):
return __wasm_functype_new([p1, p2], [])
def wasm_functype_new_3_0(p1, p2, p3):
return __wasm_functype_new([p1, p2, p3], [])
def wasm_functype_new_0_1(r1):
return __wasm_functype_new([], [r1])
def wasm_functype_new_1_1(p1, r1):
return __wasm_functype_new([p1], [r1])
def wasm_functype_new_2_1(p1, p2, r1):
return __wasm_functype_new([p1, p2], [r1])
def wasm_functype_new_3_1(p1, p2, p3, r1):
return __wasm_functype_new([p1, p2, p3], [r1])
def wasm_limits_new(min, max):
limit = wasm_limits_t()
limit.min = min
limit.max = max
return c.pointer(limit)
def wasm_i32_val(i):
v = wasm_val_t()
v.kind = WASM_I32
v.of.i32 = i
return v
def wasm_i64_val(i):
v = wasm_val_t()
v.kind = WASM_I64
v.of.i64 = i
return v
def wasm_f32_val(z):
v = wasm_val_t()
v.kind = WASM_F32
v.of.f32 = z
return v
def wasm_f64_val(z):
v = wasm_val_t()
v.kind = WASM_F64
v.of.f64 = z
return v
def wasm_func_cb_decl(func):
return wasm_func_callback_t(func)
def wasm_func_with_env_cb_decl(func):
return wasm_func_callback_with_env_t(func)