diff options
Diffstat (limited to 'tools/bindings_generator.py')
-rwxr-xr-x | tools/bindings_generator.py | 243 |
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) |