| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386 |
- # -*- 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)
|