bindgen.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386
  1. # -*- coding: utf-8 -*-
  2. #!/usr/bin/env python3
  3. #
  4. # Copyright (C) 2019 Intel Corporation. All rights reserved.
  5. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. #
  7. # pylint: disable=missing-class-docstring
  8. # pylint: disable=missing-function-docstring
  9. # pylint: disable=missing-module-docstring
  10. """
  11. - Need to run *download_wamr.py* firstly.
  12. - Parse *./wasm-micro-runtime/core/iwasm/include/wasm_c_api.h* and generate
  13. *wamr/binding.py*
  14. """
  15. import os
  16. import pathlib
  17. import shutil
  18. import sys
  19. from pycparser import c_ast, parse_file
  20. WASM_C_API_HEADER = "core/iwasm/include/wasm_c_api.h"
  21. BINDING_PATH = "language-bindings/python/wamr/wasmcapi/binding.py"
  22. # 4 spaces as default indent
  23. INDENT = " "
  24. IGNORE_SYMOLS = (
  25. "wasm_engine_new_with_args",
  26. "wasm_valkind_is_num",
  27. "wasm_valkind_is_ref",
  28. "wasm_valtype_is_num",
  29. "wasm_valtype_is_ref",
  30. "wasm_valtype_new_i32",
  31. "wasm_valtype_new_i64",
  32. "wasm_valtype_new_f32",
  33. "wasm_valtype_new_f64",
  34. "wasm_valtype_new_anyref",
  35. "wasm_valtype_new_funcref",
  36. "wasm_functype_new_0_0",
  37. "wasm_functype_new_0_0",
  38. "wasm_functype_new_1_0",
  39. "wasm_functype_new_2_0",
  40. "wasm_functype_new_3_0",
  41. "wasm_functype_new_0_1",
  42. "wasm_functype_new_1_1",
  43. "wasm_functype_new_2_1",
  44. "wasm_functype_new_3_1",
  45. "wasm_functype_new_0_2",
  46. "wasm_functype_new_1_2",
  47. "wasm_functype_new_2_2",
  48. "wasm_functype_new_3_2",
  49. "wasm_val_init_ptr",
  50. "wasm_val_ptr",
  51. "wasm_val_t",
  52. "wasm_ref_t",
  53. "wasm_name_new_from_string",
  54. "wasm_name_new_from_string_nt",
  55. )
  56. class Visitor(c_ast.NodeVisitor):
  57. def __init__(self):
  58. self.type_map = {
  59. "_Bool": "c_bool",
  60. "byte_t": "c_ubyte",
  61. "char": "c_char",
  62. "errno_t": "c_int",
  63. "int": "c_int",
  64. "long": "c_long",
  65. "size_t": "c_size_t",
  66. "uint32_t": "c_uint32",
  67. "uint8_t": "c_uint8",
  68. "void": "None",
  69. }
  70. self.ret = (
  71. "# -*- coding: utf-8 -*-\n"
  72. "#!/usr/bin/env python3\n"
  73. "#\n"
  74. "# Copyright (C) 2019 Intel Corporation. All rights reserved.\n"
  75. "# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception\n"
  76. "#\n"
  77. "#It is a generated file. DO NOT EDIT.\n"
  78. "#\n"
  79. "from ctypes import *\n"
  80. "\n"
  81. "from .ffi import dereference, libiwasm, wasm_ref_t, wasm_val_t\n"
  82. "\n"
  83. "\n"
  84. )
  85. def get_type_name(self, c_type):
  86. if isinstance(c_type, c_ast.TypeDecl):
  87. return self.get_type_name(c_type.type)
  88. elif isinstance(c_type, c_ast.PtrDecl):
  89. pointed_type = self.get_type_name(c_type.type)
  90. if isinstance(c_type.type, c_ast.FuncDecl):
  91. # CFUCNTYPE is a pointer of function
  92. return pointed_type
  93. if "None" == pointed_type:
  94. return "c_void_p"
  95. return f"POINTER({pointed_type})"
  96. elif isinstance(c_type, c_ast.ArrayDecl):
  97. return f"POINTER({self.get_type_name(c_type.type)})"
  98. elif isinstance(c_type, c_ast.IdentifierType):
  99. if len(c_type.names) > 1:
  100. raise RuntimeError(f"unexpected type with a long names: {c_type}")
  101. type_name = c_type.names[0]
  102. if type_name.startswith("wasm_"):
  103. return type_name
  104. if not type_name in self.type_map:
  105. raise RuntimeError(f"a new type should be in type_map: {type_name}")
  106. return self.type_map.get(type_name)
  107. elif isinstance(c_type, c_ast.Union):
  108. if not c_type.name:
  109. raise RuntimeError(f"found an anonymous union {c_type}")
  110. return c_type.name
  111. elif isinstance(c_type, c_ast.Struct):
  112. if not c_type.name:
  113. raise RuntimeError(f"found an anonymous union {c_type}")
  114. return c_type.name
  115. elif isinstance(c_type, c_ast.FuncDecl):
  116. content = "CFUNCTYPE("
  117. if isinstance(c_type.type, c_ast.PtrDecl):
  118. # there is a bug in CFUNCTYPE if the result type is a pointer
  119. content += "c_void_p"
  120. else:
  121. content += f"{self.get_type_name(c_type.type)}"
  122. content += f",{self.get_type_name(c_type.args)}" if c_type.args else ""
  123. content += ")"
  124. return content
  125. elif isinstance(c_type, c_ast.Decl):
  126. return self.get_type_name(c_type.type)
  127. elif isinstance(c_type, c_ast.ParamList):
  128. content = ",".join(
  129. [self.get_type_name(param.type) for param in c_type.params]
  130. )
  131. return content
  132. else:
  133. raise RuntimeError(f"unexpected type: {c_type.show()}")
  134. def visit_Struct(self, node):
  135. # pylint: disable=invalid-name
  136. def gen_fields(info, indent):
  137. content = ""
  138. for k, v in info.items():
  139. content += f'{indent}("{k}", {v}),\n'
  140. return content[:-1]
  141. def gen_equal(info, indent):
  142. content = f"{indent}return"
  143. for k, v in info.items():
  144. # not compare pointer value in __eq__
  145. if v.startswith("POINTER") or v.startswith("c_void_p"):
  146. continue
  147. content += f" self.{k} == other.{k} and"
  148. return content[:-4]
  149. def gen_repr(info, indent):
  150. content = f'{indent}return f"{{{{'
  151. for k, _ in info.items():
  152. content += f"{k}={{self.{k}}}, "
  153. content = content[:-2] + '}}"'
  154. return content
  155. def gen_vector_repr(info, indent):
  156. content = f'{indent}ret = ""\n'
  157. content += f"{indent}for i in range(self.num_elems):\n"
  158. if 1 == info["data"].count("POINTER"):
  159. # pointer
  160. content += f"{2*indent}ret += str(self.data[i])\n"
  161. else:
  162. # pointer of pointer
  163. content += f"{2*indent}ret += str(dereference(self.data[i]))\n"
  164. content += f'{2*indent}ret += " "\n'
  165. content += f"{indent}return ret\n"
  166. return content
  167. if not node.name or not node.name.lower().startswith("wasm"):
  168. return
  169. if node.name in IGNORE_SYMOLS:
  170. return
  171. name = node.name
  172. info = {}
  173. if node.decls:
  174. for decl in node.decls:
  175. info[decl.name] = self.get_type_name(decl.type)
  176. if info:
  177. self.ret += (
  178. f"class {name}(Structure):\n"
  179. f"{INDENT}_fields_ = [\n"
  180. f"{gen_fields(info, INDENT*2)}\n"
  181. f"{INDENT}]\n"
  182. f"\n"
  183. f"{INDENT}def __eq__(self, other):\n"
  184. f"{INDENT*2}if not isinstance(other, {name}):\n"
  185. f"{INDENT*3}return False\n"
  186. f"{gen_equal(info, INDENT*2)}\n"
  187. f"\n"
  188. f"{INDENT}def __repr__(self):\n"
  189. )
  190. self.ret += (
  191. f"{gen_vector_repr(info, INDENT*2)}\n"
  192. if name.endswith("_vec_t")
  193. else f"{gen_repr(info, INDENT*2)}\n"
  194. )
  195. self.ret += "\n"
  196. else:
  197. self.ret += f"class {name}(Structure):\n{INDENT}pass\n"
  198. self.ret += "\n"
  199. def visit_Union(self, node):
  200. # pylint: disable=invalid-name
  201. print(f"Union: {node.show()}")
  202. def visit_Typedef(self, node):
  203. # pylint: disable=invalid-name
  204. # system defined
  205. if not node.name:
  206. return
  207. if not node.name.startswith("wasm_"):
  208. return
  209. if node.name in IGNORE_SYMOLS:
  210. return
  211. self.visit(node.type)
  212. if node.name == self.get_type_name(node.type):
  213. return
  214. else:
  215. self.ret += f"{node.name} = {self.get_type_name(node.type)}\n"
  216. self.ret += "\n"
  217. def visit_FuncDecl(self, node):
  218. # pylint: disable=invalid-name
  219. restype = self.get_type_name(node.type)
  220. if isinstance(node.type, c_ast.TypeDecl):
  221. func_name = node.type.declname
  222. elif isinstance(node.type, c_ast.PtrDecl):
  223. func_name = node.type.type.declname
  224. else:
  225. raise RuntimeError(f"unexpected type in FuncDecl: {type}")
  226. if not func_name.startswith("wasm_") or func_name.endswith("_t"):
  227. return
  228. if func_name in IGNORE_SYMOLS:
  229. return
  230. params_len = 0
  231. for arg in node.args.params:
  232. # ignore void but not void*
  233. if isinstance(arg.type, c_ast.TypeDecl):
  234. type_name = self.get_type_name(arg.type)
  235. if "None" == type_name:
  236. continue
  237. params_len += 1
  238. args = (
  239. "" if not params_len else ",".join([f"arg{i}" for i in range(params_len)])
  240. )
  241. argtypes = f"[{self.get_type_name(node.args)}]" if params_len else "None"
  242. self.ret += (
  243. f"def {func_name}({args}):\n"
  244. f"{INDENT}_{func_name} = libiwasm.{func_name}\n"
  245. f"{INDENT}_{func_name}.restype = {restype}\n"
  246. f"{INDENT}_{func_name}.argtypes = {argtypes}\n"
  247. f"{INDENT}return _{func_name}({args})\n"
  248. )
  249. self.ret += "\n"
  250. def visit_Enum(self, node):
  251. # pylint: disable=invalid-name
  252. elem_value = 0
  253. # generate enum elementes directly as consts with values
  254. for i, elem in enumerate(node.values.enumerators):
  255. self.ret += f"{elem.name}"
  256. if elem.value:
  257. elem_value = int(elem.value.value)
  258. else:
  259. if 0 == i:
  260. elem_value = 0
  261. else:
  262. elem_value += 1
  263. self.ret += f" = {elem_value}\n"
  264. self.ret += "\n"
  265. def preflight_check(workspace):
  266. wamr_repo = workspace
  267. file_check_list = [
  268. wamr_repo.exists(),
  269. wamr_repo.joinpath(WASM_C_API_HEADER).exists(),
  270. ]
  271. if not all(file_check_list):
  272. print(
  273. "please run utils/download_wamr.py to download the repo, or re-download the repo"
  274. )
  275. return False
  276. if not shutil.which("gcc"):
  277. print("please install gcc")
  278. return False
  279. return True
  280. def do_parse(workspace):
  281. filename = workspace.joinpath(WASM_C_API_HEADER)
  282. filename = str(filename)
  283. ast = parse_file(
  284. filename,
  285. use_cpp=True,
  286. cpp_path="gcc",
  287. cpp_args=[
  288. "-E",
  289. "-D__attribute__(x)=",
  290. "-D__asm__(x)=",
  291. "-D__asm(x)=",
  292. "-D__builtin_va_list=int",
  293. "-D__extension__=",
  294. "-D__inline__=",
  295. "-D__restrict=",
  296. "-D__restrict__=",
  297. "-D_Static_assert(x, y)=",
  298. "-D__signed=",
  299. "-D__volatile__(x)=",
  300. "-Dstatic_assert(x, y)=",
  301. ],
  302. )
  303. ast_visitor = Visitor()
  304. ast_visitor.visit(ast)
  305. return ast_visitor.ret
  306. def main():
  307. current_file = pathlib.Path(__file__)
  308. if current_file.is_symlink():
  309. current_file = pathlib.Path(os.readlink(current_file))
  310. current_dir = current_file.parent.resolve()
  311. root_dir = current_dir.joinpath("../../../..").resolve()
  312. if not preflight_check(root_dir):
  313. return False
  314. wamr_repo = root_dir
  315. binding_file_path = root_dir.joinpath(BINDING_PATH)
  316. with open(binding_file_path, "wt", encoding="utf-8") as binding_file:
  317. binding_file.write(do_parse(wamr_repo))
  318. return True
  319. if __name__ == "__main__":
  320. sys.exit(0 if main() else 1)