aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlon Zakai <alonzakai@gmail.com>2011-08-17 22:56:48 -0700
committerAlon Zakai <alonzakai@gmail.com>2011-08-17 22:56:48 -0700
commit880efeba08f152e687625d08408d557234321b3e (patch)
tree98c6bcb1a84e45b7c2f3b4b0da3d86cca2cf90d9
parentefe09785c319912747f85f8c91fe2430131dc7ea (diff)
support for binding overloaded functions with differing types
-rwxr-xr-xtools/bindings_generator.py176
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)