diff options
-rwxr-xr-x | tools/bindings_generator.py | 176 |
1 files changed, 93 insertions, 83 deletions
diff --git a/tools/bindings_generator.py b/tools/bindings_generator.py index a4fbb4cc..e686f57a 100755 --- a/tools/bindings_generator.py +++ b/tools/bindings_generator.py @@ -131,8 +131,8 @@ for classname, clazz in classes.iteritems(): default_param = i+1 break - method['num_args'] = set(range(default_param-1, len(args)+1)) - print 'zz ', classname, 'has num_args of', method['num_args'] + method['parameters'] = [args[:i] for i in range(default_param-1, len(args)+1)] + print 'zz ', classname, 'has parameters in range', range(default_param-1, len(args)+1) method['returns_text'] = method['returns'] if method['static']: @@ -146,7 +146,7 @@ for classname, clazz in classes.iteritems(): if method.get('returns_reference'): method['returns_text'] += '&' method['returns_text'] = type_processor(method['returns_text']) - print 'zz %s::%s gets %s and returns %s' % (classname, method['name'], str([arg['type'] for arg in method['parameters']]), method['returns_text']) + #print 'zz %s::%s gets %s and returns %s' % (classname, method['name'], str([arg['type'] for arg in method['parameters']]), method['returns_text']) # Add getters/setters for public members for prop in clazz['properties']['public']: @@ -170,8 +170,7 @@ for classname, clazz in classes.iteritems(): 'returns_reference': reference, 'returns_pointer': '*' in type_, 'pure_virtual': False, - 'num_args': set([0]), - 'parameters': [], + 'parameters': [[]], }) clazz['methods'].append({ 'setter': True, @@ -184,11 +183,10 @@ for classname, clazz in classes.iteritems(): 'returns_reference': False, 'returns_pointer': False, 'pure_virtual': False, - 'num_args': set([1]), - 'parameters': [{ + 'parameters': [[{ 'type': type_ + ('&' if reference else ''), 'name': 'value', - }], + }]], }) # Add destroyer @@ -204,8 +202,7 @@ for classname, clazz in classes.iteritems(): 'returns_reference': False, 'returns_pointer': False, 'pure_virtual': False, - 'num_args': set([0]), - 'parameters': [], + 'parameters': [[]], }) # Explore all functions we need to generate, including parent classes, handling of overloading, etc. @@ -220,6 +217,16 @@ def fix_template_value(t): # Not sure why this is needed, might be a bug in CppH return 'unsigned int' return t +def copy_args(args): + ret = [] + for arg in args: + copiedarg = { + 'type': arg['type'], + 'name': arg['name'], + } + ret.append(copiedarg) + return ret + for classname, clazz in parsed.classes.iteritems(): clazz['final_methods'] = {} @@ -237,40 +244,38 @@ for classname, clazz in parsed.classes.iteritems(): for key in ['name', 'constructor', 'static', 'returns', 'returns_text', 'returns_reference', 'returns_pointer', 'destructor', 'pure_virtual', 'getter', 'setter', 'destroyer']: copied[key] = method.get(key) - copied['num_args'] = method['num_args'].copy() copied['origin'] = subclass copied['parameters'] = []; - # Copy the arguments, since templating may cause them to be altered - for arg in method['parameters'][:]: - copiedarg = { - 'type': arg['type'], - 'name': arg['name'], - } - copied['parameters'].append(copiedarg) + for args in method['parameters']: + # Copy the arguments, since templating may cause them to be altered + copied['parameters'].append(copy_args(args)) if template_name: # Set template values copied['returns'] = copied['returns'].replace(template_name, template_value) copied['returns_text'] = copied['returns_text'].replace(template_name, template_value) - for arg in copied['parameters']: - arg['type'] = arg['type'].replace(template_name, template_value) - + for args in copied['parameters']: + for arg in args: + arg['type'] = arg['type'].replace(template_name, template_value) else: - # Merge the new function in the best way we can. Shared arguments must match! + # Merge the new function in the best way we can. Two signatures (args) must differ in their number curr = clazz['final_methods'][method['name']] if curr['origin'] is not subclass: continue # child class functions mask/hide parent functions of the same name in C++ - if any([curr['parameters'][i]['type'] != method['parameters'][i]['type'] for i in range(min(len(curr['parameters']), len(method['parameters'])))]): + problem = False + for curr_args in curr['parameters']: + for method_args in method['parameters']: + if len(curr_args) == len(method_args): + problem = True + if problem: print 'Warning: Cannot mix in overloaded functions', method['name'], 'in class', classname, ', skipping' continue # TODO: Other compatibility checks, if any? - if len(method['parameters']) > len(curr['parameters']): - curr['parameters'] = method['parameters'] + curr['parameters'] += map(copy_args, method['parameters']) - curr['num_args'] = curr['num_args'].union(method['num_args']) - print 'zz ', classname, 'has an updated num_args of ', curr['num_args'] + print 'zz ', classname, 'has updated parameters of ', curr['parameters'] # Recurse if subclass.get('inherits'): @@ -293,8 +298,7 @@ for classname, clazz in parsed.classes.iteritems(): explore(clazz) for method in clazz['final_methods'].itervalues(): - method['num_args'] = list(method['num_args']) - method['num_args'].sort() + method['parameters'].sort(key=len) # Second pass - generate bindings # TODO: Bind virtual functions using dynamic binding in the C binding code @@ -335,6 +339,11 @@ function compare(obj1, obj2) { return obj1.ptr === obj2.ptr; } this['compare'] = compare; + +function getPointer(obj) { + return obj.ptr; +} +this['getPointer'] = getPointer; ''') def generate_wrapping_code(classname): @@ -362,43 +371,37 @@ def generate_class(generating_classname, classname, clazz): # TODO: deprecate ge print 'zz ignoring', mname continue - args = method['parameters'] + params = method['parameters'] constructor = method['constructor'] destructor = method['destructor'] static = method['static'] - print 'zz generating %s::%s. gets %s and returns %s' % (generating_classname, method['name'], str([arg['type'] for arg in method['parameters']]), method['returns_text']) + #print 'zz generating %s::%s. gets %s and returns %s' % (generating_classname, method['name'], str([arg['type'] for arg in method['parameters']]), method['returns_text']) if destructor: continue if constructor and inherited: continue if constructor and clazz['abstract']: continue # do not generate constructors for abstract base classes - skip = False - for i in range(len(args)): - #print 'zz arggggggg', classname, 'x', mname, 'x', args[i]['name'], 'x', args[i]['type'], 'x', dir(args[i]), 'y', args[i].get('default'), 'z', args[i].get('defaltValue'), args[i].keys() - - if args[i]['name'].replace(' ', '') == '': - args[i]['name'] = 'arg' + str(i+1) - elif args[i]['name'] == '&': - args[i]['name'] = 'arg' + str(i+1) - args[i]['type'] += '&' - - if '>' in args[i]['name']: - print 'WARNING: odd ">" in %s, skipping' % classname - skip = True - break - #print 'c1', parents.keys() - if args[i]['type'][-1] == '&': - sname = args[i]['type'][:-1] - if sname[-1] == ' ': sname = sname[:-1] - if sname in parents: - args[i]['type'] = parents[sname] + '::' + sname + '&' - elif sname.replace('const ', '') in parents: - sname = sname.replace('const ', '') - args[i]['type'] = 'const ' + parents[sname] + '::' + sname + '&' - #print 'POST arggggggg', classname, 'x', mname, 'x', args[i]['name'], 'x', args[i]['type'] - if skip: - continue + for args in params: + for i in range(len(args)): + #print 'zz arggggggg', classname, 'x', mname, 'x', args[i]['name'], 'x', args[i]['type'], 'x', dir(args[i]), 'y', args[i].get('default'), 'z', args[i].get('defaltValue'), args[i].keys() + + if args[i]['name'].replace(' ', '') == '': + args[i]['name'] = 'arg' + str(i+1) + elif args[i]['name'] == '&': + args[i]['name'] = 'arg' + str(i+1) + args[i]['type'] += '&' + + #print 'c1', parents.keys() + if args[i]['type'][-1] == '&': + sname = args[i]['type'][:-1] + if sname[-1] == ' ': sname = sname[:-1] + if sname in parents: + args[i]['type'] = parents[sname] + '::' + sname + '&' + elif sname.replace('const ', '') in parents: + sname = sname.replace('const ', '') + args[i]['type'] = 'const ' + parents[sname] + '::' + sname + '&' + #print 'POST arggggggg', classname, 'x', mname, 'x', args[i]['name'], 'x', args[i]['type'] ret = ((classname + ' *') if constructor else method['returns_text'])#.replace('virtual ', '') callprefix = 'new ' if constructor else ('self->' if not static else (classname + '::')) @@ -412,8 +415,12 @@ def generate_class(generating_classname, classname, clazz): # TODO: deprecate ge actualmname = actualmname[4:] need_self = not constructor and not static - typedargs = ([] if not need_self else [classname + ' * self']) + map(lambda arg: arg['type'] + ' ' + arg['name'], args) - justargs = map(lambda arg: arg['name'], args) + + def typedargs(args): + return ([] if not need_self else [classname + ' * self']) + map(lambda i: args[i]['type'] + ' arg' + str(i), range(len(args))) + def justargs(args): + return map(lambda i: 'arg' + str(i), range(len(args))) + fullname = ('emscripten_bind_' + generating_classname + '__' + mname).replace('::', '__') generating_classname_suffixed = generating_classname mname_suffixed = mname @@ -433,12 +440,13 @@ def generate_class(generating_classname, classname, clazz): # TODO: deprecate ge # C - for i in method['num_args']: + for args in params: + i = len(args) # If we are returning a *copy* of an object, we return instead to a ref of a static held here. This seems the best compromise staticize = not constructor and ret.replace(' ', '') != 'void' and method['returns'] in classes and (not method['returns_reference'] and not method['returns_pointer']) gen_c.write(''' %s %s_p%d(%s) {''' % (ret if not staticize else (ret + '&'), fullname, i, - ', '.join(typedargs[:i + (0 if not need_self else 1)]))) + ', '.join(typedargs(args)[:i + (0 if not need_self else 1)]))) if not staticize: if method.get('getter'): gen_c.write(''' @@ -446,7 +454,7 @@ def generate_class(generating_classname, classname, clazz): # TODO: deprecate ge ''' % actualmname) elif method.get('setter'): gen_c.write(''' - self->%s = value; + self->%s = arg0; ''' % actualmname) elif method.get('destroyer'): gen_c.write(''' @@ -456,7 +464,7 @@ def generate_class(generating_classname, classname, clazz): # TODO: deprecate ge gen_c.write(''' %s%s%s(%s); ''' % ('return ' if ret.replace(' ', '') != 'void' else '', - callprefix, actualmname, ', '.join(justargs[:i]))) + callprefix, actualmname, ', '.join(justargs(args)[:i]))) gen_c.write('}') else: @@ -465,40 +473,42 @@ def generate_class(generating_classname, classname, clazz): # TODO: deprecate ge return ret; }''' % (method['returns'], callprefix, actualmname, - ', '.join(justargs[:i]))) + ', '.join(justargs(args)[:i]))) c_funcs.append(fullname + '_p' + str(i)) # JS - print 'zz types:', map(lambda arg: arg['type'], args) + #print 'zz types:', map(lambda arg: arg['type'], args) # We can assume that NULL is passed for null pointers, so object arguments can always # have .ptr done on them - justargs_fixed = justargs[:] - for i in range(len(args)): - arg = args[i] - if clean_type(arg['type']) in classes: - justargs_fixed[i] += '.ptr' + def justargs_fixed(args): + ret = justargs(args)[:] + for i in range(len(args)): + arg = args[i] + if clean_type(arg['type']) in classes: + ret[i] += '.ptr' + return ret calls = '' - print 'js loopin', method['num_args'], '|', len(args)#, args - for i in method['num_args']: - print ' ', i, type(i) - if i != method['num_args'][0]: + #print 'js loopin', params, '|', len(args)#, args + for args in params: + i = len(args) + if args != params[0]: calls += ' else ' - if i != method['num_args'][-1]: - calls += ' if (' + justargs[i] + ' === undefined)' - calls += '\n ' + (' ' if len(method['num_args']) > 0 else '') + if args != params[-1]: + calls += ' if (arg' + str(i) + ' === undefined)' + calls += '\n ' + (' ' if len(params) > 0 else '') if constructor: if not dupe: calls += '''this.ptr = _%s_p%d(%s); -''' % (fullname, i, ', '.join(justargs_fixed[:i])) +''' % (fullname, i, ', '.join(justargs_fixed(args)[:i])) else: calls += '''this.ptr = _%s_p%d(%s); -''' % (fullname, i, ', '.join(justargs_fixed[:i])) +''' % (fullname, i, ', '.join(justargs_fixed(args)[:i])) else: - return_value = '''_%s_p%d(%s)''' % (fullname, i, ', '.join((['this.ptr'] if need_self else []) + justargs_fixed[:i])) + return_value = '''_%s_p%d(%s)''' % (fullname, i, ', '.join((['this.ptr'] if need_self else []) + justargs_fixed(args)[:i])) print 'zz making return', classname, method['name'], method['returns'], return_value if method['returns'] in classes: # Generate a wrapper @@ -515,14 +525,14 @@ def generate_class(generating_classname, classname, clazz): # TODO: deprecate ge function %s(%s) { %s } -%s''' % (mname_suffixed, ', '.join(justargs), calls, generate_wrapping_code(generating_classname_head)) +%s''' % (mname_suffixed, ', '.join(justargs(args)), calls, generate_wrapping_code(generating_classname_head)) else: js_text = ''' function %s(%s) { %s } %s.prototype = %s.prototype; -''' % (mname_suffixed, ', '.join(justargs), calls, mname_suffixed, classname) +''' % (mname_suffixed, ', '.join(justargs(args)), calls, mname_suffixed, classname) if export: js_text += ''' @@ -534,7 +544,7 @@ this['%s'] = %s; %s.prototype%s = function(%s) { %s } -''' % (generating_classname_head, ('.' + mname_suffixed) if not export else ("['" + mname_suffixed + "']"), ', '.join(justargs), calls) +''' % (generating_classname_head, ('.' + mname_suffixed) if not export else ("['" + mname_suffixed + "']"), ', '.join(justargs(args)), calls) js_text = js_text.replace('\n\n', '\n').replace('\n\n', '\n') gen_js.write(js_text) |