aboutsummaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
Diffstat (limited to 'tools')
-rwxr-xr-xtools/bindings_generator.py55
-rw-r--r--tools/eliminator/asm-eliminator-test-output.js52
-rw-r--r--tools/eliminator/asm-eliminator-test.js23
-rw-r--r--tools/js-optimizer.js83
-rw-r--r--tools/shared.py15
-rw-r--r--tools/webidl_binder.py432
6 files changed, 599 insertions, 61 deletions
diff --git a/tools/bindings_generator.py b/tools/bindings_generator.py
index 5bf0996e..9bc8b929 100755
--- a/tools/bindings_generator.py
+++ b/tools/bindings_generator.py
@@ -1,6 +1,61 @@
#!/usr/bin/env python2
'''
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+XXX THIS IS DEPRECATED, see webidl_binder.py XXX
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Use CppHeaderParser to parse some C++ headers, and generate binding code for them.
Usage:
diff --git a/tools/eliminator/asm-eliminator-test-output.js b/tools/eliminator/asm-eliminator-test-output.js
index 9caf99d0..1a5dca55 100644
--- a/tools/eliminator/asm-eliminator-test-output.js
+++ b/tools/eliminator/asm-eliminator-test-output.js
@@ -147,27 +147,25 @@ function looop3() {
}
}
function looop4() {
- var i = 0, helper = 0;
+ var i = 0, i$looptemp = 0;
while (1) {
do_it();
- helper = i + 1 | 0;
- f(i, helper);
- if (condition()) {
- i = helper;
- } else {
+ i$looptemp = i;
+ i = i + 1 | 0;
+ f(i$looptemp, i);
+ if (!condition()) {
break;
}
}
}
function looop4b() {
- var i = 0, helper = 0;
+ var i = 0, i$looptemp = 0;
while (1) {
do_it();
- helper = i + 1 | 0;
- g(helper);
- if (condition(i)) {
- i = helper;
- } else {
+ i$looptemp = i;
+ i = i + 1 | 0;
+ g(i);
+ if (!condition(i$looptemp)) {
break;
}
}
@@ -251,24 +249,22 @@ function multiloop($n_0, $35) {
function multiloop2($n_0, $35) {
$n_0 = $n_0 | 0;
$35 = $35 | 0;
- var $p_0 = 0, $39 = 0, $41 = 0, $46 = 0;
+ var $p_0 = 0, $41 = 0, $p_0$looptemp = 0;
$n_0 = $35;
$p_0 = (HEAP32[$15 >> 2] | 0) + ($35 << 1) | 0;
while (1) {
- $39 = $p_0 - 2 | 0;
- $41 = HEAPU16[$39 >> 1] | 0;
+ $p_0$looptemp = $p_0;
+ $p_0 = $p_0 - 2 | 0;
+ $41 = HEAPU16[$p_0 >> 1] | 0;
if ($41 >>> 0 < $2 >>> 0) {
$_off0 = 0;
} else {
$_off0 = $41 - $2 & 65535;
}
- HEAP16[$39 >> 1] = $p_0;
- $46 = $n_0 - 1 | 0;
- if (($46 | 0) == 0) {
+ HEAP16[$p_0 >> 1] = $p_0$looptemp;
+ $n_0 = $n_0 - 1 | 0;
+ if (($n_0 | 0) == 0) {
break;
- } else {
- $n_0 = $46;
- $p_0 = $39;
}
}
}
@@ -901,4 +897,18 @@ function elimOneLoopVar4() {
}
}
}
+function elimOneLoopVarStillUsed() {
+ var $call10 = Math_fround(0), $curri$012 = 0, $j$010 = 0, $retval$0 = 0, $j$010$looptemp = 0;
+ while (1) {
+ $j$010$looptemp = $j$010;
+ $j$010 = $j$010 + 1 | 0;
+ if ((($curri$012 | 0) % ($j$010$looptemp | 0) & -1 | 0) == 0) {
+ break;
+ }
+ if (!(Math_fround($j$010 | 0) < $call10)) {
+ break;
+ }
+ }
+ return $retval$0 | 0;
+}
diff --git a/tools/eliminator/asm-eliminator-test.js b/tools/eliminator/asm-eliminator-test.js
index a3de3d9d..d5bbd8d5 100644
--- a/tools/eliminator/asm-eliminator-test.js
+++ b/tools/eliminator/asm-eliminator-test.js
@@ -1131,5 +1131,26 @@ function elimOneLoopVar4() {
}
}
}
-// EMSCRIPTEN_GENERATED_FUNCTIONS: ["asm", "__Z11printResultPiS_j", "_segment_holding", "__ZN5identC2EiPKcPci", "_vec2Length", "exc", "label", "confuusion", "tempDouble", "_org_apache_harmony_luni_util_NumberConverter_freeFormat__", "__ZN23b2EdgeAndPolygonContact8EvaluateEP10b2ManifoldRK11b2TransformS4_", "_java_nio_charset_Charset_forNameInternal___java_lang_String", "looop2", "looop3", "looop4", "looop5", "looop6", "looop7", "looop8", "multiloop", "multiloop2", "tempDouble2", "watIf", "select2", "binary", "cute", "selfAssign", "elimOneLoopVar", "elimOneLoopVar2", "elimOneLoopVar3", "elimOneLoopVar4"]
+function elimOneLoopVarStillUsed() {
+ var $0 = 0, $1 = 0, $arg$0 = 0, $arrayidx = 0, $call10 = Math_fround(0), $cmp = 0, $cmp11 = 0, $cmp119 = 0, $cmp12 = 0, $cmp7 = 0, $conv = 0, $conv8 = Math_fround(0), $conv9 = Math_fround(0), $curri$012 = 0, $inc = 0, $inc14$primes$0 = 0, $inc16 = 0, $j$010 = 0, $ok$0 = 0;
+ var $primes$011 = 0, $rem = 0, $retval$0 = 0, $sub = 0, $vararg_buffer1 = 0, label = 0, sp = 0;
+ while (1) {
+ $rem = ($curri$012 | 0) % ($j$010 | 0) & -1;
+ $cmp12 = ($rem | 0) == 0;
+ $inc = $j$010 + 1 | 0;
+ if ($cmp12) {
+ $ok$0 = 0;
+ break;
+ }
+ $conv8 = Math_fround($inc | 0);
+ $cmp11 = $conv8 < $call10;
+ if ($cmp11) {
+ $j$010 = $inc;
+ } else {
+ break;
+ }
+ }
+ return $retval$0 | 0;
+}
+// EMSCRIPTEN_GENERATED_FUNCTIONS: ["asm", "__Z11printResultPiS_j", "_segment_holding", "__ZN5identC2EiPKcPci", "_vec2Length", "exc", "label", "confuusion", "tempDouble", "_org_apache_harmony_luni_util_NumberConverter_freeFormat__", "__ZN23b2EdgeAndPolygonContact8EvaluateEP10b2ManifoldRK11b2TransformS4_", "_java_nio_charset_Charset_forNameInternal___java_lang_String", "looop2", "looop3", "looop4", "looop5", "looop6", "looop7", "looop8", "multiloop", "multiloop2", "tempDouble2", "watIf", "select2", "binary", "cute", "selfAssign", "elimOneLoopVar", "elimOneLoopVar2", "elimOneLoopVar3", "elimOneLoopVar4", "elimOneLoopVarStillUsed"]
diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index c4585b84..db85965d 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -3519,12 +3519,15 @@ function eliminate(ast, memSafe) {
seenUses[name]++;
}
} else if (type === 'while') {
+ if (!asm) return;
// try to remove loop helper variables specifically
var stats = node[2][1];
var last = stats[stats.length-1];
if (last && last[0] === 'if' && last[2][0] === 'block' && last[3] && last[3][0] === 'block') {
var ifTrue = last[2];
var ifFalse = last[3];
+ clearEmptyNodes(ifTrue[1]);
+ clearEmptyNodes(ifFalse[1]);
var flip = false;
if (ifFalse[1][0] && ifFalse[1][0][0] === 'break') { // canonicalize break in the if
var temp = ifFalse;
@@ -3589,17 +3592,25 @@ function eliminate(ast, memSafe) {
}
}
if (found < 0) return;
+ // if a loop variable is used after we assigned to the helper, we must save its value and use that.
+ // (note that this can happen due to elimination, if we eliminate an expression containing the
+ // loop var far down, past the assignment!)
+ var temp = looper + '$looptemp';
var looperUsed = false;
- for (var i = found+1; i < stats.length && !looperUsed; i++) {
+ assert(!(temp in asmData.vars));
+ for (var i = found+1; i < stats.length; i++) {
var curr = i < stats.length-1 ? stats[i] : last[1]; // on the last line, just look in the condition
traverse(curr, function(node, type) {
if (type === 'name' && node[1] === looper) {
+ node[1] = temp;
looperUsed = true;
- return true;
}
});
}
- if (looperUsed) return;
+ if (looperUsed) {
+ asmData.vars[temp] = asmData.vars[looper];
+ stats.splice(found, 0, ['stat', ['assign', true, ['name', temp], ['name', looper]]]);
+ }
}
for (var l = 0; l < helpers.length; l++) {
for (var k = 0; k < helpers.length; k++) {
@@ -4915,36 +4926,44 @@ function safeHeap(ast) {
}
}
} else if (type === 'sub') {
- var heap = node[1][1];
- if (heap[0] !== 'H') return;
- var ptr = fixPtr(node[2], heap);
- // SAFE_HEAP_LOAD(ptr, bytes, isFloat)
- switch (heap) {
- case 'HEAP8': {
- return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 1], ['num', '0'], ['num', '0']]], ASM_INT);
- }
- case 'HEAPU8': {
- return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 1], ['num', '0'], ['num', '1']]], ASM_INT);
- }
- case 'HEAP16': {
- return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 2], ['num', '0'], ['num', '0']]], ASM_INT);
- }
- case 'HEAPU16': {
- return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 2], ['num', '0'], ['num', '1']]], ASM_INT);
- }
- case 'HEAP32': {
- return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 4], ['num', '0'], ['num', '0']]], ASM_INT);
- }
- case 'HEAPU32': {
- return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 4], ['num', '0'], ['num', '1']]], ASM_INT);
- }
- case 'HEAPF32': {
- return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 4], ['num', '1'], ['num', '0']]], ASM_DOUBLE);
- }
- case 'HEAPF64': {
- return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 8], ['num', '1'], ['num', '0']]], ASM_DOUBLE);
+ var target = node[1][1];
+ if (target[0] === 'H') {
+ // heap access
+ var heap = target;
+ var ptr = fixPtr(node[2], heap);
+ // SAFE_HEAP_LOAD(ptr, bytes, isFloat)
+ switch (heap) {
+ case 'HEAP8': {
+ return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 1], ['num', '0'], ['num', '0']]], ASM_INT);
+ }
+ case 'HEAPU8': {
+ return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 1], ['num', '0'], ['num', '1']]], ASM_INT);
+ }
+ case 'HEAP16': {
+ return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 2], ['num', '0'], ['num', '0']]], ASM_INT);
+ }
+ case 'HEAPU16': {
+ return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 2], ['num', '0'], ['num', '1']]], ASM_INT);
+ }
+ case 'HEAP32': {
+ return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 4], ['num', '0'], ['num', '0']]], ASM_INT);
+ }
+ case 'HEAPU32': {
+ return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 4], ['num', '0'], ['num', '1']]], ASM_INT);
+ }
+ case 'HEAPF32': {
+ return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 4], ['num', '1'], ['num', '0']]], ASM_DOUBLE);
+ }
+ case 'HEAPF64': {
+ return makeAsmCoercion(['call', ['name', 'SAFE_HEAP_LOAD'], [ptr, ['num', 8], ['num', '1'], ['num', '0']]], ASM_DOUBLE);
+ }
+ default: throw 'bad heap ' + heap;
}
- default: throw 'bad heap ' + heap;
+ } else {
+ assert(target[0] == 'F');
+ // function table indexing mask
+ assert(node[2][0] === 'binary' && node[2][1] === '&');
+ node[2][2] = makeAsmCoercion(['call', ['name', 'SAFE_FT_MASK'], [makeAsmCoercion(node[2][2], ASM_INT), makeAsmCoercion(node[2][3], ASM_INT)]], ASM_INT);
}
}
});
diff --git a/tools/shared.py b/tools/shared.py
index e912a700..82bdd98b 100644
--- a/tools/shared.py
+++ b/tools/shared.py
@@ -746,13 +746,14 @@ else:
# Engine tweaks
try:
- new_spidermonkey = listify(SPIDERMONKEY_ENGINE)
- if 'gcparam' not in str(new_spidermonkey):
- new_spidermonkey += ['-e', "gcparam('maxBytes', 1024*1024*1024);"] # Our very large files need lots of gc heap
- if '-w' not in str(new_spidermonkey):
- new_spidermonkey += ['-w']
- JS_ENGINES = map(lambda x: new_spidermonkey if x == SPIDERMONKEY_ENGINE else x, JS_ENGINES)
- SPIDERMONKEY_ENGINE = new_spidermonkey
+ if SPIDERMONKEY_ENGINE:
+ new_spidermonkey = listify(SPIDERMONKEY_ENGINE)
+ if 'gcparam' not in str(new_spidermonkey):
+ new_spidermonkey += ['-e', "gcparam('maxBytes', 1024*1024*1024);"] # Our very large files need lots of gc heap
+ if '-w' not in str(new_spidermonkey):
+ new_spidermonkey += ['-w']
+ JS_ENGINES = map(lambda x: new_spidermonkey if x == SPIDERMONKEY_ENGINE else x, JS_ENGINES)
+ SPIDERMONKEY_ENGINE = new_spidermonkey
except NameError:
pass
diff --git a/tools/webidl_binder.py b/tools/webidl_binder.py
new file mode 100644
index 00000000..0507cc78
--- /dev/null
+++ b/tools/webidl_binder.py
@@ -0,0 +1,432 @@
+
+'''
+WebIDL binder
+
+https://github.com/kripken/emscripten/wiki/WebIDL-Binder
+'''
+
+import os, sys
+
+import shared
+
+sys.path.append(shared.path_from_root('third_party'))
+sys.path.append(shared.path_from_root('third_party', 'ply'))
+
+import WebIDL
+
+class Dummy:
+ def __init__(self, init):
+ for k, v in init.iteritems():
+ self.__dict__[k] = v
+
+ def getExtendedAttribute(self, name):
+ return None
+
+input_file = sys.argv[1]
+output_base = sys.argv[2]
+
+shared.try_delete(output_base + '.cpp')
+shared.try_delete(output_base + '.js')
+
+p = WebIDL.Parser()
+p.parse(open(input_file).read())
+data = p.finish()
+
+interfaces = {}
+implements = {}
+
+for thing in data:
+ if isinstance(thing, WebIDL.IDLInterface):
+ interfaces[thing.identifier.name] = thing
+ elif isinstance(thing, WebIDL.IDLImplementsStatement):
+ implements.setdefault(thing.implementor.identifier.name, []).append(thing.implementee.identifier.name)
+
+#print interfaces
+#print implements
+
+pre_c = []
+mid_c = []
+mid_js = []
+
+pre_c += [r'''
+#include <emscripten.h>
+''']
+
+mid_c += [r'''
+extern "C" {
+''']
+
+def emit_constructor(name):
+ global mid_js
+ mid_js += [r'''%s.prototype = %s;
+%s.prototype.constructor = %s;
+%s.prototype.__class__ = %s;
+%s.__cache__ = {};
+Module['%s'] = %s;
+''' % (name, 'Object.create(%s.prototype)' % (implements[name][0] if implements.get(name) else 'WrapperObject'), name, name, name, name, name, name, name)]
+
+
+mid_js += ['''
+// Bindings utilities
+
+function WrapperObject() {
+}
+''']
+
+emit_constructor('WrapperObject')
+
+mid_js += ['''
+function getCache(__class__) {
+ return (__class__ || WrapperObject).__cache__;
+}
+Module['getCache'] = getCache;
+
+function wrapPointer(ptr, __class__) {
+ var cache = getCache(__class__);
+ var ret = cache[ptr];
+ if (ret) return ret;
+ ret = Object.create((__class__ || WrapperObject).prototype);
+ ret.ptr = ptr;
+ return cache[ptr] = ret;
+}
+Module['wrapPointer'] = wrapPointer;
+
+function castObject(obj, __class__) {
+ return wrapPointer(obj.ptr, __class__);
+}
+Module['castObject'] = castObject;
+
+Module['NULL'] = wrapPointer(0);
+
+function destroy(obj) {
+ if (!obj['__destroy__']) throw 'Error: Cannot destroy object. (Did you create it yourself?)';
+ obj['__destroy__']();
+ // Remove from cache, so the object can be GC'd and refs added onto it released
+ delete getCache(obj.__class__)[obj.ptr];
+}
+Module['destroy'] = destroy;
+
+function compare(obj1, obj2) {
+ return obj1.ptr === obj2.ptr;
+}
+Module['compare'] = compare;
+
+function getPointer(obj) {
+ return obj.ptr;
+}
+Module['getPointer'] = getPointer;
+
+function getClass(obj) {
+ return obj.__class__;
+}
+Module['getClass'] = getClass;
+
+// Converts a value into a C-style string.
+function ensureString(value) {
+ if (typeof value == 'string') return allocate(intArrayFromString(value), 'i8', ALLOC_STACK);
+ return value;
+}
+
+''']
+
+C_FLOATS = ['float', 'double']
+
+def type_to_c(t, non_pointing=False):
+ #print 'to c ', t
+ t = t.replace(' (Wrapper)', '')
+ if t == 'Long':
+ return 'int'
+ elif t == 'Short':
+ return 'short'
+ elif t == 'Void':
+ return 'void'
+ elif t == 'String':
+ return 'char*'
+ elif t == 'Float':
+ return 'float'
+ elif t == 'Double':
+ return 'double'
+ elif t == 'Boolean':
+ return 'bool'
+ elif t in interfaces:
+ return (interfaces[t].getExtendedAttribute('Prefix') or [''])[0] + t + ('' if non_pointing else '*')
+ else:
+ return t
+
+def take_addr_if_nonpointer(m):
+ if m.getExtendedAttribute('Ref') or m.getExtendedAttribute('Value'):
+ return '&'
+ return ''
+
+def deref_if_nonpointer(m):
+ if m.getExtendedAttribute('Ref') or m.getExtendedAttribute('Value'):
+ return '*'
+ return ''
+
+def type_to_cdec(raw):
+ name = ret = type_to_c(raw.type.name, non_pointing=True)
+ if raw.getExtendedAttribute('Const'): ret = 'const ' + ret
+ if name not in interfaces: return ret
+ if raw.getExtendedAttribute('Ref'):
+ return ret + '&'
+ if raw.getExtendedAttribute('Value'):
+ return ret
+ return ret + '*'
+
+def render_function(class_name, func_name, sigs, return_type, non_pointer, copy, operator, constructor, func_scope, call_content=None, const=False):
+ global mid_c, mid_js, js_impl_methods
+
+ #print 'renderfunc', class_name, func_name, sigs, return_type, constructor
+
+ bindings_name = class_name + '_' + func_name
+ min_args = min(sigs.keys())
+ max_args = max(sigs.keys())
+
+ c_names = {}
+
+ # JS
+
+ cache = ('getCache(%s)[this.ptr] = this;' % class_name) if constructor else ''
+ call_prefix = '' if not constructor else 'this.ptr = '
+ call_postfix = ''
+ if return_type != 'Void' and not constructor: call_prefix = 'return '
+ if not constructor:
+ if return_type in interfaces:
+ call_prefix += 'wrapPointer('
+ call_postfix += ', ' + return_type + ')'
+
+ args = ['arg%d' % i for i in range(max_args)]
+ if not constructor:
+ body = ' var self = this.ptr;\n'
+ pre_arg = ['self']
+ else:
+ body = ''
+ pre_arg = []
+
+ for i in range(max_args):
+ # note: null has typeof object, but is ok to leave as is, since we are calling into asm code where null|0 = 0
+ body += " if (arg%d && typeof arg%d === 'object') arg%d = arg%d.ptr;\n" % (i, i, i, i)
+ body += " else arg%d = ensureString(arg%d);\n" % (i, i)
+
+ for i in range(min_args, max_args):
+ c_names[i] = 'emscripten_bind_%s_%d' % (bindings_name, i)
+ body += ' if (arg%d === undefined) { %s%s(%s)%s%s }\n' % (i, call_prefix, '_' + c_names[i], ', '.join(pre_arg + args[:i]), call_postfix, '' if 'return ' in call_prefix else '; ' + (cache or ' ') + 'return')
+ c_names[max_args] = 'emscripten_bind_%s_%d' % (bindings_name, max_args)
+ body += ' %s%s(%s)%s;\n' % (call_prefix, '_' + c_names[max_args], ', '.join(pre_arg + args), call_postfix)
+ if cache:
+ body += ' ' + cache + '\n'
+ mid_js += [r'''function%s(%s) {
+%s
+}''' % ((' ' + func_name) if constructor else '', ', '.join(args), body[:-1])]
+
+ # C
+
+ for i in range(min_args, max_args+1):
+ raw = sigs.get(i)
+ if raw is None: continue
+ sig = [arg.type.name for arg in raw]
+
+ c_arg_types = map(type_to_c, sig)
+
+ normal_args = ', '.join(['%s arg%d' % (c_arg_types[j], j) for j in range(i)])
+ if constructor:
+ full_args = normal_args
+ else:
+ full_args = type_to_c(class_name, non_pointing=True) + '* self' + ('' if not normal_args else ', ' + normal_args)
+ call_args = ', '.join(['%sarg%d' % ('*' if raw[j].getExtendedAttribute('Ref') else '', j) for j in range(i)])
+ if constructor:
+ call = 'new ' + type_to_c(class_name, non_pointing=True)
+ call += '(' + call_args + ')'
+ elif call_content is not None:
+ call = call_content
+ else:
+ call = 'self->' + func_name
+ call += '(' + call_args + ')'
+
+ if operator:
+ assert '=' in operator, 'can only do += *= etc. for now, all with "="'
+ cast_self = 'self'
+ if class_name != func_scope:
+ # this function comes from an ancestor class; for operators, we must cast it
+ cast_self = 'dynamic_cast<' + type_to_c(func_scope) + '>(' + cast_self + ')'
+ call = '(*%s %s %sarg0)' % (cast_self, operator, '*' if sig[0] in interfaces else '')
+
+ pre = ''
+
+ basic_return = 'return ' if constructor or return_type is not 'Void' else ''
+ return_prefix = basic_return
+ return_postfix = ''
+ if non_pointer:
+ return_prefix += '&';
+ if copy:
+ pre += ' static %s temp;\n' % type_to_c(return_type, non_pointing=True)
+ return_prefix += '(temp = '
+ return_postfix += ', &temp)'
+
+ c_return_type = type_to_c(return_type)
+ mid_c += [r'''
+%s%s EMSCRIPTEN_KEEPALIVE %s(%s) {
+%s %s%s%s;
+}
+''' % ('const ' if const else '', type_to_c(class_name) if constructor else c_return_type, c_names[i], full_args, pre, return_prefix, call, return_postfix)]
+
+ if not constructor:
+ if i == max_args:
+ dec_args = ', '.join(map(lambda j: type_to_cdec(raw[j]) + ' arg' + str(j), range(i)))
+ js_call_args = ', '.join(['%sarg%d' % (('(int)' if sig[j] in interfaces else '') + ('&' if raw[j].getExtendedAttribute('Ref') or raw[j].getExtendedAttribute('Value') else ''), j) for j in range(i)])
+
+ js_impl_methods += [r''' %s %s(%s) {
+ %sEM_ASM_%s({
+ var self = Module['getCache'](Module['%s'])[$0];
+ if (!self.hasOwnProperty('%s')) throw 'a JSImplementation must implement all functions, you forgot %s::%s.';
+ %sself.%s(%s)%s;
+ }, (int)this%s);
+ }''' % (c_return_type, func_name, dec_args,
+ basic_return, 'INT' if c_return_type not in C_FLOATS else 'DOUBLE',
+ class_name,
+ func_name, class_name, func_name,
+ return_prefix,
+ func_name,
+ ','.join(['$%d' % i for i in range(1, max_args)]),
+ return_postfix,
+ (', ' if js_call_args else '') + js_call_args)]
+
+
+for name, interface in interfaces.iteritems():
+ js_impl = interface.getExtendedAttribute('JSImplementation')
+ if not js_impl: continue
+ implements[name] = [js_impl[0]]
+
+names = interfaces.keys()
+names.sort(lambda x, y: 1 if implements.get(x) and implements[x][0] == y else (-1 if implements.get(y) and implements[y][0] == x else 0))
+
+for name in names:
+ interface = interfaces[name]
+
+ mid_js += ['\n// ' + name + '\n']
+ mid_c += ['\n// ' + name + '\n']
+
+ global js_impl_methods
+ js_impl_methods = []
+
+ cons = interface.getExtendedAttribute('Constructor')
+ if type(cons) == list: raise Exception('do not use "Constructor", instead create methods with the name of the interface')
+
+ js_impl = interface.getExtendedAttribute('JSImplementation')
+ if js_impl:
+ js_impl = js_impl[0]
+
+ # Methods
+
+ seen_constructor = False # ensure a constructor, even for abstract base classes
+ for m in interface.members:
+ if m.identifier.name == name:
+ seen_constructor = True
+ break
+ if not seen_constructor:
+ mid_js += ['function %s() { throw "cannot construct a %s, no constructor in IDL" }\n' % (name, name)]
+ emit_constructor(name)
+
+ for m in interface.members:
+ if not m.isMethod(): continue
+ constructor = m.identifier.name == name
+ if not constructor:
+ parent_constructor = False
+ temp = m.parentScope
+ while temp.parentScope:
+ if temp.identifier.name == m.identifier.name:
+ parent_constructor = True
+ temp = temp.parentScope
+ if parent_constructor:
+ continue
+ if not constructor:
+ mid_js += [r'''
+%s.prototype.%s = ''' % (name, m.identifier.name)]
+ sigs = {}
+ return_type = None
+ for ret, args in m.signatures():
+ if return_type is None:
+ return_type = ret.name
+ else:
+ assert return_type == ret.name, 'overloads must have the same return type'
+ for i in range(len(args)+1):
+ if i == len(args) or args[i].optional:
+ assert i not in sigs, 'overloading must differentiate by # of arguments (cannot have two signatures that differ by types but not by length)'
+ sigs[i] = args[:i]
+ render_function(name,
+ m.identifier.name, sigs, return_type,
+ m.getExtendedAttribute('Ref'),
+ m.getExtendedAttribute('Value'),
+ (m.getExtendedAttribute('Operator') or [None])[0],
+ constructor,
+ func_scope=m.parentScope.identifier.name)
+ mid_js += [';\n']
+ if constructor:
+ emit_constructor(name)
+
+ for m in interface.members:
+ if not m.isAttr(): continue
+ attr = m.identifier.name
+
+ get_name = 'get_' + attr
+ mid_js += [r'''
+ %s.prototype.%s= ''' % (name, get_name)]
+ render_function(name,
+ get_name, { 0: [] }, m.type.name,
+ None,
+ None,
+ None,
+ False,
+ func_scope=interface,
+ call_content=take_addr_if_nonpointer(m) + 'self->' + attr,
+ const=m.getExtendedAttribute('Const'))
+
+ set_name = 'set_' + attr
+ mid_js += [r'''
+ %s.prototype.%s= ''' % (name, set_name)]
+ render_function(name,
+ set_name, { 1: [Dummy({ 'type': m.type })] }, 'Void',
+ None,
+ None,
+ None,
+ False,
+ func_scope=interface,
+ call_content='self->' + attr + ' = ' + deref_if_nonpointer(m) + 'arg0',
+ const=m.getExtendedAttribute('Const'))
+
+ if not interface.getExtendedAttribute('NoDelete'):
+ mid_js += [r'''
+ %s.prototype.__destroy__ = ''' % name]
+ render_function(name,
+ '__destroy__', { 0: [] }, 'Void',
+ None,
+ None,
+ None,
+ False,
+ func_scope=interface,
+ call_content='delete self')
+
+ # Emit C++ class implementation that calls into JS implementation
+
+ if js_impl:
+ pre_c += [r'''
+class %s : public %s {
+public:
+%s
+};
+''' % (name, type_to_c(js_impl, non_pointing=True), '\n'.join(js_impl_methods))]
+
+mid_c += ['\n}\n\n']
+mid_js += ['\n']
+
+# Write
+
+c = open(output_base + '.cpp', 'w')
+for x in pre_c: c.write(x)
+for x in mid_c: c.write(x)
+c.close()
+
+js = open(output_base + '.js', 'w')
+for x in mid_js: js.write(x)
+js.close()
+