aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/analyzer.js55
-rw-r--r--src/compiler.js2
-rw-r--r--src/dlmalloc.c5
-rw-r--r--src/library.js20
-rw-r--r--src/parseTools.js1
-rw-r--r--src/runtime.js3
-rw-r--r--system/include/net/arpa/inet.h22
-rw-r--r--system/include/net/netdb.h13
-rw-r--r--system/include/net/netinet/in.h19
-rw-r--r--system/include/sys/socket.h13
-rw-r--r--tests/runner.py37
-rw-r--r--tests/test-fix-closure.js37
-rwxr-xr-xtools/bindings_generator.py49
-rwxr-xr-xtools/fix_closure.py88
-rw-r--r--tools/js-optimizer.js101
-rw-r--r--tools/shared.py4
16 files changed, 404 insertions, 65 deletions
diff --git a/src/analyzer.js b/src/analyzer.js
index 4aac53a8..6fd7ef66 100644
--- a/src/analyzer.js
+++ b/src/analyzer.js
@@ -788,26 +788,49 @@ function analyzer(data, sidePass) {
delete item.allocatedSize;
}
func.initialStack = index;
- // We need to note if stack allocations other than initial allocs can happen here
- // (for example, via alloca). If so, we need to rewind the stack when we leave.
func.otherStackAllocations = false;
- var finishedInitial = false;
- for (var i = 0; i < lines.length; i++) {
- var item = lines[i].value;
- if (!item || item.intertype != 'alloca') {
- finishedInitial = true;
- continue;
+ while (func.initialStack == 0) { // one-time loop with possible abort in the middle
+ // If there is no obvious need for stack management, perhaps we don't need it
+ // (we try to optimize that way with SKIP_STACK_IN_SMALL). However,
+ // we need to note if stack allocations other than initial allocs can happen here
+ // If so, we need to rewind the stack when we leave.
+
+ // By-value params are causes of additional allocas (although we could in theory make them normal allocas too)
+ func.params.forEach(function(param) {
+ if (param.byVal) {
+ func.otherStackAllocations = true;
+ }
+ });
+ if (func.otherStackAllocations) break;
+
+ // Allocas
+ var finishedInitial = false;
+ for (var i = 0; i < lines.length; i++) {
+ var item = lines[i].value;
+ if (!item || item.intertype != 'alloca') {
+ finishedInitial = true;
+ continue;
+ }
+ if (item.intertype == 'alloca' && finishedInitial) {
+ func.otherStackAllocations = true;
+ break;
+ }
}
- if (item.intertype == 'alloca' && finishedInitial) {
- func.otherStackAllocations = true;
+ if (func.otherStackAllocations) break;
+
+ // Varargs
+ for (var i = 0; i < lines.length; i++) {
+ var item = lines[i];
+ if (item.value) item = item.value;
+ if (item.intertype == 'call' && isVarArgsFunctionType(item.type)) {
+ func.otherStackAllocations = true;
+ break;
+ }
}
+ if (func.otherStackAllocations) break;
+
+ break;
}
- // by-value params are also causes of additional allocas (although we could in theory make them normal allocas too)
- func.params.forEach(function(param) {
- if (param.byVal) {
- func.otherStackAllocations = true;
- }
- });
});
this.forwardItem(data, 'Relooper');
}
diff --git a/src/compiler.js b/src/compiler.js
index 655fd9a6..bf9d9c54 100644
--- a/src/compiler.js
+++ b/src/compiler.js
@@ -8,7 +8,7 @@ try {
} catch(e) {}
-// The environment setup code appears here, in shell.js, and in tests/hello_world.js because it can't be shared. Keep them in sync!
+// The environment setup code appears here, in shell.js, in js_optimizer.js and in tests/hello_world.js because it can't be shared. Keep them in sync!
// *** Environment setup code ***
var arguments_ = [];
diff --git a/src/dlmalloc.c b/src/dlmalloc.c
index aa37dc0d..6185b1c7 100644
--- a/src/dlmalloc.c
+++ b/src/dlmalloc.c
@@ -588,7 +588,10 @@ MAX_RELEASE_CHECK_RATE default: 4095 unless not HAVE_MMAP
#define INSECURE 0
#endif /* INSECURE */
#ifndef HAVE_MMAP
-#define HAVE_MMAP 1
+/* XXX Emscripten
+ * mmap uses malloc, so malloc can't use mmap
+ */
+#define HAVE_MMAP 0
#endif /* HAVE_MMAP */
#ifndef MMAP_CLEARS
#define MMAP_CLEARS 1
diff --git a/src/library.js b/src/library.js
index 426f7035..3ede01af 100644
--- a/src/library.js
+++ b/src/library.js
@@ -3177,10 +3177,13 @@ LibraryManager.library = {
mmap__deps: ['$FS'],
mmap: function(start, num, prot, flags, stream, offset) {
- // FIXME: Leaky and non-share
- //if (stream == -1) { // XXX We should handle -1 here, but this code leads to an infinite loop
- // return allocate(num, 'i8', ALLOC_NORMAL);
- //}
+ /* FIXME: Since mmap is normally implemented at the kernel level,
+ * this implementation simply uses malloc underneath the call to
+ * mmap.
+ */
+ if (stream == -1) {
+ return allocate(num, 'i8', ALLOC_NORMAL);
+ }
var info = FS.streams[stream];
if (!info) return -1;
return allocate(info.object.contents.slice(offset, offset+num),
@@ -3189,7 +3192,6 @@ LibraryManager.library = {
__01mmap64_: 'mmap',
munmap: function(start, num) {
- // FIXME: Not really correct at all.
_free(start);
},
@@ -3200,7 +3202,13 @@ LibraryManager.library = {
// ==========================================================================
malloc: function(bytes) {
- return Runtime.staticAlloc(bytes || 1); // accept 0 as an input because libc implementations tend to
+ /* Over-allocate to make sure it is byte-aligned by 8.
+ * This will leak memory, but this is only the dummy
+ * implementation (replaced by dlmalloc normally) so
+ * not an issue.
+ */
+ ptr = Runtime.staticAlloc(bytes + 8);
+ return (ptr+8) & 0xFFFFFFF8;
},
_Znwj: 'malloc',
_Znaj: 'malloc',
diff --git a/src/parseTools.js b/src/parseTools.js
index 22c2f440..18ce807e 100644
--- a/src/parseTools.js
+++ b/src/parseTools.js
@@ -779,6 +779,7 @@ function generateStructTypes(type) {
var start = index;
for (var i = 0; i < typeData.fields.length; i++) {
var type = typeData.fields[i];
+ if (!SAFE_HEAP && isPointerType(type)) type = '*'; // do not include unneeded type names without safe heap
if (Runtime.isNumberType(type) || isPointerType(type)) {
if (I64_MODE == 1 && type == 'i64') {
ret[index++] = 'i64';
diff --git a/src/runtime.js b/src/runtime.js
index 2b81258d..6439d0ed 100644
--- a/src/runtime.js
+++ b/src/runtime.js
@@ -34,7 +34,8 @@ var RuntimeGenerator = {
stackEnter: function(initial, force) {
if (initial === 0 && SKIP_STACK_IN_SMALL && !force) return '';
- var ret = 'var __stackBase__ = STACKTOP; STACKTOP += ' + initial;
+ var ret = 'var __stackBase__ = STACKTOP';
+ if (initial > 0) ret += '; STACKTOP += ' + initial;
if (USE_TYPED_ARRAYS == 2) {
assert(initial % QUANTUM_SIZE == 0);
if (ASSERTIONS) {
diff --git a/system/include/net/arpa/inet.h b/system/include/net/arpa/inet.h
index af7845fd..06d44183 100644
--- a/system/include/net/arpa/inet.h
+++ b/system/include/net/arpa/inet.h
@@ -1,9 +1,27 @@
-/* */
+
+#ifndef _NET_ARPA_INET_H
+#define _NET_ARPA_INET_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
#include <stdint.h>
+uint32_t htonl(uint32_t hostlong);
+uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
-
uint16_t ntohs(uint16_t netshort);
int inet_aton(const char *cp, struct in_addr *addr);
+char *inet_ntoa(struct in_addr in);
+
+typedef long in_addr_t;
+in_addr_t inet_addr(const char *cp);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/system/include/net/netdb.h b/system/include/net/netdb.h
index ee33be5f..4151ccb5 100644
--- a/system/include/net/netdb.h
+++ b/system/include/net/netdb.h
@@ -1,4 +1,11 @@
+#ifndef _NET_NETDB_H
+#define _NET_NETDB_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
struct addrinfo
{
int ai_flags;
@@ -25,3 +32,9 @@ struct hostent
char **h_addr_list;
};
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/system/include/net/netinet/in.h b/system/include/net/netinet/in.h
index 7ac40c03..4547696b 100644
--- a/system/include/net/netinet/in.h
+++ b/system/include/net/netinet/in.h
@@ -1,4 +1,17 @@
+#ifndef _NET_NETINET_IN_H
+#define _NET_NETINET_IN_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+ IPPROTO_IP = 0,
+#define IPPROTO_IP IPPROTO_IP
+ IPPROTO_MAX
+};
+
#define INET_ADDRSTRLEN 16
#define INADDR_ANY 0
@@ -26,3 +39,9 @@ struct sockaddr_in6 {
int sin6_scope_id;
};
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/system/include/sys/socket.h b/system/include/sys/socket.h
index 68e06509..2c23ddb7 100644
--- a/system/include/sys/socket.h
+++ b/system/include/sys/socket.h
@@ -1,4 +1,9 @@
-/* */
+#ifndef _SYS_SOCKET_H
+#define _SYS_SOCKET_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
#define SOMAXCONN 128
#define PF_INET 2
@@ -53,3 +58,9 @@ struct msghdr
int msg_flags;
};
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/tests/runner.py b/tests/runner.py
index 1093789c..a23297f6 100644
--- a/tests/runner.py
+++ b/tests/runner.py
@@ -1508,6 +1508,26 @@ if 'benchmark' not in str(sys.argv) and 'sanity' not in str(sys.argv):
'''
self.do_run(src, 'sum:9780*')
+ # We should not blow up the stack with numerous varargs
+
+ src = r'''
+ #include <stdio.h>
+ #include <stdlib.h>
+
+ void func(int i) {
+ printf("%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d,%d\n",
+ i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i, i);
+ }
+ int main() {
+ for (int i = 0; i < 1024; i++)
+ func(i);
+ printf("ok!\n");
+ return 0;
+ }
+ '''
+ Settings.TOTAL_STACK = 1024
+ self.do_run(src, 'ok!')
+
def test_array2(self):
src = '''
#include <stdio.h>
@@ -5517,16 +5537,25 @@ f.close()
output = Popen([NODE_JS, COFFEESCRIPT, VARIABLE_ELIMINATOR], stdin=PIPE, stdout=PIPE).communicate(input)[0]
self.assertIdentical(expected, output)
+ def test_fix_closure(self):
+ input = path_from_root('tests', 'test-fix-closure.js')
+ expected = path_from_root('tests', 'test-fix-closure.out.js')
+ Popen(['python', path_from_root('tools', 'fix_closure.py'), input, 'out.js']).communicate(input)
+ output = open('out.js').read()
+ assert '0,zzz_Q_39fa,0' in output
+ assert 'function(a,c)' not in output # should be uninlined, so it gets a name
+ assert run_js(input) == run_js('out.js')
+
def test_js_optimizer(self):
for input, expected, passes in [
- (open(path_from_root('tools', 'test-js-optimizer.js')).read(), open(path_from_root('tools', 'test-js-optimizer-output.js')).read(),
+ (path_from_root('tools', 'test-js-optimizer.js'), open(path_from_root('tools', 'test-js-optimizer-output.js')).read(),
['hoistMultiples', 'loopOptimizer', 'unGlobalize', 'removeAssignsToUndefined', 'simplifyExpressionsPre', 'simplifyExpressionsPost']),
- (open(path_from_root('tools', 'test-js-optimizer-t2c.js')).read(), open(path_from_root('tools', 'test-js-optimizer-t2c-output.js')).read(),
+ (path_from_root('tools', 'test-js-optimizer-t2c.js'), open(path_from_root('tools', 'test-js-optimizer-t2c-output.js')).read(),
['simplifyExpressionsPre', 'optimizeShiftsConservative']),
- (open(path_from_root('tools', 'test-js-optimizer-t2.js')).read(), open(path_from_root('tools', 'test-js-optimizer-t2-output.js')).read(),
+ (path_from_root('tools', 'test-js-optimizer-t2.js'), open(path_from_root('tools', 'test-js-optimizer-t2-output.js')).read(),
['simplifyExpressionsPre', 'optimizeShiftsAggressive']),
]:
- output = Popen([NODE_JS, JS_OPTIMIZER] + passes, stdin=PIPE, stdout=PIPE).communicate(input)[0]
+ output = Popen([NODE_JS, JS_OPTIMIZER, input] + passes, stdin=PIPE, stdout=PIPE).communicate()[0]
self.assertIdentical(expected, output.replace('\n\n', '\n'))
elif 'benchmark' in str(sys.argv):
diff --git a/tests/test-fix-closure.js b/tests/test-fix-closure.js
new file mode 100644
index 00000000..8ba0c69d
--- /dev/null
+++ b/tests/test-fix-closure.js
@@ -0,0 +1,37 @@
+var k=void 0,l=!0,m=null,n=!1,p=[],aa="object"===typeof process,ba="object"===typeof window,ca="function"===typeof importScripts,da=!ba&&!aa&&!ca;
+if(aa){print=function(a){process.stdout.write(a+"\n")};printErr=function(a){process.stderr.write(a+"\n")};var ea=require("fs");read=function(a){var b=ea.readFileSync(a).toString();!b&&"/"!=a[0]&&(a=__dirname.split("/").slice(0,-1).join("/")+"/src/"+a,b=ea.readFileSync(a).toString());return b};p=process.argv.slice(2)}else if(da)this.read||(read=function(a){snarf(a)}),p=this.arguments?arguments:scriptArgs;else if(ba)print=printErr=function(a){console.log(a)},read=function(a){var b=new XMLHttpRequest;
+b.open("GET",a,n);b.send(m);return b.responseText},this.arguments&&(p=arguments);else if(ca)load=importScripts;else throw"Unknown runtime environment. Where are we?";function fa(a){eval.call(m,a)}"undefined"==typeof load&&"undefined"!=typeof read&&(load=function(a){fa(read(a))});"undefined"===typeof printErr&&(printErr=function(){});"undefined"===typeof print&&(print=printErr);try{this.Module=Module}catch(ga){this.Module=Module={}}Module.arguments||(Module.arguments=p);Module.print&&(print=Module.print);
+var r={T:function(){return q},S:function(a){q=a},Y:function(a,b){b=b||4;return isNumber(a)&&isNumber(b)?Math.ceil(a/b)*b:"Math.ceil(("+a+")/"+b+")*"+b},M:function(a){return a in r.C||a in r.B},N:function(a){return"*"==a[a.length-1]},P:function(a){return isPointerType(a)?n:/^\[\d+\ x\ (.*)\]/.test(a)||/<?{ [^}]* }>?/.test(a)?l:"%"==a[0]},C:{i1:0,i8:0,i16:0,i32:0,i64:0},B:{"float":0,"double":0},aa:function(a,b){return(a|0|b|0)+4294967296*(Math.round(a/4294967296)|Math.round(b/4294967296))},W:function(a,
+b){return((a|0)&(b|0))+4294967296*(Math.round(a/4294967296)&Math.round(b/4294967296))},ea:function(a,b){return((a|0)^(b|0))+4294967296*(Math.round(a/4294967296)^Math.round(b/4294967296))},m:function(a){if(1==r.e)return 1;var b={"%i1":1,"%i8":1,"%i16":2,"%i32":4,"%i64":8,"%float":4,"%double":8}["%"+a];!b&&"*"==a[a.length-1]&&(b=r.e);return b},K:function(a){return Math.max(r.m(a),r.e)},H:function(a,b){var d={};return b?a.filter(function(a){return d[a[b]]?n:d[a[b]]=l}):a.filter(function(a){return d[a]?
+n:d[a]=l})},set:function(){for(var a="object"===typeof arguments[0]?arguments[0]:arguments,b={},d=0;d<a.length;d++)b[a[d]]=0;return b},F:function(a){a.b=0;a.f=0;var b=[],d=-1;a.p=a.l.map(function(c){var e;if(r.M(c)||r.N(c))c=e=r.m(c);else if(r.P(c))e=Types.types[c].b,c=Types.types[c].f;else throw"Unclear type in struct: "+c+", in "+a.Q+" :: "+dump(Types.types[a.Q]);c=a.ba?1:Math.min(c,r.e);a.f=Math.max(a.f,c);c=r.i(a.b,c);a.b=c+e;0<=d&&b.push(c-d);return d=c});a.b=r.i(a.b,a.f);0==b.length?a.o=a.b:
+1==r.H(b).length&&(a.o=b[0]);a.$=1!=a.o;return a.p},J:function(a,b,d){var c,e;if(b){d=d||0;c=("undefined"===typeof Types?r.da:Types.types)[b];if(!c)return m;a||(a=("undefined"===typeof Types?r:Types).ca[b.replace(/.*\./,"")]);if(!a)return m;s(c.l.length===a.length,"Number of named fields must match the type for "+b+". Perhaps due to inheritance, which is not supported yet?");e=c.p}else c={l:a.map(function(a){return a[0]})},e=r.F(c);var i={V:c.b};b?a.forEach(function(a,b){if("string"===typeof a)i[a]=
+e[b]+d;else{var j,v;for(v in a)j=v;i[j]=r.J(a[j],c.l[b],e[b])}}):a.forEach(function(a,b){i[a[1]]=e[b]});return i},R:function(a){var b=q;q+=a;q=4*Math.ceil(q/4);s(q<t+u,"Ran out of stack");return b},z:function(a){var b=w;w+=a;w=4*Math.ceil(w/4);if(w>=x){printErr("Warning: Enlarging memory arrays, this is not fast! "+[w,x]);s(w>=x);for(s(4<x);x<=w;)x=Math.ceil(2*x/A)*A;var a=B,d=new ArrayBuffer(x);B=new Int8Array(d);C=new Int16Array(d);D=new Int32Array(d);H=new Uint8Array(d);ha=new Uint16Array(d);I=
+new Uint32Array(d);J=new Float32Array(d);B.set(a)}return b},i:function(a,b){return Math.ceil(a/(b?b:4))*(b?b:4)},e:4,U:0},ia=[],L,ja=new ArrayBuffer(8),M=new Int32Array(ja),ka=new Float64Array(ja);function la(a){print(a+":\n"+Error().stack);throw"Assertion: "+a;}function s(a,b){a||la("Assertion failed: "+b)}
+function ma(a,b,d){d=d||"i8";"*"===d[d.length-1]&&(d="i32");switch(d){case "i1":B[a]=b;break;case "i8":B[a]=b;break;case "i16":C[a>>1]=b;break;case "i32":D[a>>2]=b;break;case "i64":D[a>>2]=b[0];D[a+4>>2]=b[1];break;case "float":J[a>>2]=b;break;case "double":ka[0]=b;D[a>>2]=M[0];D[a+4>>2]=M[1];break;default:la("invalid type for setValue: "+d)}}Module.setValue=ma;
+Module.getValue=function(a,b){b=b||"i8";"*"===b[b.length-1]&&(b="i32");switch(b){case "i1":return B[a];case "i8":return B[a];case "i16":return C[a>>1];case "i32":return D[a>>2];case "i64":return[I[a>>2],I[a+4>>2]];case "float":return J[a>>2];case "double":return M[0]=D[a>>2],M[1]=D[a+4>>2],ka[0];default:la("invalid type for setValue: "+b)}return m};var na=1,N=2;Module.ALLOC_NORMAL=0;Module.ALLOC_STACK=na;Module.ALLOC_STATIC=N;
+function O(a,b,d){var c,e;"number"===typeof a?(c=l,e=a):(c=n,e=a.length);for(var i="string"===typeof b?b:m,d=[oa,r.R,r.z][d===k?N:d](Math.max(e,i?1:b.length)),h=0,f;h<e;){var j=c?0:a[h];"function"===typeof j&&(j=r.Z(j));f=i||b[h];0===f?h++:(s(f,"Must know what type to store in allocate!"),"i64"==f&&(f="i32"),ma(d+h,j,f),h+=r.m(f))}return d}Module.allocate=O;Module.Pointer_stringify=function(a){for(var b="",d=0,c,e=String.fromCharCode(0);;){c=String.fromCharCode(H[a+d]);if(c==e)break;b+=c;d+=1}return b};
+Module.Array_stringify=function(a){for(var b="",d=0;d<a.length;d++)b+=String.fromCharCode(a[d]);return b};var Q,A=4096,B,H,C,ha,D,I,J,t,q,u,w,pa=Module.TOTAL_STACK||5242880,x=Module.TOTAL_MEMORY||10485760;s(!!Int32Array&&!!Float64Array&&!!(new Int32Array(1)).subarray&&!!(new Int32Array(1)).set,"Cannot fallback to non-typed array case: Code is too specialized");var R=new ArrayBuffer(x);B=new Int8Array(R);C=new Int16Array(R);D=new Int32Array(R);H=new Uint8Array(R);ha=new Uint16Array(R);I=new Uint32Array(R);
+J=new Float32Array(R);D[0]=255;s(255===H[0]&&0===H[3],"Typed arrays 2 must be run on a little-endian system");var qa=S("(null)");w=qa.length;for(var T=0;T<qa.length;T++)B[T]=qa[T];Module.HEAP=k;Module.HEAP8=B;Module.HEAP16=C;Module.HEAP32=D;Module.HEAPU8=H;Module.HEAPU16=ha;Module.HEAPU32=I;Module.HEAPF32=J;t=q=r.i(w);u=t+pa;w=Math.ceil(u/A)*A;function ra(a,b){return Array.prototype.slice.call(B.subarray(a,a+b))}Module.Array_copy=ra;function sa(a){for(var b=0;B[a+b];)b++;return b}
+Module.String_len=sa;function ta(a,b){var d=sa(a);b&&d++;var c=ra(a,d);b&&(c[d-1]=0);return c}Module.String_copy=ta;function S(a,b){for(var d=[],c=0;c<a.length;){var e=a.charCodeAt(c);255<e&&(s(n,"Character code "+e+" ("+a[c]+") at offset "+c+" not in 0x00-0xFF."),e&=255);d.push(e);c+=1}b||d.push(0);return d}Module.intArrayFromString=S;
+Module.intArrayToString=function(a){for(var b=[],d=0;d<a.length;d++){var c=a[d];255<c&&(s(n,"Character code "+c+" ("+String.fromCharCode(c)+") at offset "+d+" not in 0x00-0xFF."),c&=255);b.push(String.fromCharCode(c))}return b.join("")};function U(a,b){return 0<=a?a:32>=b?2*Math.abs(1<<b-1)+a:Math.pow(2,b)+a}function ua(a,b){if(0>=a)return a;var d=32>=b?Math.abs(1<<b-1):Math.pow(2,b-1);if(a>=d&&(32>=b||a>d))a=-2*d+a;return a}
+function va(){var a;a=Q[2]();var b=Q[2](),d=Q[4](),c=Q[4](),e=Q[D[wa>>2]](),i=Q[D[xa>>2]]();ya(za|0,(L=q,q+=24,s(q<t+u,"Ran out of stack"),D[L>>2]=a,D[L+4>>2]=b,D[L+8>>2]=d,D[L+12>>2]=c,D[L+16>>2]=e,D[L+20>>2]=i,L));a=2;b=2==(a|0)&1;a=4==(a|0)&1;ya(Aa|0,(L=q,q+=8,s(q<t+u,"Ran out of stack"),D[L>>2]=b,D[L+4>>2]=a,L));a=4;b=2==(a|0)&1;a=4==(a|0)&1;ya(Ba|0,(L=q,q+=8,s(q<t+u,"Ran out of stack"),D[L>>2]=b,D[L+4>>2]=a,L));Q[6](Ca|0);Q[8](Da|0);return 0}Module._main=va;va.X=1;
+var V=13,Ea=9,Fa=22,Ga=5,Ha=21,Ia=6;function W(a){Ja||(Ja=O([0],"i32",N));D[Ja>>2]=a}var Ja,Ka=0,X=0,La=0,Ma=2,Y=[m],Na=l;function Oa(a,b){if("string"!==typeof a)return m;b===k&&(b="/");a&&"/"==a[0]&&(b="");for(var d=(b+"/"+a).split("/").reverse(),c=[""];d.length;){var e=d.pop();""==e||"."==e||(".."==e?1<c.length&&c.pop():c.push(e))}return 1==c.length?"/":c.join("/")}
+function Pa(a,b,d){var c={O:n,k:n,error:0,name:m,path:m,object:m,s:n,u:m,t:m},a=Oa(a);if("/"==a)c.O=l,c.k=c.s=l,c.name="/",c.path=c.u="/",c.object=c.t=Z;else if(a!==m)for(var d=d||0,a=a.slice(1).split("/"),e=Z,i=[""];a.length;){1==a.length&&e.c&&(c.s=l,c.u=1==i.length?"/":i.join("/"),c.t=e,c.name=a[0]);var h=a.shift();if(e.c)if(e.w){if(!e.a.hasOwnProperty(h)){c.error=2;break}}else{c.error=V;break}else{c.error=20;break}e=e.a[h];if(e.link&&!(b&&0==a.length)){if(40<d){c.error=40;break}c=Oa(e.link,i.join("/"));
+return Pa([c].concat(a).join("/"),b,d+1)}i.push(h);0==a.length&&(c.k=l,c.path=i.join("/"),c.object=e)}return c}
+function Qa(a,b,d,c,e){a||(a="/");"string"===typeof a&&(Ra(),a=Pa(a,k),a.k?a=a.object:(W(a.error),a=m));if(!a)throw W(V),Error("Parent path must exist.");if(!a.c)throw W(20),Error("Parent must be a folder.");if(!a.write&&!Na)throw W(V),Error("Parent folder must be writeable.");if(!b||"."==b||".."==b)throw W(2),Error("Name must not be empty.");if(a.a.hasOwnProperty(b))throw W(17),Error("Can't overwrite object.");a.a[b]={w:c===k?l:c,write:e===k?n:e,timestamp:Date.now(),L:Ma++};for(var i in d)d.hasOwnProperty(i)&&
+(a.a[b][i]=d[i]);return a.a[b]}function Sa(a,b){return Qa("/",a,{c:l,g:n,a:{}},l,b)}function $(a,b,d,c){if(!d&&!c)throw Error("A device must have at least one callback defined.");var e={g:l,input:d,d:c};e.c=n;return Qa(a,b,e,Boolean(d),Boolean(c))}function Ra(){Z||(Z={w:l,write:n,c:l,g:n,timestamp:Date.now(),L:1,a:{}})}var Ta,Z;
+function Ua(a,b,d){var c=Y[a];if(c){if(c.h){if(0>d)return W(Fa),-1;if(c.object.g){if(c.object.d){for(var e=0;e<d;e++)try{c.object.d(B[b+e])}catch(i){return W(Ga),-1}c.object.timestamp=Date.now();return e}W(Ia);return-1}e=c.position;a=Y[a];if(!a||a.object.g)W(Ea),b=-1;else if(a.h)if(a.object.c)W(Ha),b=-1;else if(0>d||0>e)W(Fa),b=-1;else{for(var h=a.object.a;h.length<e;)h.push(0);for(var f=0;f<d;f++)h[e+f]=H[b+f];a.object.timestamp=Date.now();b=f}else W(V),b=-1;-1!=b&&(c.position+=b);return b}W(V);
+return-1}W(Ea);return-1}
+function Va(a,b){function d(a){var c;"double"===a?c=(M[0]=D[b+e>>2],M[1]=D[b+e+4>>2],ka[0]):"i64"==a?(c=[D[b+e>>2],D[b+e+4>>2]],c=U(c[0],32)+U(c[1],32)*Math.pow(2,32)):(a="i32",c=D[b+e>>2]);e+=r.K(a);return Number(c)}for(var c=a,e=0,i=[],h,f;;){var j=c;h=B[c];if(0===h)break;f=B[c+1];if(37==h){var v=n,E=n,F=n,K=n;a:for(;;){switch(f){case 43:v=l;break;case 45:E=l;break;case 35:F=l;break;case 48:if(K)break a;else{K=l;break}default:break a}c++;f=B[c+1]}var G=0;if(42==f)G=d("i32"),c++,f=B[c+1];else for(;48<=
+f&&57>=f;)G=10*G+(f-48),c++,f=B[c+1];var P=n;if(46==f){var y=0,P=l;c++;f=B[c+1];if(42==f)y=d("i32"),c++;else for(;;){f=B[c+1];if(48>f||57<f)break;y=10*y+(f-48);c++}f=B[c+1]}else y=6;var o;switch(String.fromCharCode(f)){case "h":f=B[c+2];104==f?(c++,o=1):o=2;break;case "l":f=B[c+2];108==f?(c++,o=8):o=4;break;case "L":case "q":case "j":o=8;break;case "z":case "t":case "I":o=4;break;default:o=m}o&&c++;f=B[c+1];if(-1!="d,i,u,o,x,X,p".split(",").indexOf(String.fromCharCode(f))){j=100==f||105==f;o=o||4;
+h=d("i"+8*o);4>=o&&(h=(j?ua:U)(h&Math.pow(256,o)-1,8*o));var z=Math.abs(h),g,j="";if(100==f||105==f)g=ua(h,8*o).toString(10);else if(117==f)g=U(h,8*o).toString(10),h=Math.abs(h);else if(111==f)g=(F?"0":"")+z.toString(8);else if(120==f||88==f){j=F?"0x":"";if(0>h){h=-h;g=(z-1).toString(16);F=[];for(z=0;z<g.length;z++)F.push((15-parseInt(g[z],16)).toString(16));for(g=F.join("");g.length<2*o;)g="f"+g}else g=z.toString(16);88==f&&(j=j.toUpperCase(),g=g.toUpperCase())}else 112==f&&(0===z?g="(nil)":(j="0x",
+g=z.toString(16)));if(P)for(;g.length<y;)g="0"+g;for(v&&(j=0>h?"-"+j:"+"+j);j.length+g.length<G;)E?g+=" ":K?g="0"+g:j=" "+j;g=j+g;g.split("").forEach(function(a){i.push(a.charCodeAt(0))})}else if(-1!="f,F,e,E,g,G".split(",").indexOf(String.fromCharCode(f))){h=d("double");if(isNaN(h))g="nan",K=n;else if(isFinite(h)){P=n;o=Math.min(y,20);if(103==f||71==f)P=l,y=y||1,o=parseInt(h.toExponential(o).split("e")[1],10),y>o&&-4<=o?(f=(103==f?"f":"F").charCodeAt(0),y-=o+1):(f=(103==f?"e":"E").charCodeAt(0),
+y--),o=Math.min(y,20);if(101==f||69==f)g=h.toExponential(o),/[eE][-+]\d$/.test(g)&&(g=g.slice(0,-1)+"0"+g.slice(-1));else if(102==f||70==f)g=h.toFixed(o);j=g.split("e");if(P&&!F)for(;1<j[0].length&&-1!=j[0].indexOf(".")&&("0"==j[0].slice(-1)||"."==j[0].slice(-1));)j[0]=j[0].slice(0,-1);else for(F&&-1==g.indexOf(".")&&(j[0]+=".");y>o++;)j[0]+="0";g=j[0]+(1<j.length?"e"+j[1]:"");69==f&&(g=g.toUpperCase());v&&0<=h&&(g="+"+g)}else g=(0>h?"-":"")+"inf",K=n;for(;g.length<G;)g=E?g+" ":K&&("-"==g[0]||"+"==
+g[0])?g[0]+"0"+g.slice(1):(K?"0":" ")+g;97>f&&(g=g.toUpperCase());g.split("").forEach(function(a){i.push(a.charCodeAt(0))})}else if(115==f){(v=d("i8*"))?(v=ta(v),P&&v.length>y&&(v=v.slice(0,y))):v=S("(null)",l);if(!E)for(;v.length<G--;)i.push(32);i=i.concat(v);if(E)for(;v.length<G--;)i.push(32)}else if(99==f){for(E&&i.push(d("i8"));0<--G;)i.push(32);E||i.push(d("i8"))}else if(110==f)E=d("i32*"),D[E>>2]=i.length;else if(37==f)i.push(h);else for(z=j;z<c+2;z++)i.push(B[z]);c+=2}else i.push(h),c+=1}return i}
+function ya(a,b){var d=D[X>>2],c=Va(a,b),e=r.T();var i=O(c,"i8",na),c=1*c.length;0!=c&&-1==Ua(d,i,c)&&Y[d]&&(Y[d].error=l);r.S(e)}function oa(a){ptr=r.z(a+8);return ptr+8&4294967288}
+(function(a,b,d){if(!Ta){Ta=l;Ra();a||(a=function(){if(!a.j||!a.j.length){var b;"undefined"!=typeof window&&"function"==typeof window.prompt?b=window.prompt("Input: "):"function"==typeof readline&&(b=readline());b||(b="");a.j=S(b+"\n",l)}return a.j.shift()});b||(b=function(a){a===m||10===a?(b.v(b.buffer.join("")),b.buffer=[]):b.buffer.push(String.fromCharCode(a))});b.v||(b.v=print);b.buffer||(b.buffer=[]);d||(d=b);Sa("tmp",l);var c=Sa("dev",n),e=$(c,"stdin",a),i=$(c,"stdout",m,b),d=$(c,"stderr",m,
+d);$(c,"tty",a,b);Y[1]={path:"/dev/stdin",object:e,position:0,r:l,h:n,q:n,error:n,n:n,A:[]};Y[2]={path:"/dev/stdout",object:i,position:0,r:n,h:l,q:n,error:n,n:n,A:[]};Y[3]={path:"/dev/stderr",object:d,position:0,r:n,h:l,q:n,error:n,n:n,A:[]};Ka=O([1],"void*",N);X=O([2],"void*",N);La=O([3],"void*",N);Y[Ka]=Y[1];Y[X]=Y[2];Y[La]=Y[3];O([O([0,0,0,0,Ka,0,0,0,X,0,0,0,La,0,0,0],"void*",N)],"void*",N)}})();
+ia.push({I:function(){Ta&&(0<Y[2].object.d.buffer.length&&Y[2].object.d(10),0<Y[3].object.d.buffer.length&&Y[3].object.d(10))}});W(0);var Wa=O([0],"i8",N);Module.G=function(a){function b(){for(var a=0;3>a;a++)c.push(0)}var d=a.length+1,c=[O(S("/bin/this.program"),"i8",N)];b();for(var e=0;e<d-1;e+=1)c.push(O(S(a[e]),"i8",N)),b();c.push(0);c=O(c,"i32",N);return va()};var wa,xa,za,Aa,Ba,Ca,Da;wa=O([2],["i32 ()*",0,0,0,0],N);xa=O([4],["i32 ()*",0,0,0,0],N);
+za=O([42,37,100,44,37,100,44,37,100,44,37,100,44,37,100,44,37,100,42,10,0],"i8",N);Aa=O([42,37,100,44,37,100,0],"i8",N);Ba=O([44,37,100,44,37,100,42,10,0],"i8",N);Ca=O([42,104,101,108,108,111,33,42,0],"i8",N);Da=O([42,103,111,111,100,98,121,101,33,42,0],"i8",N);Q=[0,0,function(){return 26},0,function(){return 90},0,function(){return 0},0,function(a){var b=D[X>>2],a=Ua(b,a,sa(a));if(0>a)b=a;else{var d=U(10);B[Wa]=d;-1==Ua(b,Wa,1)?(b in Y&&(Y[b].error=l),b=-1):b=d;b=0>b?-1:a+1}return b},0,function(a,c){},0,
+function(a,c,d,e){J[u[u[a>>2]+72>>2]](a,c,d,e)},0,function(a,c){var d=u[a+12>>2];J[u[u[d>>2]+24>>2]](d,c)},0];
+Module.FUNCTION_TABLE=Q;function Xa(a){var a=a||Module.arguments,b=m;if(Module._main)for(b=Module.G(a);0<ia.length;){var a=ia.pop(),d=a.I;"number"===typeof d&&(d=Q[d]);d(a.D===k?m:a.D)}return b}Module.run=Xa;try{Na=n}catch(Ya){}Module.noInitialRun||Xa();
diff --git a/tools/bindings_generator.py b/tools/bindings_generator.py
index a40cf136..0dbcca8e 100755
--- a/tools/bindings_generator.py
+++ b/tools/bindings_generator.py
@@ -33,6 +33,9 @@ We generate the following:
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.
+
For example, JSON can be { "ignored": "class1,class2::func" }.
The C bindings file is basically a tiny C wrapper around the C++ code.
@@ -63,6 +66,7 @@ basename = sys.argv[1]
ignored = []
type_processor = lambda t: t
export = 0
+prevent_dfe = 0
if '--' in sys.argv:
index = sys.argv.index('--')
@@ -75,6 +79,8 @@ if '--' in sys.argv:
type_processor = eval(json['type_processor'])
if json.get('export'):
export = json['export']
+ if json.get('prevent_dfe'):
+ prevent_dfe = eval(json['prevent_dfe'])
print 'zz ignoring', ignored
@@ -163,7 +169,7 @@ for classname, clazz in classes.iteritems():
method['operator'] = ' return *self *= arg0;'
elif 'mul' in method['name']:
method['name'] = 'op_mul'
- method['operator'] = ' static %s ret = *self * arg0; return ret;' % method['returns']
+ method['operator'] = ' static %s ret; ret = *self * arg0; return ret;' % method['returns']
elif 'div' in method['name']:
method['name'] = 'op_div'
method['operator'] = ' return *self /= arg0;'
@@ -598,7 +604,7 @@ def generate_class(generating_classname, classname, clazz): # TODO: deprecate ge
if method.get('operator'):
gen_c.write(method['operator'])
else:
- gen_c.write(''' static %s ret = %s%s(%s);
+ gen_c.write(''' static %s ret; ret = %s%s(%s);
return ret;''' % (method['returns'],
callprefix, actualmname,
', '.join(justargs(args)[:i])))
@@ -754,29 +760,34 @@ for classname, clazz in classes.iteritems():
# Finish up
gen_c.write('''
+
}
+''')
-#include <stdio.h>
+if prevent_dfe:
+ gen_c.write('''
-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])
+ #include <stdio.h>
-for func in c_funcs[1:]:
- gen_c.write(''' sum += (void*)%s == seen;
-''' % func)
+ 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])
-gen_c.write(''' printf("(%d)\\n", sum);
- }
-};
+ for func in c_funcs[1:]:
+ gen_c.write(''' sum += (void*)%s == seen;
+ ''' % func)
-EmscriptenEnsurer emscriptenEnsurer;
-''')
+ gen_c.write(''' printf("(%d)\\n", sum);
+ }
+ };
+
+ EmscriptenEnsurer emscriptenEnsurer;
+ ''')
gen_c.close()
gen_js.close()
diff --git a/tools/fix_closure.py b/tools/fix_closure.py
new file mode 100755
index 00000000..ab2dacbc
--- /dev/null
+++ b/tools/fix_closure.py
@@ -0,0 +1,88 @@
+#!/usr/bin/env python
+
+'''
+With very very large projects, closure compiler can translate FUNCTION_TABLE into something like
+
+ J = [0, 0, func, 0, f(), 0, function() { ... }, 0, ..]
+
+where f() returns a new empty function, and after it is an inlined function. This inlining can be of very very large functions, in which case it can make the source unparsable by any JS engine due to "too much recursion" or "Maximum call stack size exceeded".
+
+This script uninlines those functions. Note that we can't do this using Uglify, since these scripts can't be parsed by it either!
+'''
+
+import os, sys, re
+
+infile = open(sys.argv[1], 'r')
+outfile = open(sys.argv[2], 'w')
+
+class ObjectParser:
+ def read(self, s, line):
+ '''
+ Read an element of the FUNCTION_TABLE until the end (a comma or the end of FUNCTION_TABLE), returning that location
+ '''
+ #print 'zz start parsing!', line[s-1:s+100]
+ curly = 0
+ paren = 0
+ string = 0
+ is_func = 0
+ i = s
+ while True:
+ c = line[i]
+ #print 'parsing! CPSF', c, curly, paren, string, is_func
+ if not string:
+ if c == '"' or c == "'":
+ string = 1
+ elif c == '{':
+ is_func = 1
+ curly += 1
+ elif c == '}':
+ curly -= 1
+ elif c == '(':
+ paren += 1
+ elif c == ')':
+ paren -= 1
+ elif not curly and not paren:
+ if c in [',', ']']:
+ #print 'zz done,', line[s:i], line[s:i].startswith('function(')
+ return (i, is_func and line[s:i].startswith('function('))
+ else:
+ if c == '"' or c == "'":
+ string = 0
+ i += 1
+
+line = infile.read()
+curr = 0
+while True:
+ curr = line.find('=[0,0,', curr)
+ if curr < 0: break
+ # a suspect
+ #print 'zz suspect!', curr, line[curr-10:curr+10]
+ target = line[curr-1]
+ curr += 5
+ parser = ObjectParser()
+ add = []
+ while line[curr] != ']':
+ assert line[curr] == ','
+ curr += 1
+ while line[curr] in ['\n', ' ']:
+ curr += 1
+ next, is_func = parser.read(curr, line)
+ if is_func:
+ text = line[curr:next]
+ #print 'zz func!', text
+ assert text.startswith('function(')
+ ident = 'zzz_' + target + '_' + hex(curr)[2:] # these idents should be unique, but might in theory collide with the rest of the JS code! XXX
+ line = line[:curr] + ident + line[next:]
+ add += 'function ' + ident + '(' + text[len('function('):] + '\n'
+ #print 'zz after func fix:', line[curr:curr+100]
+ while line[curr] != ',' and line[curr] != ']':
+ curr += 1
+ #print 'zz exited:', line[curr:curr+100]
+ curr += 1
+ assert line[curr] == ';', 'odd char: ' + str([line[curr], line[curr-10:curr+10]])
+ curr += 1
+ line = line[:curr] + '\n' + ''.join(add) + line[curr:]
+
+outfile.write(line)
+outfile.close()
+
diff --git a/tools/js-optimizer.js b/tools/js-optimizer.js
index 7ac2195f..a77e0e09 100644
--- a/tools/js-optimizer.js
+++ b/tools/js-optimizer.js
@@ -5,26 +5,103 @@
// conjunction with closure compiler.
//==============================================================================
-var uglify = require('../tools/eliminator/node_modules/uglify-js');
-var fs = require('fs');
+// *** Environment setup code ***
+var arguments_ = [];
+
+var ENVIRONMENT_IS_NODE = typeof process === 'object';
+var ENVIRONMENT_IS_WEB = typeof window === 'object';
+var ENVIRONMENT_IS_WORKER = typeof importScripts === 'function';
+var ENVIRONMENT_IS_SHELL = !ENVIRONMENT_IS_WEB && !ENVIRONMENT_IS_NODE && !ENVIRONMENT_IS_WORKER;
+
+if (ENVIRONMENT_IS_NODE) {
+ // Expose functionality in the same simple way that the shells work
+ print = function(x) {
+ process['stdout'].write(x + '\n');
+ };
+ printErr = function(x) {
+ process['stderr'].write(x + '\n');
+ };
+
+ var nodeFS = require('fs');
+
+ read = function(filename) {
+ var ret = nodeFS['readFileSync'](filename).toString();
+ if (!ret && filename[0] != '/') {
+ filename = __dirname.split('/').slice(0, -1).join('/') + '/src/' + filename;
+ ret = nodeFS['readFileSync'](filename).toString();
+ }
+ return ret;
+ };
+
+ arguments_ = process['argv'].slice(2);
+
+} else if (ENVIRONMENT_IS_SHELL) {
+ // Polyfill over SpiderMonkey/V8 differences
+ if (!this['read']) {
+ read = function(f) { snarf(f) };
+ }
+
+ if (!this['arguments']) {
+ arguments_ = scriptArgs;
+ } else {
+ arguments_ = arguments;
+ }
+
+} else if (ENVIRONMENT_IS_WEB) {
+ print = printErr = function(x) {