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