mirror of
https://github.com/bytecodealliance/wasm-micro-runtime.git
synced 2024-11-26 15:32:05 +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
|
||||
![python-package-lifecycle](images/python_package_life_cycle.png)
|
||||
|
||||
## CI
|
||||
|
||||
There are several parts:
|
||||
|
||||
- code format check.
|
||||
- test. include running all unit test cases and examples.
|
||||
- publish built distribution.
|
Binary file not shown.
After Width: | Height: | Size: 75 KiB |
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