embindgen.py 44 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077
  1. ###############################################################################
  2. #
  3. # IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
  4. #
  5. # By downloading, copying, installing or using the software you agree to this license.
  6. # If you do not agree to this license, do not download, install,
  7. # copy or use the software.
  8. #
  9. #
  10. # License Agreement
  11. # For Open Source Computer Vision Library
  12. #
  13. # Copyright (C) 2013, OpenCV Foundation, all rights reserved.
  14. # Third party copyrights are property of their respective owners.
  15. #
  16. # Redistribution and use in source and binary forms, with or without modification,
  17. # are permitted provided that the following conditions are met:
  18. #
  19. # * Redistribution's of source code must retain the above copyright notice,
  20. # this list of conditions and the following disclaimer.
  21. #
  22. # * Redistribution's in binary form must reproduce the above copyright notice,
  23. # this list of conditions and the following disclaimer in the documentation
  24. # and/or other materials provided with the distribution.
  25. #
  26. # * The name of the copyright holders may not be used to endorse or promote products
  27. # derived from this software without specific prior written permission.
  28. #
  29. # This software is provided by the copyright holders and contributors "as is" and
  30. # any express or implied warranties, including, but not limited to, the implied
  31. # warranties of merchantability and fitness for a particular purpose are disclaimed.
  32. # In no event shall the Intel Corporation or contributors be liable for any direct,
  33. # indirect, incidental, special, exemplary, or consequential damages
  34. # (including, but not limited to, procurement of substitute goods or services;
  35. # loss of use, data, or profits; or business interruption) however caused
  36. # and on any theory of liability, whether in contract, strict liability,
  37. # or tort (including negligence or otherwise) arising in any way out of
  38. # the use of this software, even if advised of the possibility of such damage.
  39. #
  40. ###############################################################################
  41. # AUTHOR: Sajjad Taheri, University of California, Irvine. sajjadt[at]uci[dot]edu
  42. #
  43. # LICENSE AGREEMENT
  44. # Copyright (c) 2015, 2015 The Regents of the University of California (Regents)
  45. #
  46. # Redistribution and use in source and binary forms, with or without
  47. # modification, are permitted provided that the following conditions are met:
  48. # 1. Redistributions of source code must retain the above copyright
  49. # notice, this list of conditions and the following disclaimer.
  50. # 2. Redistributions in binary form must reproduce the above copyright
  51. # notice, this list of conditions and the following disclaimer in the
  52. # documentation and/or other materials provided with the distribution.
  53. # 3. Neither the name of the University nor the
  54. # names of its contributors may be used to endorse or promote products
  55. # derived from this software without specific prior written permission.
  56. #
  57. # THIS SOFTWARE IS PROVIDED BY COPYRIGHT HOLDERS AND CONTRIBUTORS ''AS IS'' AND ANY
  58. # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  59. # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  60. # DISCLAIMED. IN NO EVENT SHALL COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY
  61. # DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  62. # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  63. # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  64. # ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  65. # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  66. # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  67. ###############################################################################
  68. from __future__ import print_function
  69. import sys, re, os
  70. from templates import *
  71. if sys.version_info[0] >= 3:
  72. from io import StringIO
  73. else:
  74. from cStringIO import StringIO
  75. import json
  76. func_table = {}
  77. # Ignore these functions due to Embind limitations for now
  78. ignore_list = ['locate', #int&
  79. 'minEnclosingCircle', #float&
  80. 'checkRange',
  81. 'minMaxLoc', #double*
  82. 'floodFill', # special case, implemented in core_bindings.cpp
  83. 'phaseCorrelate',
  84. 'randShuffle',
  85. 'calibrationMatrixValues', #double&
  86. 'undistortPoints', # global redefinition
  87. 'CamShift', #Rect&
  88. 'meanShift' #Rect&
  89. ]
  90. def makeWhiteList(module_list):
  91. wl = {}
  92. for m in module_list:
  93. for k in m.keys():
  94. if k in wl:
  95. wl[k] += m[k]
  96. else:
  97. wl[k] = m[k]
  98. return wl
  99. def makeWhiteListJson(module_list):
  100. wl = {}
  101. for n, gen_dict in module_list.items():
  102. m = gen_dict["whitelist"]
  103. for k in m.keys():
  104. if k in wl:
  105. wl[k] += m[k]
  106. else:
  107. wl[k] = m[k]
  108. return wl
  109. def makeNamespacePrefixOverride(module_list):
  110. wl = {}
  111. for n, gen_dict in module_list.items():
  112. if "namespace_prefix_override" in gen_dict:
  113. m = gen_dict["namespace_prefix_override"]
  114. for k in m.keys():
  115. if k in wl:
  116. wl[k] += m[k]
  117. else:
  118. wl[k] = m[k]
  119. return wl
  120. white_list = None
  121. namespace_prefix_override = None
  122. # Features to be exported
  123. export_enums = True
  124. export_consts = True
  125. with_wrapped_functions = True
  126. with_default_params = True
  127. with_vec_from_js_array = True
  128. wrapper_namespace = "Wrappers"
  129. type_dict = {
  130. 'InputArray': 'const cv::Mat&',
  131. 'OutputArray': 'cv::Mat&',
  132. 'InputOutputArray': 'cv::Mat&',
  133. 'InputArrayOfArrays': 'const std::vector<cv::Mat>&',
  134. 'OutputArrayOfArrays': 'std::vector<cv::Mat>&',
  135. 'string': 'std::string',
  136. 'String': 'std::string',
  137. 'const String&':'const std::string&'
  138. }
  139. def normalize_class_name(name):
  140. return re.sub(r"^cv\.", "", name).replace(".", "_")
  141. class ClassProp(object):
  142. def __init__(self, decl):
  143. self.tp = decl[0].replace("*", "_ptr").strip()
  144. self.name = decl[1]
  145. self.readonly = True
  146. if "/RW" in decl[3]:
  147. self.readonly = False
  148. class ClassInfo(object):
  149. def __init__(self, name, decl=None):
  150. self.cname = name.replace(".", "::")
  151. self.name = self.wname = normalize_class_name(name)
  152. self.ismap = False
  153. self.issimple = False
  154. self.isalgorithm = False
  155. self.methods = {}
  156. self.ext_constructors = {}
  157. self.props = []
  158. self.consts = {}
  159. customname = False
  160. self.jsfuncs = {}
  161. self.constructor_arg_num = set()
  162. self.has_smart_ptr = False
  163. if decl:
  164. self.bases = decl[1].split()[1:]
  165. if len(self.bases) > 1:
  166. self.bases = [self.bases[0].strip(",")]
  167. # return sys.exit(-1)
  168. if self.bases and self.bases[0].startswith("cv::"):
  169. self.bases[0] = self.bases[0][4:]
  170. if self.bases and self.bases[0] == "Algorithm":
  171. self.isalgorithm = True
  172. for m in decl[2]:
  173. if m.startswith("="):
  174. self.wname = m[1:]
  175. customname = True
  176. elif m == "/Map":
  177. self.ismap = True
  178. elif m == "/Simple":
  179. self.issimple = True
  180. self.props = [ClassProp(p) for p in decl[3]]
  181. if not customname and self.wname.startswith("Cv"):
  182. self.wname = self.wname[2:]
  183. def handle_ptr(tp):
  184. if tp.startswith('Ptr_'):
  185. tp = 'Ptr<' + "::".join(tp.split('_')[1:]) + '>'
  186. return tp
  187. def handle_vector(tp):
  188. if tp.startswith('vector_'):
  189. tp = handle_vector(tp[tp.find('_') + 1:])
  190. tp = 'std::vector<' + "::".join(tp.split('_')) + '>'
  191. return tp
  192. class ArgInfo(object):
  193. def __init__(self, arg_tuple):
  194. self.tp = handle_ptr(arg_tuple[0]).strip()
  195. self.name = arg_tuple[1]
  196. self.defval = arg_tuple[2]
  197. self.isarray = False
  198. self.arraylen = 0
  199. self.arraycvt = None
  200. self.inputarg = True
  201. self.outputarg = False
  202. self.returnarg = False
  203. self.const = False
  204. self.reference = False
  205. for m in arg_tuple[3]:
  206. if m == "/O":
  207. self.inputarg = False
  208. self.outputarg = True
  209. self.returnarg = True
  210. elif m == "/IO":
  211. self.inputarg = True
  212. self.outputarg = True
  213. self.returnarg = True
  214. elif m.startswith("/A"):
  215. self.isarray = True
  216. self.arraylen = m[2:].strip()
  217. elif m.startswith("/CA"):
  218. self.isarray = True
  219. self.arraycvt = m[2:].strip()
  220. elif m == "/C":
  221. self.const = True
  222. elif m == "/Ref":
  223. self.reference = True
  224. if self.tp == "Mat" and (self.inputarg or self.outputarg):
  225. self.tp = "cv::Mat&"
  226. if self.inputarg and not self.outputarg:
  227. self.const = True
  228. if self.tp == "vector_Mat" and (self.inputarg or self.outputarg):
  229. self.tp = "std::vector<cv::Mat>&"
  230. if self.reference and not self.const:
  231. self.inputarg = False
  232. self.outputarg = True
  233. elif self.inputarg and not self.outputarg:
  234. self.const = True
  235. self.tp = handle_vector(self.tp).strip()
  236. if self.const:
  237. self.tp = "const " + self.tp
  238. if self.reference:
  239. self.tp = self.tp + "&"
  240. self.py_inputarg = False
  241. self.py_outputarg = False
  242. class FuncVariant(object):
  243. def __init__(self, class_name, name, decl, is_constructor, is_class_method, is_const, is_virtual, is_pure_virtual, ref_return, const_return):
  244. self.class_name = class_name
  245. self.name = self.wname = name
  246. self.is_constructor = is_constructor
  247. self.is_class_method = is_class_method
  248. self.is_const = is_const
  249. self.is_virtual = is_virtual
  250. self.is_pure_virtual = is_pure_virtual
  251. self.refret = ref_return
  252. self.constret = const_return
  253. self.rettype = handle_vector(handle_ptr(decl[1]).strip()).strip()
  254. if self.rettype == "void":
  255. self.rettype = ""
  256. self.args = []
  257. self.array_counters = {}
  258. for a in decl[3]:
  259. ainfo = ArgInfo(a)
  260. if ainfo.isarray and not ainfo.arraycvt:
  261. c = ainfo.arraylen
  262. c_arrlist = self.array_counters.get(c, [])
  263. if c_arrlist:
  264. c_arrlist.append(ainfo.name)
  265. else:
  266. self.array_counters[c] = [ainfo.name]
  267. self.args.append(ainfo)
  268. class FuncInfo(object):
  269. def __init__(self, class_name, name, cname, namespace, isconstructor):
  270. self.name_id = '_'.join([namespace] + ([class_name] if class_name else []) + [name]) # unique id for dict key
  271. self.class_name = class_name
  272. self.name = name
  273. self.cname = cname
  274. self.namespace = namespace
  275. self.variants = []
  276. self.is_constructor = isconstructor
  277. def add_variant(self, variant):
  278. self.variants.append(variant)
  279. class Namespace(object):
  280. def __init__(self):
  281. self.funcs = {}
  282. self.enums = {}
  283. self.consts = {}
  284. class JSWrapperGenerator(object):
  285. def __init__(self, preprocessor_definitions=None):
  286. self.bindings = []
  287. self.wrapper_funcs = []
  288. self.classes = {} # FIXIT 'classes' should belong to 'namespaces'
  289. self.namespaces = {}
  290. self.enums = {} # FIXIT 'enums' should belong to 'namespaces'
  291. self.parser = hdr_parser.CppHeaderParser(
  292. preprocessor_definitions=preprocessor_definitions
  293. )
  294. self.class_idx = 0
  295. def _is_string_type(self, tp: str) -> bool:
  296. """Check if a type should be treated as string in bindings."""
  297. string_types = {
  298. "std::string",
  299. "char",
  300. "signed char",
  301. "unsigned char",
  302. }
  303. return tp in string_types
  304. def _generate_class_properties(self, class_info, class_bindings):
  305. # Generate bindings for properties
  306. for prop in class_info.props:
  307. if prop.tp in type_dict and not self._is_string_type(prop.tp):
  308. _class_property = class_property_enum_template
  309. else:
  310. _class_property = class_property_template
  311. class_bindings.append(_class_property.substitute(
  312. js_name=prop.name,
  313. cpp_name='::'.join([class_info.cname, prop.name])
  314. ))
  315. def add_class(self, stype, name, decl):
  316. class_info = ClassInfo(name, decl)
  317. class_info.decl_idx = self.class_idx
  318. self.class_idx += 1
  319. if class_info.name in self.classes:
  320. print("Generator error: class %s (cpp_name=%s) already exists" \
  321. % (class_info.name, class_info.cname))
  322. sys.exit(-1)
  323. self.classes[class_info.name] = class_info
  324. def resolve_class_inheritance(self):
  325. new_classes = {}
  326. for name, class_info in self.classes.items():
  327. if not hasattr(class_info, 'bases'):
  328. new_classes[name] = class_info
  329. continue # not class
  330. if class_info.bases:
  331. chunks = class_info.bases[0].split('::')
  332. base = '_'.join(chunks)
  333. while base not in self.classes and len(chunks) > 1:
  334. del chunks[-2]
  335. base = '_'.join(chunks)
  336. if base not in self.classes:
  337. print("Generator error: unable to resolve base %s for %s"
  338. % (class_info.bases[0], class_info.name))
  339. sys.exit(-1)
  340. else:
  341. class_info.bases[0] = "::".join(chunks)
  342. class_info.isalgorithm |= self.classes[base].isalgorithm
  343. new_classes[name] = class_info
  344. self.classes = new_classes
  345. def split_decl_name(self, name):
  346. chunks = name.split('.')
  347. namespace = chunks[:-1]
  348. classes = []
  349. while namespace and '.'.join(namespace) not in self.parser.namespaces:
  350. classes.insert(0, namespace.pop())
  351. return namespace, classes, chunks[-1]
  352. def add_enum(self, decl):
  353. name = decl[0].rsplit(" ", 1)[1]
  354. namespace, classes, val = self.split_decl_name(name)
  355. namespace = '.'.join(namespace)
  356. ns = self.namespaces.setdefault(namespace, Namespace())
  357. if len(name) == 0: name = "<unnamed>"
  358. if name.endswith("<unnamed>"):
  359. i = 0
  360. while True:
  361. i += 1
  362. candidate_name = name.replace("<unnamed>", "unnamed_%u" % i)
  363. if candidate_name not in ns.enums:
  364. name = candidate_name
  365. break;
  366. cname = name.replace('.', '::')
  367. type_dict[normalize_class_name(name)] = cname
  368. if name in ns.enums:
  369. print("Generator warning: enum %s (cname=%s) already exists" \
  370. % (name, cname))
  371. # sys.exit(-1)
  372. else:
  373. ns.enums[name] = []
  374. for item in decl[3]:
  375. ns.enums[name].append(item)
  376. const_decls = decl[3]
  377. for decl in const_decls:
  378. name = decl[0]
  379. self.add_const(name.replace("const ", "").strip(), decl)
  380. def add_const(self, name, decl):
  381. cname = name.replace('.','::')
  382. namespace, classes, name = self.split_decl_name(name)
  383. namespace = '.'.join(namespace)
  384. name = '_'.join(classes+[name])
  385. ns = self.namespaces.setdefault(namespace, Namespace())
  386. if name in ns.consts:
  387. print("Generator error: constant %s (cname=%s) already exists" \
  388. % (name, cname))
  389. sys.exit(-1)
  390. ns.consts[name] = cname
  391. def add_func(self, decl):
  392. namespace, classes, barename = self.split_decl_name(decl[0])
  393. cpp_name = "::".join(namespace + classes + [barename])
  394. name = barename
  395. class_name = ''
  396. bare_class_name = ''
  397. if classes:
  398. class_name = normalize_class_name('.'.join(namespace + classes))
  399. bare_class_name = classes[-1]
  400. namespace = '.'.join(namespace)
  401. is_constructor = name == bare_class_name
  402. is_class_method = False
  403. is_const_method = False
  404. is_virtual_method = False
  405. is_pure_virtual_method = False
  406. const_return = False
  407. ref_return = False
  408. for m in decl[2]:
  409. if m == "/S":
  410. is_class_method = True
  411. elif m == "/C":
  412. is_const_method = True
  413. elif m == "/V":
  414. is_virtual_method = True
  415. elif m == "/PV":
  416. is_pure_virtual_method = True
  417. elif m == "/Ref":
  418. ref_return = True
  419. elif m == "/CRet":
  420. const_return = True
  421. elif m.startswith("="):
  422. name = m[1:]
  423. if class_name:
  424. cpp_name = barename
  425. func_map = self.classes[class_name].methods
  426. else:
  427. func_map = self.namespaces.setdefault(namespace, Namespace()).funcs
  428. fi = FuncInfo(class_name, name, cpp_name, namespace, is_constructor)
  429. func = func_map.setdefault(fi.name_id, fi)
  430. variant = FuncVariant(class_name, name, decl, is_constructor, is_class_method, is_const_method,
  431. is_virtual_method, is_pure_virtual_method, ref_return, const_return)
  432. func.add_variant(variant)
  433. def save(self, path, name, buf):
  434. f = open(path + "/" + name, "wt")
  435. f.write(buf.getvalue())
  436. f.close()
  437. def gen_function_binding_with_wrapper(self, func, ns_name, class_info):
  438. binding_text = None
  439. wrapper_func_text = None
  440. bindings = []
  441. wrappers = []
  442. for index, variant in enumerate(func.variants):
  443. factory = False
  444. if class_info and 'Ptr<' in variant.rettype:
  445. factory = True
  446. base_class_name = variant.rettype
  447. base_class_name = base_class_name.replace("Ptr<","").replace(">","").strip()
  448. if base_class_name in self.classes:
  449. self.classes[base_class_name].has_smart_ptr = True
  450. else:
  451. print(base_class_name, ' not found in classes for registering smart pointer using ', class_info.name, 'instead')
  452. self.classes[class_info.name].has_smart_ptr = True
  453. def_args = []
  454. has_def_param = False
  455. # Return type
  456. ret_type = 'void' if variant.rettype.strip() == '' else variant.rettype
  457. # FIX: Ensure namespaced smart-pointer return types in factory methods, e.g.:
  458. # Ptr<EdgeDrawing> → Ptr<cv::ximgproc::EdgeDrawing>
  459. if factory and class_info is not None and ret_type.startswith('Ptr<'):
  460. inner = ret_type[len('Ptr<'):-1].strip()
  461. if '::' not in inner and inner == class_info.name:
  462. ret_type = 'Ptr<%s>' % class_info.cname
  463. if ret_type.startswith('Ptr'): # smart pointer
  464. ptr_type = ret_type.replace('Ptr<', '').replace('>', '')
  465. if ptr_type in type_dict:
  466. ret_type = type_dict[ptr_type]
  467. for key in type_dict:
  468. if key in ret_type:
  469. ret_type = re.sub(r"\b" + key + r"\b", type_dict[key], ret_type)
  470. arg_types = []
  471. unwrapped_arg_types = []
  472. for arg in variant.args:
  473. arg_type = None
  474. if arg.tp in type_dict:
  475. arg_type = type_dict[arg.tp]
  476. else:
  477. arg_type = arg.tp
  478. # Add default value
  479. if with_default_params and arg.defval != '':
  480. def_args.append(arg.defval);
  481. arg_types.append(arg_type)
  482. unwrapped_arg_types.append(arg_type)
  483. # Function attribute
  484. func_attribs = ''
  485. if '*' in ''.join(arg_types):
  486. func_attribs += ', allow_raw_pointers()'
  487. if variant.is_pure_virtual:
  488. func_attribs += ', pure_virtual()'
  489. # Wrapper function
  490. if ns_name != None and ns_name != "cv":
  491. ns_parts = ns_name.split(".")
  492. if ns_parts[0] == "cv":
  493. ns_parts = ns_parts[1:]
  494. ns_part = "_".join(ns_parts) + "_"
  495. ns_id = '_'.join(ns_parts)
  496. ns_prefix = namespace_prefix_override.get(ns_id, ns_id)
  497. if ns_prefix:
  498. ns_prefix = ns_prefix + '_'
  499. else:
  500. ns_prefix = ''
  501. if class_info == None:
  502. js_func_name = ns_prefix + func.name
  503. wrap_func_name = js_func_name + "_wrapper"
  504. else:
  505. wrap_func_name = ns_prefix + func.class_name + "_" + func.name + "_wrapper"
  506. js_func_name = func.name
  507. # TODO: Name functions based wrap directives or based on arguments list
  508. if index > 0:
  509. wrap_func_name += str(index)
  510. js_func_name += str(index)
  511. c_func_name = 'Wrappers::' + wrap_func_name
  512. # Binding template-
  513. raw_arg_names = ['arg' + str(i + 1) for i in range(0, len(variant.args))]
  514. arg_names = []
  515. w_signature = []
  516. casted_arg_types = []
  517. for arg_type, arg_name in zip(arg_types, raw_arg_names):
  518. casted_arg_name = arg_name
  519. if with_vec_from_js_array:
  520. # Only support const vector reference as input parameter
  521. match = re.search(r'const std::vector<(.*)>&', arg_type)
  522. if match:
  523. type_in_vect = match.group(1)
  524. if type_in_vect in ['int', 'float', 'double', 'char', 'uchar', 'String', 'std::string']:
  525. casted_arg_name = 'emscripten::vecFromJSArray<' + type_in_vect + '>(' + arg_name + ')'
  526. arg_type = re.sub(r'std::vector<(.*)>', 'emscripten::val', arg_type)
  527. w_signature.append(arg_type + ' ' + arg_name)
  528. arg_names.append(casted_arg_name)
  529. casted_arg_types.append(arg_type)
  530. arg_types = casted_arg_types
  531. # Argument list, signature
  532. arg_names_casted = [c if a == b else c + '.as<' + a + '>()' for a, b, c in
  533. zip(unwrapped_arg_types, arg_types, arg_names)]
  534. # Add self object to the parameters
  535. if class_info and not factory:
  536. arg_types = [class_info.cname + '&'] + arg_types
  537. w_signature = [class_info.cname + '& arg0 '] + w_signature
  538. for j in range(0, len(def_args) + 1):
  539. postfix = ''
  540. if j > 0:
  541. postfix = '_' + str(j);
  542. ###################################
  543. # Wrapper
  544. if factory: # TODO or static
  545. name = class_info.cname+'::' if variant.class_name else ""
  546. cpp_call_text = static_class_call_template.substitute(scope=name,
  547. func=func.cname,
  548. args=', '.join(arg_names[:len(arg_names)-j]))
  549. elif class_info:
  550. cpp_call_text = class_call_template.substitute(obj='arg0',
  551. func=func.cname,
  552. args=', '.join(arg_names[:len(arg_names)-j]))
  553. else:
  554. cpp_call_text = call_template.substitute(func=func.cname,
  555. args=', '.join(arg_names[:len(arg_names)-j]))
  556. wrapper_func_text = wrapper_function_template.substitute(ret_val=ret_type,
  557. func=wrap_func_name+postfix,
  558. signature=', '.join(w_signature[:len(w_signature)-j]),
  559. cpp_call=cpp_call_text,
  560. const='' if variant.is_const else '')
  561. ###################################
  562. # Binding
  563. if class_info:
  564. if factory:
  565. # print("Factory Function: ", c_func_name, len(variant.args) - j, class_info.name)
  566. if variant.is_pure_virtual:
  567. # FIXME: workaround for pure virtual in constructor
  568. # e.g. DescriptorMatcher_clone_wrapper
  569. continue
  570. # consider the default parameter variants
  571. args_num = len(variant.args) - j
  572. if args_num in class_info.constructor_arg_num:
  573. # FIXME: workaround for constructor overload with same args number
  574. # e.g. DescriptorMatcher
  575. continue
  576. class_info.constructor_arg_num.add(args_num)
  577. binding_text = ctr_template.substitute(const='const' if variant.is_const else '',
  578. cpp_name=c_func_name+postfix,
  579. ret=ret_type,
  580. args=','.join(arg_types[:len(arg_types)-j]),
  581. optional=func_attribs)
  582. else:
  583. binding_template = overload_class_static_function_template if variant.is_class_method else \
  584. overload_class_function_template
  585. binding_text = binding_template.substitute(js_name=js_func_name,
  586. const='' if variant.is_const else '',
  587. cpp_name=c_func_name+postfix,
  588. ret=ret_type,
  589. args=','.join(arg_types[:len(arg_types)-j]),
  590. optional=func_attribs)
  591. else:
  592. binding_text = overload_function_template.substitute(js_name=js_func_name,
  593. cpp_name=c_func_name+postfix,
  594. const='const' if variant.is_const else '',
  595. ret=ret_type,
  596. args=', '.join(arg_types[:len(arg_types)-j]),
  597. optional=func_attribs)
  598. bindings.append(binding_text)
  599. wrappers.append(wrapper_func_text)
  600. return [bindings, wrappers]
  601. def gen_function_binding(self, func, class_info):
  602. if not class_info == None :
  603. func_name = class_info.cname+'::'+func.cname
  604. else :
  605. func_name = func.cname
  606. binding_text = None
  607. binding_text_list = []
  608. for index, variant in enumerate(func.variants):
  609. factory = False
  610. #TODO if variant.is_class_method and variant.rettype == ('Ptr<' + class_info.name + '>'):
  611. if (not class_info == None) and variant.rettype == ('Ptr<' + class_info.name + '>') or (func.name.startswith("create") and variant.rettype):
  612. factory = True
  613. base_class_name = variant.rettype
  614. base_class_name = base_class_name.replace("Ptr<","").replace(">","").strip()
  615. if base_class_name in self.classes:
  616. self.classes[base_class_name].has_smart_ptr = True
  617. else:
  618. print(base_class_name, ' not found in classes for registering smart pointer using ', class_info.name, 'instead')
  619. self.classes[class_info.name].has_smart_ptr = True
  620. # Return type
  621. ret_type = 'void' if variant.rettype.strip() == '' else variant.rettype
  622. ret_type = ret_type.strip()
  623. # Same namespace fix for factory methods: Ptr<EdgeDrawing> -> Ptr<cv::ximgproc::EdgeDrawing>
  624. if factory and class_info is not None and ret_type.startswith('Ptr<'):
  625. inner = ret_type[len('Ptr<'):-1].strip()
  626. if '::' not in inner and inner == class_info.name:
  627. ret_type = 'Ptr<%s>' % class_info.cname
  628. if ret_type.startswith('Ptr'): #smart pointer
  629. ptr_type = ret_type.replace('Ptr<', '').replace('>', '')
  630. if ptr_type in type_dict:
  631. ret_type = type_dict[ptr_type]
  632. for key in type_dict:
  633. if key in ret_type:
  634. # Replace types. Instead of ret_type.replace we use regular
  635. # expression to exclude false matches.
  636. # See https://github.com/opencv/opencv/issues/15514
  637. ret_type = re.sub(r"\b" + key + r"\b", type_dict[key], ret_type)
  638. if variant.constret and ret_type.startswith('const') == False:
  639. ret_type = 'const ' + ret_type
  640. if variant.refret and ret_type.endswith('&') == False:
  641. ret_type += '&'
  642. arg_types = []
  643. orig_arg_types = []
  644. def_args = []
  645. for arg in variant.args:
  646. if arg.tp in type_dict:
  647. arg_type = type_dict[arg.tp]
  648. else:
  649. arg_type = arg.tp
  650. #if arg.outputarg:
  651. # arg_type += '&'
  652. orig_arg_types.append(arg_type)
  653. if with_default_params and arg.defval != '':
  654. def_args.append(arg.defval)
  655. arg_types.append(orig_arg_types[-1])
  656. # Function attribute
  657. func_attribs = ''
  658. if '*' in ''.join(orig_arg_types):
  659. func_attribs += ', allow_raw_pointers()'
  660. if variant.is_pure_virtual:
  661. func_attribs += ', pure_virtual()'
  662. #TODO better naming
  663. #if variant.name in self.jsfunctions:
  664. #else
  665. js_func_name = variant.name
  666. c_func_name = func.cname if (factory and variant.is_class_method == False) else func_name
  667. ################################### Binding
  668. for j in range(0, len(def_args) + 1):
  669. postfix = ''
  670. if j > 0:
  671. postfix = '_' + str(j);
  672. if factory:
  673. binding_text = ctr_template.substitute(const='const' if variant.is_const else '',
  674. cpp_name=c_func_name+postfix,
  675. ret=ret_type,
  676. args=','.join(arg_types[:len(arg_types)-j]),
  677. optional=func_attribs)
  678. else:
  679. binding_template = overload_class_static_function_template if variant.is_class_method else \
  680. overload_function_template if class_info == None else overload_class_function_template
  681. binding_text = binding_template.substitute(js_name=js_func_name,
  682. const='const' if variant.is_const else '',
  683. cpp_name=c_func_name+postfix,
  684. ret=ret_type,
  685. args=','.join(arg_types[:len(arg_types)-1]),
  686. optional=func_attribs)
  687. binding_text_list.append(binding_text)
  688. return binding_text_list
  689. def print_decls(self, decls):
  690. """
  691. Prints the list of declarations, retrieived by the parse() method
  692. """
  693. for d in decls:
  694. print(d[0], d[1], ";".join(d[2]))
  695. for a in d[3]:
  696. print(" ", a[0], a[1], a[2], end="")
  697. if a[3]:
  698. print("; ".join(a[3]))
  699. else:
  700. print()
  701. def gen(self, dst_file, src_files, core_bindings):
  702. # step 1: scan the headers and extract classes, enums and functions
  703. headers = []
  704. for hdr in src_files:
  705. decls = self.parser.parse(hdr)
  706. # print(hdr);
  707. # self.print_decls(decls);
  708. if len(decls) == 0:
  709. continue
  710. headers.append(hdr[hdr.rindex('opencv2/'):])
  711. for decl in decls:
  712. name = decl[0]
  713. type = name[:name.find(" ")]
  714. if type == "struct" or type == "class": # class/structure case
  715. name = name[name.find(" ") + 1:].strip()
  716. self.add_class(type, name, decl)
  717. elif name.startswith("enum"): # enumerations
  718. self.add_enum(decl)
  719. elif name.startswith("const"):
  720. # constant
  721. self.add_const(name.replace("const ", "").strip(), decl)
  722. else: # class/global function
  723. self.add_func(decl)
  724. self.resolve_class_inheritance()
  725. # step 2: generate bindings
  726. # Global functions
  727. for ns_name, ns in sorted(self.namespaces.items()):
  728. ns_parts = ns_name.split('.')
  729. if ns_parts[0] != 'cv':
  730. print('Ignore namespace: {}'.format(ns_name))
  731. continue
  732. else:
  733. ns_parts = ns_parts[1:]
  734. ns_id = '_'.join(ns_parts)
  735. ns_prefix = namespace_prefix_override.get(ns_id, ns_id)
  736. for name_id, func in sorted(ns.funcs.items()):
  737. name = func.name
  738. if ns_prefix:
  739. name = ns_prefix + '_' + name
  740. if name in ignore_list:
  741. continue
  742. if not name in white_list['']:
  743. #print('Not in whitelist: "{}" from ns={}'.format(name, ns_name))
  744. continue
  745. ext_cnst = False
  746. # Check if the method is an external constructor
  747. for variant in func.variants:
  748. if "Ptr<" in variant.rettype:
  749. # Register the smart pointer
  750. base_class_name = variant.rettype
  751. base_class_name = base_class_name.replace("Ptr<","").replace(">","").strip()
  752. if base_class_name not in self.classes:
  753. new_base_class_name = '%s_%s' % (ns_id, base_class_name)
  754. print(base_class_name, ' not found in classes for registering smart pointer using ', new_base_class_name, 'instead')
  755. base_class_name = new_base_class_name
  756. self.classes[base_class_name].has_smart_ptr = True
  757. # Adds the external constructor
  758. class_name = func.name.replace("create", "")
  759. if not class_name in self.classes:
  760. self.classes[base_class_name].methods[func.cname] = func
  761. else:
  762. self.classes[class_name].methods[func.cname] = func
  763. ext_cnst = True
  764. if ext_cnst:
  765. continue
  766. if with_wrapped_functions:
  767. binding, wrapper = self.gen_function_binding_with_wrapper(func, ns_name, class_info=None)
  768. self.bindings += binding
  769. self.wrapper_funcs += wrapper
  770. else:
  771. binding = self.gen_function_binding(func, class_info=None)
  772. self.bindings+=binding
  773. # generate code for the classes and their methods
  774. for name, class_info in sorted(self.classes.items()):
  775. class_bindings = []
  776. if not name in white_list:
  777. #print('Not in whitelist: "{}" from ns={}'.format(name, ns_name))
  778. continue
  779. # Generate bindings for methods
  780. for method_name, method in sorted(class_info.methods.items()):
  781. if method.cname in ignore_list:
  782. continue
  783. if not method.name in white_list[method.class_name]:
  784. #print('Not in whitelist: "{}"'.format(method.name))
  785. continue
  786. if method.is_constructor:
  787. for variant in method.variants:
  788. args = []
  789. for arg in variant.args:
  790. arg_type = type_dict[arg.tp] if arg.tp in type_dict else arg.tp
  791. args.append(arg_type)
  792. # print('Constructor: ', class_info.name, len(variant.args))
  793. args_num = len(variant.args)
  794. if args_num in class_info.constructor_arg_num:
  795. continue
  796. class_info.constructor_arg_num.add(args_num)
  797. class_bindings.append(constructor_template.substitute(signature=', '.join(args)))
  798. else:
  799. if with_wrapped_functions and (len(method.variants) > 1 or len(method.variants[0].args)>0 or "String" in method.variants[0].rettype):
  800. binding, wrapper = self.gen_function_binding_with_wrapper(method, None, class_info=class_info)
  801. self.wrapper_funcs = self.wrapper_funcs + wrapper
  802. class_bindings = class_bindings + binding
  803. else:
  804. binding = self.gen_function_binding(method, class_info=class_info)
  805. class_bindings = class_bindings + binding
  806. # Regiseter Smart pointer
  807. if class_info.has_smart_ptr:
  808. class_bindings.append(smart_ptr_reg_template.substitute(cname=class_info.cname, name=class_info.name))
  809. # Attach external constructors
  810. # for method_name, method in class_info.ext_constructors.items():
  811. # print("ext constructor", method_name)
  812. #if class_info.ext_constructors:
  813. # Generate bindings for properties(prop)
  814. for prop in class_info.props:
  815. if prop.tp in type_dict and not self._is_string_type(prop.tp):
  816. _class_property = class_property_enum_template
  817. else:
  818. _class_property = class_property_template
  819. class_bindings.append(_class_property.substitute(
  820. js_name=prop.name,
  821. cpp_name='::'.join([class_info.cname, prop.name])
  822. ))
  823. dv = ''
  824. base = Template("""base<$base>""")
  825. assert len(class_info.bases) <= 1 , "multiple inheritance not supported"
  826. if len(class_info.bases) == 1:
  827. dv = "," + base.substitute(base=', '.join(class_info.bases))
  828. self.bindings.append(class_template.substitute(cpp_name=class_info.cname,
  829. js_name=name,
  830. class_templates=''.join(class_bindings),
  831. derivation=dv))
  832. if export_enums:
  833. # step 4: generate bindings for enums
  834. # TODO anonymous enums are ignored for now.
  835. for ns_name, ns in sorted(self.namespaces.items()):
  836. if ns_name.split('.')[0] != 'cv':
  837. continue
  838. for name, enum in sorted(ns.enums.items()):
  839. if '.unnamed_' not in name:
  840. name = name.replace("cv.", "")
  841. enum_values = []
  842. for enum_val in enum:
  843. value = enum_val[0][enum_val[0].rfind(".")+1:]
  844. enum_values.append(enum_item_template.substitute(val=value,
  845. cpp_val=name.replace('.', '::')+'::'+value))
  846. self.bindings.append(enum_template.substitute(cpp_name=name.replace(".", "::"),
  847. js_name=name.replace(".", "_"),
  848. enum_items=''.join(enum_values)))
  849. else:
  850. print(name)
  851. #TODO: represent anonymous enums with constants
  852. if export_consts:
  853. # step 5: generate bindings for consts
  854. for ns_name, ns in sorted(self.namespaces.items()):
  855. if ns_name.split('.')[0] != 'cv':
  856. continue
  857. # TODO CALIB_FIX_FOCAL_LENGTH is defined both in cv:: and cv::fisheye
  858. prefix = 'FISHEYE_' if 'fisheye' in ns_name else ''
  859. for name, const in sorted(ns.consts.items()):
  860. name = prefix + name
  861. # print("Gen consts: ", name, const)
  862. self.bindings.append(const_template.substitute(js_name=name, value=const))
  863. with open(core_bindings) as f:
  864. ret = f.read()
  865. header_includes = '\n'.join(['#include "{}"'.format(hdr) for hdr in headers])
  866. ret = ret.replace('@INCLUDES@', header_includes)
  867. defis = '\n'.join(self.wrapper_funcs)
  868. ret += wrapper_codes_template.substitute(ns=wrapper_namespace, defs=defis)
  869. ret += emscripten_binding_template.substitute(binding_name='testBinding', bindings=''.join(self.bindings))
  870. # print(ret)
  871. text_file = open(dst_file, "w")
  872. text_file.write(ret)
  873. text_file.close()
  874. if __name__ == "__main__":
  875. import argparse
  876. arg_parser = argparse.ArgumentParser(
  877. description="OpenCV JavaScript bindings generator"
  878. )
  879. arg_parser.add_argument(
  880. "-p", "--parser",
  881. required=True,
  882. help="Full path to OpenCV header parser `hdr_parser.py`"
  883. )
  884. arg_parser.add_argument(
  885. "-o", "--output_file",
  886. dest="output_file_path",
  887. required=True,
  888. help="Path to output file containing js bindings"
  889. )
  890. arg_parser.add_argument(
  891. "-c", "--config",
  892. dest="config_json_path",
  893. required=True,
  894. help="Path to generator configuration file in .json format"
  895. )
  896. arg_parser.add_argument(
  897. "--whitelist",
  898. dest="whitelist_file_path",
  899. required=True,
  900. help="Path to whitelist.js or opencv_js.config.py"
  901. )
  902. args = arg_parser.parse_args()
  903. # import header parser
  904. hdr_parser_path = os.path.abspath(args.parser)
  905. if hdr_parser_path.endswith(".py"):
  906. hdr_parser_path = os.path.dirname(hdr_parser_path)
  907. sys.path.append(hdr_parser_path)
  908. import hdr_parser
  909. with open(args.config_json_path, "r") as fh:
  910. config_json = json.load(fh)
  911. headers = config_json.get("headers", ())
  912. bindings_cpp = args.output_file_path
  913. core_bindings_path = config_json["core_bindings_file_path"]
  914. whitelist_file_path = args.whitelist_file_path
  915. if whitelist_file_path.endswith(".json") or whitelist_file_path.endswith(".JSON"):
  916. with open(whitelist_file_path) as f:
  917. gen_dict = json.load(f)
  918. white_list = makeWhiteListJson(gen_dict)
  919. namespace_prefix_override = makeNamespacePrefixOverride(gen_dict)
  920. elif whitelist_file_path.endswith(".py") or whitelist_file_path.endswith(".PY"):
  921. with open(whitelist_file_path) as fh:
  922. exec(fh.read())
  923. assert white_list
  924. namespace_prefix_override = {
  925. 'dnn' : '',
  926. 'aruco' : '',
  927. }
  928. else:
  929. print("Unexpected format of OpenCV config file", whitelist_file_path)
  930. exit(1)
  931. generator = JSWrapperGenerator(
  932. preprocessor_definitions=config_json.get("preprocessor_definitions", None)
  933. )
  934. generator.gen(bindings_cpp, headers, core_bindings_path)