mirror of
https://github.com/bytecodealliance/wasm-micro-runtime.git
synced 2025-05-07 12:16:24 +00:00
Implement the first version of Python language binding Co-authored-by: liang.he <liang.he@intel.com>
This commit is contained in:
parent
3168ba8dcf
commit
3d34a91f0b
160
language-bindings/python/.gitignore
vendored
Normal file
160
language-bindings/python/.gitignore
vendored
Normal 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/
|
||||||
|
|
1
language-bindings/python/LICENSE
Symbolic link
1
language-bindings/python/LICENSE
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../../LICENSE
|
31
language-bindings/python/README.md
Normal file
31
language-bindings/python/README.md
Normal 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.
|
708
language-bindings/python/docs/design.md
Normal file
708
language-bindings/python/docs/design.md
Normal 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, you’ll 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
|
||||||
|

|
||||||
|
|
||||||
|
## 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 |
12
language-bindings/python/docs/setup_dev_env.md
Normal file
12
language-bindings/python/docs/setup_dev_env.md
Normal 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
|
||||||
|
```
|
3
language-bindings/python/pyproject.toml
Normal file
3
language-bindings/python/pyproject.toml
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=42"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
5
language-bindings/python/requirements.txt
Normal file
5
language-bindings/python/requirements.txt
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
black
|
||||||
|
nose
|
||||||
|
pycparser
|
||||||
|
pylint
|
||||||
|
|
4
language-bindings/python/samples/hello.wat
Normal file
4
language-bindings/python/samples/hello.wat
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
(module
|
||||||
|
(func $hello (import "" "hello"))
|
||||||
|
(func (export "run") (call $hello))
|
||||||
|
)
|
41
language-bindings/python/samples/hello_oop.py
Normal file
41
language-bindings/python/samples/hello_oop.py
Normal 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()
|
93
language-bindings/python/samples/hello_procedural.py
Normal file
93
language-bindings/python/samples/hello_procedural.py
Normal 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()
|
30
language-bindings/python/setup.py
Executable file
30
language-bindings/python/setup.py
Executable 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"],
|
||||||
|
)
|
7
language-bindings/python/tests/__init__.py
Normal file
7
language-bindings/python/tests/__init__.py
Normal 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"]
|
13
language-bindings/python/tests/context.py
Normal file
13
language-bindings/python/tests/context.py
Normal 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
|
525
language-bindings/python/tests/test_advanced.py
Normal file
525
language-bindings/python/tests/test_advanced.py
Normal 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()
|
1588
language-bindings/python/tests/test_basic.py
Normal file
1588
language-bindings/python/tests/test_basic.py
Normal file
File diff suppressed because it is too large
Load Diff
386
language-bindings/python/utils/bindgen.py
Normal file
386
language-bindings/python/utils/bindgen.py
Normal 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)
|
7
language-bindings/python/wamr/__init__.py
Normal file
7
language-bindings/python/wamr/__init__.py
Normal 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"]
|
2020
language-bindings/python/wamr/binding.py
Normal file
2020
language-bindings/python/wamr/binding.py
Normal file
File diff suppressed because it is too large
Load Diff
642
language-bindings/python/wamr/ffi.py
Normal file
642
language-bindings/python/wamr/ffi.py
Normal 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)
|
Loading…
Reference in New Issue
Block a user