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