collector.py 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. from typing import List
  2. from cxxheaderparser.parser import CxxParser
  3. from . import (
  4. ApiEntries,
  5. ApiEntryFunction,
  6. ApiEntryVariable,
  7. ApiHeader,
  8. )
  9. # 'Fixing' complaints about typedefs
  10. CxxParser._fundamentals.discard("wchar_t")
  11. from cxxheaderparser.types import (
  12. EnumDecl,
  13. Field,
  14. ForwardDecl,
  15. FriendDecl,
  16. Function,
  17. Method,
  18. Typedef,
  19. UsingAlias,
  20. UsingDecl,
  21. Variable,
  22. Pointer,
  23. Type,
  24. PQName,
  25. NameSpecifier,
  26. FundamentalSpecifier,
  27. Parameter,
  28. Array,
  29. Value,
  30. Token,
  31. FunctionType,
  32. )
  33. from cxxheaderparser.parserstate import (
  34. State,
  35. EmptyBlockState,
  36. ClassBlockState,
  37. ExternBlockState,
  38. NamespaceBlockState,
  39. )
  40. class SymbolManager:
  41. def __init__(self):
  42. self.api = ApiEntries()
  43. self.name_hashes = set()
  44. # Calculate hash of name and raise exception if it already is in the set
  45. def _name_check(self, name: str):
  46. name_hash = gnu_sym_hash(name)
  47. if name_hash in self.name_hashes:
  48. raise Exception(f"Hash collision on {name}")
  49. self.name_hashes.add(name_hash)
  50. def add_function(self, function_def: ApiEntryFunction):
  51. if function_def in self.api.functions:
  52. return
  53. self._name_check(function_def.name)
  54. self.api.functions.add(function_def)
  55. def add_variable(self, variable_def: ApiEntryVariable):
  56. if variable_def in self.api.variables:
  57. return
  58. self._name_check(variable_def.name)
  59. self.api.variables.add(variable_def)
  60. def add_header(self, header: str):
  61. self.api.headers.add(ApiHeader(header))
  62. def gnu_sym_hash(name: str):
  63. h = 0x1505
  64. for c in name:
  65. h = (h << 5) + h + ord(c)
  66. return str(hex(h))[-8:]
  67. class SdkCollector:
  68. def __init__(self):
  69. self.symbol_manager = SymbolManager()
  70. def add_header_to_sdk(self, header: str):
  71. self.symbol_manager.add_header(header)
  72. def process_source_file_for_sdk(self, file_path: str):
  73. visitor = SdkCxxVisitor(self.symbol_manager)
  74. with open(file_path, "rt") as f:
  75. content = f.read()
  76. parser = CxxParser(file_path, content, visitor, None)
  77. parser.parse()
  78. def get_api(self):
  79. return self.symbol_manager.api
  80. def stringify_array_dimension(size_descr):
  81. if not size_descr:
  82. return ""
  83. return stringify_descr(size_descr)
  84. def stringify_array_descr(type_descr):
  85. assert isinstance(type_descr, Array)
  86. return (
  87. stringify_descr(type_descr.array_of),
  88. stringify_array_dimension(type_descr.size),
  89. )
  90. def stringify_descr(type_descr):
  91. if isinstance(type_descr, (NameSpecifier, FundamentalSpecifier)):
  92. return type_descr.name
  93. elif isinstance(type_descr, PQName):
  94. return "::".join(map(stringify_descr, type_descr.segments))
  95. elif isinstance(type_descr, Pointer):
  96. # Hack
  97. if isinstance(type_descr.ptr_to, FunctionType):
  98. return stringify_descr(type_descr.ptr_to)
  99. return f"{stringify_descr(type_descr.ptr_to)}*"
  100. elif isinstance(type_descr, Type):
  101. return (
  102. f"{'const ' if type_descr.const else ''}"
  103. f"{'volatile ' if type_descr.volatile else ''}"
  104. f"{stringify_descr(type_descr.typename)}"
  105. )
  106. elif isinstance(type_descr, Parameter):
  107. return stringify_descr(type_descr.type)
  108. elif isinstance(type_descr, Array):
  109. # Hack for 2d arrays
  110. if isinstance(type_descr.array_of, Array):
  111. argtype, dimension = stringify_array_descr(type_descr.array_of)
  112. return (
  113. f"{argtype}[{stringify_array_dimension(type_descr.size)}][{dimension}]"
  114. )
  115. return f"{stringify_descr(type_descr.array_of)}[{stringify_array_dimension(type_descr.size)}]"
  116. elif isinstance(type_descr, Value):
  117. return " ".join(map(stringify_descr, type_descr.tokens))
  118. elif isinstance(type_descr, FunctionType):
  119. return f"{stringify_descr(type_descr.return_type)} (*)({', '.join(map(stringify_descr, type_descr.parameters))})"
  120. elif isinstance(type_descr, Token):
  121. return type_descr.value
  122. elif type_descr is None:
  123. return ""
  124. else:
  125. raise Exception("unsupported type_descr: %s" % type_descr)
  126. class SdkCxxVisitor:
  127. def __init__(self, symbol_manager: SymbolManager):
  128. self.api = symbol_manager
  129. def on_variable(self, state: State, v: Variable) -> None:
  130. if not v.extern:
  131. return
  132. self.api.add_variable(
  133. ApiEntryVariable(
  134. stringify_descr(v.name),
  135. stringify_descr(v.type),
  136. )
  137. )
  138. def on_function(self, state: State, fn: Function) -> None:
  139. if fn.inline or fn.has_body:
  140. return
  141. self.api.add_function(
  142. ApiEntryFunction(
  143. stringify_descr(fn.name),
  144. stringify_descr(fn.return_type),
  145. ", ".join(map(stringify_descr, fn.parameters))
  146. + (", ..." if fn.vararg else ""),
  147. )
  148. )
  149. def on_define(self, state: State, content: str) -> None:
  150. pass
  151. def on_pragma(self, state: State, content: str) -> None:
  152. pass
  153. def on_include(self, state: State, filename: str) -> None:
  154. pass
  155. def on_empty_block_start(self, state: EmptyBlockState) -> None:
  156. pass
  157. def on_empty_block_end(self, state: EmptyBlockState) -> None:
  158. pass
  159. def on_extern_block_start(self, state: ExternBlockState) -> None:
  160. pass
  161. def on_extern_block_end(self, state: ExternBlockState) -> None:
  162. pass
  163. def on_namespace_start(self, state: NamespaceBlockState) -> None:
  164. pass
  165. def on_namespace_end(self, state: NamespaceBlockState) -> None:
  166. pass
  167. def on_forward_decl(self, state: State, fdecl: ForwardDecl) -> None:
  168. pass
  169. def on_typedef(self, state: State, typedef: Typedef) -> None:
  170. pass
  171. def on_using_namespace(self, state: State, namespace: List[str]) -> None:
  172. pass
  173. def on_using_alias(self, state: State, using: UsingAlias) -> None:
  174. pass
  175. def on_using_declaration(self, state: State, using: UsingDecl) -> None:
  176. pass
  177. def on_enum(self, state: State, enum: EnumDecl) -> None:
  178. pass
  179. def on_class_start(self, state: ClassBlockState) -> None:
  180. pass
  181. def on_class_field(self, state: State, f: Field) -> None:
  182. pass
  183. def on_class_method(self, state: ClassBlockState, method: Method) -> None:
  184. pass
  185. def on_class_friend(self, state: ClassBlockState, friend: FriendDecl) -> None:
  186. pass
  187. def on_class_end(self, state: ClassBlockState) -> None:
  188. pass