diff options
author | Alon Zakai <alonzakai@gmail.com> | 2012-03-14 13:41:30 -0700 |
---|---|---|
committer | Alon Zakai <alonzakai@gmail.com> | 2012-03-14 13:41:30 -0700 |
commit | e3f438b1088f4da77157d3240d25f4c62a8d0207 (patch) | |
tree | c51c7c0140b7f848b010f4557d4410954bd97e5d | |
parent | 5fd0826bb86bac11150ace522f652b66f02baa22 (diff) |
support c strings as parameters in bindings generator
-rwxr-xr-x | tests/runner.py | 42 | ||||
-rwxr-xr-x | tools/bindings_generator.py | 55 |
2 files changed, 85 insertions, 12 deletions
diff --git a/tests/runner.py b/tests/runner.py index 27100cbb..a1d29da4 100755 --- a/tests/runner.py +++ b/tests/runner.py @@ -5358,6 +5358,48 @@ Child2:9 *ok* ''', post_build=[post2, post3]) + def test_scriptaclass_2(self): + header_filename = os.path.join(self.get_dir(), 'header.h') + header = ''' + #include <stdio.h> + #include <string.h> + + class StringUser { + char *s; + int i; + public: + StringUser(char *string, int integer) : s(strdup(string)), i(integer) {} + void Print(int anotherInteger, char *anotherString) { + printf("|%s|%d|%s|%d|\\n", s, i, anotherString, anotherInteger); + } + void CallOther(StringUser *fr) { fr->Print(i, s); } + }; + ''' + open(header_filename, 'w').write(header) + + basename = os.path.join(self.get_dir(), 'bindingtest') + output = Popen([BINDINGS_GENERATOR, basename, header_filename], stdout=PIPE, stderr=self.stderr_redirect).communicate()[0] + #print output + assert 'Traceback' not in output, 'Failure in binding generation: ' + output + + src = ''' + #include "header.h" + + #include "bindingtest.cpp" + ''' + + post = ''' +def process(filename): + src = open(filename, 'a') + src.write(open('bindingtest.js').read() + '\\n\\n') + src.write(\'\'\' + var user = new Module.StringUser("hello", 43); + user.Print(41, "world"); + \'\'\') + src.close() +''' + self.do_run(src, '|hello|43|world|41|', post_build=post) + def test_typeinfo(self): if self.emcc_args is not None and self.emcc_args != []: return self.skip('full LLVM opts optimize out all the code that uses the type') diff --git a/tools/bindings_generator.py b/tools/bindings_generator.py index 4731abf3..5f65c82e 100755 --- a/tools/bindings_generator.py +++ b/tools/bindings_generator.py @@ -42,8 +42,22 @@ bindings, and to prevent DFE from removing the code we care about. The JS bindings do more serious work, creating class structures in JS and linking them to the C bindings. -NOTE: ammo.js is currently the biggest consumer of this code. For some - more docs you can see that project's README +Notes: + * ammo.js is currently the biggest consumer of this code. For some + more docs you can see that project's README, + + https://github.com/kripken/ammo.js + + Another project using these bindings is box2d.js, + + https://github.com/kripken/box2d.js + + * C strings (char *) passed to functions are treated in a special way. + If you pass in a pointer, it is assumed to be a pointer and left as + is. Otherwise it must be a JS string, and we convert it to a C + string on the *stack*. The C string will live for the current + function call. If you need it for longer, you need to create a copy + in your C++ code. ''' import os, sys, glob, re @@ -507,6 +521,12 @@ function customizeVTable(object, replacementPairs) { return object; } Module['customizeVTable'] = customizeVTable; + +// Converts a value into a C-style string. +function ensureString(value) { + if (typeof value == 'number') return value; + return allocate(intArrayFromString(value), 'i8', ALLOC_STACK); +} ''') def generate_wrapping_code(classname): @@ -645,17 +665,25 @@ def generate_class(generating_classname, classname, clazz): # TODO: deprecate ge #print 'zz types:', map(lambda arg: arg['type'], args) + has_string_convs = False + # We can assume that NULL is passed for null pointers, so object arguments can always # have .ptr done on them - 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 + justargs_fixed = justargs(args)[:] + for i in range(len(args)): + arg = args[i] + clean = clean_type(arg['type']) + if clean in classes: + justargs_fixed[i] += '.ptr' + elif arg['type'].replace(' ', '') == 'char*': + justargs_fixed[i] = 'ensureString(' + justargs_fixed[i] + ')' + has_string_convs = True calls = '' + if has_string_convs: + calls += 'var stack = Runtime.stackSave();\n'; + calls += 'try {\n' + #print 'js loopin', params, '|', len(args)#, args for args in params: i = len(args) @@ -667,12 +695,12 @@ def generate_class(generating_classname, classname, clazz): # TODO: deprecate ge if constructor: if not dupe: calls += '''this.ptr = _%s_p%d(%s); -''' % (fullname, i, ', '.join(justargs_fixed(args)[:i])) +''' % (fullname, i, ', '.join(justargs_fixed[:i])) else: calls += '''this.ptr = _%s_p%d(%s); -''' % (fullname, i, ', '.join(justargs_fixed(args)[:i])) +''' % (fullname, i, ', '.join(justargs_fixed[:i])) else: - return_value = '''_%s_p%d(%s)''' % (fullname, i, ', '.join((['this.ptr'] if need_self else []) + justargs_fixed(args)[:i])) + return_value = '''_%s_p%d(%s)''' % (fullname, i, ', '.join((['this.ptr'] if need_self else []) + justargs_fixed[:i])) print 'zz making return', classname, method['name'], method['returns'], return_value if method['returns'] in classes: # Generate a wrapper @@ -682,6 +710,9 @@ def generate_class(generating_classname, classname, clazz): # TODO: deprecate ge calls += ('return ' if ret != 'void' else '') + return_value + ';' calls += '\n' + if has_string_convs: + calls += '} finally { Runtime.stackRestore(stack) }\n'; + print 'Maekin:', classname, generating_classname, mname, mname_suffixed if constructor: calls += ''' |