aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xemcc1
-rwxr-xr-xtests/runner.py71
-rwxr-xr-xtools/bindings_generator.py52
3 files changed, 72 insertions, 52 deletions
diff --git a/emcc b/emcc
index 8a3e2bae..d8b45dea 100755
--- a/emcc
+++ b/emcc
@@ -597,6 +597,7 @@ try:
if DEBUG: save_intermediate('ll', 'll')
if AUTODEBUG:
+ if DEBUG: print >> sys.stderr, 'emcc: autodebug'
Popen(['python', shared.AUTODEBUGGER, final, final + '.ad.ll']).communicate()[0]
final += '.ad.ll'
if DEBUG: save_intermediate('autodebug', 'll')
diff --git a/tests/runner.py b/tests/runner.py
index 4fd7ddfc..2be6453b 100755
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -111,28 +111,39 @@ class RunnerCore(unittest.TestCase):
# that post_build is called on unoptimized JS, so we send it to emcc (otherwise, if run after
# emcc, it would not apply on the optimized/minified JS)
def ll_to_js(self, filename, extra_emscripten_args, post_build):
+ if type(post_build) in (list, tuple):
+ post1, post2 = post_build
+ else:
+ post1 = post_build
+ post2 = None
+
+ def run_post(post):
+ if not post: return
+ exec post in locals()
+ shutil.copyfile(filename + '.o.js', filename + '.o.js.prepost.js')
+ process(filename + '.o.js')
+
if self.emcc_args is None:
Building.emscripten(filename, append_ext=True, extra_args=extra_emscripten_args)
- if post_build is not None:
- exec post_build in locals()
- shutil.copyfile(filename + '.o.js', filename + '.o.js.prepost.js')
- process(filename + '.o.js')
+ run_post(post1)
+ run_post(post2)
else:
transform_args = []
- if post_build:
+ if post1:
transform_filename = os.path.join(self.get_dir(), 'transform.py')
transform = open(transform_filename, 'w')
transform.write('''
import sys
sys.path += ['%s']
''' % path_from_root(''))
- transform.write(post_build)
+ transform.write(post1)
transform.write('''
process(sys.argv[1])
''')
transform.close()
transform_args = ['--js-transform', "python %s" % transform_filename]
Building.emcc(filename + '.o.ll', Settings.serialize() + self.emcc_args + transform_args, filename + '.o.js')
+ run_post(post2)
# Build JavaScript code from source code
def build(self, src, dirname, filename, output_processor=None, main_file=None, additional_files=[], libraries=[], includes=[], build_ll_hook=None, extra_emscripten_args=[], post_build=None):
@@ -4684,6 +4695,9 @@ def process(filename):
'''
# XXX disable due to possible v8 bug -- self.do_run(src, '*166*\n*ok*', post_build=post)
+ if self.emcc_args is not None and '-O2' in self.emcc_args:
+ self.emcc_args += ['--closure', '1'] # Use closure here, to test we export things right
+
# Way 2: use CppHeaderParser
Settings.RUNTIME_TYPE_INFO = 1
@@ -4740,15 +4754,22 @@ def process(filename):
post2 = '''
def process(filename):
+ src = open(filename, 'a')
+ src.write(open('bindingtest.js').read() + '\\n\\n')
+ src.close()
+'''
+
+ post3 = '''
+def process(filename):
script_src_2 = \'\'\'
- var sme = new Parent(42);
+ var sme = new Module.Parent(42);
sme.mulVal(2);
print('*')
print(sme.getVal());
print('c1');
- var c1 = new Child1();
+ var c1 = new Module.Child1();
print(c1.getVal());
c1.mulVal(2);
print(c1.getVal());
@@ -4759,7 +4780,7 @@ def process(filename):
print('c1 v2');
- c1 = new Child1(8); // now with a parameter, we should handle the overloading automatically and properly and use constructor #2
+ c1 = new Module.Child1(8); // now with a parameter, we should handle the overloading automatically and properly and use constructor #2
print(c1.getVal());
c1.mulVal(2);
print(c1.getVal());
@@ -4768,7 +4789,7 @@ def process(filename):
print('c2')
- var c2 = new Child2();
+ var c2 = new Module.Child2();
print(c2.getVal());
c2.mulVal(2);
print(c2.getVal());
@@ -4793,42 +4814,39 @@ def process(filename):
} catch(e) {}
print(succeeded);
- Child2.prototype.printStatic(); // static calls go through the prototype
+ Module.Child2.prototype.printStatic(); // static calls go through the prototype
// virtual function
c2.virtualFunc();
- Child2.prototype.runVirtualFunc(c2);
+ Module.Child2.prototype.runVirtualFunc(c2);
c2.virtualFunc2();
// extend the class from JS
- var c3 = new Child2;
- customizeVTable(c3, [{
- original: Child2.prototype.virtualFunc,
+ var c3 = new Module.Child2;
+ Module.customizeVTable(c3, [{
+ original: Module.Child2.prototype.virtualFunc,
replacement: function() {
print('*js virtualf replacement*');
}
}, {
- original: Child2.prototype.virtualFunc2,
+ original: Module.Child2.prototype.virtualFunc2,
replacement: function() {
print('*js virtualf2 replacement*');
}
}]);
c3.virtualFunc();
- Child2.prototype.runVirtualFunc(c3);
+ Module.Child2.prototype.runVirtualFunc(c3);
c3.virtualFunc2();
c2.virtualFunc(); // original should remain the same
- Child2.prototype.runVirtualFunc(c2);
+ Module.Child2.prototype.runVirtualFunc(c2);
c2.virtualFunc2();
print('*ok*');
\'\'\'
- src = open(filename, 'r').read().replace(
- '// {{MODULE_ADDITIONS}',
- open('bindingtest.js').read() + '\\n\\n' + script_src_2 + '\\n\\n' +
- '// {{MODULE_ADDITIONS}'
- )
- open(filename, 'w').write(src)
+ src = open(filename, 'a')
+ src.write(script_src_2 + '\\n')
+ src.close()
'''
self.do_run(src, '''*
@@ -4871,7 +4889,7 @@ Child2:9
*virtualf*
*virtualf2*
*ok*
-''', post_build=post2)
+''', post_build=[post2, post3])
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')
@@ -5731,9 +5749,6 @@ f.close()
output = Popen([NODE_JS, JS_OPTIMIZER, input] + passes, stdin=PIPE, stdout=PIPE).communicate()[0]
self.assertIdentical(expected, output.replace('\n\n', '\n'))
- def test_reminder(self):
- assert False, 'Ensure all opts including linktime apply to bindings generator. might need to adjust visibility of bindings C funcs'
-
elif 'benchmark' in str(sys.argv):
# Benchmarks. Run them with argument |benchmark|. To run a specific test, do
# |benchmark.test_X|.
diff --git a/tools/bindings_generator.py b/tools/bindings_generator.py
index 8146215c..d02af6b4 100755
--- a/tools/bindings_generator.py
+++ b/tools/bindings_generator.py
@@ -29,12 +29,15 @@ We generate the following:
change all arguments of type float& to float by
"type_processor": "lambda t: t if t != 'float&' else 'float'"
- export: If true, will export all bindings in the .js file. This allows
+ export: If false, will not export all bindings in the .js file. The
+ default is to export all bindings, which allows
you to run something like closure compiler advanced opts on
the library+bindings, and the bindings will remain accessible.
prevent_dfe: If true, will use all the generated C functions, to prevent
- dead function elimination from removing them.
+ dead function elimination from removing them. The use is
+ by creating an artificial "main" functions. You should then
+ remove that function later or just ignore it.
For example, JSON can be { "ignored": "class1,class2::func" }.
@@ -66,8 +69,8 @@ basename = sys.argv[1]
ignored = []
type_processor = lambda t: t
-export = 0
-prevent_dfe = 0
+export = 1
+prevent_dfe = 1
if '--' in sys.argv:
index = sys.argv.index('--')
@@ -363,7 +366,7 @@ for classname, clazz in parsed.classes.iteritems():
# 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 = []
+c_funcs = [] # the C functions generated, including dummy params we can use to keep them alive from dfe
gen_c = open(basename + '.cpp', 'w')
gen_js = open(basename + '.js', 'w')
@@ -543,6 +546,7 @@ def generate_class(generating_classname, classname, clazz): # TODO: deprecate ge
#print 'POST arggggggg', classname, 'x', mname, 'x', args[i]['name'], 'x', args[i]['type']
ret = ((classname + ' *') if constructor else method['returns_text'])#.replace('virtual ', '')
+ has_return = ret.replace(' ', '') != 'void'
callprefix = 'new ' if constructor else ('self->' if not static else (classname + '::'))
'' # mname used in C
@@ -558,6 +562,8 @@ def generate_class(generating_classname, classname, clazz): # TODO: deprecate ge
return map(lambda i: 'arg' + str(i), range(len(args)))
def justtypes(args): # note: this ignores 'self'
return map(lambda i: args[i]['type'], range(len(args)))
+ def dummyargs(args):
+ return ([] if not need_self else ['(%s*)argv[argc]' % classname]) + map(lambda i: '(%s)argv[argc]' % args[i]['type'], range(len(args)))
fullname = ('emscripten_bind_' + generating_classname + '__' + mname).replace('::', '__')
generating_classname_suffixed = generating_classname
@@ -582,8 +588,9 @@ def generate_class(generating_classname, classname, clazz): # TODO: deprecate ge
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'])
+ # Make sure to mark our bindings wrappers in a way that they will not be inlined, eliminated as unneeded, or optimized into other signatures
gen_c.write('''
-%s %s_p%d(%s) {''' % (ret if not staticize else (ret + '&'), fullname, i,
+%s __attribute__((externally_visible, used, noinline)) %s_p%d(%s) {''' % (ret if not staticize else (ret + '&'), fullname, i,
', '.join(typedargs(args)[:i + (0 if not need_self else 1)])))
if not staticize:
gen_c.write('\n')
@@ -596,7 +603,7 @@ def generate_class(generating_classname, classname, clazz): # TODO: deprecate ge
elif method.get('operator'):
gen_c.write(method['operator'])
else: # normal method
- gen_c.write(''' %s%s%s(%s);''' % ('return ' if ret.replace(' ', '') != 'void' else '',
+ gen_c.write(''' %s%s%s(%s);''' % ('return ' if has_return else '',
callprefix, actualmname, ', '.join(justargs(args)[:i])))
gen_c.write('\n')
gen_c.write('}')
@@ -610,7 +617,7 @@ def generate_class(generating_classname, classname, clazz): # TODO: deprecate ge
callprefix, actualmname,
', '.join(justargs(args)[:i])))
gen_c.write('\n}')
- c_funcs.append(fullname + '_p' + str(i))
+ c_funcs.append(('argc += (int)' if has_return else '') + fullname + '_p' + str(i) + '(' + ', '.join(dummyargs(args)[:i + (0 if not need_self else 1)]) + ')')
# JS
@@ -768,26 +775,23 @@ gen_c.write('''
if prevent_dfe:
gen_c.write('''
- #include <stdio.h>
+ #include <stdio.h>
+ #include <string.h>
+ int main(int argc, char **argv) {
+ if (argc >= 1 && !strcmp(argv[0], "this is a dummy |main| function, just for bindings generator dfe prevention!")) {
+ // Actually 'use' the binding functions, so DFE will not eliminate them.
+ printf("We should never actually get here!\\n");
- struct EmscriptenEnsurer
- {
- EmscriptenEnsurer() {
- // Actually use the binding functions, so DFE will not eliminate them
- // FIXME: A negative side effect of this is that they take up space in FUNCTION_TABLE
- int sum = 0;
- void *seen = (void*)%s;
- ''' % c_funcs[0])
+''')
- for func in c_funcs[1:]:
- gen_c.write(''' sum += (void*)%s == seen;
- ''' % func)
+ for i in range(len(c_funcs)):
+ func = c_funcs[i]
+ gen_c.write(' if (argc == %d+1) %s;\n' % (i, func))
- gen_c.write(''' printf("(%d)\\n", sum);
+ gen_c.write('''
+ }
+ return argc;
}
- };
-
- EmscriptenEnsurer emscriptenEnsurer;
''')
gen_c.close()