aboutsummaryrefslogtreecommitdiff
path: root/tools/bindings_generator.py
diff options
context:
space:
mode:
Diffstat (limited to 'tools/bindings_generator.py')
-rwxr-xr-xtools/bindings_generator.py243
1 files changed, 178 insertions, 65 deletions
diff --git a/tools/bindings_generator.py b/tools/bindings_generator.py
index 61d04942..2abc5bf3 100755
--- a/tools/bindings_generator.py
+++ b/tools/bindings_generator.py
@@ -47,7 +47,7 @@ import CppHeaderParser
basename = sys.argv[1]
-processor = lambda line: line
+processor = lambda text: text
ignored = []
if '--' in sys.argv:
@@ -62,54 +62,124 @@ if '--' in sys.argv:
classes = {}
struct_parents = {}
+text = ''
+for header in sys.argv[2:]:
+ text += '//// ' + header + '\n'
+ text += open(header, 'r').read()
all_h_name = basename + '.all.h'
all_h = open(all_h_name, 'w')
-for header in sys.argv[2:]:
- all_h.write('//// ' + header + '\n')
- all_h.write(processor(open(header, 'r').read()))
-
+all_h.write(processor(text))
all_h.close()
parsed = CppHeaderParser.CppHeader(all_h_name)
-for cname, clazz in parsed.classes.iteritems():
- #print 'zz see', cname
- if len(clazz['methods']['public']) > 0: # Do not notice stub classes
- #print 'zz for real', cname, clazz, dir(clazz)
- classes[cname] = clazz
- for sname, struct in clazz._public_structs.iteritems():
- struct_parents[sname] = cname
- #print 'zz seen struct %s in %s' % (sname, cname)
+for classname, clazz in parsed.classes.iteritems():
+ print 'zz see', classname
+ classes[classname] = clazz
+ for sname, struct in clazz._public_structs.iteritems():
+ struct_parents[sname] = classname
+ #print 'zz seen struct %s in %s' % (sname, classname)
+
+ # Various precalculations
+ for method in clazz['methods']['public'][:]:
+ constructor = method['name'] == classname
+ method['constructor'] = constructor # work around cppheaderparser issue
+ args = method['parameters']
+
+ default_param = len(args)+1
+ for i in range(len(args)):
+ if args[i].get('default'):
+ 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']
+
+ if method['static']:
+ method['rtnType'] = method['rtnType'].replace('static', '')
+
+# Explore all functions we need to generate, including parent classes, handling of overloading, etc.
+
+for classname, clazz in parsed.classes.iteritems():
+ clazz['final_methods'] = {}
+
+ def explore(subclass):
+ # Do our functions first, and do not let later classes override
+ for method in subclass['methods']['public']:
+ if method['constructor']:
+ if clazz != subclass: continue # Subclasses cannot directly use their parent's constructors
+ if method['destructor']: continue # Nothing to do there
+
+ if method['name'] not in clazz['final_methods']:
+ clazz['final_methods'][method['name']] = {}
+ for key in ['name', 'constructor', 'static', 'rtnType', 'destructor', 'pure_virtual']:
+ clazz['final_methods'][method['name']][key] = method[key]
+ clazz['final_methods'][method['name']]['num_args'] = method['num_args'].copy()
+ clazz['final_methods'][method['name']]['parameters'] = method['parameters'][:]
+ clazz['final_methods'][method['name']]['origin'] = subclass
+ else:
+ # Merge the new function in the best way we can. Shared arguments must match!
+
+ 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'])))]):
+ 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['num_args'] = curr['num_args'].union(method['num_args'])
+ print 'zz ', classname, 'has an updated num_args of ', curr['num_args']
+
+ # Recurse
+ for parent in subclass['inherits']:
+ if parent['class'] not in classes:
+ print 'Warning: parent class', parent, 'not a known class. Ignoring.'
+ return
+ explore(classes[parent['class']])
+
+ explore(clazz)
+
+ for method in clazz['final_methods'].itervalues():
+ method['num_args'] = list(method['num_args'])
+ method['num_args'].sort()
# Second pass - generate bindings
# TODO: Bind virtual functions using dynamic binding in the C binding code
funcs = {} # name -> # of copies in the original, and originalname in a copy
+c_funcs = []
gen_c = open(basename + '.c', 'w')
gen_js = open(basename + '.js', 'w')
gen_c.write('extern "C" {\n')
-def generate_class(generating_cname, cname, clazz):
- inherited = generating_cname != cname
+def generate_class(generating_classname, classname, clazz): # TODO: deprecate generating?
+ inherited = generating_classname != classname
- for method in clazz['methods']['public']:
+ for method in clazz['final_methods'].itervalues():
mname = method['name']
- #print "zz generating: ", generating_cname, cname, mname
- if cname + '::' + mname in ignored: continue
+ if classname + '::' + mname in ignored: continue
args = method['parameters']
- constructor = mname == cname
+ constructor = method['constructor'] # we fixed this before
destructor = method['destructor']
+ static = method['static']
+
+ print "zz generating: ", generating_classname, classname, mname, constructor, method['rtnType']
if destructor: continue
if constructor and inherited: continue
- if method['pure_virtual']: continue
skip = False
for i in range(len(args)):
- #print 'zz arggggggg', cname, 'x', mname, 'x', args[i]['name'], 'x', args[i]['type'], 'x'
+ #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'] == '&':
@@ -117,7 +187,7 @@ def generate_class(generating_cname, cname, clazz):
args[i]['type'] += '&'
if '>' in args[i]['name']:
- print 'WARNING: odd ">" in %s, skipping' % cname
+ print 'WARNING: odd ">" in %s, skipping' % classname
skip = True
break
#print 'c1', struct_parents.keys()
@@ -129,14 +199,12 @@ def generate_class(generating_cname, cname, clazz):
elif sname.replace('const ', '') in struct_parents:
sname = sname.replace('const ', '')
args[i]['type'] = 'const ' + struct_parents[sname] + '::' + sname + '&'
- #print 'POST arggggggg', cname, 'x', mname, 'x', args[i]['name'], 'x', args[i]['type']
+ #print 'POST arggggggg', classname, 'x', mname, 'x', args[i]['name'], 'x', args[i]['type']
if skip:
continue
- # C
-
- ret = ((cname + ' *') if constructor else method['rtnType']).replace('virtual ', '')
- callprefix = 'new ' if constructor else 'self->'
+ ret = ((classname + ' *') if constructor else method['rtnType']).replace('virtual ', '')
+ callprefix = 'new ' if constructor else ('self->' if not static else (classname + '::'))
actualmname = ''
if mname == '__operator___assignment_':
@@ -170,100 +238,145 @@ def generate_class(generating_cname, cname, clazz):
callprefix = '*self - '
continue # TODO
else:
- actualmname = mname
+ actualmname = method.get('truename') or mname
- typedargs = ', '.join( ([] if constructor else [cname + ' * self']) + map(lambda arg: arg['type'] + ' ' + arg['name'], args) )
- justargs = ', '.join(map(lambda arg: arg['name'], args))
- fullname = 'emscripten_bind_' + generating_cname + '__' + mname
- generating_cname_suffixed = generating_cname
+ 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)
+ fullname = 'emscripten_bind_' + generating_classname + '__' + mname
+ generating_classname_suffixed = generating_classname
mname_suffixed = mname
count = funcs.setdefault(fullname, 0)
funcs[fullname] += 1
# handle overloading
+ dupe = False
if count > 0:
+ dupe = True
suffix = '_' + str(count+1)
- funcs[fullname + suffix] = fullname # this should never change
+ funcs[fullname + suffix] = 0
fullname += suffix
mname_suffixed += suffix
if constructor:
- generating_cname_suffixed += suffix
+ generating_classname_suffixed += suffix
+
+ argfixes = '\n'.join(map(lambda arg: ''' %s = (%s && %s.ptr) ? %s.ptr : %s;''' % (arg['name'], arg['name'], arg['name'], arg['name'], arg['name']), args))
+
+ for i in method['num_args']:
+ # C
- gen_c.write('''
-%s %s(%s) {
+ gen_c.write('''
+%s %s_p%d(%s) {
%s%s%s(%s);
}
-''' % (ret, fullname, typedargs, 'return ' if ret.replace(' ', '') != 'void' else '', callprefix, actualmname, justargs))
+''' % (ret, fullname, i, ', '.join(typedargs[:i + (0 if not need_self else 1)]), 'return ' if ret.replace(' ', '') != 'void' else '', callprefix, actualmname, ', '.join(justargs[:i])))
+
+ c_funcs.append(fullname + '_p' + str(i))
# JS
+ 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]:
+ calls += ' else '
+ if i != method['num_args'][-1]:
+ calls += ' if (' + justargs[i] + ' === undefined)'
+ calls += '\n ' + (' ' if len(method['num_args']) > 0 else '')
+ if constructor:
+ if not dupe:
+ calls += '''this.ptr = _%s_p%d(%s);
+''' % (fullname, i, ', '.join(justargs[:i]))
+ else:
+ calls += '''this.ptr = _%s_p%d(%s);
+''' % (fullname, i, ', '.join(justargs[:i]))
+ else:
+ calls += '''%s_%s_p%d(%s);
+''' % ('return ' if ret != 'void' else '', fullname, i, ', '.join((['this.ptr'] if need_self else []) + justargs[:i]))
+ print 'Maekin:', classname, generating_classname, mname, mname_suffixed
if constructor:
- dupe = type(funcs[fullname]) is str
if not dupe:
- gen_js.write('''
+ js_text = '''
function %s(%s) {
- this.ptr = _%s(%s);
+%s
+%s
}
-''' % (generating_cname_suffixed, justargs, fullname, justargs))
+''' % (mname_suffixed, ', '.join(justargs), argfixes, calls)
else:
- gen_js.write('''
+ js_text = '''
function %s(%s) {
- this.ptr = _%s(%s);
+%s
+%s
}
%s.prototype = %s.prototype;
-''' % (generating_cname_suffixed, justargs, fullname, justargs, generating_cname_suffixed, cname))
+''' % (mname_suffixed, ', '.join(justargs), argfixes, calls, mname_suffixed, classname)
else:
- gen_js.write('''
+ js_text = '''
%s.prototype.%s = function(%s) {
- %s_%s(this.ptr%s);
+%s
+%s
}
-''' % (generating_cname, mname_suffixed, justargs, 'return ' if ret != 'void' else '', fullname, (', ' if len(justargs) > 0 else '') + justargs))
+''' % (generating_classname, mname_suffixed, ', '.join(justargs), argfixes, calls)
+
+ js_text = js_text.replace('\n\n', '\n').replace('\n\n', '\n')
+ gen_js.write(js_text)
+
+# Main loop
-for cname, clazz in classes.iteritems():
- if cname in ignored: continue
+for classname, clazz in classes.iteritems():
+ if classname in ignored: continue
# Nothing to generate for pure virtual classes
def check_pure_virtual(clazz, progeny):
- if any([check_pure_virtual(classes[parent['class']], [clazz] + progeny) for parent in clazz['inherits']]): return True
+ print 'Checking pure virtual for', clazz['name'], clazz['inherits']
+ # If we do not recognize any of the parent classes, assume this is pure virtual - ignore it
+ if any([((not parent['class'] in classes) or check_pure_virtual(classes[parent['class']], [clazz] + progeny)) for parent in clazz['inherits']]): return True
def dirtied(mname):
+ #print 'zz checking dirtiness for', mname, 'in', progeny
for progen in progeny:
- for method in clazz['methods']['public']:
- if method['name'] == mname and not method['pure_virtual']: return True
+ for method in progen['methods']['public']:
+ if method['name'] == mname and not method['pure_virtual']:
+ #print 'zz dirty'
+ return True
+ #print 'zz not dirtied'
return False
for method in clazz['methods']['public']:
- if method['pure_virtual'] and not dirtied(method['name']): return True
+ if method['pure_virtual'] and not dirtied(method['name']):
+ print 'zz ignoring pure virtual class', classname, 'due to', method['name']
+ return True
- if check_pure_virtual(clazz, []): continue
+ if check_pure_virtual(clazz, []):
+ continue
# Add a constructor if none exist
has_constructor = False
for method in clazz['methods']['public']:
mname = method['name']
- has_constructor = has_constructor or (cname == mname)
+ has_constructor = has_constructor or (classname == mname and not method['destructor'])
+
+ print 'zz ', classname, 'has constructor?', has_constructor
if not has_constructor:
+ print 'zz no constructor for', classname, 'so ignoring'
+ continue
+
clazz['methods']['public'] = [{
- 'name': cname,
+ 'name': classname,
'parameters': [],
'pure_virtual': False,
'destructor': False,
}] + clazz['methods']['public']
- generate_class(cname, cname, clazz)
-
- # In addition, generate all methods of parent classes. We do not inherit in JS (how would we do multiple inheritance etc.?)
- for parent in clazz['inherits']:
- generate_class(cname, parent['class'], classes[parent['class']])
+ generate_class(classname, classname, clazz)
# TODO: Add a destructor
# Finish up
-funcs = funcs.keys()
-
gen_c.write('''
}
@@ -275,9 +388,9 @@ struct EmscriptenEnsurer
// Actually use the binding functions, so DFE will not eliminate them
int sum = 0;
void *seen = (void*)%s;
-''' % funcs[0])
+''' % c_funcs[0])
-for func in funcs[1:]:
+for func in c_funcs[1:]:
gen_c.write(''' sum += (void*)%s == seen;
''' % func)